diff --git a/src/OpenFOAM/primitives/Vector2D/Vector2D.H b/src/OpenFOAM/primitives/Vector2D/Vector2D.H
index 9b67b6187c372da948280af0d211bb784e3b4689..23664464d3ace407d868ea7fb81d90ce7b5d620e 100644
--- a/src/OpenFOAM/primitives/Vector2D/Vector2D.H
+++ b/src/OpenFOAM/primitives/Vector2D/Vector2D.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -116,13 +116,18 @@ public:
         //- Access to the vector y component
         inline Cmpt& y();
 
-
         //- Normalise the vector by its magnitude
         inline Vector2D<Cmpt>& normalise();
 
-
         //- Perp dot product (dot product with perpendicular vector)
         inline scalar perp(const Vector2D<Cmpt>& b) const;
+
+        //- Return true if vector is within tol
+        inline bool isClose
+        (
+            const Vector2D<Cmpt>& b,
+            const scalar tol = 1e-10
+        ) const;
 };
 
 
diff --git a/src/OpenFOAM/primitives/Vector2D/Vector2DI.H b/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
index b7387191cec2c29c3f57696d76535a2708bfbaab..46599dfc4875678a651476c550bc796fb48e20a4 100644
--- a/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
+++ b/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -126,7 +126,18 @@ operator&(const Vector2D<Cmpt>& v1, const Vector2D<Cmpt>& v2)
 template<class Cmpt>
 inline scalar Vector2D<Cmpt>::perp(const Vector2D<Cmpt>& b) const
 {
-    return x()*b.y()-y()*b.x();
+    return x()*b.y() - y()*b.x();
+}
+
+
+template<class Cmpt>
+inline bool Vector2D<Cmpt>::isClose
+(
+    const Vector2D<Cmpt>& b,
+    const scalar tol
+) const
+{
+    return (mag(x() - b.x()) < tol && mag(y() - b.y()) < tol);
 }
 
 
diff --git a/src/functionObjects/field/mapFields/mapFields.H b/src/functionObjects/field/mapFields/mapFields.H
index a699b313929deb1b952209faa67d6b963ea7d471..873ab0899b15ec9d595f495fadd0d50e0b9e77fa 100644
--- a/src/functionObjects/field/mapFields/mapFields.H
+++ b/src/functionObjects/field/mapFields/mapFields.H
@@ -98,7 +98,6 @@ Usage
     \verbatim
       nearestFaceAMI
       faceAreaWeightAMI
-      partialFaceAreaWeightAMI
     \endverbatim
 
     The inherited entries are elaborated in:
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
index 96ba959fa05c99d168da3d855d66c24e2865c0b8..7f6d4ea5406d924cfad6fa1397a10d1fe24a6ea7 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
@@ -1246,7 +1246,7 @@ void Foam::AMIInterpolation::write(Ostream& os) const
 
     if (reverseTarget_)
     {
-        os.writeEntry("flipNormals", reverseTarget_);
+        os.writeEntry("reverseTarget", reverseTarget_);
     }
 
     if (lowWeightCorrection_ > 0)
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
index 7a2259066e8c4d71fecf43dd240c3bdf7999fe65..f691d6ace3c9bb430352ee0ac0f5237702a36e96 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -364,6 +364,9 @@ public:
             //- Access to the requireMatch flag
             inline bool setRequireMatch(const bool flag);
 
+            //- Return true if requireMatch and lowWeightCorrectionin active
+            inline bool mustMatchFaces() const;
+
             //- Access to the reverseTarget flag
             inline bool reverseTarget() const;
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
index 22823a23ec6d8d57f334b1731d8dba53d92506f9..1effda038534ba3eda4b9915dfbfcea305874db6 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -73,7 +73,7 @@ inline bool Foam::AMIInterpolation::distributed() const
 
 inline bool Foam::AMIInterpolation::requireMatch() const
 {
-    return requireMatch_ && lowWeightCorrection_ < 0;
+    return requireMatch_;
 }
 
 
@@ -84,6 +84,12 @@ inline bool Foam::AMIInterpolation::setRequireMatch(const bool flag)
 }
 
 
+inline bool Foam::AMIInterpolation::mustMatchFaces() const
+{
+    return requireMatch_ && !applyLowWeightCorrection();
+}
+
+
 inline bool Foam::AMIInterpolation::reverseTarget() const
 {
     return reverseTarget_;
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
index dc41c132ea60a990f89c16e796022146944c596d..7c03cbed1cf488a860169f1ee7166837c548cf08 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
@@ -55,7 +55,7 @@ void Foam::advancingFrontAMI::checkPatches() const
     }
 
 
-    if (conformal())
+    if (requireMatch_)
     {
         const scalar maxBoundsError = 0.05;
 
@@ -345,6 +345,27 @@ void Foam::advancingFrontAMI::triangulatePatch
 }
 
 
+void Foam::advancingFrontAMI::nonConformalCorrection()
+{
+    if (!requireMatch_ && distributed())
+    {
+        scalarList newTgtMagSf(std::move(tgtMagSf_));
+
+        // Assign default sizes. Override selected values with calculated
+        // values. This is to support ACMI where some of the target faces
+        // are never used (so never get sent over and hence never assigned
+        // to)
+        tgtMagSf_ = tgtPatch0().magFaceAreas();
+
+        for (const labelList& smap : this->extendedTgtMapPtr_->subMap())
+        {
+            UIndirectList<scalar>(tgtMagSf_, smap) =
+                UIndirectList<scalar>(newTgtMagSf, smap);
+        }
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::advancingFrontAMI::advancingFrontAMI
@@ -421,7 +442,8 @@ bool Foam::advancingFrontAMI::calculate
 {
     if (AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr))
     {
-        // Create a representation of the target patch that covers the source patch
+        // Create a representation of the target patch that covers the source
+        // patch
         if (distributed())
         {
             createExtendedTgtPatch();
@@ -454,10 +476,4 @@ bool Foam::advancingFrontAMI::calculate
 }
 
 
-bool Foam::advancingFrontAMI::conformal() const
-{
-    return true;
-}
-
-
 // ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.H
index 84031daa733f3e53df6d86d6bdaca30a458ee3db..07e08b68d8c683397c4d8712bf5a2ab2b7be0f43 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.H
@@ -199,6 +199,9 @@ protected:
                 List<scalar>& magSf
             ) const;
 
+            //- Correction for non-conformal interpolations, e.g. for ACMI
+            virtual void nonConformalCorrection();
+
 
 public:
 
@@ -249,10 +252,6 @@ public:
         //- Labels of faces that are not overlapped by any target faces
         //  Note: this should be empty for correct functioning
         inline const labelList& srcNonOverlap() const;
-
-        //- Flag to indicate that interpolation patches are conformal, i.e.
-        //- should fully cover each other
-        virtual bool conformal() const;
 };
 
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
index e6fbbb1816a1a9c7e970bafa02589d0082d0e4bf..395b1830f49d26b5268b3573950ff3b23c2d78b0 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
@@ -115,6 +115,9 @@ void Foam::faceAreaWeightAMI::calcAddressing
     // Reset starting seed
     label startSeedi = 0;
 
+    // Should all faces be matched?
+    const bool mustMatch = mustMatchFaces();
+
     bool continueWalk = true;
     DynamicList<label> nonOverlapFaces;
     do
@@ -154,7 +157,7 @@ void Foam::faceAreaWeightAMI::calcAddressing
             mapFlag,
             seedFaces,
             visitedFaces,
-            requireMatch_ && (lowWeightCorrection_ < 0)
+            mustMatch
             // pass in nonOverlapFaces for failed tree search?
         );
     } while (continueWalk);
@@ -675,7 +678,7 @@ bool Foam::faceAreaWeightAMI::calculate
         }
 
         // Check for badly covered faces
-        if (restartUncoveredSourceFace_) // && requireMatch_???
+        if (restartUncoveredSourceFace_) //  && mustMatchFaces())
         {
             restartUncoveredSourceFace
             (
@@ -775,7 +778,9 @@ bool Foam::faceAreaWeightAMI::calculate
     }
 
     // Convert the weights from areas to normalised values
-    normaliseWeights(conformal(), true);
+    normaliseWeights(requireMatch_, true);
+
+    nonConformalCorrection();
 
     upToDate_ = true;
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
index 818d26ebea8aec4310209e3d46261a90384a3f4d..5c3d61dbd84305daabcba4ab49a9f74c03e1f859 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
@@ -30,6 +30,8 @@ Class
 Description
     Face area weighted Arbitrary Mesh Interface (AMI) method
 
+    Searching is performed using an advancing front.
+
 SourceFiles
     faceAreaWeightAMI.C
 
@@ -61,7 +63,6 @@ private:
         const bool restartUncoveredSourceFace_;
 
 
-
 protected:
 
     // Protected Member Functions
@@ -69,15 +70,6 @@ protected:
         //- No copy assignment
         void operator=(const faceAreaWeightAMI&) = delete;
 
-        //- Initialise the geometry
-        void initGeom
-        (
-            const primitivePatch& srcPatch,
-            const primitivePatch& tgtPatch,
-            const globalIndex& globalTgtFaces,
-            labelList& extendedTgtFaceIDs
-        );
-
 
         // Marching front
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.C b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.C
new file mode 100644
index 0000000000000000000000000000000000000000..c8270c6d313992f45c7f9b35dadd22ce17b7aeb9
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.C
@@ -0,0 +1,526 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "faceAreaWeightAMI2D.H"
+#include "profiling.H"
+#include "OBJstream.H"
+#include "addToRunTimeSelectionTable.H"
+#include "triangle2D.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(faceAreaWeightAMI2D, 0);
+    addToRunTimeSelectionTable(AMIInterpolation, faceAreaWeightAMI2D, dict);
+    addToRunTimeSelectionTable
+    (
+        AMIInterpolation,
+        faceAreaWeightAMI2D,
+        component
+    );
+}
+
+// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
+
+void Foam::faceAreaWeightAMI2D::writeNoMatch
+(
+    const label srcFacei,
+    const labelList& tgtFaceCandidates,
+    const boundBox& srcFaceBb
+) const
+{
+    Info<< "NO MATCH for source face " << srcFacei << endl;
+    {
+        const auto& src = this->srcPatch();
+        const auto& tgt = this->tgtPatch(); // might be the extended patch!
+
+        OFstream os("no_match_" + Foam::name(srcFacei) + ".obj");
+
+        const pointField& srcPoints = src.points();
+        const pointField& tgtPoints = tgt.points();
+
+        label np = 0;
+
+        // Write source face
+        const face& srcF = src[srcFacei];
+        string faceStr = "f";
+        for (const label pointi : srcF)
+        {
+            const point& p = srcPoints[pointi];
+            os  << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
+            ++np;
+            faceStr += " " + Foam::name(np);
+        }
+        os << faceStr.c_str() << " " << (np - srcF.size() + 1) << nl;
+
+        // Write target faces as lines
+        for (const label tgtFacei : tgtFaceCandidates)
+        {
+            const face& tgtF = tgt[tgtFacei];
+            forAll(tgtF, pointi)
+            {
+                const point& p = tgtPoints[tgtF[pointi]];
+                os  << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
+                ++np;
+                if (pointi)
+                {
+                    os << "l " << np-1 << " " << np << nl;
+                }
+            }
+            os << "l " << (np - tgtF.size() + 1) << " " << np << nl;
+        }
+    }
+
+    {
+        OFstream os("no_match_" + Foam::name(srcFacei) + "_bb.obj");
+
+        const pointField points(srcFaceBb.points());
+        for (const point& p : points)
+        {
+            os  << "v " << p.x() << " " << p.y() << " " << p.z() << endl;
+        }
+        os  << "l 1 2" << nl;
+        os  << "l 2 4" << nl;
+        os  << "l 4 3" << nl;
+        os  << "l 3 1" << nl;
+        os  << "l 5 6" << nl;
+        os  << "l 6 8" << nl;
+        os  << "l 8 7" << nl;
+        os  << "l 7 5" << nl;
+        os  << "l 5 1" << nl;
+        os  << "l 6 2" << nl;
+        os  << "l 8 4" << nl;
+        os  << "l 7 3" << nl;
+    }
+}
+
+
+void Foam::faceAreaWeightAMI2D::storeInterArea
+(
+    const label srcFacei,
+    const label tgtFacei,
+    DynamicList<label>& srcAddr,
+    DynamicList<scalar>& srcWght,
+    DynamicList<vector>& srcCtr,
+    DynamicList<label>& tgtAddr,
+    DynamicList<scalar>& tgtWght
+) const
+{
+    addProfiling(ami, "faceAreaWeightAMI2D::calcInterArea");
+
+    // Quick reject if either face has zero area
+    if
+    (
+        (srcMagSf_[srcFacei] < ROOTVSMALL)
+     || (tgtMagSf_[tgtFacei] < ROOTVSMALL)
+    )
+    {
+        return;
+    }
+
+    const auto& srcPatch = this->srcPatch();
+    const auto& tgtPatch = this->tgtPatch();
+
+    const pointField& srcPoints = srcPatch.points();
+    const pointField& tgtPoints = tgtPatch.points();
+
+    const auto& srcTris = srcTris_[srcFacei];
+    const auto& tgtTris = tgtTris_[tgtFacei];
+
+    const auto& srcFaceNormals = srcPatch.faceNormals();
+
+    //triangle2D::debug = 1;
+
+    scalar area = 0;
+    vector centroid = Zero;
+
+    for (const auto& tris : srcTris)
+    {
+        const vector& origin = srcPoints[tris[0]];
+        const vector p10(srcPoints[tris[1]] - origin);
+        const vector p20(srcPoints[tris[2]] - origin);
+        const vector axis1(p10/(mag(p10) + ROOTVSMALL));
+        const vector axis2(srcFaceNormals[srcFacei]^axis1);
+
+        triangle2D s
+        (
+            vector2D(0, 0),
+            vector2D(axis1&p10, axis2&p10),
+            vector2D(axis1&p20, axis2&p20)
+        );
+
+        for (const auto& trit : tgtTris)
+        {
+            // Triangle t has opposite orientation wrt triangle s
+            triangle2D t
+            (
+                tgtPoints[trit[0]] - origin,
+                tgtPoints[trit[2]] - origin,
+                tgtPoints[trit[1]] - origin,
+                axis1,
+                axis2
+            );
+
+            scalar da = 0;
+            vector2D c(Zero);
+
+            if (t.snapClosePoints(s) == 3)
+            {
+                c = s.centre();
+                da = mag(s.area());
+            }
+            else
+            {
+                s.interArea(t, c, da);
+            }
+
+            area += da;
+            centroid += da*(origin + c.x()*axis1 + c.y()*axis2);
+        }
+    }
+
+    //triangle2D::debug = 0;
+
+    if (area/srcMagSf_[srcFacei] > triangle2D::relTol)
+    {
+        centroid /= area + ROOTVSMALL;
+
+        srcAddr.append(tgtFacei);
+        srcWght.append(area);
+        srcCtr.append(centroid);
+
+        tgtAddr.append(srcFacei);
+        tgtWght.append(area);
+    }
+}
+
+
+Foam::labelList Foam::faceAreaWeightAMI2D::overlappingTgtFaces
+(
+    const AABBTree<face>& tree,
+    const List<boundBox>& tgtFaceBbs,
+    const boundBox& srcFaceBb
+) const
+{
+    labelHashSet faceIds(16);
+
+    const auto& treeBb = tree.boundBoxes();
+    const auto& treeAddr = tree.addressing();
+
+    forAll(treeBb, boxi)
+    {
+        const auto& tbb = treeBb[boxi];
+
+        if (srcFaceBb.overlaps(tbb))
+        {
+            const auto& boxAddr = treeAddr[boxi];
+
+            for (const auto& tgtFacei : boxAddr)
+            {
+                if (srcFaceBb.overlaps(tgtFaceBbs[tgtFacei]))
+                {
+                    faceIds.insert(tgtFacei);
+                }
+            }
+        }
+    }
+
+    return faceIds.toc();
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::faceAreaWeightAMI2D::faceAreaWeightAMI2D
+(
+    const dictionary& dict,
+    const bool reverseTarget
+)
+:
+    advancingFrontAMI(dict, reverseTarget),
+    Cbb_(dict.getCheckOrDefault<scalar>("Cbb", 0.1, scalarMinMax::ge(SMALL)))
+{}
+
+
+Foam::faceAreaWeightAMI2D::faceAreaWeightAMI2D
+(
+    const bool requireMatch,
+    const bool reverseTarget,
+    const scalar lowWeightCorrection,
+    const faceAreaIntersect::triangulationMode triMode,
+    const bool restartUncoveredSourceFace
+)
+:
+    advancingFrontAMI
+    (
+        requireMatch,
+        reverseTarget,
+        lowWeightCorrection,
+        triMode
+    ),
+    Cbb_(0.1)
+{}
+
+
+Foam::faceAreaWeightAMI2D::faceAreaWeightAMI2D
+(
+    const faceAreaWeightAMI2D& ami
+)
+:
+    advancingFrontAMI(ami),
+    Cbb_(ami.Cbb_)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::faceAreaWeightAMI2D::calculate
+(
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
+    const autoPtr<searchableSurface>& surfPtr
+)
+{
+    if (upToDate_)
+    {
+        return false;
+    }
+
+    addProfiling(ami, "faceAreaWeightAMI2D::calculate");
+
+    advancingFrontAMI::calculate(srcPatch, tgtPatch, surfPtr);
+
+    const auto& src = this->srcPatch();
+    const auto& tgt = this->tgtPatch(); // might be the extended patch!
+
+    bool validSize = true;
+
+    // Check that patch sizes are valid
+    if (!src.size())
+    {
+        validSize = false;
+    }
+    else if (!tgt.size())
+    {
+        WarningInFunction
+            << src.size() << " source faces but no target faces" << endl;
+
+        validSize = false;
+    }
+
+    srcCentroids_.setSize(srcAddress_.size());
+
+    // Temporary storage for addressing and weights
+    List<DynamicList<label>> tgtAddr(tgt.size());
+    List<DynamicList<scalar>> tgtWght(tgt.size());
+
+    // Find an approximate face length scale
+    const scalar lf(Cbb_*Foam::sqrt(gAverage(srcMagSf_)));
+
+    // Expansion to apply to source face bounding box
+    const vector d(lf*vector::one);
+
+    if (validSize)
+    {
+        // Create the tgt tree
+        const bool equalBinSize = true;
+        const label maxLevel = 10;
+        const label minBinSize = 4;
+        AABBTree<face> tree
+        (
+            tgt,
+            tgt.points(),
+            equalBinSize,
+            maxLevel,
+            minBinSize
+        );
+
+        const auto& tgtPoints = tgt.points();
+        List<boundBox> tgtFaceBbs(tgt.size());
+        forAll(tgt, facei)
+        {
+            tgtFaceBbs[facei] = boundBox(tgtPoints, tgt[facei], false);
+        }
+
+        DynamicList<label> nonOverlapFaces;
+
+        const auto& srcPoints = src.points();
+
+        forAll(src, srcFacei)
+        {
+            const face& srcFace = src[srcFacei];
+
+            treeBoundBox srcFaceBb(srcPoints, srcFace);
+            srcFaceBb.min() -= d;
+            srcFaceBb.max() += d;
+
+            const labelList tgtFaces
+            (
+                overlappingTgtFaces(tree, tgtFaceBbs, srcFaceBb)
+            );
+
+            DynamicList<label> srcAddr(tgtFaces.size());
+            DynamicList<scalar> srcWght(tgtFaces.size());
+            DynamicList<point> srcCtr(tgtFaces.size());
+
+            for (const label tgtFacei : tgtFaces)
+            {
+                storeInterArea
+                (
+                    srcFacei,
+                    tgtFacei,
+                    srcAddr,
+                    srcWght,
+                    srcCtr,
+                    tgtAddr[tgtFacei],
+                    tgtWght[tgtFacei]
+                );
+            }
+
+            if (mustMatchFaces() && srcAddr.empty())
+            {
+                if (debug) writeNoMatch(srcFacei, tgtFaces, srcFaceBb);
+
+                // FatalErrorInFunction
+                //    << "Unable to find match for source face " << srcFace
+                //    << exit(FatalError);
+            }
+
+            srcAddress_[srcFacei].transfer(srcAddr);
+            srcWeights_[srcFacei].transfer(srcWght);
+            srcCentroids_[srcFacei].transfer(srcCtr);
+        }
+
+        srcNonOverlap_.transfer(nonOverlapFaces);
+
+        if (debug && !srcNonOverlap_.empty())
+        {
+            Pout<< "    AMI: " << srcNonOverlap_.size()
+                << " non-overlap faces identified"
+                << endl;
+        }
+    }
+
+    // Transfer data to persistent storage
+
+    forAll(tgtAddr, i)
+    {
+        tgtAddress_[i].transfer(tgtAddr[i]);
+        tgtWeights_[i].transfer(tgtWght[i]);
+    }
+
+    if (distributed())
+    {
+        const primitivePatch& srcPatch0 = this->srcPatch0();
+        const primitivePatch& tgtPatch0 = this->tgtPatch0();
+
+        // Create global indexing for each original patch
+        globalIndex globalSrcFaces(srcPatch0.size());
+        globalIndex globalTgtFaces(tgtPatch0.size());
+
+        for (labelList& addressing : srcAddress_)
+        {
+            for (label& addr : addressing)
+            {
+                addr = extendedTgtFaceIDs_[addr];
+            }
+        }
+
+        for (labelList& addressing : tgtAddress_)
+        {
+            globalSrcFaces.inplaceToGlobal(addressing);
+        }
+
+        // Send data back to originating procs. Note that contributions
+        // from different processors get added (ListOps::appendEqOp)
+
+        mapDistributeBase::distribute
+        (
+            Pstream::commsTypes::nonBlocking,
+            List<labelPair>(),
+            tgtPatch0.size(),
+            extendedTgtMapPtr_->constructMap(),
+            false,                      // has flip
+            extendedTgtMapPtr_->subMap(),
+            false,                      // has flip
+            tgtAddress_,
+            labelList(),
+            ListOps::appendEqOp<label>(),
+            flipOp()
+        );
+
+        mapDistributeBase::distribute
+        (
+            Pstream::commsTypes::nonBlocking,
+            List<labelPair>(),
+            tgtPatch0.size(),
+            extendedTgtMapPtr_->constructMap(),
+            false,
+            extendedTgtMapPtr_->subMap(),
+            false,
+            tgtWeights_,
+            scalarList(),
+            ListOps::appendEqOp<scalar>(),
+            flipOp()
+        );
+
+        // Note: using patch face areas calculated by the AMI method
+        extendedTgtMapPtr_->reverseDistribute(tgtPatch0.size(), tgtMagSf_);
+
+        // Cache maps and reset addresses
+        List<Map<label>> cMapSrc;
+        srcMapPtr_.reset
+        (
+            new mapDistribute(globalSrcFaces, tgtAddress_, cMapSrc)
+        );
+
+        List<Map<label>> cMapTgt;
+        tgtMapPtr_.reset
+        (
+            new mapDistribute(globalTgtFaces, srcAddress_, cMapTgt)
+        );
+    }
+
+    // Convert the weights from areas to normalised values
+    normaliseWeights(requireMatch_, true);
+
+    nonConformalCorrection();
+
+    upToDate_ = true;
+
+    return upToDate_;
+}
+
+
+void Foam::faceAreaWeightAMI2D::write(Ostream& os) const
+{
+    advancingFrontAMI::write(os);
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.H
similarity index 59%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
rename to src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.H
index f03f8d155dd4281eb33555170e4b0b1328f48206..3fc20bc7a9935477b466a95cbc3e2879ed0d298d 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.H
@@ -5,8 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -25,20 +24,22 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Class
-    Foam::partialFaceAreaWeightAMI
+    Foam::faceAreaWeightAMI2D
 
 Description
-    Partial face area weighted Arbitrary Mesh Interface (AMI) method
+    Face area weighted Arbitrary Mesh Interface (AMI) method that performs
+    the intersection of src and tgt face area in 2D.
 
 SourceFiles
-    partialFaceAreaWeightAMI.C
+    faceAreaWeightAMI2D.C
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef partialFaceAreaWeightAMI_H
-#define partialFaceAreaWeightAMI_H
+#ifndef faceAreaWeightAMI2D_H
+#define faceAreaWeightAMI2D_H
 
-#include "faceAreaWeightAMI.H"
+#include "advancingFrontAMI.H"
+#include "AABBTree.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -46,58 +47,80 @@ namespace Foam
 {
 
 /*---------------------------------------------------------------------------*\
-                  Class partialFaceAreaWeightAMI Declaration
+                     Class faceAreaWeightAMI2D Declaration
 \*---------------------------------------------------------------------------*/
 
-class partialFaceAreaWeightAMI
+class faceAreaWeightAMI2D
 :
-    public faceAreaWeightAMI
+    public advancingFrontAMI
 {
+protected:
 
-private:
-
-    // Private Member Functions
+    // Protected Data
 
-        //- No copy assignment
-        void operator=(const partialFaceAreaWeightAMI&) = delete;
+        //- Face bounding box factor
+        scalar Cbb_;
 
 
-protected:
-
     // Protected Member Functions
 
-        //- Set the source and target seed faces
-        virtual bool setNextFaces
+        //- No copy assignment
+        void operator=(const faceAreaWeightAMI2D&) = delete;
+
+        //- Helper function to write non-matched source faces to the set
+        //- of candidate faces
+        void writeNoMatch
         (
-            label& startSeedi,
-            label& srcFacei,
-            label& tgtFacei,
-            const bitSet& mapFlag,
-            labelList& seedFaces,
-            const DynamicList<label>& visitedFaces,
-            const bool errorOnNotFound = true
+            const label srcFacei,
+            const labelList& tgtFaceCandidates,
+            const boundBox& srcFaceBb
         ) const;
 
 
+        // Evaluation
+
+            //- Calculate and store the area of intersection between source and
+            //- target faces
+            void storeInterArea
+            (
+                const label srcFacei,
+                const label tgtFacei,
+                DynamicList<label>& srcAddr,
+                DynamicList<scalar>& srcWght,
+                DynamicList<vector>& srcCtr,
+                DynamicList<label>& tgtAddr,
+                DynamicList<scalar>& tgtWght
+            ) const;
+
+
+            //- Return the set of tgt face IDs that overlap the src face bb
+            labelList overlappingTgtFaces
+            (
+                const AABBTree<face>& tree,
+                const List<boundBox>& tgtFaceBbs,
+                const boundBox& srcFaceBb
+            ) const;
+
+
 public:
 
     //- Runtime type information
-    TypeName("partialFaceAreaWeightAMI");
+    TypeName("faceAreaWeightAMI2D");
 
 
     // Constructors
 
         //- Construct from dictionary
-        partialFaceAreaWeightAMI
+        faceAreaWeightAMI2D
         (
             const dictionary& dict,
             const bool reverseTarget = false
         );
 
         //- Construct from components
-        partialFaceAreaWeightAMI
+        faceAreaWeightAMI2D
         (
-            const bool requireMatch = false,
+            const bool requireMatch,
             const bool reverseTarget = false,
             const scalar lowWeightCorrection = -1,
             const faceAreaIntersect::triangulationMode triMode =
@@ -106,25 +129,21 @@ public:
         );
 
         //- Construct as copy
-        partialFaceAreaWeightAMI(const partialFaceAreaWeightAMI& ami);
+        faceAreaWeightAMI2D(const faceAreaWeightAMI2D& ami);
 
         //- Construct and return a clone
         virtual autoPtr<AMIInterpolation> clone() const
         {
-            return
-                autoPtr<AMIInterpolation>(new partialFaceAreaWeightAMI(*this));
+            return autoPtr<AMIInterpolation>(new faceAreaWeightAMI2D(*this));
         }
 
 
     //- Destructor
-    virtual ~partialFaceAreaWeightAMI() = default;
+    virtual ~faceAreaWeightAMI2D() = default;
 
 
     // Member Functions
 
-        //- Flag to indicate that interpolation patches are conformal
-        virtual bool conformal() const;
-
         //- Update addressing, weights and (optional) centroids
         virtual bool calculate
         (
@@ -132,6 +151,9 @@ public:
             const primitivePatch& tgtPatch,
             const autoPtr<searchableSurface>& surfPtr = nullptr
         );
+
+        //- Write
+        virtual void write(Ostream& os) const;
 };
 
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
deleted file mode 100644
index 61b4197f2b0b5354f02bfa7c3dada538d4a46555..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
+++ /dev/null
@@ -1,159 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2020 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 "partialFaceAreaWeightAMI.H"
-#include "addToRunTimeSelectionTable.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    defineTypeNameAndDebug(partialFaceAreaWeightAMI, 0);
-    addToRunTimeSelectionTable
-    (
-        AMIInterpolation,
-        partialFaceAreaWeightAMI,
-        dict
-    );
-    addToRunTimeSelectionTable
-    (
-        AMIInterpolation,
-        partialFaceAreaWeightAMI,
-        component
-    );
-}
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-bool Foam::partialFaceAreaWeightAMI::setNextFaces
-(
-    label& startSeedi,
-    label& srcFacei,
-    label& tgtFacei,
-    const bitSet& mapFlag,
-    labelList& seedFaces,
-    const DynamicList<label>& visitedFaces,
-    const bool errorOnNotFound
-) const
-{
-    return faceAreaWeightAMI::setNextFaces
-    (
-        startSeedi,
-        srcFacei,
-        tgtFacei,
-        mapFlag,
-        seedFaces,
-        visitedFaces,
-        false // no error on not found
-    );
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::partialFaceAreaWeightAMI::partialFaceAreaWeightAMI
-(
-    const dictionary& dict,
-    const bool reverseTarget
-)
-:
-    faceAreaWeightAMI(dict, reverseTarget)
-{}
-
-
-Foam::partialFaceAreaWeightAMI::partialFaceAreaWeightAMI
-(
-    const bool requireMatch,
-    const bool reverseTarget,
-    const scalar lowWeightCorrection,
-    const faceAreaIntersect::triangulationMode triMode,
-    const bool restartUncoveredSourceFace
-)
-:
-    faceAreaWeightAMI
-    (
-        requireMatch,
-        reverseTarget,
-        lowWeightCorrection,
-        triMode,
-        restartUncoveredSourceFace
-    )
-{}
-
-
-Foam::partialFaceAreaWeightAMI::partialFaceAreaWeightAMI
-(
-    const partialFaceAreaWeightAMI& ami
-)
-:
-    faceAreaWeightAMI(ami)
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-bool Foam::partialFaceAreaWeightAMI::conformal() const
-{
-    return false;
-}
-
-
-bool Foam::partialFaceAreaWeightAMI::calculate
-(
-    const primitivePatch& srcPatch,
-    const primitivePatch& tgtPatch,
-    const autoPtr<searchableSurface>& surfPtr
-)
-{
-    if (faceAreaWeightAMI::calculate(srcPatch, tgtPatch, surfPtr))
-    {
-        if (distributed())
-        {
-            scalarList newTgtMagSf(std::move(tgtMagSf_));
-
-            // Assign default sizes. Override selected values with calculated
-            // values. This is to support ACMI where some of the target faces
-            // are never used (so never get sent over and hence never assigned
-            // to)
-            tgtMagSf_ = tgtPatch0().magFaceAreas();
-
-            for (const labelList& smap : this->extendedTgtMapPtr_->subMap())
-            {
-                UIndirectList<scalar>(tgtMagSf_, smap) =
-                    UIndirectList<scalar>(newTgtMagSf, smap);
-            }
-        }
-
-        return true;
-    }
-
-    return false;
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H
index 7813526c858bccc1e212fcf79a1fb0896ab0a700..a12b5ddeaa3e91bb77afc3ec822bdc33bba50739 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -63,7 +63,6 @@ SourceFiles
 #include "cyclicAMIPolyPatch.H"
 #include "AMIPatchToPatchInterpolation.H"
 #include "polyBoundaryMesh.H"
-#include "partialFaceAreaWeightAMI.H"
 #include "PatchFunction1.H"
 #include "uniformDimensionedFields.H"
 #include "vectorList.H"
@@ -197,7 +196,7 @@ public:
             const polyBoundaryMesh& bm,
             const word& patchType,
             const transformType transform = UNKNOWN,
-            const word& defaultAMIMethod = partialFaceAreaWeightAMI::typeName
+            const word& defaultAMIMethod = faceAreaWeightAMI::typeName
         );
 
         //- Construct from dictionary
@@ -208,7 +207,7 @@ public:
             const label index,
             const polyBoundaryMesh& bm,
             const word& patchType,
-            const word& defaultAMIMethod = partialFaceAreaWeightAMI::typeName
+            const word& defaultAMIMethod = faceAreaWeightAMI::typeName
         );
 
         //- Construct as copy, resetting the boundary mesh
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
index bc63d3ed1e51d023f45a226164ce7f6e8f66a1fa..a7e652c6b8c93dfa4ebbab2831ac4755c9a08add 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
+++ b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,7 +26,6 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cyclicPeriodicAMIPolyPatch.H"
-#include "partialFaceAreaWeightAMI.H"
 #include "addToRunTimeSelectionTable.H"
 
 // For debugging
@@ -560,14 +559,16 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
         bm,
         patchType,
         transform,
-        partialFaceAreaWeightAMI::typeName
+        faceAreaWeightAMI::typeName
     ),
     periodicPatchName_(word::null),
     periodicPatchID_(-1),
     nTransforms_(0),
     nSectors_(0),
     maxIter_(36)
-{}
+{
+    AMIPtr_->setRequireMatch(false);
+}
 
 
 Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
@@ -586,14 +587,16 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
         index,
         bm,
         patchType,
-        partialFaceAreaWeightAMI::typeName
+        faceAreaWeightAMI::typeName
     ),
     periodicPatchName_(dict.lookup("periodicPatch")),
     periodicPatchID_(-1),
     nTransforms_(dict.getOrDefault<label>("nTransforms", 0)),
     nSectors_(dict.getOrDefault<label>("nSectors", 0)),
     maxIter_(dict.getOrDefault<label>("maxIter", 36))
-{}
+{
+    AMIPtr_->setRequireMatch(false);
+}
 
 
 Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
@@ -608,7 +611,9 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
     nTransforms_(pp.nTransforms_),
     nSectors_(pp.nSectors_),
     maxIter_(pp.maxIter_)
-{}
+{
+    AMIPtr_->setRequireMatch(false);
+}
 
 
 Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
@@ -627,7 +632,9 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
     nTransforms_(pp.nTransforms_),
     nSectors_(pp.nSectors_),
     maxIter_(pp.maxIter_)
-{}
+{
+    AMIPtr_->setRequireMatch(false);
+}
 
 
 Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
@@ -645,7 +652,9 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
     nTransforms_(pp.nTransforms_),
     nSectors_(pp.nSectors_),
     maxIter_(pp.maxIter_)
-{}
+{
+    AMIPtr_->setRequireMatch(false);
+}
 
 
 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
diff --git a/src/meshTools/AMIInterpolation/triangle2D/triangle2D.C b/src/meshTools/AMIInterpolation/triangle2D/triangle2D.C
new file mode 100644
index 0000000000000000000000000000000000000000..d0c92c24eda50e4dd2b3fa6f09825a7554f23fb8
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/triangle2D/triangle2D.C
@@ -0,0 +1,303 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020-2021 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 "triangle2D.H"
+#include "OFstream.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+int Foam::triangle2D::debug = 0;
+
+Foam::scalar Foam::triangle2D::relTol = 1e-8;
+
+Foam::scalar Foam::triangle2D::absTol = 1e-10;
+
+Foam::FixedList<Foam::vector2D, 7> Foam::triangle2D::work_
+(
+    vector2D::zero
+);
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::triangle2D::triangle2D
+(
+    const vector2D& a,
+    const vector2D& b,
+    const vector2D& c,
+    const bool orient
+)
+:
+    FixedList<vector2D, 3>({a, b, c}),
+    area_(area(a, b, c))
+{
+    if (orient && area_ < 0)
+    {
+        // Inverted triangle
+        triangle2D& tri = *this;
+        vector2D tmp = tri[0];
+        tri[0] = tri[2];
+        tri[2] = tmp;
+
+        area_ = mag(area_);
+    }
+}
+
+
+Foam::triangle2D::triangle2D
+(
+    const vector& a3d,
+    const vector& b3d,
+    const vector& c3d,
+    const vector& axis1,
+    const vector& axis2,
+    const bool orient
+)
+:
+    triangle2D
+    (
+        vector2D(axis1 & a3d, axis2 & a3d),
+        vector2D(axis1 & b3d, axis2 & b3d),
+        vector2D(axis1 & c3d, axis2 & c3d),
+        orient
+    )
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::label Foam::triangle2D::snapClosePoints(const triangle2D& triB)
+{
+    label nSnapped = 0;
+    triangle2D& triA = *this;
+
+    FixedList<bool, 3> match(true);
+
+    for (auto& a : triA)
+    {
+        forAll(triB, tb)
+        {
+            if (match[tb] && a.isClose(triB[tb]))
+            {
+                a = triB[tb];
+                match[tb] = false;
+                ++nSnapped;
+                break;
+            }
+        }
+    }
+
+    return nSnapped;
+}
+
+
+void Foam::triangle2D::interArea
+(
+    const triangle2D& triB,
+    vector2D& centre,
+    scalar& area
+) const
+{
+    const triangle2D& triA = *this;
+
+    // Potential short-cut if the triangles are the same-ish.  Happens rarely
+    // for moving mesh cases.
+    // if (nClosePoints(triA, triB) == 3)
+    // {
+    //     centre = triA.centre();
+    //     area = triA.area();
+    //     return;
+    // }
+
+    if (debug)
+    {
+        static int nInter = 0;
+        OFstream os("intersection-tris-"+Foam::name(nInter++)+".obj");
+        writeOBJ(os, triA, 0);
+        writeOBJ(os, triB, 3);
+        Info<< "written " << os.name() << endl;
+    }
+
+
+    // Use work_ to store intersections
+
+    // Current number of intersections
+    int nPoint = 0;
+
+    static FixedList<vector2D, 7> work2;
+
+    // Clipped triangle starts as triA
+    work2[0] = triA[0];
+    work2[1] = triA[1];
+    work2[2] = triA[2];
+
+    int nPoint2 = 3;
+
+
+    vector2D intersection(Zero);
+
+    // Cut triA using triB's edges as clipping planes
+    for (int i0 = 0; i0 <= 2; ++i0)
+    {
+        if (debug)
+        {
+            Info<< "\nstarting points:";
+            for (int i = 0; i < nPoint2; ++i)
+            {
+                Info<< work2[i];
+            }
+            Info<< endl;
+        }
+
+        // Clipping plane points
+        const label i1 = (i0 + 1) % 3;
+        const vector2D& c0 = triB[i0];
+        const vector2D& c1 = triB[i1];
+
+        nPoint = 0;
+
+        // Test against all intersection poly points
+        for (int j = 0; j < nPoint2; ++j)
+        {
+            const vector2D& p0 = work2[j];
+            const vector2D& p1 = work2[(j+1) % nPoint2];
+
+            if (triangle2D(c0, c1, p0).order() == 1)
+            {
+                if (triangle2D(c0, c1, p1).order() == 1)
+                {
+                    work_[nPoint++] = p1;
+                }
+                else
+                {
+                    if (lineIntersectionPoint(p0, p1, c0, c1, intersection))
+                    {
+                        work_[nPoint++] = intersection;
+                    }
+                }
+            }
+            else
+            {
+                if (triangle2D(c0, c1, p1).order() == 1)
+                {
+                    if (lineIntersectionPoint(p0, p1, c0, c1, intersection))
+                    {
+                        work_[nPoint++] = intersection;
+                    }
+                    work_[nPoint++] = p1;
+                }
+            }
+        }
+
+        work2 = work_;
+        nPoint2 = nPoint;
+    }
+
+    if (debug)
+    {
+        static int n = 0;
+        OFstream os("intersection-poly-"+Foam::name(n++)+".obj");
+        for (int i = 0; i < nPoint; ++i)
+        {
+            os << "v " << work_[i].x() << " " << work_[i].y() << " 0" << nl;
+        }
+        os  << "l";
+        for (int i = 0; i < nPoint; ++i)
+        {
+            os << " " << (i + 1);
+        }
+        os  << " 1" << endl;
+        Info<< "written " << os.name() << endl;
+
+        Info<< "Intersection points:" << endl;
+        for (int i = 0; i < nPoint; ++i)
+        {
+            Info<< "    " << work_[i] << endl;
+        }
+    }
+
+    // Calculate the area of the clipped triangle
+    scalar twoArea = 0;
+    centre = vector2D::zero;
+    if (nPoint)
+    {
+        for (int i = 0; i < nPoint - 1; ++i)
+        {
+            twoArea += work_[i].x()*work_[i+1].y();
+            twoArea -= work_[i].y()*work_[i+1].x();
+            centre += work_[i];
+        }
+        twoArea += work_[nPoint-1].x()*work_[0].y();
+        twoArea -= work_[nPoint-1].y()*work_[0].x();
+
+        centre += work_[nPoint - 1];
+        centre /= scalar(nPoint);
+    }
+
+    area = 0.5*twoArea;
+}
+
+
+Foam::scalar Foam::triangle2D::interArea(const triangle2D& triB) const
+{
+    vector2D dummyCentre(Zero);
+    scalar area;
+
+    interArea(triB, dummyCentre, area);
+
+    return area;
+}
+
+
+bool Foam::triangle2D::overlaps(const triangle2D& triB) const
+{
+    const triangle2D& triA = *this;
+
+    // True if any of triB's edges intersect a triA edge
+    for (int i = 0; i < 3; ++i)
+    {
+        int i1 = (i + 1) % 3;
+
+        for (int j = 0; j < 3; ++j)
+        {
+            int j1 = (j + 1) % 3;
+
+            if (lineIntersects(triA[i], triA[i1], triB[j], triB[j1]))
+            {
+                return true;
+            }
+        }
+    }
+
+    return
+        (nClosePoints(triA, triB) == 3) // same tri
+     || triA.contains(triB)             // triA contains triB
+     || triB.contains(triA);            // triB contains triA
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/triangle2D/triangle2D.H b/src/meshTools/AMIInterpolation/triangle2D/triangle2D.H
new file mode 100644
index 0000000000000000000000000000000000000000..c85385d6165aae0f1766ea634aab9299c6f51d39
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/triangle2D/triangle2D.H
@@ -0,0 +1,207 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020-2021 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::triangle2D
+
+Description
+    2-D triangle and queries
+
+SourceFiles
+    triangle2DI.H
+    triangle2D.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef triangle2D_H
+#define triangle2D_H
+
+#include "vector.H"
+#include "vector2D.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class triangle2D Declaration
+\*---------------------------------------------------------------------------*/
+
+class triangle2D
+:
+    public FixedList<vector2D, 3>
+{
+    // Private Data
+
+        //- Area
+        scalar area_;
+
+        //- Helper for intersections
+        static FixedList<vector2D, 7> work_;
+
+
+public:
+
+    static int debug;
+
+    //- Relative tolerance
+    static scalar relTol;
+
+    //- Absolute tolerance
+    static scalar absTol;
+
+    //- Construct from 3 2-D points
+    triangle2D
+    (
+        const vector2D& a,
+        const vector2D& b,
+        const vector2D& c,
+        const bool orient = false
+    );
+
+    //- Construct from 3 3-D points and axes
+    triangle2D
+    (
+        const vector& a3d,
+        const vector& b3d,
+        const vector& c3d,
+        const vector& axis1,
+        const vector& axis2,
+        const bool orient = false
+    );
+
+
+    // Member Functions
+
+        //- Returns:
+        //-   1 if points are ordered in anti-clockwise direction
+        //-  -1 if points are ordered in clockwise direction
+        //-   0 if the triangle  has collapsed to a line
+        inline label order() const;
+
+        //- Write the triangle in OBJ format
+        inline static void writeOBJ
+        (
+            Ostream& os,
+            const triangle2D& tri,
+            label offset
+        );
+
+        //- Return the number of similar points between two triangles
+        inline static label nClosePoints
+        (
+            const triangle2D& triA,
+            const triangle2D& triB
+        );
+
+        //- Return the signed area
+        inline static scalar area
+        (
+            const vector2D& a,
+            const vector2D& b,
+            const vector2D& c
+        );
+
+        //- Set the intersection between a line and segment
+        //- Return true if lines intersect
+        inline static bool lineSegmentIntersectionPoint
+        (
+            const vector2D& lp1,
+            const vector2D& lp2,
+            const vector2D& sp1,
+            const vector2D& sp2,
+            vector2D& intersection
+        );
+
+        //- Set the intersection between two lines
+        //- Return true if lines intersect
+        inline static bool lineIntersectionPoint
+        (
+            const vector2D& a,
+            const vector2D& b,
+            const vector2D& c,
+            const vector2D& d,
+            vector2D& intersection
+        );
+
+        //- Return true if lines ab and cd intersect
+        inline static bool lineIntersects
+        (
+            const vector2D& a,
+            const vector2D& b,
+            const vector2D& c,
+            const vector2D& d
+        );
+
+        //- Snap [this] triangle's points to those of triB if they are within
+        //- absTol
+        //  Returns the number of snapped points
+        label snapClosePoints(const triangle2D& triB);
+
+        //- Return the intersection centre and area
+        void interArea
+        (
+            const triangle2D& triB,
+            vector2D& centre,
+            scalar& area
+        ) const;
+
+        //- Return the intersection area
+        scalar interArea(const triangle2D& triB) const;
+
+        //- Return true if triB overlaps
+        bool overlaps(const triangle2D& triB) const;
+
+        //- Return the triangle area
+        inline scalar area() const noexcept;
+
+        //- Return the triangle centre
+        inline vector2D centre() const;
+
+        //- Return true if tri is within this triangle
+        inline bool contains(const triangle2D& tri) const;
+
+        //- Return true if triB is the same as this triangle
+        inline bool isSame(const triangle2D& triB) const;
+
+        //- Return true if t point p is inside this triangle
+        inline bool pointInside(const vector2D& p) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "triangle2DI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/triangle2D/triangle2DI.H b/src/meshTools/AMIInterpolation/triangle2D/triangle2DI.H
new file mode 100644
index 0000000000000000000000000000000000000000..6f73594f60f3996160df5501c80cbb04a91825eb
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/triangle2D/triangle2DI.H
@@ -0,0 +1,241 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020-2021 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 "OFstream.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+inline void Foam::triangle2D::writeOBJ
+(
+    Ostream& os,
+    const triangle2D& tri,
+    label offset
+)
+{
+    os  << "v " << tri[0].x() << " " << tri[0].y() << " 0" << nl
+        << "v " << tri[1].x() << " " << tri[1].y() << " 0" << nl
+        << "v " << tri[2].x() << " " << tri[2].y() << " 0" << nl
+        << "l"
+        << " " << 1 + offset
+        << " " << 2 + offset
+        << " " << 3 + offset
+        << " " << 1 + offset
+        << endl;
+}
+
+
+inline Foam::label Foam::triangle2D::nClosePoints
+(
+    const triangle2D& triA,
+    const triangle2D& triB
+)
+{
+    label nClose = 0;
+
+    FixedList<bool, 3> match(true);
+
+    for (auto& a : triA)
+    {
+        forAll(triB, tb)
+        {
+            if (match[tb] && a.isClose(triB[tb]))
+            {
+                match[tb] = false;
+                ++nClose;
+                break;
+            }
+        }
+    }
+
+    return nClose;
+}
+
+
+inline Foam::scalar Foam::triangle2D::area
+(
+    const vector2D& a,
+    const vector2D& b,
+    const vector2D& c
+)
+{
+    const vector2D e1(a - c);
+    const vector2D e2(b - c);
+
+    return 0.5*e1.perp(e2);
+}
+
+
+inline Foam::vector2D Foam::triangle2D::centre() const
+{
+    const triangle2D& tri = *this;
+
+    return (1/3.0)*(tri[0] + tri[1] + tri[2]);
+}
+
+
+inline bool Foam::triangle2D::lineSegmentIntersectionPoint
+(
+    const vector2D& lp1,
+    const vector2D& lp2,
+    const vector2D& sp1,
+    const vector2D& sp2,
+    vector2D& intersection
+)
+{
+    const vector2D r(lp2 - lp1);
+    const vector2D s(sp2 - sp1);
+
+    const scalar rcs = r.perp(s);
+
+    if (mag(rcs) > ROOTVSMALL)
+    {
+        const scalar u = (sp1 - lp1).perp(r)/rcs;
+
+        if (u >= -relTol && u <= 1+relTol)
+        {
+            intersection = sp1 + u*s;
+            return true;
+        }
+    }
+
+/*
+        // Collapsed to line
+        if (mag(triangle2D(lp1, lp2, sp1).area()) < absTol)
+        {
+            intersection = sp1;
+            return true;
+        }
+
+        if (mag(triangle2D(lp1, lp2, sp2).area()) < absTol)
+        {
+            intersection = sp2;
+            return true;
+        }
+*/
+
+    if (debug)
+    {
+        OFstream os("bad-intersection.obj");
+        os  << "v " << lp1.x() << " " << lp1.y() << " 0" << nl
+            << "v " << lp2.x() << " " << lp2.y() << " 0" << nl
+            << "v " << sp1.x() << " " << sp1.y() << " 0" << nl
+            << "v " << sp2.x() << " " << sp2.y() << " 0" << nl
+            << "l 1 2"
+            << "l 3 4"
+            << endl;
+    }
+
+    return false;
+}
+
+
+inline bool Foam::triangle2D::lineIntersectionPoint
+(
+    const vector2D& a,
+    const vector2D& b,
+    const vector2D& c,
+    const vector2D& d,
+    vector2D& intersection
+)
+{
+    return lineSegmentIntersectionPoint(c, d, a, b, intersection);
+}
+
+
+inline bool Foam::triangle2D::lineIntersects
+(
+    const vector2D& a,
+    const vector2D& b,
+    const vector2D& c,
+    const vector2D& d
+)
+{
+    if
+    (
+        (triangle2D(a, c, d).order() == triangle2D(b, c, d).order())
+     || (triangle2D(a, b, c).order() == triangle2D(a, b, d).order())
+    )
+    {
+        return false;
+    }
+
+
+    DebugInfo<< "line " << a << b << " intersects " << c << d << endl;
+
+    return true;
+}
+
+
+inline Foam::scalar Foam::triangle2D::area() const noexcept
+{
+    return area_;
+}
+
+
+inline Foam::label Foam::triangle2D::order() const
+{
+    return mag(area_) < SMALL ? 0 : sign(area_);
+}
+
+
+inline bool Foam::triangle2D::contains(const triangle2D& tri) const
+{
+    return
+        pointInside(tri[0])
+     && pointInside(tri[1])
+     && pointInside(tri[2]);
+}
+
+
+inline bool Foam::triangle2D::isSame(const triangle2D& triB) const
+{
+    const triangle2D& triA = *this;
+
+    return
+        triA[0].isClose(triB[0])
+     && triA[1].isClose(triB[1])
+     && triA[2].isClose(triB[2]);
+}
+
+
+inline bool Foam::triangle2D::pointInside(const vector2D& p) const
+{
+    const triangle2D& triA = *this;
+
+    for (int i = 0; i < 3; ++i)
+    {
+        if ((triA[(i + 1) % 3] - triA[i]).perp(p - triA[i]) < 0)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 3d32aa57367f12a8cf17512276d4115acf9a3f5c..d92cd130ad11bd503cb2289ca69461f7b9353ea4 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -269,7 +269,6 @@ $(AMI)/AMIInterpolation/AMIInterpolationNew.C
 $(AMI)/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
 $(AMI)/AMIInterpolation/advancingFrontAMI/advancingFrontAMIParallelOps.C
 $(AMI)/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
-$(AMI)/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
 $(AMI)/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.C
 $(AMI)/faceAreaIntersect/faceAreaIntersect.C
 $(AMI)/GAMG/interfaces/cyclicAMIGAMGInterface/cyclicAMIGAMGInterface.C
@@ -277,6 +276,10 @@ $(AMI)/GAMG/interfaceFields/cyclicAMIGAMGInterfaceField/cyclicAMIGAMGInterfaceFi
 $(AMI)/GAMG/interfaces/cyclicACMIGAMGInterface/cyclicACMIGAMGInterface.C
 $(AMI)/GAMG/interfaceFields/cyclicACMIGAMGInterfaceField/cyclicACMIGAMGInterfaceField.C
 
+$(AMI)/triangle2D/triangle2D.C
+$(AMI)/AMIInterpolation/faceAreaWeightAMI2D/faceAreaWeightAMI2D.C
+
+
 AMICycPatches=$(AMI)/patches/cyclicAMI
 $(AMICycPatches)/cyclicAMILduInterfaceField/cyclicAMILduInterface.C
 $(AMICycPatches)/cyclicAMILduInterfaceField/cyclicAMILduInterfaceField.C