diff --git a/applications/solvers/incompressible/pimpleFoam/pEqn.H b/applications/solvers/incompressible/pimpleFoam/pEqn.H
index e3c61fe37da88f90ab2907b813b55dd81eed85ad..8f49f635992f9dcfaf68f1a22ddf78870ad7ccdb 100644
--- a/applications/solvers/incompressible/pimpleFoam/pEqn.H
+++ b/applications/solvers/incompressible/pimpleFoam/pEqn.H
@@ -1,11 +1,11 @@
 volScalarField rAU(1.0/UEqn.A());
 volVectorField HbyA(constrainHbyA(rAU*UEqn.H(), U, p));
-surfaceScalarField phiHbyA
-(
-    "phiHbyA",
-    fvc::flux(HbyA)
-  + MRF.zeroFilter(fvc::interpolate(rAU)*fvc::ddtCorr(U, phi, Uf))
-);
+surfaceScalarField phiHbyA("phiHbyA", fvc::flux(HbyA));
+
+if (pimple.ddtCorr())
+{
+    phiHbyA += MRF.zeroFilter(fvc::interpolate(rAU)*fvc::ddtCorr(U, phi, Uf));
+}
 
 MRF.makeRelative(phiHbyA);
 
diff --git a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
index 6c3f66c121d524c7b670dc7283069b1cf924d637..7c6435f1e424cba6338ecc46f94ce734bc2aae68 100644
--- a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
+++ b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
@@ -252,11 +252,7 @@ int main(int argc, char *argv[])
         meshToMesh::interpolationMethod method =
             meshToMesh::interpolationMethodNames_[mapMethod];
 
-        patchMapMethod =
-            AMIPatchToPatchInterpolation::interpolationMethodNames_
-            [
-                meshToMesh::interpolationMethodAMI(method)
-            ];
+        patchMapMethod = meshToMesh::interpolationMethodAMI(method);
     }
 
     word procMapMethod =
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshUpdate.C b/src/OpenFOAM/meshes/polyMesh/polyMeshUpdate.C
index 80232d4498e081d7e3098538278b994bf3426bc5..e5818171d53ac6f85da1ceb6399a5e40ab79df99 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyMeshUpdate.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshUpdate.C
@@ -45,6 +45,7 @@ void Foam::polyMesh::updateMesh(const mapPolyMesh& mpm)
         << "Updating addressing and (optional) pointMesh/pointFields" << endl;
 
     // Update boundaryMesh (note that patches themselves already ok)
+//    boundary_.updateMesh(mpm);
     boundary_.updateMesh();
 
     // Update zones
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
index 3e915b3e7e6938eb96cbbd47ae49c16b44365b25..6db7dc22fcb8cb648992b24ff9cbfe31edaeb668 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
@@ -63,6 +63,7 @@ void Foam::polyPatch::movePoints(PstreamBuffers&, const pointField& p)
     primitivePatch::movePoints(p);
 }
 
+
 void Foam::polyPatch::updateMesh(PstreamBuffers&)
 {
     primitivePatch::clearGeom();
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.H b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.H
index 08bbf0c732aa34cc498a86359be0b314378086c6..f7f01abb091986e668a1cf66b95ff9fa121ad05b 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.H
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.H
@@ -55,6 +55,7 @@ namespace Foam
 // Forward declarations
 class polyBoundaryMesh;
 class polyPatch;
+class polyTopoChange;
 class PstreamBuffers;
 
 Ostream& operator<<(Ostream&, const polyPatch&);
@@ -419,6 +420,19 @@ public:
             labelList& rotation
         ) const;
 
+        //- For dynamic mesh cases - return true if this patch will change the
+        //- topology
+        virtual bool changeTopology() const
+        {
+            return false;
+        }
+
+        //- Collect topology changes in a polyTopoChange object
+        virtual bool setTopology(polyTopoChange&)
+        {
+            return false;
+        }
+
 
     // Member operators
 
diff --git a/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C b/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
index 3a13ad4c5e3a939589eeb3f7e37363191e44205b..51d558a1fb9ae57423cead0c32bf0344fc4632d3 100644
--- a/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
+++ b/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
@@ -47,6 +47,7 @@ License
 #include "gravityMeshObject.H"
 #include "turbulentTransportModel.H"
 #include "demandDrivenData.H"
+#include "unitConversion.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
diff --git a/src/dynamicFvMesh/Make/files b/src/dynamicFvMesh/Make/files
index b8432c07c905108747f9b94eb4cffefd26d1e0bd..794af6f3d0dd9754f6371c669da5e3c10da2b07d 100644
--- a/src/dynamicFvMesh/Make/files
+++ b/src/dynamicFvMesh/Make/files
@@ -10,4 +10,9 @@ dynamicMotionSolverListFvMesh/dynamicMotionSolverListFvMesh.C
 simplifiedDynamicFvMesh/simplifiedDynamicFvMeshes.C
 simplifiedDynamicFvMesh/simplifiedDynamicFvMesh.C
 
+
+
+dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.C
+
+
 LIB = $(FOAM_LIBBIN)/libdynamicFvMesh
diff --git a/src/dynamicFvMesh/dynamicMotionSolverFvMesh/dynamicMotionSolverFvMesh.C b/src/dynamicFvMesh/dynamicMotionSolverFvMesh/dynamicMotionSolverFvMesh.C
index ba1d636969a1aeb010e0e0712c4a0a6ffc5cad1d..5b7a90cddc85ae66857042fc5c54bb5347372998 100644
--- a/src/dynamicFvMesh/dynamicMotionSolverFvMesh/dynamicMotionSolverFvMesh.C
+++ b/src/dynamicFvMesh/dynamicMotionSolverFvMesh/dynamicMotionSolverFvMesh.C
@@ -92,6 +92,9 @@ const Foam::motionSolver& Foam::dynamicMotionSolverFvMesh::motion() const
 
 bool Foam::dynamicMotionSolverFvMesh::update()
 {
+    // Scan through AMI patches and update
+
+
     fvMesh::movePoints(motionPtr_->newPoints());
 
     volVectorField* Uptr = getObjectPtr<volVectorField>("U");
diff --git a/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.C b/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.C
new file mode 100644
index 0000000000000000000000000000000000000000..e431a39536d6a900d3cae632a116fea67eeb0235
--- /dev/null
+++ b/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.C
@@ -0,0 +1,223 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2019-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 "dynamicMotionSolverFvMeshAMI.H"
+#include "addToRunTimeSelectionTable.H"
+#include "motionSolver.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "cyclicAMIPolyPatch.H"
+#include "polyTopoChange.H"
+#include "MeshObject.H"
+#include "lduMesh.H"
+#include "surfaceInterpolate.H"
+
+#include "processorFvPatch.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(dynamicMotionSolverFvMeshAMI, 0);
+    addToRunTimeSelectionTable
+    (
+        dynamicFvMesh,
+        dynamicMotionSolverFvMeshAMI,
+        IOobject
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::dynamicMotionSolverFvMeshAMI::dynamicMotionSolverFvMeshAMI
+(
+    const IOobject& io
+)
+:
+    dynamicFvMesh(io),
+    motionPtr_(motionSolver::New(*this))
+{}
+
+
+Foam::dynamicMotionSolverFvMeshAMI::dynamicMotionSolverFvMeshAMI
+(
+    const IOobject& io,
+    pointField&& points,
+    faceList&& faces,
+    labelList&& allOwner,
+    labelList&& allNeighbour,
+    const bool syncPar
+)
+:
+    dynamicFvMesh
+    (
+        io,
+        std::move(points),
+        std::move(faces),
+        std::move(allOwner),
+        std::move(allNeighbour),
+        syncPar
+    ),
+    motionPtr_(motionSolver::New(*this))
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::motionSolver& Foam::dynamicMotionSolverFvMeshAMI::motion() const
+{
+    return *motionPtr_;
+}
+
+
+bool Foam::dynamicMotionSolverFvMeshAMI::update()
+{
+    // Mesh not moved/changed yet
+    moving(false);
+    topoChanging(false);
+
+    if (debug)
+    {
+        for (const fvPatch& fvp : boundary())
+        {
+            if (!isA<processorFvPatch>(fvp))
+            {
+                Info<< "1 --- patch:" << fvp.patch().name()
+                    << " area:" << gSum(fvp.magSf()) << endl;
+            }
+        }
+    }
+
+    pointField newPoints(motionPtr_->curPoints());
+
+    polyBoundaryMesh& pbm = const_cast<polyBoundaryMesh&>(boundaryMesh());
+
+    // Scan all patches and see if we want to apply a mesh topology  update
+    bool changeRequired = false;
+    for (polyPatch& pp : pbm)
+    {
+        DebugInfo
+            << "pre-topology change: patch " << pp.name()
+            << " size:" << returnReduce(pp.size(), sumOp<label>())
+            << " mag(faceAreas):" << gSum(mag(pp.faceAreas())) << endl;
+
+        //changeRequired = pp.changeTopology(newPoints) || changeRequired;
+        changeRequired = pp.changeTopology() || changeRequired;
+    }
+
+    reduce(changeRequired, orOp<bool>());
+
+    if (changeRequired)
+    {
+        polyTopoChange polyTopo(*this);
+
+        // Set new point positions in polyTopo object
+        polyTopo.movePoints(newPoints);
+
+        // Accumulate the patch-based mesh changes on the current mesh
+        // Note:
+        // - updates the AMIs using the new points
+        // - creates a topo change object that removes old added faces and
+        //   adds the new faces
+        for (polyPatch& pp : pbm)
+        {
+            pp.setTopology(polyTopo);
+        }
+
+        // Update geometry
+        // Note
+        // - changeMesh leads to polyMesh::resetPrimitives which will also
+        //   trigger polyBoundaryMesh::updateMesh (init and update) and
+        //   ::calcGeometry (with topoChanging = false)
+        // - BUT: mesh still corresponds to original (non-extended mesh) so
+        //   we want to bypass these calls...
+        // - after changes topoChanging = true
+        autoPtr<mapPolyMesh> map =
+            polyTopo.changeMesh
+            (
+                *this,
+                true       // We will be calling movePoints after this update
+            );
+
+        // Apply topology change - update fv geometry and map fields
+        // - polyMesh::updateMesh
+        //   - fires initUpdateMesh and updateMesh in AMI BCs - called before
+        //     mapFields
+        // - AMI addressing must be up-to-date - used by, e.g. FaceCellWave
+        // - will trigger (again) polyBoundaryMesh::updateMesh (init and update)
+        updateMesh(map());
+
+        // Move points and update derived properties
+        // Note:
+        // - resets face areas based on raw point locations!
+        // - polyBoundaryMesh::updateMesh (init and update)
+        // Note:
+        // - processorPolyPatches will trigger calculation of faceCentres
+        //   (and therefore cell volumes), so need to update faceAreas in
+        //   initMovePoints since proc patches will be evaluated later than
+        //   AMI patches
+        if (map().hasMotionPoints())
+        {
+            movePoints(map().preMotionPoints());
+        }
+    }
+    else
+    {
+        fvMesh::movePoints(newPoints);
+    }
+
+    volVectorField* Uptr = getObjectPtr<volVectorField>("U");
+
+    if (Uptr)
+    {
+        Uptr->correctBoundaryConditions();
+
+        surfaceVectorField* UfPtr = getObjectPtr<surfaceVectorField>("Uf");
+        if (UfPtr)
+        {
+            *UfPtr = fvc::interpolate(*Uptr);
+        }
+    }
+
+    if (debug)
+    {
+        for (const fvPatch& fvp : boundary())
+        {
+            if (!isA<processorFvPatch>(fvp))
+            {
+                Info<< "2 --- patch:" << fvp.patch().name()
+                    << " area:" << gSum(fvp.magSf()) << endl;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.H b/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.H
new file mode 100644
index 0000000000000000000000000000000000000000..e21f5623ed7df6416380459ef8b6470b6ebb42f0
--- /dev/null
+++ b/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.H
@@ -0,0 +1,121 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2011-2017 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/>.
+
+Class
+    Foam::dynamicMotionSolverFvMeshAMI
+
+Description
+    The dynamicMotionSolverFvMeshAMI
+
+SourceFiles
+    dynamicMotionSolverFvMeshAMI.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef dynamicMotionSolverFvMeshAMI_H
+#define dynamicMotionSolverFvMeshAMI_H
+
+#include "dynamicFvMesh.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class motionSolver;
+
+/*---------------------------------------------------------------------------*\
+               Class dynamicMotionSolverFvMeshAMI Declaration
+\*---------------------------------------------------------------------------*/
+
+class dynamicMotionSolverFvMeshAMI
+:
+    public dynamicFvMesh
+{
+    // Private data
+
+        autoPtr<motionSolver> motionPtr_;
+
+
+    // Private Member Functions
+
+        //- No copy construct
+        dynamicMotionSolverFvMeshAMI
+        (
+            const dynamicMotionSolverFvMeshAMI&
+        ) = delete;
+
+        //- No copy assignment
+        void operator=(const dynamicMotionSolverFvMeshAMI&) = delete;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("dynamicMotionSolverFvMeshAMI");
+
+
+    // Constructors
+
+        //- Construct from IOobject
+        dynamicMotionSolverFvMeshAMI(const IOobject& io);
+
+        //- Construct from components without boundary.
+        //  Boundary is added using addFvPatches() member function
+        dynamicMotionSolverFvMeshAMI
+        (
+            const IOobject& io,
+            pointField&& points,
+            faceList&& faces,
+            labelList&& allOwner,
+            labelList&& allNeighbour,
+            const bool syncPar = true
+        );
+
+
+    //- Destructor
+    virtual ~dynamicMotionSolverFvMeshAMI() = default;
+
+
+    // Member Functions
+
+        //- Return the motionSolver
+        const motionSolver& motion() const;
+
+        //- Update the mesh for both mesh motion and topology change
+        virtual bool update();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/Make/options b/src/finiteVolume/Make/options
index 851f94b6b3910365ebdd5ad09a66903da978f118..ea11ab0d45a46d24a226464a14238a684ff6278a 100644
--- a/src/finiteVolume/Make/options
+++ b/src/finiteVolume/Make/options
@@ -1,7 +1,8 @@
 EXE_INC = \
     -I$(LIB_SRC)/fileFormats/lnInclude \
     -I$(LIB_SRC)/surfMesh/lnInclude \
-    -I$(LIB_SRC)/meshTools/lnInclude
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/dynamicMesh/lnInclude
 
 LIB_LIBS = \
     -lOpenFOAM \
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.C b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.C
index e49d3c75fc13b924e2819aa22c30482eddb072a8..aa4d3d6bcbbffde7e378dec00b8b0f7622602e1d 100644
--- a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.C
+++ b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.C
@@ -52,6 +52,7 @@ bool Foam::pimpleControl::read()
         pimpleDict.getOrDefault("turbOnFinalIterOnly", true);
     finalOnLastPimpleIterOnly_ =
         pimpleDict.getOrDefault("finalOnLastPimpleIterOnly", false);
+    ddtCorr_ = pimpleDict.getOrDefault("ddtCorr", true);
 
     return true;
 }
@@ -155,8 +156,9 @@ Foam::pimpleControl::pimpleControl
     corrPISO_(0),
     SIMPLErho_(false),
     turbOnFinalIterOnly_(true),
-    converged_(false),
-    finalOnLastPimpleIterOnly_(false)
+    finalOnLastPimpleIterOnly_(false),
+    ddtCorr_(true),
+    converged_(false)
 {
     read();
 
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.H b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.H
index 1433215df84c5ca1712f0da4472e8db8f7d8e61c..017f4675b534d89695b009796886f2edfb344918 100644
--- a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.H
+++ b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControl.H
@@ -85,19 +85,22 @@ protected:
             label corrPISO_;
 
             //- Flag to indicate whether to update density in SIMPLE
-            //  rather than PISO mode
+            //- rather than PISO mode
             bool SIMPLErho_;
 
             //- Flag to indicate whether to only solve turbulence on final iter
             bool turbOnFinalIterOnly_;
 
-            //- Converged flag
-            bool converged_;
-
             //- Flag to indicate wheter the final solver is used only on the
-            //  final pimple iter
+            //- final pimple iter
             bool finalOnLastPimpleIterOnly_;
 
+            //- Flag to indicate that ddtCorr should be applied; default = yes
+            bool ddtCorr_;
+
+            //- Converged flag
+            bool converged_;
+
 
     // Protected Member Functions
 
@@ -181,6 +184,9 @@ public:
 
             //- Return true to solve for turbulence
             inline bool turbCorr();
+
+            //- Return true to apply ddtCorr
+            inline bool ddtCorr() const;
 };
 
 
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControlI.H b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControlI.H
index 344db63f93e197fd5e835b4dbf66a885e3471491..b0fd30e6fd13267e506b0fd534c05d848ba73196 100644
--- a/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControlI.H
+++ b/src/finiteVolume/cfdTools/general/solutionControl/pimpleControl/pimpleControlI.H
@@ -143,4 +143,10 @@ inline bool Foam::pimpleControl::turbCorr()
 }
 
 
+inline bool Foam::pimpleControl::ddtCorr() const
+{
+    return ddtCorr_;
+}
+
+
 // ************************************************************************* //
diff --git a/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.C
index beb923b8990b14762f9d25957689e994c524bad9..96415c00e56adaaa1645d264230e02b901037a02 100644
--- a/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.C
+++ b/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.C
@@ -226,10 +226,9 @@ void Foam::cyclicACMIFvPatchField<Type>::updateInterfaceMatrix
 {
     const cyclicACMIPolyPatch& cpp = cyclicACMIPatch_.cyclicACMIPatch();
 
-    // note: only applying coupled contribution
+    // Note: only applying coupled contribution
 
-    const labelUList& nbrFaceCellsCoupled =
-        cpp.neighbPatch().faceCells();
+    const labelUList& nbrFaceCellsCoupled = cpp.neighbPatch().faceCells();
 
     solveScalarField pnf(psiInternal, nbrFaceCellsCoupled);
 
@@ -254,7 +253,7 @@ void Foam::cyclicACMIFvPatchField<Type>::updateInterfaceMatrix
 {
     const cyclicACMIPolyPatch& cpp = cyclicACMIPatch_.cyclicACMIPatch();
 
-    // note: only applying coupled contribution
+    // Note: only applying coupled contribution
 
     const labelUList& nbrFaceCellsCoupled = cpp.neighbPatch().faceCells();
 
@@ -277,7 +276,7 @@ void Foam::cyclicACMIFvPatchField<Type>::manipulateMatrix
 {
     const scalarField& mask = cyclicACMIPatch_.cyclicACMIPatch().mask();
 
-    // nothing to be done by the AMI, but re-direct to non-overlap patch
+    // Nothing to be done by the AMI, but re-direct to non-overlap patch
     // with non-overlap patch weights
     const fvPatchField<Type>& npf = nonOverlapPatchField();
 
diff --git a/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.H b/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.H
index eae2dd49dad5656f8de4b2d6690f9dc8dad0f3b1..db093b3940174646b4663dd5a93f52491f220d18 100644
--- a/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.H
+++ b/src/finiteVolume/fields/fvPatchFields/constraint/cyclicACMI/cyclicACMIFvPatchField.H
@@ -173,7 +173,7 @@ public:
 
             //- Return true if this patch field fixes a value
             //  Needed to check if a level has to be specified while solving
-            //  Poissons equations
+            //  Poisson equations
             virtual bool fixesValue() const
             {
                 const scalarField& mask =
diff --git a/src/finiteVolume/fvMesh/fvMesh.C b/src/finiteVolume/fvMesh/fvMesh.C
index 9aff27408a42320329b01ab30f83d41d4349f1d5..e02ae63dccb3177dc8a6766dc2b9f2187453afe9 100644
--- a/src/finiteVolume/fvMesh/fvMesh.C
+++ b/src/finiteVolume/fvMesh/fvMesh.C
@@ -726,6 +726,8 @@ void Foam::fvMesh::mapFields(const mapPolyMesh& meshMap)
 
 Foam::tmp<Foam::scalarField> Foam::fvMesh::movePoints(const pointField& p)
 {
+    DebugInFunction << endl;
+
     // Grab old time volumes if the time has been incremented
     // This will update V0, V00
     if (curTimeIndex_ < time().timeIndex())
@@ -733,6 +735,14 @@ Foam::tmp<Foam::scalarField> Foam::fvMesh::movePoints(const pointField& p)
         storeOldVol(V());
     }
 
+
+    // Move the polyMesh and set the mesh motion fluxes to the swept-volumes
+
+    scalar rDeltaT = 1.0/time().deltaTValue();
+
+    tmp<scalarField> tsweptVols = polyMesh::movePoints(p);
+    scalarField& sweptVols = tsweptVols.ref();
+
     if (!phiPtr_)
     {
         // Create mesh motion flux
@@ -761,14 +771,6 @@ Foam::tmp<Foam::scalarField> Foam::fvMesh::movePoints(const pointField& p)
     }
 
     surfaceScalarField& phi = *phiPtr_;
-
-    // Move the polyMesh and set the mesh motion fluxes to the swept-volumes
-
-    scalar rDeltaT = 1.0/time().deltaTValue();
-
-    tmp<scalarField> tsweptVols = polyMesh::movePoints(p);
-    scalarField& sweptVols = tsweptVols.ref();
-
     phi.primitiveFieldRef() =
         scalarField::subField(sweptVols, nInternalFaces());
     phi.primitiveFieldRef() *= rDeltaT;
@@ -776,7 +778,6 @@ Foam::tmp<Foam::scalarField> Foam::fvMesh::movePoints(const pointField& p)
     const fvPatchList& patches = boundary();
 
     surfaceScalarField::Boundary& phibf = phi.boundaryFieldRef();
-
     forAll(patches, patchi)
     {
         phibf[patchi] = patches[patchi].patchSlice(sweptVols);
@@ -805,6 +806,8 @@ Foam::tmp<Foam::scalarField> Foam::fvMesh::movePoints(const pointField& p)
 
 void Foam::fvMesh::updateMesh(const mapPolyMesh& mpm)
 {
+    DebugInFunction << endl;
+
     // Update polyMesh. This needs to keep volume existent!
     polyMesh::updateMesh(mpm);
 
diff --git a/src/finiteVolume/fvMesh/fvMesh.H b/src/finiteVolume/fvMesh/fvMesh.H
index 53e57c7f683590c72fa7f0cd51eda9a89fbb46c9..0e35ef9f9d2a4da19d29e39bfedc28a33c41f9fb 100644
--- a/src/finiteVolume/fvMesh/fvMesh.H
+++ b/src/finiteVolume/fvMesh/fvMesh.H
@@ -91,6 +91,8 @@ class fvMesh
     public fvSolution,
     public data
 {
+protected:
+
     // Private data
 
         //- Boundary mesh
diff --git a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.C b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.C
index c4427d54849224973829e37955bd6144034c7c95..a66ae7d06b98271b7e39a4d4c45ba5f61e995801 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.C
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
+    Copyright (C) 2019 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,9 +27,10 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cyclicACMIFvPatch.H"
-#include "addToRunTimeSelectionTable.H"
 #include "fvMesh.H"
 #include "transform.H"
+#include "surfaceFields.H"
+#include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -41,45 +43,14 @@ namespace Foam
 
 // * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
 
-void Foam::cyclicACMIFvPatch::updateAreas() const
+void Foam::cyclicACMIFvPatch::resetPatchAreas(const fvPatch& fvp) const
 {
-    if (cyclicACMIPolyPatch_.updated())
-    {
-        if (debug)
-        {
-            Pout<< "cyclicACMIFvPatch::updateAreas() : updating fv areas for "
-                << name() << " and " << this->nonOverlapPatch().name()
-                << endl;
-        }
+    const_cast<vectorField&>(fvp.Sf()) = fvp.patch().faceAreas();
+    const_cast<vectorField&>(fvp.Cf()) = fvp.patch().faceCentres();
+    const_cast<scalarField&>(fvp.magSf()) = mag(fvp.patch().faceAreas());
 
-        // owner couple
-        const_cast<vectorField&>(Sf()) = patch().faceAreas();
-        const_cast<scalarField&>(magSf()) = mag(patch().faceAreas());
-
-        // owner non-overlapping
-        const fvPatch& nonOverlapPatch = this->nonOverlapPatch();
-        const_cast<vectorField&>(nonOverlapPatch.Sf()) =
-            nonOverlapPatch.patch().faceAreas();
-        const_cast<scalarField&>(nonOverlapPatch.magSf()) =
-            mag(nonOverlapPatch.patch().faceAreas());
-
-        // neighbour couple
-        const cyclicACMIFvPatch& nbrACMI = neighbPatch();
-        const_cast<vectorField&>(nbrACMI.Sf()) =
-            nbrACMI.patch().faceAreas();
-        const_cast<scalarField&>(nbrACMI.magSf()) =
-            mag(nbrACMI.patch().faceAreas());
-
-        // neighbour non-overlapping
-        const fvPatch& nbrNonOverlapPatch = nbrACMI.nonOverlapPatch();
-        const_cast<vectorField&>(nbrNonOverlapPatch.Sf()) =
-            nbrNonOverlapPatch.patch().faceAreas();
-        const_cast<scalarField&>(nbrNonOverlapPatch.magSf()) =
-            mag(nbrNonOverlapPatch.patch().faceAreas());
-
-        // set the updated flag
-        cyclicACMIPolyPatch_.setUpdated(false);
-    }
+    DebugPout
+        << fvp.patch().name() << " area:" << sum(fvp.magSf()) << endl;
 }
 
 
@@ -100,13 +71,13 @@ void Foam::cyclicACMIFvPatch::makeWeights(scalarField& w) const
             )
         );
 
-        scalar tol = cyclicACMIPolyPatch::tolerance();
+        const scalar tol = cyclicACMIPolyPatch::tolerance();
 
 
         forAll(deltas, facei)
         {
-            scalar di = deltas[facei];
-            scalar dni = nbrDeltas[facei];
+            scalar di = mag(deltas[facei]);
+            scalar dni = mag(nbrDeltas[facei]);
 
             if (dni < tol)
             {
@@ -147,8 +118,7 @@ Foam::tmp<Foam::vectorField> Foam::cyclicACMIFvPatch::delta() const
 
         vectorField nbrPatchD(interpolate(nbrPatch.coupledFvPatch::delta()));
 
-
-        tmp<vectorField> tpdv(new vectorField(patchD.size()));
+        auto tpdv = tmp<vectorField>::New(patchD.size());
         vectorField& pdv = tpdv.ref();
 
         // do the transformation if necessary
@@ -201,4 +171,100 @@ Foam::tmp<Foam::labelField> Foam::cyclicACMIFvPatch::internalFieldTransfer
 }
 
 
+void Foam::cyclicACMIFvPatch::movePoints()
+{
+    if (!cyclicACMIPolyPatch_.owner())
+    {
+        return;
+    }
+
+    // Set the patch face areas to be consistent with the changes made at the
+    // polyPatch level
+
+    const fvPatch& nonOverlapPatch = this->nonOverlapPatch();
+    const cyclicACMIFvPatch& nbrACMI = neighbPatch();
+    const fvPatch& nbrNonOverlapPatch = nbrACMI.nonOverlapPatch();
+
+    resetPatchAreas(*this);
+    resetPatchAreas(nonOverlapPatch);
+    resetPatchAreas(nbrACMI);
+    resetPatchAreas(nbrNonOverlapPatch);
+
+    // Scale the mesh flux
+
+    const labelListList& newSrcAddr = AMI().srcAddress();
+    const labelListList& newTgtAddr = AMI().tgtAddress();
+
+    const fvMesh& mesh = boundaryMesh().mesh();
+    surfaceScalarField& meshPhi = const_cast<fvMesh&>(mesh).setPhi();
+    surfaceScalarField::Boundary& meshPhiBf = meshPhi.boundaryFieldRef();
+
+    // Note: phip and phiNonOverlapp will be different sizes if new faces
+    // have been added
+    scalarField& phip = meshPhiBf[cyclicACMIPolyPatch_.index()];
+    scalarField& phiNonOverlapp =
+        meshPhiBf[nonOverlapPatch.patch().index()];
+
+    const auto& localFaces = cyclicACMIPolyPatch_.localFaces();
+    const auto& localPoints = cyclicACMIPolyPatch_.localPoints();
+
+    forAll(phip, facei)
+    {
+        if (newSrcAddr[facei].empty())
+        {
+            // AMI patch with no connection to other coupled faces
+            phip[facei] = 0.0;
+        }
+        else
+        {
+            // Scale the mesh flux according to the area fraction
+            const face& fAMI = localFaces[facei];
+
+            // Note: using raw point locations to calculate the geometric
+            // area - faces areas are currently scaled (decoupled from
+            // mesh points)
+            const scalar geomArea = fAMI.mag(localPoints);
+            phip[facei] *= magSf()[facei]/geomArea;
+        }
+    }
+
+    forAll(phiNonOverlapp, facei)
+    {
+        const scalar w = 1.0 - cyclicACMIPolyPatch_.srcMask()[facei];
+        phiNonOverlapp[facei] *= w;
+    }
+
+    scalarField& nbrPhip = meshPhiBf[nbrACMI.patch().index()];
+    scalarField& nbrPhiNonOverlapp =
+        meshPhiBf[nbrNonOverlapPatch.patch().index()];
+
+    const auto& nbrLocalFaces = nbrACMI.patch().localFaces();
+    const auto& nbrLocalPoints = nbrACMI.patch().localPoints();
+
+    forAll(nbrPhip, facei)
+    {
+        if (newTgtAddr[facei].empty())
+        {
+            nbrPhip[facei] = 0.0;
+        }
+        else
+        {
+            const face& fAMI = nbrLocalFaces[facei];
+
+            // Note: using raw point locations to calculate the geometric
+            // area - faces areas are currently scaled (decoupled from
+            // mesh points)
+            const scalar geomArea = fAMI.mag(nbrLocalPoints);
+            nbrPhip[facei] *= nbrACMI.magSf()[facei]/geomArea;
+        }
+    }
+
+    forAll(nbrPhiNonOverlapp, facei)
+    {
+        const scalar w = 1.0 - cyclicACMIPolyPatch_.tgtMask()[facei];
+        nbrPhiNonOverlapp[facei] *= w;
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.H b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.H
index 037df53fe3797d7dffdef2af16cafdfc6a325603..c3aed02df707219e3826b30a8ee8c8ae7b140df4 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.H
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicACMI/cyclicACMIFvPatch.H
@@ -65,12 +65,15 @@ protected:
 
     // Protected Member functions
 
-        //- Update the patch areas after AMI update
-        void updateAreas() const;
+        //- Helper function to reset the FV patch areas from the primitive patch
+        void resetPatchAreas(const fvPatch& fvp) const;
 
         //- Make patch weighting factors
         void makeWeights(scalarField&) const;
 
+        //- Correct patches after moving points
+        virtual void movePoints();
+
 
 public:
 
@@ -134,12 +137,7 @@ public:
             //- Return a reference to the AMI interpolator
             virtual const AMIPatchToPatchInterpolation& AMI() const
             {
-                const AMIPatchToPatchInterpolation& AMI =
-                    cyclicACMIPolyPatch_.AMI();
-
-                updateAreas();
-
-                return AMI;
+                return cyclicACMIPolyPatch_.AMI();
             }
 
             //- Are the cyclic planes parallel
@@ -169,8 +167,8 @@ public:
             }
 
             //- Return true if this patch is coupled. This is equivalent
-            //  to the coupledPolyPatch::coupled() if parallel running or
-            //  both sides present, false otherwise
+            //- to the coupledPolyPatch::coupled() if parallel running or
+            //- both sides present, false otherwise
             virtual bool coupled() const;
 
             //- Return delta (P to N) vectors across coupled patch
@@ -183,8 +181,6 @@ public:
                 const Field<Type>& fld
             ) const
             {
-                updateAreas();
-
                 return
                     cyclicACMIPolyPatch_.cyclicAMIPolyPatch::interpolate
                     (
@@ -192,7 +188,7 @@ public:
                     );
             }
 
-            //- Interpolate (make sure to have uptodate areas)
+            //- Interpolate (make sure to have up-to-date areas)
             template<class Type>
             tmp<Field<Type>> interpolate
             (
@@ -206,7 +202,7 @@ public:
         // Interface transfer functions
 
             //- Return the values of the given internal data adjacent to
-            //  the interface as a field
+            //- the interface as a field
             virtual tmp<labelField> interfaceInternalField
             (
                 const labelUList& internalData
diff --git a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C
index 356c61db10f7e9ed761916ed54bb4cbbb417b6bc..73398e753e08eb40f3cc967b726250606a8084a5 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2019-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,6 +30,7 @@ License
 #include "addToRunTimeSelectionTable.H"
 #include "fvMesh.H"
 #include "transform.H"
+#include "surfaceFields.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -82,8 +84,9 @@ void Foam::cyclicAMIFvPatch::makeWeights(scalarField& w) const
 
         forAll(deltas, facei)
         {
-            scalar di = deltas[facei];
-            scalar dni = nbrDeltas[facei];
+            // Note use of mag
+            scalar di = mag(deltas[facei]);
+            scalar dni = mag(nbrDeltas[facei]);
 
             w[facei] = dni/(di + dni);
         }
@@ -96,6 +99,26 @@ void Foam::cyclicAMIFvPatch::makeWeights(scalarField& w) const
 }
 
 
+void Foam::cyclicAMIFvPatch::makeDeltaCoeffs(scalarField& coeffs) const
+{
+    // Apply correction to default coeffs
+}
+
+
+void Foam::cyclicAMIFvPatch::makeNonOrthoDeltaCoeffs(scalarField& coeffs) const
+{
+    // Apply correction to default coeffs
+    //coeffs = Zero;
+}
+
+
+void Foam::cyclicAMIFvPatch::makeNonOrthoCorrVectors(vectorField& vecs) const
+{
+    // Apply correction to default vectors
+    //vecs = Zero;
+}
+
+
 Foam::tmp<Foam::vectorField> Foam::cyclicAMIFvPatch::delta() const
 {
     const cyclicAMIFvPatch& nbrPatch = neighbFvPatch();
@@ -121,7 +144,7 @@ Foam::tmp<Foam::vectorField> Foam::cyclicAMIFvPatch::delta() const
 
         const vectorField& nbrPatchD = tnbrPatchD();
 
-        tmp<vectorField> tpdv(new vectorField(patchD.size()));
+        auto tpdv = tmp<vectorField>::New(patchD.size());
         vectorField& pdv = tpdv.ref();
 
         // do the transformation if necessary
@@ -174,4 +197,75 @@ Foam::tmp<Foam::labelField> Foam::cyclicAMIFvPatch::internalFieldTransfer
 }
 
 
+void Foam::cyclicAMIFvPatch::movePoints()
+{
+    if (!owner() || !cyclicAMIPolyPatch_.createAMIFaces())
+    {
+        // Only manipulating patch face areas and mesh motion flux if the AMI
+        // creates additional faces
+        return;
+    }
+
+    // Update face data based on values set by the AMI manipulations
+    const_cast<vectorField&>(Sf()) = cyclicAMIPolyPatch_.faceAreas();
+    const_cast<vectorField&>(Cf()) = cyclicAMIPolyPatch_.faceCentres();
+    const_cast<scalarField&>(magSf()) = mag(Sf());
+
+    const cyclicAMIFvPatch& nbr = neighbPatch();
+    const_cast<vectorField&>(nbr.Sf()) = nbr.cyclicAMIPatch().faceAreas();
+    const_cast<vectorField&>(nbr.Cf()) = nbr.cyclicAMIPatch().faceCentres();
+    const_cast<scalarField&>(nbr.magSf()) = mag(nbr.Sf());
+
+
+    // Set consitent mesh motion flux
+    // TODO: currently maps src mesh flux to tgt - update to
+    // src = src + mapped(tgt) and tgt = tgt + mapped(src)?
+
+    const fvMesh& mesh = boundaryMesh().mesh();
+    surfaceScalarField& meshPhi = const_cast<fvMesh&>(mesh).setPhi();
+    surfaceScalarField::Boundary& meshPhiBf = meshPhi.boundaryFieldRef();
+
+    if (cyclicAMIPolyPatch_.owner())
+    {
+        scalarField& phip = meshPhiBf[patch().index()];
+        forAll(phip, facei)
+        {
+            const face& f = cyclicAMIPolyPatch_.localFaces()[facei];
+
+            // Note: using raw point locations to calculate the geometric
+            // area - faces areas are currently scaled by the AMI weights
+            // (decoupled from mesh points)
+            const scalar geomArea = f.mag(cyclicAMIPolyPatch_.localPoints());
+
+            const scalar scaledArea = magSf()[facei];
+            phip[facei] *= scaledArea/geomArea;
+        }
+
+        scalarField srcMeshPhi(phip);
+        if (Pstream::parRun())
+        {
+            AMI().srcMap().distribute(srcMeshPhi);
+        }
+
+        const labelListList& tgtToSrcAddr = AMI().tgtAddress();
+        scalarField& nbrPhip = meshPhiBf[nbr.index()];
+
+        forAll(tgtToSrcAddr, tgtFacei)
+        {
+            // Note: now have 1-to-1 mapping so tgtToSrcAddr[tgtFacei] is size 1
+            const label srcFacei = tgtToSrcAddr[tgtFacei][0];
+            nbrPhip[tgtFacei] = -srcMeshPhi[srcFacei];
+        }
+
+        DebugInfo
+            << "patch:" << patch().name()
+            << " sum(area):" << gSum(magSf())
+            << " min(mag(faceAreas):" << gMin(magSf())
+            << " sum(meshPhi):" << gSum(phip) << nl
+            << " sum(nbrMeshPhi):" << gSum(nbrPhip) << nl
+            << endl;
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H
index 2b643895982dbf9bedab765a25894d67dc8a79e4..1c3b46ab574592ac1a30dd22373de263d1532422 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H
@@ -68,6 +68,18 @@ protected:
         //- Make patch weighting factors
         void makeWeights(scalarField&) const;
 
+        //- Correct patch deltaCoeffs
+        virtual void makeDeltaCoeffs(scalarField&) const;
+
+        //- Correct patch non-ortho deltaCoeffs
+        virtual void makeNonOrthoDeltaCoeffs(scalarField&) const;
+
+        //- Correct patch non-ortho correction vectors
+        virtual void makeNonOrthoCorrVectors(vectorField&) const;
+
+        //- Correct patches after moving points
+        virtual void movePoints();
+
 
 public:
 
@@ -156,8 +168,8 @@ public:
             }
 
             //- Return true if this patch is coupled. This is equivalent
-            //  to the coupledPolyPatch::coupled() if parallel running or
-            //  both sides present, false otherwise
+            //- to the coupledPolyPatch::coupled() if parallel running or
+            //- both sides present, false otherwise
             virtual bool coupled() const;
 
             //- Return delta (P to N) vectors across coupled patch
@@ -187,7 +199,7 @@ public:
         // Interface transfer functions
 
             //- Return the values of the given internal data adjacent to
-            //  the interface as a field
+            //- the interface as a field
             virtual tmp<labelField> interfaceInternalField
             (
                 const labelUList& internalData
diff --git a/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.C b/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.C
index 2b0b8ee5cba6e28b171382559c7eb6c205e1c5b4..b100323976d3f15a0bafe2adc491f0e7b06a119a 100644
--- a/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.C
+++ b/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.C
@@ -167,6 +167,18 @@ void Foam::fvPatch::makeWeights(scalarField& w) const
 }
 
 
+void Foam::fvPatch::makeDeltaCoeffs(scalarField& w) const
+{}
+
+
+void Foam::fvPatch::makeNonOrthoDeltaCoeffs(scalarField& w) const
+{}
+
+
+void Foam::fvPatch::makeNonOrthoCorrVectors(vectorField& w) const
+{}
+
+
 void Foam::fvPatch::initMovePoints()
 {}
 
diff --git a/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.H b/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.H
index 7f1e99775f9beda654b35a3285f5812bcdf4b442..3a04adfb38f06e0dec75600fbe38a75b4d2082a9 100644
--- a/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.H
+++ b/src/finiteVolume/fvMesh/fvPatches/fvPatch/fvPatch.H
@@ -81,6 +81,15 @@ protected:
         //- Make patch weighting factors
         virtual void makeWeights(scalarField&) const;
 
+        //- Correct patch deltaCoeffs
+        virtual void makeDeltaCoeffs(scalarField&) const;
+
+        //- Correct patch non-ortho deltaCoeffs
+        virtual void makeNonOrthoDeltaCoeffs(scalarField&) const;
+
+        //- Correct patch non-ortho correction vectors
+        virtual void makeNonOrthoCorrVectors(vectorField&) const;
+
         //- Initialise the patches for moving points
         virtual void initMovePoints();
 
diff --git a/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C b/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C
index 2b31e5c66bded5a82ef8aef4f05d36121a1e54ff..7642933971866680ca7687b0ba82604db92cf956 100644
--- a/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C
+++ b/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C
@@ -172,7 +172,6 @@ void Foam::surfaceInterpolation::makeWeights() const
 
     // ... and reference to the internal field of the weighting factors
     scalarField& w = weights.primitiveFieldRef();
-
     forAll(owner, facei)
     {
         // Note: mag in the dot-product.
@@ -247,7 +246,11 @@ void Foam::surfaceInterpolation::makeDeltaCoeffs() const
 
     forAll(deltaCoeffsBf, patchi)
     {
-        deltaCoeffsBf[patchi] = 1.0/mag(mesh_.boundary()[patchi].delta());
+        const fvPatch& p = mesh_.boundary()[patchi];
+        deltaCoeffsBf[patchi] = 1.0/mag(p.delta());
+
+        // Optionally correct
+        p.makeDeltaCoeffs(deltaCoeffsBf[patchi]);
     }
 }
 
@@ -330,6 +333,9 @@ void Foam::surfaceInterpolation::makeNonOrthDeltaCoeffs() const
             patchDeltaCoeffs[patchFacei] =
                 1.0/max(unitArea & delta, 0.05*mag(delta));
         }
+
+        // Optionally correct
+        p.makeNonOrthoDeltaCoeffs(patchDeltaCoeffs);
     }
 }
 
@@ -386,6 +392,8 @@ void Foam::surfaceInterpolation::makeNonOrthCorrectionVectors() const
     {
         fvsPatchVectorField& patchCorrVecs = corrVecsBf[patchi];
 
+        const fvPatch& p = patchCorrVecs.patch();
+
         if (!patchCorrVecs.coupled())
         {
             patchCorrVecs = Zero;
@@ -395,8 +403,6 @@ void Foam::surfaceInterpolation::makeNonOrthCorrectionVectors() const
             const fvsPatchScalarField& patchNonOrthDeltaCoeffs =
                 NonOrthDeltaCoeffs.boundaryField()[patchi];
 
-            const fvPatch& p = patchCorrVecs.patch();
-
             const vectorField patchDeltas(mesh_.boundary()[patchi].delta());
 
             forAll(p, patchFacei)
@@ -411,6 +417,9 @@ void Foam::surfaceInterpolation::makeNonOrthCorrectionVectors() const
                     unitArea - delta*patchNonOrthDeltaCoeffs[patchFacei];
             }
         }
+
+        // Optionally correct
+        p.makeNonOrthoCorrVectors(patchCorrVecs);
     }
 
     if (debug)
diff --git a/src/functionObjects/field/mapFields/mapFields.C b/src/functionObjects/field/mapFields/mapFields.C
index 66fa2370379f46ab1f23db806b93330c2ed7f876..02170a93c84b1ab56f38ed32262f150198c764a8 100644
--- a/src/functionObjects/field/mapFields/mapFields.C
+++ b/src/functionObjects/field/mapFields/mapFields.C
@@ -86,11 +86,7 @@ void Foam::functionObjects::mapFields::createInterpolation
     );
 
     // Lookup corresponding AMI method
-    word patchMapMethodName =
-        AMIPatchToPatchInterpolation::interpolationMethodNames_
-        [
-            meshToMesh::interpolationMethodAMI(mapMethod)
-        ];
+    word patchMapMethodName = meshToMesh::interpolationMethodAMI(mapMethod);
 
     // Optionally override
     if (dict.readIfPresent("patchMapMethod", patchMapMethodName))
diff --git a/src/lagrangian/basic/particle/particle.C b/src/lagrangian/basic/particle/particle.C
index 42138a46b11dabb3090c740b13d1c4a5ef0c3d2e..6588607fbb283331e8d2bac294543a6a665ecb7b 100644
--- a/src/lagrangian/basic/particle/particle.C
+++ b/src/lagrangian/basic/particle/particle.C
@@ -31,6 +31,7 @@ License
 #include "treeDataCell.H"
 #include "cubicEqn.H"
 #include "registerSwitch.H"
+#include "indexedOctree.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
index b7c297829447d505c46765b0afa9119056d508fa..f01f8570da98bf03348f4edf895e0affa672234c 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2015-2018 OpenCFD Ltd.
+    Copyright (C) 2015-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,78 +27,104 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "AMIInterpolation.H"
-#include "AMIMethod.H"
 #include "meshTools.H"
 #include "mapDistribute.H"
 #include "flipOp.H"
 #include "profiling.H"
+#include "triPointRef.H"
+#include "OFstream.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-const Foam::Enum
-<
-    typename Foam::AMIInterpolation<SourcePatch, TargetPatch>::
-    interpolationMethod
->
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolationMethodNames_
-({
-    { interpolationMethod::imDirect, "directAMI" },
-    { interpolationMethod::imMapNearest, "mapNearestAMI" },
-    { interpolationMethod::imFaceAreaWeight, "faceAreaWeightAMI" },
-    { interpolationMethod::imPartialFaceAreaWeight, "partialFaceAreaWeightAMI" }
-});
-
-template<class SourcePatch, class TargetPatch>
-bool Foam::AMIInterpolation<SourcePatch, TargetPatch>::cacheIntersections_ =
-    false;
-
-template<class SourcePatch, class TargetPatch>
-template<class Patch>
-Foam::tmp<Foam::scalarField>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::patchMagSf
+namespace Foam
+{
+    defineTypeNameAndDebug(AMIInterpolation, 0);
+    defineRunTimeSelectionTable(AMIInterpolation, dict);
+    defineRunTimeSelectionTable(AMIInterpolation, component);
+}
+
+bool Foam::AMIInterpolation::cacheIntersections_ = false;
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::indexedOctree<Foam::AMIInterpolation::treeType>>
+Foam::AMIInterpolation::createTree
 (
-    const Patch& patch,
-    const faceAreaIntersect::triangulationMode triMode
-)
+    const primitivePatch& patch
+) const
 {
-    tmp<scalarField> tResult(new scalarField(patch.size(), Zero));
-    scalarField& result = tResult.ref();
+    treeBoundBox bb(patch.points(), patch.meshPoints());
+    bb.inflate(0.01);
+
+    return autoPtr<indexedOctree<treeType>>::New
+    (
+        treeType
+        (
+            false,
+            patch,
+            indexedOctree<treeType>::perturbTol()
+        ),
+        bb,                         // overall search domain
+        8,                          // maxLevel
+        10,                         // leaf size
+        3.0                         // duplicity
+    );
+}
 
-    const pointField& patchPoints = patch.localPoints();
 
-    faceList patchFaceTris;
+Foam::label Foam::AMIInterpolation::calcDistribution
+(
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch
+) const
+{
+    label proci = 0;
 
-    forAll(result, patchFacei)
+    if (Pstream::parRun())
     {
-        faceAreaIntersect::triangulate
-        (
-            patch.localFaces()[patchFacei],
-            patchPoints,
-            triMode,
-            patchFaceTris
-        );
+        labelList facesPresentOnProc(Pstream::nProcs(), Zero);
+        if ((srcPatch.size() > 0) || (tgtPatch.size() > 0))
+        {
+            facesPresentOnProc[Pstream::myProcNo()] = 1;
+        }
+        else
+        {
+            facesPresentOnProc[Pstream::myProcNo()] = 0;
+        }
+
+        Pstream::gatherList(facesPresentOnProc);
+        Pstream::scatterList(facesPresentOnProc);
+
+        label nHaveFaces = sum(facesPresentOnProc);
 
-        forAll(patchFaceTris, i)
+        if (nHaveFaces > 1)
         {
-            result[patchFacei] +=
-                triPointRef
-                (
-                    patchPoints[patchFaceTris[i][0]],
-                    patchPoints[patchFaceTris[i][1]],
-                    patchPoints[patchFaceTris[i][2]]
-                ).mag();
+            proci = -1;
+            if (debug)
+            {
+                InfoInFunction
+                    << "AMI split across multiple processors" << endl;
+            }
+        }
+        else if (nHaveFaces == 1)
+        {
+            proci = facesPresentOnProc.find(1);
+            if (debug)
+            {
+                InfoInFunction
+                    << "AMI local to processor" << proci << endl;
+            }
         }
     }
 
-    return tResult;
-}
 
+    // Either not parallel or no faces on any processor
+    return proci;
+}
 
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::projectPointsToSurface
+void Foam::AMIInterpolation::projectPointsToSurface
 (
     const searchableSurface& surf,
     pointField& pts
@@ -138,8 +164,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::projectPointsToSurface
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
+void Foam::AMIInterpolation::normaliseWeights
 (
     const scalarList& patchAreas,
     const word& patchName,
@@ -167,7 +192,6 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
 
             scalar s = sum(w);
             scalar t = s/denom;
-
             if (conformal)
             {
                 denom = s;
@@ -179,10 +203,9 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
             }
 
             wghtSum[facei] = t;
-
             if (t < lowWeightTol)
             {
-                nLowWeight++;
+                ++nLowWeight;
             }
         }
         else
@@ -191,7 +214,6 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
         }
     }
 
-
     if (output)
     {
         const label nFace = returnReduce(wght.size(), sumOp<label>());
@@ -220,8 +242,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
+void Foam::AMIInterpolation::agglomerate
 (
     const autoPtr<mapDistribute>& targetMapPtr,
     const scalarList& fineSrcMagSf,
@@ -264,7 +285,6 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
         }
     }
 
-
     // Agglomerate weights and indices
     if (targetMapPtr.valid())
     {
@@ -298,7 +318,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
         //           the slots are equal to face indices.
         // A mapDistribute has:
         // - a subMap : these are face indices
-        // - a constructMap : these are from 'transferred-date' to slots
+        // - a constructMap : these are from 'transferred-data' to slots
 
         labelListList tgtSubMap(Pstream::nProcs());
 
@@ -492,10 +512,10 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
 
             forAll(elems, i)
             {
-                label elemi = elems[i];
-                label coarseElemi = targetRestrictAddressing[elemi];
+                const label elemi = elems[i];
+                const label coarseElemi = targetRestrictAddressing[elemi];
 
-                label index = newElems.find(coarseElemi);
+                const label index = newElems.find(coarseElemi);
                 if (index == -1)
                 {
                     newElems.append(coarseElemi);
@@ -524,235 +544,87 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::constructFromSurface
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const autoPtr<searchableSurface>& surfPtr
-)
-{
-    if (surfPtr.valid())
-    {
-        // Create new patches for source and target
-        pointField srcPoints = srcPatch.points();
-        SourcePatch srcPatch0
-        (
-            SubList<face>
-            (
-                srcPatch,
-                srcPatch.size(),
-                0
-            ),
-            srcPoints
-        );
-
-        if (debug)
-        {
-            OFstream os("amiSrcPoints.obj");
-            for (const point& pt : srcPoints)
-            {
-                meshTools::writeOBJ(os, pt);
-            }
-        }
-
-        pointField tgtPoints = tgtPatch.points();
-        TargetPatch tgtPatch0
-        (
-            SubList<face>
-            (
-                tgtPatch,
-                tgtPatch.size(),
-                0
-            ),
-            tgtPoints
-        );
-
-        if (debug)
-        {
-            OFstream os("amiTgtPoints.obj");
-            for (const point& pt : tgtPoints)
-            {
-                meshTools::writeOBJ(os, pt);
-            }
-        }
-
-
-        // Map source and target patches onto projection surface
-        projectPointsToSurface(surfPtr(), srcPoints);
-        projectPointsToSurface(surfPtr(), tgtPoints);
-
-
-        // Calculate AMI interpolation
-        update(srcPatch0, tgtPatch0);
-    }
-    else
-    {
-        update(srcPatch, tgtPatch);
-    }
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
+Foam::AMIInterpolation::AMIInterpolation
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool requireMatch,
-    const interpolationMethod& method,
-    const scalar lowWeightCorrection,
+    const dictionary& dict,
     const bool reverseTarget
 )
 :
-    methodName_(interpolationMethodNames_[method]),
-    reverseTarget_(reverseTarget),
-    requireMatch_(requireMatch),
+    requireMatch_(dict.getOrDefault("requireMatch", true)),
+    reverseTarget_(dict.getOrDefault("reverseTarget", reverseTarget)),
+    lowWeightCorrection_(dict.getOrDefault<scalar>("lowWeightCorrection", -1)),
     singlePatchProc_(-999),
-    lowWeightCorrection_(lowWeightCorrection),
     srcMagSf_(),
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
-    tgtMagSf_(),
-    tgtAddress_(),
-    tgtWeights_(),
-    tgtWeightsSum_(),
-    triMode_(triMode),
+    srcCentroids_(),
     srcMapPtr_(nullptr),
-    tgtMapPtr_(nullptr)
-{
-    update(srcPatch, tgtPatch);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool requireMatch,
-    const word& methodName,
-    const scalar lowWeightCorrection,
-    const bool reverseTarget
-)
-:
-    methodName_(methodName),
-    reverseTarget_(reverseTarget),
-    requireMatch_(requireMatch),
-    singlePatchProc_(-999),
-    lowWeightCorrection_(lowWeightCorrection),
-    srcMagSf_(),
-    srcAddress_(),
-    srcWeights_(),
-    srcWeightsSum_(),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
-    triMode_(triMode),
-    srcMapPtr_(nullptr),
-    tgtMapPtr_(nullptr)
-{
-    update(srcPatch, tgtPatch);
-}
+    tgtCentroids_(),
+    tgtMapPtr_(nullptr),
+    upToDate_(false)
+{}
 
 
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
+Foam::AMIInterpolation::AMIInterpolation
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const autoPtr<searchableSurface>& surfPtr,
-    const faceAreaIntersect::triangulationMode& triMode,
     const bool requireMatch,
-    const interpolationMethod& method,
-    const scalar lowWeightCorrection,
-    const bool reverseTarget
+    const bool reverseTarget,
+    const scalar lowWeightCorrection
 )
 :
-    methodName_(interpolationMethodNames_[method]),
-    reverseTarget_(reverseTarget),
     requireMatch_(requireMatch),
-    singlePatchProc_(-999),
-    lowWeightCorrection_(lowWeightCorrection),
-    srcMagSf_(),
-    srcAddress_(),
-    srcWeights_(),
-    srcWeightsSum_(),
-    tgtMagSf_(),
-    tgtAddress_(),
-    tgtWeights_(),
-    tgtWeightsSum_(),
-    triMode_(triMode),
-    srcMapPtr_(nullptr),
-    tgtMapPtr_(nullptr)
-{
-    constructFromSurface(srcPatch, tgtPatch, surfPtr);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const autoPtr<searchableSurface>& surfPtr,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool requireMatch,
-    const word& methodName,
-    const scalar lowWeightCorrection,
-    const bool reverseTarget
-)
-:
-    methodName_(methodName),
     reverseTarget_(reverseTarget),
-    requireMatch_(requireMatch),
-    singlePatchProc_(-999),
     lowWeightCorrection_(lowWeightCorrection),
+    singlePatchProc_(-999),
     srcMagSf_(),
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcCentroids_(),
+    srcPatchPts_(),
+    srcMapPtr_(nullptr),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
-    triMode_(triMode),
-    srcMapPtr_(nullptr),
-    tgtMapPtr_(nullptr)
-{
-    constructFromSurface(srcPatch, tgtPatch, surfPtr);
-}
+    tgtCentroids_(),
+    tgtPatchPts_(),
+    tgtMapPtr_(nullptr),
+    upToDate_(false)
+{}
 
 
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
+Foam::AMIInterpolation::AMIInterpolation
 (
-    const AMIInterpolation<SourcePatch, TargetPatch>& fineAMI,
+    const AMIInterpolation& fineAMI,
     const labelList& sourceRestrictAddressing,
     const labelList& targetRestrictAddressing
 )
 :
-    methodName_(fineAMI.methodName_),
-    reverseTarget_(fineAMI.reverseTarget_),
     requireMatch_(fineAMI.requireMatch_),
-    singlePatchProc_(fineAMI.singlePatchProc_),
+    reverseTarget_(fineAMI.reverseTarget_),
     lowWeightCorrection_(-1.0),
+    singlePatchProc_(fineAMI.singlePatchProc_),
     srcMagSf_(),
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcPatchPts_(),
+    srcMapPtr_(nullptr),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
-    triMode_(fineAMI.triMode_),
-    srcMapPtr_(nullptr),
-    tgtMapPtr_(nullptr)
+    tgtPatchPts_(),
+    tgtMapPtr_(nullptr),
+    upToDate_(false)
 {
     label sourceCoarseSize =
     (
@@ -834,23 +706,67 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
 }
 
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::~AMIInterpolation()
+Foam::AMIInterpolation::AMIInterpolation(const AMIInterpolation& ami)
+:
+    requireMatch_(ami.requireMatch_),
+    reverseTarget_(ami.reverseTarget_),
+    lowWeightCorrection_(ami.lowWeightCorrection_),
+    singlePatchProc_(ami.singlePatchProc_),
+    srcMagSf_(ami.srcMagSf_),
+    srcAddress_(ami.srcAddress_),
+    srcWeights_(ami.srcWeights_),
+    srcWeightsSum_(ami.srcWeightsSum_),
+    srcCentroids_(ami.srcCentroids_),
+    srcMapPtr_(nullptr),
+    tgtMagSf_(ami.tgtMagSf_),
+    tgtAddress_(ami.tgtAddress_),
+    tgtWeights_(ami.tgtWeights_),
+    tgtWeightsSum_(ami.tgtWeightsSum_),
+    tgtCentroids_(ami.tgtCentroids_),
+    tgtMapPtr_(nullptr),
+    upToDate_(false)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
+bool Foam::AMIInterpolation::calculate
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
+    const autoPtr<searchableSurface>& surfPtr
 )
 {
-    addProfiling(ami, "AMIInterpolation::update");
+    if (upToDate_)
+    {
+        return false;
+    }
+
+    addProfiling(ami, "AMIInterpolation::calculate");
+
+    if (surfPtr)
+    {
+        srcPatchPts_ = srcPatch.points();
+        projectPointsToSurface(surfPtr(), srcPatchPts_);
+        tsrcPatch0_ = tmpNrc<primitivePatch>::New
+        (
+            SubList<face>(srcPatch),
+            srcPatchPts_
+        );
+
+        tgtPatchPts_ = tgtPatch.points();
+        projectPointsToSurface(surfPtr(), tgtPatchPts_);
+        ttgtPatch0_ = tmpNrc<primitivePatch>::New
+        (
+            SubList<face>(tgtPatch),
+            tgtPatchPts_
+        );
+    }
+    else
+    {
+        tsrcPatch0_.cref(srcPatch);
+        ttgtPatch0_.cref(tgtPatch);
+    }
 
     label srcTotalSize = returnReduce(srcPatch.size(), sumOp<label>());
     label tgtTotalSize = returnReduce(tgtPatch.size(), sumOp<label>());
@@ -860,7 +776,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
         DebugInfo<< "AMI: no source faces present - no addressing constructed"
             << endl;
 
-        return;
+        return false;
     }
 
     Info<< indent
@@ -869,219 +785,70 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
         << tgtTotalSize << " target faces"
         << endl;
 
-    // Calculate if patches present on multiple processors
     singlePatchProc_ = calcDistribution(srcPatch, tgtPatch);
 
-    if (singlePatchProc_ == -1)
+    if (debug)
     {
-        // Convert local addressing to global addressing
-        globalIndex globalSrcFaces(srcPatch.size());
-        globalIndex globalTgtFaces(tgtPatch.size());
-
-        // Create processor map of overlapping faces. This map gets
-        // (possibly remote) faces from the tgtPatch such that they (together)
-        // cover all of the srcPatch
-        autoPtr<mapDistribute> mapPtr = calcProcMap(srcPatch, tgtPatch);
-        const mapDistribute& map = mapPtr();
-
-        // Create new target patch that fully encompasses source patch
-
-        // Faces and points
-        faceList newTgtFaces;
-        pointField newTgtPoints;
-
-        // Original faces from tgtPatch (in globalIndexing since might be
-        // remote)
-        labelList tgtFaceIDs;
-        distributeAndMergePatches
-        (
-            map,
-            tgtPatch,
-            globalTgtFaces,
-            newTgtFaces,
-            newTgtPoints,
-            tgtFaceIDs
-        );
-
-        const TargetPatch
-            newTgtPatch
-            (
-                SubList<face>
-                (
-                    newTgtFaces,
-                    newTgtFaces.size()
-                ),
-                newTgtPoints
-            );
-
-        // Calculate AMI interpolation
-        autoPtr<AMIMethod<SourcePatch, TargetPatch>> AMIPtr
-        (
-            AMIMethod<SourcePatch, TargetPatch>::New
-            (
-                methodName_,
-                srcPatch,
-                newTgtPatch,
-                triMode_,
-                reverseTarget_,
-                requireMatch_ && (lowWeightCorrection_ < 0)
-            )
-        );
-
-        AMIPtr->calculate
-        (
-            srcAddress_,
-            srcWeights_,
-            tgtAddress_,
-            tgtWeights_
-        );
-
-
-        // Note: using patch face areas calculated by the AMI method
-        // - TODO: move into the calculate or normalise method?
-        AMIPtr->setMagSf(tgtPatch, map, srcMagSf_, tgtMagSf_);
-
-
-        // Now
-        // ~~~
-        //  srcAddress_ :   per srcPatch face a list of the newTgtPatch (not
-        //                  tgtPatch) faces it overlaps
-        //  tgtAddress_ :   per newTgtPatch (not tgtPatch) face a list of the
-        //                  srcPatch faces it overlaps
-
-        if (debug)
-        {
-            writeFaceConnectivity(srcPatch, newTgtPatch, srcAddress_);
-        }
-
-
-        // Rework newTgtPatch indices into globalIndices of tgtPatch
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
-        for (labelList& addressing : srcAddress_)
-        {
-            for (label& addr : addressing)
-            {
-                addr = tgtFaceIDs[addr];
-            }
-        }
-
-        for (labelList& addressing : tgtAddress_)
-        {
-            globalSrcFaces.inplaceToGlobal(addressing);
-        }
+        Info<< "AMIInterpolation:" << nl
+            << "    singlePatchProc:" << singlePatchProc_ << nl
+            << endl;
+    }
 
-        // Send data back to originating procs. Note that contributions
-        // from different processors get added (ListOps::appendEqOp)
+    return true;
+}
 
-        mapDistributeBase::distribute
-        (
-            Pstream::commsTypes::nonBlocking,
-            List<labelPair>(),
-            tgtPatch.size(),
-            map.constructMap(),
-            false,                      // has flip
-            map.subMap(),
-            false,                      // has flip
-            tgtAddress_,
-            ListOps::appendEqOp<label>(),
-            flipOp(),                   // flip operation
-            labelList()
-        );
 
-        mapDistributeBase::distribute
-        (
-            Pstream::commsTypes::nonBlocking,
-            List<labelPair>(),
-            tgtPatch.size(),
-            map.constructMap(),
-            false,
-            map.subMap(),
-            false,
-            tgtWeights_,
-            ListOps::appendEqOp<scalar>(),
-            flipOp(),
-            scalarList()
-        );
+void Foam::AMIInterpolation::reset
+(
+    autoPtr<mapDistribute>&& srcToTgtMap,
+    autoPtr<mapDistribute>&& tgtToSrcMap,
+    labelListList&& srcAddress,
+    scalarListList&& srcWeights,
+    labelListList&& tgtAddress,
+    scalarListList&& tgtWeights
+)
+{
+    DebugInFunction<< endl;
 
-        // weights normalisation
-        AMIPtr->normaliseWeights(true, *this);
+    srcAddress_.transfer(srcAddress);
+    srcWeights_.transfer(srcWeights);
+    tgtAddress_.transfer(tgtAddress);
+    tgtWeights_.transfer(tgtWeights);
 
-        // Cache maps and reset addresses
-        List<Map<label>> cMap;
-        srcMapPtr_.reset(new mapDistribute(globalSrcFaces, tgtAddress_, cMap));
-        tgtMapPtr_.reset(new mapDistribute(globalTgtFaces, srcAddress_, cMap));
-    }
-    else
+    // Reset the sums of the weights
+    srcWeightsSum_.setSize(srcWeights_.size());
+    forAll(srcWeights_, facei)
     {
-        // Calculate AMI interpolation
-        autoPtr<AMIMethod<SourcePatch, TargetPatch>> AMIPtr
-        (
-            AMIMethod<SourcePatch, TargetPatch>::New
-            (
-                methodName_,
-                srcPatch,
-                tgtPatch,
-                triMode_,
-                reverseTarget_,
-                requireMatch_ && (lowWeightCorrection_ < 0)
-            )
-        );
-
-        AMIPtr->calculate
-        (
-            srcAddress_,
-            srcWeights_,
-            tgtAddress_,
-            tgtWeights_
-        );
-
-        srcMagSf_.transfer(AMIPtr->srcMagSf());
-        tgtMagSf_.transfer(AMIPtr->tgtMagSf());
-
-        AMIPtr->normaliseWeights(true, *this);
+        srcWeightsSum_[facei] = sum(srcWeights_[facei]);
     }
 
-    if (debug)
+    tgtWeightsSum_.setSize(tgtWeights_.size());
+    forAll(tgtWeights_, facei)
     {
-        Info<< "AMIInterpolation : Constructed addressing and weights" << nl
-            << "    triMode        :"
-            << faceAreaIntersect::triangulationModeNames_[triMode_] << nl
-            << "    singlePatchProc:" << singlePatchProc_ << nl
-            << "    srcMagSf       :" << gSum(srcMagSf_) << nl
-            << "    tgtMagSf       :" << gSum(tgtMagSf_) << nl
-            << endl;
+        tgtWeightsSum_[facei] = sum(tgtWeights_[facei]);
     }
+
+    srcMapPtr_ = srcToTgtMap;
+    tgtMapPtr_ = tgtToSrcMap;
+
+    upToDate_ = true;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::append
+void Foam::AMIInterpolation::append
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch
 )
 {
     addProfiling(ami, "AMIInterpolation::append");
 
     // Create a new interpolation
-    autoPtr<AMIInterpolation<SourcePatch, TargetPatch>> newPtr
-    (
-        new AMIInterpolation<SourcePatch, TargetPatch>
-        (
-            srcPatch,
-            tgtPatch,
-            triMode_,
-            requireMatch_,
-            methodName_,
-            lowWeightCorrection_,
-            reverseTarget_
-        )
-    );
+    auto newPtr = clone();
+    newPtr->calculate(srcPatch, tgtPatch);
 
     // If parallel then combine the mapDistribution and re-index
-    if (singlePatchProc_ == -1)
+    if (distributed())
     {
         labelListList& srcSubMap = srcMapPtr_->subMap();
         labelListList& srcConstructMap = srcMapPtr_->constructMap();
@@ -1140,8 +907,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::append
             {
                 forAll(tgtAddress_[tgti], tgtj)
                 {
-                    tgtAddress_[tgti][tgtj] =
-                        mapMap[tgtAddress_[tgti][tgtj]];
+                    tgtAddress_[tgti][tgtj] = mapMap[tgtAddress_[tgti][tgtj]];
                 }
             }
 
@@ -1248,8 +1014,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::append
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
+void Foam::AMIInterpolation::normaliseWeights
 (
     const bool conformal,
     const bool output
@@ -1281,327 +1046,10 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::normaliseWeights
 }
 
 
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToTarget
-(
-    const UList<Type>& fld,
-    const CombineOp& cop,
-    List<Type>& result,
-    const UList<Type>& defaultValues
-) const
-{
-    addProfiling(ami, "AMIInterpolation::interpolateToTarget");
-
-    if (fld.size() != srcAddress_.size())
-    {
-        FatalErrorInFunction
-            << "Supplied field size is not equal to source patch size" << nl
-            << "    source patch   = " << srcAddress_.size() << nl
-            << "    target patch   = " << tgtAddress_.size() << nl
-            << "    supplied field = " << fld.size()
-            << abort(FatalError);
-    }
-
-    if (lowWeightCorrection_ > 0)
-    {
-        if (defaultValues.size() != tgtAddress_.size())
-        {
-            FatalErrorInFunction
-                << "Employing default values when sum of weights falls below "
-                << lowWeightCorrection_
-                << " but supplied default field size is not equal to target "
-                << "patch size" << nl
-                << "    default values = " << defaultValues.size() << nl
-                << "    target patch   = " << tgtAddress_.size() << nl
-                << abort(FatalError);
-        }
-    }
-
-    result.setSize(tgtAddress_.size());
-
-    if (singlePatchProc_ == -1)
-    {
-        const mapDistribute& map = srcMapPtr_();
-
-        List<Type> work(fld);
-        map.distribute(work);
-
-        forAll(result, facei)
-        {
-            if (tgtWeightsSum_[facei] < lowWeightCorrection_)
-            {
-                result[facei] = defaultValues[facei];
-            }
-            else
-            {
-                const labelList& faces = tgtAddress_[facei];
-                const scalarList& weights = tgtWeights_[facei];
-
-                forAll(faces, i)
-                {
-                    cop(result[facei], facei, work[faces[i]], weights[i]);
-                }
-            }
-        }
-    }
-    else
-    {
-        forAll(result, facei)
-        {
-            if (tgtWeightsSum_[facei] < lowWeightCorrection_)
-            {
-                result[facei] = defaultValues[facei];
-            }
-            else
-            {
-                const labelList& faces = tgtAddress_[facei];
-                const scalarList& weights = tgtWeights_[facei];
-
-                forAll(faces, i)
-                {
-                    cop(result[facei], facei, fld[faces[i]], weights[i]);
-                }
-            }
-        }
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
-(
-    const UList<Type>& fld,
-    const CombineOp& cop,
-    List<Type>& result,
-    const UList<Type>& defaultValues
-) const
-{
-    addProfiling(ami, "AMIInterpolation::interpolateToSource");
-
-    if (fld.size() != tgtAddress_.size())
-    {
-        FatalErrorInFunction
-            << "Supplied field size is not equal to target patch size" << nl
-            << "    source patch   = " << srcAddress_.size() << nl
-            << "    target patch   = " << tgtAddress_.size() << nl
-            << "    supplied field = " << fld.size()
-            << abort(FatalError);
-    }
-
-    if (lowWeightCorrection_ > 0)
-    {
-        if (defaultValues.size() != srcAddress_.size())
-        {
-            FatalErrorInFunction
-                << "Employing default values when sum of weights falls below "
-                << lowWeightCorrection_
-                << " but supplied default field size is not equal to target "
-                << "patch size" << nl
-                << "    default values = " << defaultValues.size() << nl
-                << "    source patch   = " << srcAddress_.size() << nl
-                << abort(FatalError);
-        }
-    }
-
-    result.setSize(srcAddress_.size());
-
-    if (singlePatchProc_ == -1)
-    {
-        const mapDistribute& map = tgtMapPtr_();
-
-        List<Type> work(fld);
-        map.distribute(work);
-
-        forAll(result, facei)
-        {
-            if (srcWeightsSum_[facei] < lowWeightCorrection_)
-            {
-                result[facei] = defaultValues[facei];
-            }
-            else
-            {
-                const labelList& faces = srcAddress_[facei];
-                const scalarList& weights = srcWeights_[facei];
-
-                forAll(faces, i)
-                {
-                    cop(result[facei], facei, work[faces[i]], weights[i]);
-                }
-            }
-        }
-    }
-    else
-    {
-        forAll(result, facei)
-        {
-            if (srcWeightsSum_[facei] < lowWeightCorrection_)
-            {
-                result[facei] = defaultValues[facei];
-            }
-            else
-            {
-                const labelList& faces = srcAddress_[facei];
-                const scalarList& weights = srcWeights_[facei];
-
-                forAll(faces, i)
-                {
-                    cop(result[facei], facei, fld[faces[i]], weights[i]);
-                }
-            }
-        }
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
+Foam::label Foam::AMIInterpolation::srcPointFace
 (
-    const Field<Type>& fld,
-    const CombineOp& cop,
-    const UList<Type>& defaultValues
-) const
-{
-    tmp<Field<Type>> tresult
-    (
-        new Field<Type>
-        (
-            srcAddress_.size(),
-            Zero
-        )
-    );
-
-    interpolateToSource
-    (
-        fld,
-        multiplyWeightedOp<Type, CombineOp>(cop),
-        tresult.ref(),
-        defaultValues
-    );
-
-    return tresult;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
-(
-    const tmp<Field<Type>>& tFld,
-    const CombineOp& cop,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToSource(tFld(), cop, defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToTarget
-(
-    const Field<Type>& fld,
-    const CombineOp& cop,
-    const UList<Type>& defaultValues
-) const
-{
-    tmp<Field<Type>> tresult
-    (
-        new Field<Type>
-        (
-            tgtAddress_.size(),
-            Zero
-        )
-    );
-
-    interpolateToTarget
-    (
-        fld,
-        multiplyWeightedOp<Type, CombineOp>(cop),
-        tresult.ref(),
-        defaultValues
-    );
-
-    return tresult;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type, class CombineOp>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToTarget
-(
-    const tmp<Field<Type>>& tFld,
-    const CombineOp& cop,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToTarget(tFld(), cop, defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
-(
-    const Field<Type>& fld,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToSource(fld, plusEqOp<Type>(), defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
-(
-    const tmp<Field<Type>>& tFld,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToSource(tFld(), plusEqOp<Type>(), defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToTarget
-(
-    const Field<Type>& fld,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToTarget(fld, plusEqOp<Type>(), defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-template<class Type>
-Foam::tmp<Foam::Field<Type>>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToTarget
-(
-    const tmp<Field<Type>>& tFld,
-    const UList<Type>& defaultValues
-) const
-{
-    return interpolateToTarget(tFld(), plusEqOp<Type>(), defaultValues);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::label Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcPointFace
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
     const vector& n,
     const label tgtFacei,
     point& tgtPoint
@@ -1645,11 +1093,10 @@ const
 }
 
 
-template<class SourcePatch, class TargetPatch>
-Foam::label Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtPointFace
+Foam::label Foam::AMIInterpolation::tgtPointFace
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
     const vector& n,
     const label srcFacei,
     point& srcPoint
@@ -1693,11 +1140,73 @@ const
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::writeFaceConnectivity
+bool Foam::AMIInterpolation::checkSymmetricWeights(const bool log) const
+{
+    if (Pstream::parRun() && (singlePatchProc_ == -1))
+    {
+        Log << "Checks only valid for serial running (currently)" << endl;
+
+        return true;
+    }
+
+    bool symmetricSrc = true;
+
+    Log << "    Checking for missing src face in tgt lists" << nl;
+
+    forAll(srcAddress_, srcFacei)
+    {
+        const labelList& tgtIds = srcAddress_[srcFacei];
+        for (const label tgtFacei : tgtIds)
+        {
+            if (!tgtAddress_[tgtFacei].found(srcFacei))
+            {
+                symmetricSrc = false;
+
+                Log << "       srcFacei:" << srcFacei
+                    << " not found in tgtToSrc list for tgtFacei:"
+                    << tgtFacei << nl;
+            }
+        }
+    }
+
+    if (symmetricSrc)
+    {
+        Log << "    - symmetric" << endl;
+    }
+
+    bool symmetricTgt = true;
+
+    Log << "    Checking for missing tgt face in src lists" << nl;
+
+    forAll(tgtAddress_, tgtFacei)
+    {
+        const labelList& srcIds = tgtAddress_[tgtFacei];
+        for (const label srcFacei : srcIds)
+        {
+            if (!srcAddress_[srcFacei].found(tgtFacei))
+            {
+                symmetricTgt = false;
+
+                Log << "       tgtFacei:" << tgtFacei
+                    << " not found in srcToTgt list for srcFacei:"
+                    << srcFacei << nl;
+            }
+        }
+    }
+
+    if (symmetricTgt)
+    {
+        Log << "    - symmetric" << endl;
+    }
+
+    return symmetricSrc && symmetricTgt;
+}
+
+
+void Foam::AMIInterpolation::writeFaceConnectivity
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
     const labelListList& srcAddress
 )
 const
@@ -1726,4 +1235,20 @@ const
 }
 
 
+void Foam::AMIInterpolation::write(Ostream& os) const
+{
+    os.writeEntry("AMIMethod", type());
+
+    if (reverseTarget_)
+    {
+        os.writeEntry("flipNormals", reverseTarget_);
+    }
+
+    if (lowWeightCorrection_ > 0)
+    {
+        os.writeEntry("lowWeightCorrection", lowWeightCorrection_);
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
index fcf418ff6bf2b67087a6c2cfbcfed0ca3c3fd8fb..8ce226bbb4f392b1fcf3bb66491c069a0548397b 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-2018 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -61,76 +61,53 @@ SourceFiles
 #include "globalIndex.H"
 #include "ops.H"
 #include "Enum.H"
+#include "pointList.H"
+#include "indexedOctree.H"
+#include "treeDataPrimitivePatch.H"
+
+#include "runTimeSelectionTables.H"
+
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
-/*---------------------------------------------------------------------------*\
-                    Class AMIInterpolationName Declaration
-\*---------------------------------------------------------------------------*/
-
-TemplateName(AMIInterpolation);
-
-
 /*---------------------------------------------------------------------------*\
                       Class AMIInterpolation Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class SourcePatch, class TargetPatch>
 class AMIInterpolation
-:
-    public AMIInterpolationName
 {
 public:
 
     // Public data types
 
-        //- Enumeration specifying interpolation method
-        enum interpolationMethod
-        {
-            imDirect,
-            imMapNearest,
-            imFaceAreaWeight,
-            imPartialFaceAreaWeight
-        };
-
-        static const Enum<interpolationMethod> interpolationMethodNames_;
-
         static bool cacheIntersections_;
 
-        //- Calculate the patch face magnitudes for the given tri-mode
-        template<class Patch>
-        static tmp<scalarField> patchMagSf
-        (
-            const Patch& patch,
-            const faceAreaIntersect::triangulationMode triMode
-        );
 
+protected:
 
-private:
+    //- Local typedef to octree tree-type
+    typedef treeDataPrimitivePatch<primitivePatch> treeType;
 
-    // Private data
+    // Protected data
 
-        //- Interpolation method
-        const word methodName_;
+        //- Flag to indicate that the two patches must be matched/an overlap
+        //- exists between them
+        bool requireMatch_;
 
         //- Flag to indicate that the two patches are co-directional and
         //- that the orientation of the target patch should be reversed
         const bool reverseTarget_;
 
-        //- Flag to indicate that the two patches must be matched/an overlap
-        //- exists between them
-        const bool requireMatch_;
+        //- Threshold weight below which interpolation is deactivated
+        scalar lowWeightCorrection_;
 
         //- Index of processor that holds all of both sides. -1 in all other
         //- cases
         label singlePatchProc_;
 
-        //- Threshold weight below which interpolation is deactivated
-        scalar lowWeightCorrection_;
-
 
         // Source patch
 
@@ -146,6 +123,19 @@ private:
             //- Sum of weights of target faces per source face
             scalarField srcWeightsSum_;
 
+            //- Centroid of target faces per source face
+            pointListList srcCentroids_;
+
+            //- Source patch points if input points are manipulated, e.g.
+            //- projected
+            pointField srcPatchPts_;
+
+            //- Source patch using manipulated input points
+            tmpNrc<primitivePatch> tsrcPatch0_;
+
+            //- Source map pointer - parallel running only
+            autoPtr<mapDistribute> srcMapPtr_;
+
 
         // Target patch
 
@@ -161,71 +151,44 @@ private:
             //- Sum of weights of source faces per target face
             scalarField tgtWeightsSum_;
 
+            //- Centroid of source faces per target face
+            pointListList tgtCentroids_;
 
-        //- Face triangulation mode
-        const faceAreaIntersect::triangulationMode triMode_;
+            //- Target patch points if input points are manipulated, e.g.
+            //- projected
+            pointField tgtPatchPts_;
 
-        //- Source map pointer - parallel running only
-        autoPtr<mapDistribute> srcMapPtr_;
+            //- Target patch using manipulated input points
+            tmpNrc<primitivePatch> ttgtPatch0_;
 
-        //- Target map pointer - parallel running only
-        autoPtr<mapDistribute> tgtMapPtr_;
+            //- Target map pointer - parallel running only
+            autoPtr<mapDistribute> tgtMapPtr_;
 
+        //- Up-to-date flag
+        bool upToDate_;
 
-    // Private Member Functions
 
-        //- No copy construct
-        AMIInterpolation(const AMIInterpolation&) = delete;
+    // Protected Member Functions
 
         //- No copy assignment
         void operator=(const AMIInterpolation&) = delete;
 
 
-        // Parallel functionality
-
-            //- Calculate if patches are on multiple processors
-            label calcDistribution
-            (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch
-            ) const;
-
-            label calcOverlappingProcs
-            (
-                const List<treeBoundBoxList>& procBb,
-                const treeBoundBox& bb,
-                boolList& overlaps
-            ) const;
-
-            void distributePatches
-            (
-                const mapDistribute& map,
-                const TargetPatch& pp,
-                const globalIndex& gi,
-                List<faceList>& faces,
-                List<pointField>& points,
-                List<labelList>& tgtFaceIDs
-            ) const;
+        // Initialisation
 
-            void distributeAndMergePatches
+            //- Reset the octree for the patch face search
+            autoPtr<indexedOctree<treeType>> createTree
             (
-                const mapDistribute& map,
-                const TargetPatch& tgtPatch,
-                const globalIndex& gi,
-                faceList& tgtFaces,
-                pointField& tgtPoints,
-                labelList& tgtFaceIDs
+                const primitivePatch& patch
             ) const;
 
-            autoPtr<mapDistribute> calcProcMap
+            //- Calculate if patches are on multiple processors
+            label calcDistribution
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch
             ) const;
 
-
-        // Initialisation
-
             //- Project points to surface
             void projectPointsToSurface
             (
@@ -234,6 +197,15 @@ private:
             ) const;
 
 
+        // Access
+
+            //- Return the orginal src patch with optionally updated points
+            inline const primitivePatch& srcPatch0() const;
+
+            //- Return the orginal tgt patch with optionally updated points
+            inline const primitivePatch& tgtPatch0() const;
+
+
         // Evaluation
 
             //- Normalise the (area) weights - suppresses numerical error in
@@ -273,96 +245,127 @@ private:
                 autoPtr<mapDistribute>& tgtMap
             );
 
-            void constructFromSurface
-            (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch,
-                const autoPtr<searchableSurface>& surfPtr
-            );
 
 public:
 
-    // Constructors
+    //- Runtime type information
+    TypeName("AMIInterpolation");
 
-        //- Construct from components
-        AMIInterpolation
+    // Selection tables
+
+        //- Selection table for dictionary construction
+        declareRunTimeSelectionTable
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
-            const bool requireMatch = true,
-            const interpolationMethod& method = imFaceAreaWeight,
-            const scalar lowWeightCorrection = -1,
+            autoPtr,
+            AMIInterpolation,
+            dict,
+            (
+                const dictionary& dict,
+                const bool reverseTarget
+            ),
+            (
+                dict,
+                reverseTarget
+            )
+        );
+
+        //- Selection table for component-wise construction
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            AMIInterpolation,
+            component,
+            (
+                const bool requireMatch,
+                const bool reverseTarget,
+                const scalar lowWeightCorrection
+            ),
+            (
+                requireMatch,
+                reverseTarget,
+                lowWeightCorrection
+            )
+        );
+
+        //- Selector for dictionary
+        static autoPtr<AMIInterpolation> New
+        (
+            const word& modelName,
+            const dictionary& dict,
             const bool reverseTarget = false
         );
 
-        //- Construct from components
-        AMIInterpolation
+        //- Selector for components
+        static autoPtr<AMIInterpolation> New
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
+            const word& modelName,
             const bool requireMatch = true,
-            const word& methodName =
-                interpolationMethodNames_[imFaceAreaWeight],
-            const scalar lowWeightCorrection = -1,
-            const bool reverseTarget = false
+            const bool reverseTarget = false,
+            const scalar lowWeightCorrection = -1
         );
 
-        //- Construct from components, with projection surface
+
+    // Constructors
+
+        //- Construct from dictionary
         AMIInterpolation
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const autoPtr<searchableSurface>& surf,
-            const faceAreaIntersect::triangulationMode& triMode,
-            const bool requireMatch = true,
-            const interpolationMethod& method = imFaceAreaWeight,
-            const scalar lowWeightCorrection = -1,
+            const dictionary& dict,
             const bool reverseTarget = false
         );
 
-        //- Construct from components, with projection surface
+        //- Construct from components
         AMIInterpolation
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const autoPtr<searchableSurface>& surf,
-            const faceAreaIntersect::triangulationMode& triMode,
             const bool requireMatch = true,
-            const word& methodName =
-                interpolationMethodNames_[imFaceAreaWeight],
-            const scalar lowWeightCorrection = -1,
-            const bool reverseTarget = false
+            const bool reverseTarget = false,
+            const scalar lowWeightCorrection = -1
         );
 
         //- Construct from agglomeration of AMIInterpolation. Agglomeration
-        //  passed in as new coarse size and addressing from fine from coarse
+        //- passed in as new coarse size and addressing from fine from coarse
         AMIInterpolation
         (
-            const AMIInterpolation<SourcePatch, TargetPatch>& fineAMI,
+            const AMIInterpolation& fineAMI,
             const labelList& sourceRestrictAddressing,
             const labelList& neighbourRestrictAddressing
         );
 
+        //- Construct as copy
+        AMIInterpolation(const AMIInterpolation& ami);
 
-    //- Destructor
-    ~AMIInterpolation();
+        //- Construct and return a clone
+        virtual autoPtr<AMIInterpolation> clone() const
+        {
+            return autoPtr<AMIInterpolation>::New(*this);
+        }
 
-    // Typedef to SourcePatch type this AMIInterpolation is instantiated on
-    typedef SourcePatch sourcePatchType;
 
-    // Typedef to TargetPatch type this AMIInterpolation is instantiated on
-    typedef TargetPatch targetPatchType;
+    //- Destructor
+    virtual ~AMIInterpolation() = default;
 
 
     // Member Functions
 
         // Access
 
-            //- Set to -1, or the processor holding all faces (both sides) of
-            //- the AMI
-            inline label singlePatchProc() const;
+            //- Access to the up-to-date flag
+            inline bool upToDate() const;
+
+            //- Access to the up-to-date flag
+            inline bool& upToDate();
+
+            //- Access to the distributed flag
+            inline bool distributed() const;
+
+            //- Access to the requireMatch flag
+            inline bool requireMatch() const;
+
+            //- Access to the requireMatch flag
+            inline bool setRequireMatch(const bool flag);
+
+            //- Access to the reverseTarget flag
+            inline bool reverseTarget() const;
 
             //- Threshold weight below which interpolation is deactivated
             inline scalar lowWeightCorrection() const;
@@ -370,6 +373,10 @@ public:
             //- Return true if employing a 'lowWeightCorrection'
             inline bool applyLowWeightCorrection() const;
 
+            //- Set to -1, or the processor holding all faces (both sides) of
+            //- the AMI
+            inline label singlePatchProc() const;
+
 
             // Source patch
 
@@ -399,6 +406,12 @@ public:
                 //- patch weights (i.e. the sum before normalisation)
                 inline scalarField& srcWeightsSum();
 
+                //- Return const access to source patch face centroids
+                inline const pointListList& srcCentroids() const;
+
+                //- Return access to source patch face centroids
+                inline pointListList& srcCentroids();
+
                 //- Source map pointer - valid only if singlePatchProc = -1
                 //- This gets source data into a form to be consumed by
                 //- tgtAddress, tgtWeights
@@ -441,18 +454,30 @@ public:
 
         // Manipulation
 
-            //- Update addressing and weights
-            void update
+            //- Update addressing, weights and (optional) centroids
+            virtual bool calculate
+            (
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch,
+                const autoPtr<searchableSurface>& surfPtr = nullptr
+            );
+
+            //- Set the maps, addresses and weights from an external source
+            void reset
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch
+                autoPtr<mapDistribute>&& srcToTgtMap,
+                autoPtr<mapDistribute>&& tgtToSrcMap,
+                labelListList&& srcAddress,
+                scalarListList&& srcWeights,
+                labelListList&& tgtAddress,
+                scalarListList&& tgtWeights
             );
 
             //- Append additional addressing and weights
             void append
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch
             );
 
             //- Normalise the weights
@@ -560,8 +585,8 @@ public:
             //- Return source patch face index of point on target patch face
             label srcPointFace
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch,
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch,
                 const vector& n,
                 const label tgtFacei,
                 point& tgtPoint
@@ -571,8 +596,8 @@ public:
             //- Return target patch face index of point on source patch face
             label tgtPointFace
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch,
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch,
                 const vector& n,
                 const label srcFacei,
                 point& srcPoint
@@ -582,13 +607,23 @@ public:
 
         // Checks
 
+            //- Check if src addresses are present in tgt addresses and
+            //- viceversa
+            bool checkSymmetricWeights(const bool log) const;
+
             //- Write face connectivity as OBJ file
             void writeFaceConnectivity
             (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch,
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch,
                 const labelListList& srcAddress
             ) const;
+
+
+        // I-O
+
+            //- Write
+            virtual void write(Ostream& os) const;
 };
 
 
@@ -603,8 +638,7 @@ public:
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 #ifdef NoRepository
-    #include "AMIInterpolation.C"
-    #include "AMIInterpolationParallelOps.C"
+    #include "AMIInterpolationTemplates.C"
 #endif
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
index 9cd5e51ff1dce34480e328ddd6cd7532e31c5fff..22823a23ec6d8d57f334b1731d8dba53d92506f9 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 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,170 +26,203 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::label
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::singlePatchProc() const
+inline const Foam::primitivePatch& Foam::AMIInterpolation::srcPatch0() const
 {
-    return singlePatchProc_;
+    if (!tsrcPatch0_.valid())
+    {
+        FatalErrorInFunction
+            << "tsrcPatch0Ptr_ not set"
+            << abort(FatalError);
+    }
+
+    return tsrcPatch0_();
+}
+
+
+inline const Foam::primitivePatch& Foam::AMIInterpolation::tgtPatch0() const
+{
+
+    if (!ttgtPatch0_.valid())
+    {
+        FatalErrorInFunction
+            << "ttgtPatch0Ptr_ not set"
+            << abort(FatalError);
+    }
+
+    return ttgtPatch0_();
+}
+
+
+inline bool Foam::AMIInterpolation::upToDate() const
+{
+    return upToDate_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::scalar
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::lowWeightCorrection() const
+inline bool& Foam::AMIInterpolation::upToDate()
+{
+    return upToDate_;
+}
+
+
+inline bool Foam::AMIInterpolation::distributed() const
+{
+    return singlePatchProc_ == -1;
+}
+
+
+inline bool Foam::AMIInterpolation::requireMatch() const
+{
+    return requireMatch_ && lowWeightCorrection_ < 0;
+}
+
+
+inline bool Foam::AMIInterpolation::setRequireMatch(const bool flag)
+{
+    requireMatch_ = flag;
+    return requireMatch_;
+}
+
+
+inline bool Foam::AMIInterpolation::reverseTarget() const
+{
+    return reverseTarget_;
+}
+
+
+inline Foam::scalar Foam::AMIInterpolation::lowWeightCorrection() const
 {
     return lowWeightCorrection_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline bool
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::
-applyLowWeightCorrection() const
+inline bool Foam::AMIInterpolation::applyLowWeightCorrection() const
 {
     return lowWeightCorrection_ > 0;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::List<Foam::scalar>&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcMagSf() const
+inline Foam::label Foam::AMIInterpolation::singlePatchProc() const
+{
+    return singlePatchProc_;
+}
+
+
+inline const Foam::List<Foam::scalar>& Foam::AMIInterpolation::srcMagSf() const
 {
     return srcMagSf_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::List<Foam::scalar>&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcMagSf()
+inline Foam::List<Foam::scalar>& Foam::AMIInterpolation::srcMagSf()
 {
     return srcMagSf_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::labelListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcAddress() const
+inline const Foam::labelListList& Foam::AMIInterpolation::srcAddress() const
 {
     return srcAddress_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::labelListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcAddress()
+inline Foam::labelListList& Foam::AMIInterpolation::srcAddress()
 {
     return srcAddress_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::scalarListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcWeights() const
+inline const Foam::scalarListList& Foam::AMIInterpolation::srcWeights() const
 {
     return srcWeights_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::scalarListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcWeights()
+inline Foam::scalarListList& Foam::AMIInterpolation::srcWeights()
 {
     return srcWeights_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::scalarField&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcWeightsSum() const
+inline const Foam::scalarField& Foam::AMIInterpolation::srcWeightsSum() const
 {
     return srcWeightsSum_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::scalarField&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcWeightsSum()
+inline Foam::scalarField& Foam::AMIInterpolation::srcWeightsSum()
 {
     return srcWeightsSum_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::mapDistribute&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcMap() const
+inline const Foam::pointListList& Foam::AMIInterpolation::srcCentroids() const
+{
+    return srcCentroids_;
+}
+
+
+inline Foam::pointListList& Foam::AMIInterpolation::srcCentroids()
+{
+    return srcCentroids_;
+}
+
+
+inline const Foam::mapDistribute& Foam::AMIInterpolation::srcMap() const
 {
     return *srcMapPtr_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::List<Foam::scalar>&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtMagSf() const
+inline const Foam::List<Foam::scalar>& Foam::AMIInterpolation::tgtMagSf() const
 {
     return tgtMagSf_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::List<Foam::scalar>&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtMagSf()
+inline Foam::List<Foam::scalar>& Foam::AMIInterpolation::tgtMagSf()
 {
     return tgtMagSf_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::labelListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtAddress() const
+inline const Foam::labelListList& Foam::AMIInterpolation::tgtAddress() const
 {
     return tgtAddress_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::labelListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtAddress()
+inline Foam::labelListList& Foam::AMIInterpolation::tgtAddress()
 {
     return tgtAddress_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::scalarListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtWeights() const
+inline const Foam::scalarListList& Foam::AMIInterpolation::tgtWeights() const
 {
     return tgtWeights_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::scalarListList&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtWeights()
+inline Foam::scalarListList& Foam::AMIInterpolation::tgtWeights()
 {
     return tgtWeights_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::scalarField&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtWeightsSum() const
+inline const Foam::scalarField& Foam::AMIInterpolation::tgtWeightsSum() const
 {
     return tgtWeightsSum_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline Foam::scalarField&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtWeightsSum()
+inline Foam::scalarField& Foam::AMIInterpolation::tgtWeightsSum()
 {
     return tgtWeightsSum_;
 }
 
 
-template<class SourcePatch, class TargetPatch>
-inline const Foam::mapDistribute&
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::tgtMap() const
+inline const Foam::mapDistribute& Foam::AMIInterpolation::tgtMap() const
 {
     return *tgtMapPtr_;
 }
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationName.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationName.C
deleted file mode 100644
index b09ca433fbef4a6a4f8da8d4083a7bb8ec255750..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationName.C
+++ /dev/null
@@ -1,38 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2011-2012 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "AMIInterpolation.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-defineTypeNameAndDebug(AMIInterpolationName, 0);
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodNew.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationNew.C
similarity index 59%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodNew.C
rename to src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationNew.C
index 5ed107dc23e47de7f3d35a44d06c9d8e44e8eb37..6bc4ba2c2e673869b8afae534ea2ecd5a212a9d2 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodNew.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationNew.C
@@ -5,8 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,46 +25,66 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+#include "AMIInterpolation.H"
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-Foam::autoPtr<Foam::AMIMethod<SourcePatch, TargetPatch>>
-Foam::AMIMethod<SourcePatch, TargetPatch>::New
+Foam::autoPtr<Foam::AMIInterpolation> Foam::AMIInterpolation::New
 (
-    const word& methodName,
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
+    const word& modelName,
+    const dictionary& dict,
+    const bool reverseTarget
+)
+{
+    DebugInfo << "Selecting model " << modelName << endl;
+
+    auto cstrIter = dictConstructorTablePtr_->cfind(modelName);
+
+    if (!cstrIter.found())
+    {
+        FatalErrorInLookup
+        (
+            typeName,
+            modelName,
+            *dictConstructorTablePtr_
+        ) << exit(FatalError);
+    }
+
+    return autoPtr<AMIInterpolation>(cstrIter()(dict, reverseTarget));
+}
+
+
+Foam::autoPtr<Foam::AMIInterpolation> Foam::AMIInterpolation::New
+(
+    const word& modelName,
+    const bool requireMatch,
     const bool reverseTarget,
-    const bool requireMatch
+    const scalar lowWeightCorrection
 )
 {
-    DebugInfo << "Selecting AMIMethod " << methodName << endl;
+    DebugInfo << "Selecting model " << modelName << endl;
 
-    auto cstrIter = componentsConstructorTablePtr_->cfind(methodName);
+    auto cstrIter = componentConstructorTablePtr_->cfind(modelName);
 
     if (!cstrIter.found())
     {
         FatalErrorInLookup
         (
-            "AMIMethod",
-            methodName,
-            *componentsConstructorTablePtr_
+            typeName,
+            modelName,
+            *componentConstructorTablePtr_
         ) << exit(FatalError);
     }
 
-    return autoPtr<AMIMethod<SourcePatch, TargetPatch>>
+    return autoPtr<AMIInterpolation>
     (
         cstrIter()
         (
-            srcPatch,
-            tgtPatch,
-            triMode,
+            requireMatch,
             reverseTarget,
-            requireMatch
+            lowWeightCorrection
         )
     );
 }
 
-
 // ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationTemplates.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..418bf48cc863fe6a977cdd81b54a4ab92f7ec37c
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationTemplates.C
@@ -0,0 +1,318 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2015-2018 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 "profiling.H"
+#include "mapDistribute.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+template<class Type, class CombineOp>
+void Foam::AMIInterpolation::interpolateToTarget
+(
+    const UList<Type>& fld,
+    const CombineOp& cop,
+    List<Type>& result,
+    const UList<Type>& defaultValues
+) const
+{
+    addProfiling(ami, "AMIInterpolation::interpolateToTarget");
+
+    if (fld.size() != srcAddress_.size())
+    {
+        FatalErrorInFunction
+            << "Supplied field size is not equal to source patch size" << nl
+            << "    source patch   = " << srcAddress_.size() << nl
+            << "    target patch   = " << tgtAddress_.size() << nl
+            << "    supplied field = " << fld.size()
+            << abort(FatalError);
+    }
+
+    if (lowWeightCorrection_ > 0)
+    {
+        if (defaultValues.size() != tgtAddress_.size())
+        {
+            FatalErrorInFunction
+                << "Employing default values when sum of weights falls below "
+                << lowWeightCorrection_
+                << " but supplied default field size is not equal to target "
+                << "patch size" << nl
+                << "    default values = " << defaultValues.size() << nl
+                << "    target patch   = " << tgtAddress_.size() << nl
+                << abort(FatalError);
+        }
+    }
+
+    result.setSize(tgtAddress_.size());
+
+    if (distributed())
+    {
+        const mapDistribute& map = srcMapPtr_();
+
+        List<Type> work(fld);
+        map.distribute(work);
+
+        forAll(result, facei)
+        {
+            if (tgtWeightsSum_[facei] < lowWeightCorrection_)
+            {
+                result[facei] = defaultValues[facei];
+            }
+            else
+            {
+                const labelList& faces = tgtAddress_[facei];
+                const scalarList& weights = tgtWeights_[facei];
+
+                forAll(faces, i)
+                {
+                    cop(result[facei], facei, work[faces[i]], weights[i]);
+                }
+            }
+        }
+    }
+    else
+    {
+        forAll(result, facei)
+        {
+            if (tgtWeightsSum_[facei] < lowWeightCorrection_)
+            {
+                result[facei] = defaultValues[facei];
+            }
+            else
+            {
+                const labelList& faces = tgtAddress_[facei];
+                const scalarList& weights = tgtWeights_[facei];
+
+                forAll(faces, i)
+                {
+                    cop(result[facei], facei, fld[faces[i]], weights[i]);
+                }
+            }
+        }
+    }
+}
+
+
+template<class Type, class CombineOp>
+void Foam::AMIInterpolation::interpolateToSource
+(
+    const UList<Type>& fld,
+    const CombineOp& cop,
+    List<Type>& result,
+    const UList<Type>& defaultValues
+) const
+{
+    addProfiling(ami, "AMIInterpolation::interpolateToSource");
+
+    if (fld.size() != tgtAddress_.size())
+    {
+        FatalErrorInFunction
+            << "Supplied field size is not equal to target patch size" << nl
+            << "    source patch   = " << srcAddress_.size() << nl
+            << "    target patch   = " << tgtAddress_.size() << nl
+            << "    supplied field = " << fld.size()
+            << abort(FatalError);
+    }
+
+    if (lowWeightCorrection_ > 0)
+    {
+        if (defaultValues.size() != srcAddress_.size())
+        {
+            FatalErrorInFunction
+                << "Employing default values when sum of weights falls below "
+                << lowWeightCorrection_
+                << " but supplied default field size is not equal to source "
+                << "patch size" << nl
+                << "    default values = " << defaultValues.size() << nl
+                << "    source patch   = " << srcAddress_.size() << nl
+                << abort(FatalError);
+        }
+    }
+
+    result.setSize(srcAddress_.size());
+
+    if (distributed())
+    {
+        const mapDistribute& map = tgtMapPtr_();
+
+        List<Type> work(fld);
+        map.distribute(work);
+
+        forAll(result, facei)
+        {
+            if (srcWeightsSum_[facei] < lowWeightCorrection_)
+            {
+                result[facei] = defaultValues[facei];
+            }
+            else
+            {
+                const labelList& faces = srcAddress_[facei];
+                const scalarList& weights = srcWeights_[facei];
+
+                forAll(faces, i)
+                {
+                    cop(result[facei], facei, work[faces[i]], weights[i]);
+                }
+            }
+        }
+    }
+    else
+    {
+        forAll(result, facei)
+        {
+            if (srcWeightsSum_[facei] < lowWeightCorrection_)
+            {
+                result[facei] = defaultValues[facei];
+            }
+            else
+            {
+                const labelList& faces = srcAddress_[facei];
+                const scalarList& weights = srcWeights_[facei];
+
+                forAll(faces, i)
+                {
+                    cop(result[facei], facei, fld[faces[i]], weights[i]);
+                }
+            }
+        }
+    }
+}
+
+
+template<class Type, class CombineOp>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToSource
+(
+    const Field<Type>& fld,
+    const CombineOp& cop,
+    const UList<Type>& defaultValues
+) const
+{
+    auto tresult = tmp<Field<Type>>::New(srcAddress_.size(), Zero);
+
+    interpolateToSource
+    (
+        fld,
+        multiplyWeightedOp<Type, CombineOp>(cop),
+        tresult.ref(),
+        defaultValues
+    );
+
+    return tresult;
+}
+
+
+template<class Type, class CombineOp>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToSource
+(
+    const tmp<Field<Type>>& tFld,
+    const CombineOp& cop,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToSource(tFld(), cop, defaultValues);
+}
+
+
+template<class Type, class CombineOp>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToTarget
+(
+    const Field<Type>& fld,
+    const CombineOp& cop,
+    const UList<Type>& defaultValues
+) const
+{
+    auto tresult = tmp<Field<Type>>::New(tgtAddress_.size(), Zero);
+
+    interpolateToTarget
+    (
+        fld,
+        multiplyWeightedOp<Type, CombineOp>(cop),
+        tresult.ref(),
+        defaultValues
+    );
+
+    return tresult;
+}
+
+
+template<class Type, class CombineOp>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToTarget
+(
+    const tmp<Field<Type>>& tFld,
+    const CombineOp& cop,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToTarget(tFld(), cop, defaultValues);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToSource
+(
+    const Field<Type>& fld,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToSource(fld, plusEqOp<Type>(), defaultValues);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToSource
+(
+    const tmp<Field<Type>>& tFld,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToSource(tFld(), plusEqOp<Type>(), defaultValues);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToTarget
+(
+    const Field<Type>& fld,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToTarget(fld, plusEqOp<Type>(), defaultValues);
+}
+
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::AMIInterpolation::interpolateToTarget
+(
+    const tmp<Field<Type>>& tFld,
+    const UList<Type>& defaultValues
+) const
+{
+    return interpolateToTarget(tFld(), plusEqOp<Type>(), defaultValues);
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H
deleted file mode 100644
index bf569aad560a8e392ed97e9277573397b55bead0..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H
+++ /dev/null
@@ -1,321 +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) 2016-2018 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::AMIMethod
-
-Description
-    Base class for Arbitrary Mesh Interface (AMI) methods
-
-SourceFiles
-    AMIMethod.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef AMIMethod_H
-#define AMIMethod_H
-
-#include "className.H"
-#include "DynamicList.H"
-#include "faceAreaIntersect.H"
-#include "indexedOctree.H"
-#include "treeDataPrimitivePatch.H"
-#include "treeBoundBoxList.H"
-#include "runTimeSelectionTables.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-template<class SourcePatch, class TargetPatch> class AMIInterpolation;
-
-/*---------------------------------------------------------------------------*\
-                          Class AMIMethod Declaration
-\*---------------------------------------------------------------------------*/
-
-template<class SourcePatch, class TargetPatch>
-class AMIMethod
-{
-
-private:
-
-    // Private Member Functions
-
-        //- No copy construct
-        AMIMethod(const AMIMethod&) = delete;
-
-        //- No copy assignment
-        void operator=(const AMIMethod&) = delete;
-
-
-protected:
-
-    //- Local typedef to octree tree-type
-    typedef treeDataPrimitivePatch<TargetPatch> treeType;
-
-
-    // Protected data
-
-        //- Reference to source patch
-        const SourcePatch& srcPatch_;
-
-        //- Reference to target patch
-        const TargetPatch& tgtPatch_;
-
-        //- Flag to indicate that the two patches are co-directional and
-        //- that the orientation of the target patch should be reversed
-        const bool reverseTarget_;
-
-        //- Flag to indicate that the two patches must be matched/an overlap
-        //- exists between them
-        const bool requireMatch_;
-
-        //- Source face areas
-        List<scalar> srcMagSf_;
-
-        //- Target face areas
-        List<scalar> tgtMagSf_;
-
-        //- Labels of faces that are not overlapped by any target faces
-        //- (should be empty for correct functioning)
-        labelList srcNonOverlap_;
-
-        //- Octree used to find face seeds
-        autoPtr<indexedOctree<treeType>> treePtr_;
-
-        //- Face triangulation mode
-        const faceAreaIntersect::triangulationMode triMode_;
-
-
-    // Protected Member Functions
-
-        // Helper functions
-
-            //- Check AMI patch coupling
-            void checkPatches() const;
-
-            //- Initialise and return true if all ok
-            bool initialise
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label& srcFacei,
-                label& tgtFacei
-            );
-
-            //- Write triangle intersection to OBJ file
-            void writeIntersectionOBJ
-            (
-                const scalar area,
-                const face& f1,
-                const face& f2,
-                const pointField& f1Points,
-                const pointField& f2Points
-            ) const;
-
-
-        // Common AMI method functions
-
-            //- Reset the octree for the target patch face search
-            void resetTree();
-
-            //- Find face on target patch that overlaps source face
-            label findTargetFace(const label srcFacei) const;
-
-            //- Add faces neighbouring facei to the ID list
-            void appendNbrFaces
-            (
-                const label facei,
-                const TargetPatch& patch,
-                const DynamicList<label>& visitedFaces,
-                DynamicList<label>& faceIDs
-            ) const;
-
-            //- Helper function to decompose a patch
-            template<class PatchType>
-            void triangulatePatch
-            (
-                const PatchType& patch,
-                List<DynamicList<face>>& tris,
-                List<scalar>& magSf
-            ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("AMIMethod");
-
-    //- Declare runtime constructor selection table
-    declareRunTimeSelectionTable
-    (
-        autoPtr,
-        AMIMethod,
-        components,
-        (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
-            const bool reverseTarget,
-            const bool requireMatch
-        ),
-        (
-            srcPatch,
-            tgtPatch,
-            triMode,
-            reverseTarget,
-            requireMatch
-        )
-    );
-
-    //- Construct from components
-    AMIMethod
-    (
-        const SourcePatch& srcPatch,
-        const TargetPatch& tgtPatch,
-        const faceAreaIntersect::triangulationMode& triMode,
-        const bool reverseTarget,
-        const bool requireMatch
-    );
-
-    //- Selector
-    static autoPtr<AMIMethod> New
-    (
-        const word& methodName,
-        const SourcePatch& srcPatch,
-        const TargetPatch& tgtPatch,
-        const faceAreaIntersect::triangulationMode& triMode,
-        const bool reverseTarget,
-        const bool requireMatch
-    );
-
-
-    //- Destructor
-    virtual ~AMIMethod() = default;
-
-
-    // Member Functions
-
-        // Access
-
-            //- 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
-            virtual bool conformal() const;
-
-            //- Return const access to source patch face areas
-            inline const List<scalar>& srcMagSf() const;
-
-            //- Return access to source patch face areas
-            inline List<scalar>& srcMagSf();
-
-            //- Return const access to target patch face areas
-            inline const List<scalar>& tgtMagSf() const;
-
-            //- Return access to target patch face areas
-            inline List<scalar>& tgtMagSf();
-
-
-        // Manipulation
-
-            //- Update addressing and weights
-            virtual void calculate
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label srcFacei = -1,
-                label tgtFacei = -1
-            ) = 0;
-
-            //- Set the face areas for parallel runs
-            virtual void setMagSf
-            (
-                const TargetPatch& tgtPatch,
-                const mapDistribute& map,
-                scalarList& srcMagSf,
-                scalarList& tgtMagSf
-            ) const = 0;
-
-            //- Normalise the weight. Can optionally subset addressing
-            //- (e.g. for mapNearest)
-            virtual void normaliseWeights
-            (
-                const bool verbose,
-                AMIInterpolation<SourcePatch, TargetPatch>& inter
-            ) = 0;
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#define makeAMIMethod(AMIType)                                                 \
-                                                                               \
-    typedef AMIMethod<AMIType::sourcePatchType,AMIType::targetPatchType>       \
-        AMIMethod##AMIType;                                                    \
-                                                                               \
-    defineNamedTemplateTypeNameAndDebug(AMIMethod##AMIType, 0);                \
-    defineTemplateRunTimeSelectionTable(AMIMethod##AMIType, components);
-
-
-#define makeAMIMethodType(AMIType, Method)                                     \
-                                                                               \
-    typedef Method<AMIType::sourcePatchType,AMIType::targetPatchType>          \
-        Method##AMIType;                                                       \
-                                                                               \
-    defineNamedTemplateTypeNameAndDebug(Method##AMIType, 0);                   \
-                                                                               \
-    AMIMethod<AMIType::sourcePatchType,AMIType::targetPatchType>::             \
-        addcomponentsConstructorToTable<Method##AMIType>                       \
-        add##Method##AMIType##ConstructorToTable_;
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#include "AMIMethodI.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#ifdef NoRepository
-    #include "AMIMethod.C"
-    #include "AMIMethodNew.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodI.H
deleted file mode 100644
index caea348e2df5fad06cea4bee21328e7bad7df2d2..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethodI.H
+++ /dev/null
@@ -1,68 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2013 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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/>.
-
-\*---------------------------------------------------------------------------*/
-
-template<class SourcePatch, class TargetPatch>
-inline const Foam::List<Foam::scalar>&
-Foam::AMIMethod<SourcePatch, TargetPatch>::srcMagSf() const
-{
-    return srcMagSf_;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-inline Foam::List<Foam::scalar>&
-Foam::AMIMethod<SourcePatch, TargetPatch>::srcMagSf()
-{
-    return srcMagSf_;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-inline const Foam::List<Foam::scalar>&
-Foam::AMIMethod<SourcePatch, TargetPatch>::tgtMagSf() const
-{
-    return tgtMagSf_;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-inline Foam::List<Foam::scalar>&
-Foam::AMIMethod<SourcePatch, TargetPatch>::tgtMagSf()
-{
-    return tgtMagSf_;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-inline const Foam::labelList&
-Foam::AMIMethod<SourcePatch, TargetPatch>::srcNonOverlap() const
-{
-    return srcNonOverlap_;
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C
deleted file mode 100644
index 894b33fe0f63d5f0292d48e3a46495ca7ced0d30..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C
+++ /dev/null
@@ -1,343 +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
--------------------------------------------------------------------------------
-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 "directAMI.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::directAMI<SourcePatch, TargetPatch>::appendToDirectSeeds
-(
-    labelList& mapFlag,
-    labelList& srcTgtSeed,
-    DynamicList<label>& srcSeeds,
-    DynamicList<label>& nonOverlapFaces,
-    label& srcFacei,
-    label& tgtFacei
-) const
-{
-    const labelList& srcNbr = this->srcPatch_.faceFaces()[srcFacei];
-    const labelList& tgtNbr = this->tgtPatch_.faceFaces()[tgtFacei];
-
-    const pointField& srcPoints = this->srcPatch_.points();
-    const pointField& tgtPoints = this->tgtPatch_.points();
-
-    const vectorField& srcCf = this->srcPatch_.faceCentres();
-
-    for (const label srcI : srcNbr)
-    {
-        if ((mapFlag[srcI] == 0) && (srcTgtSeed[srcI] == -1))
-        {
-            // first attempt: match by comparing face centres
-            const face& srcF = this->srcPatch_[srcI];
-            const point& srcC = srcCf[srcI];
-
-            scalar tol = GREAT;
-            forAll(srcF, fpI)
-            {
-                const point& p = srcPoints[srcF[fpI]];
-                scalar d2 = magSqr(p - srcC);
-                if (d2 < tol)
-                {
-                    tol = d2;
-                }
-            }
-            tol = max(SMALL, 0.0001*sqrt(tol));
-
-            bool found = false;
-            for (const label tgtI : tgtNbr)
-            {
-                const face& tgtF = this->tgtPatch_[tgtI];
-                const point tgtC = tgtF.centre(tgtPoints);
-
-                if (mag(srcC - tgtC) < tol)
-                {
-                    // new match - append to lists
-                    found = true;
-
-                    srcTgtSeed[srcI] = tgtI;
-                    srcSeeds.append(srcI);
-
-                    break;
-                }
-            }
-
-            // second attempt: match by shooting a ray into the tgt face
-            if (!found)
-            {
-                const vector srcN = srcF.areaNormal(srcPoints);
-
-                for (const label tgtI : tgtNbr)
-                {
-                    const face& tgtF = this->tgtPatch_[tgtI];
-                    pointHit ray = tgtF.ray(srcCf[srcI], srcN, tgtPoints);
-
-                    if (ray.hit())
-                    {
-                        // new match - append to lists
-                        found = true;
-
-                        srcTgtSeed[srcI] = tgtI;
-                        srcSeeds.append(srcI);
-
-                        break;
-                    }
-                }
-            }
-
-            // no match available for source face srcI
-            if (!found)
-            {
-                mapFlag[srcI] = -1;
-                nonOverlapFaces.append(srcI);
-
-                if (debug)
-                {
-                    Pout<< "source face not found: id=" << srcI
-                        << " centre=" << srcCf[srcI]
-                        << " normal=" << srcF.areaNormal(srcPoints)
-                        << " points=" << srcF.points(srcPoints)
-                        << endl;
-
-                    Pout<< "target neighbours:" << nl;
-                    for (const label tgtI : tgtNbr)
-                    {
-                        const face& tgtF = this->tgtPatch_[tgtI];
-
-                        Pout<< "face id: " << tgtI
-                            << " centre=" << tgtF.centre(tgtPoints)
-                            << " normal=" << tgtF.areaNormal(tgtPoints)
-                            << " points=" << tgtF.points(tgtPoints)
-                            << endl;
-                    }
-                }
-            }
-        }
-    }
-
-    if (srcSeeds.size())
-    {
-        srcFacei = srcSeeds.remove();
-        tgtFacei = srcTgtSeed[srcFacei];
-    }
-    else
-    {
-        srcFacei = -1;
-        tgtFacei = -1;
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::directAMI<SourcePatch, TargetPatch>::restartAdvancingFront
-(
-    labelList& mapFlag,
-    DynamicList<label>& nonOverlapFaces,
-    label& srcFacei,
-    label& tgtFacei
-) const
-{
-    forAll(mapFlag, facei)
-    {
-        if (mapFlag[facei] == 0)
-        {
-            tgtFacei = this->findTargetFace(facei);
-
-            if (tgtFacei < 0)
-            {
-                mapFlag[facei] = -1;
-                nonOverlapFaces.append(facei);
-            }
-            else
-            {
-                srcFacei = facei;
-                break;
-            }
-        }
-    }
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::directAMI<SourcePatch, TargetPatch>::directAMI
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool reverseTarget,
-    const bool requireMatch
-)
-:
-    AMIMethod<SourcePatch, TargetPatch>
-    (
-        srcPatch,
-        tgtPatch,
-        triMode,
-        reverseTarget,
-        requireMatch
-    )
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::directAMI<SourcePatch, TargetPatch>::~directAMI()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::directAMI<SourcePatch, TargetPatch>::calculate
-(
-    labelListList& srcAddress,
-    scalarListList& srcWeights,
-    labelListList& tgtAddress,
-    scalarListList& tgtWeights,
-    label srcFacei,
-    label tgtFacei
-)
-{
-    bool ok =
-        this->initialise
-        (
-            srcAddress,
-            srcWeights,
-            tgtAddress,
-            tgtWeights,
-            srcFacei,
-            tgtFacei
-        );
-
-    if (!ok)
-    {
-        return;
-    }
-
-
-    // temporary storage for addressing and weights
-    List<DynamicList<label>> srcAddr(this->srcPatch_.size());
-    List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
-
-
-    // construct weights and addressing
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    // list of faces currently visited for srcFacei to avoid multiple hits
-    DynamicList<label> srcSeeds(10);
-
-    // list to keep track of tgt faces used to seed src faces
-    labelList srcTgtSeed(srcAddr.size(), -1);
-    srcTgtSeed[srcFacei] = tgtFacei;
-
-    // list to keep track of whether src face can be mapped
-    // 1 = mapped, 0 = untested, -1 = cannot map
-    labelList mapFlag(srcAddr.size(), Zero);
-
-    label nTested = 0;
-    DynamicList<label> nonOverlapFaces;
-    do
-    {
-        srcAddr[srcFacei].append(tgtFacei);
-        tgtAddr[tgtFacei].append(srcFacei);
-
-        mapFlag[srcFacei] = 1;
-
-        nTested++;
-
-        // Do advancing front starting from srcFacei, tgtFacei
-        appendToDirectSeeds
-        (
-            mapFlag,
-            srcTgtSeed,
-            srcSeeds,
-            nonOverlapFaces,
-            srcFacei,
-            tgtFacei
-        );
-
-        if (srcFacei < 0 && nTested < this->srcPatch_.size())
-        {
-            restartAdvancingFront(mapFlag, nonOverlapFaces, srcFacei, tgtFacei);
-        }
-
-    } while (srcFacei >= 0);
-
-    if (nonOverlapFaces.size() != 0)
-    {
-        Pout<< "    AMI: " << nonOverlapFaces.size()
-            << " non-overlap faces identified"
-            << endl;
-
-        this->srcNonOverlap_.transfer(nonOverlapFaces);
-    }
-
-    // transfer data to persistent storage
-    forAll(srcAddr, i)
-    {
-        scalar magSf = this->srcMagSf_[i];
-        srcAddress[i].transfer(srcAddr[i]);
-        srcWeights[i] = scalarList(1, magSf);
-    }
-    forAll(tgtAddr, i)
-    {
-        scalar magSf = this->tgtMagSf_[i];
-        tgtAddress[i].transfer(tgtAddr[i]);
-        tgtWeights[i] = scalarList(1, magSf);
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::directAMI<SourcePatch, TargetPatch>::setMagSf
-(
-    const TargetPatch& tgtPatch,
-    const mapDistribute& map,
-    scalarList& srcMagSf,
-    scalarList& tgtMagSf
-) const
-{
-    srcMagSf = std::move(this->srcMagSf_);
-    tgtMagSf = scalarList(tgtPatch.size(), 1.0);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::directAMI<SourcePatch, TargetPatch>::normaliseWeights
-(
-    const bool verbose,
-    AMIInterpolation<SourcePatch, TargetPatch>& inter
-)
-{
-    inter.normaliseWeights(this->conformal(), verbose);
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H
deleted file mode 100644
index a451cad7da2c5e5c3bb4a15ea42b31eab12245be..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H
+++ /dev/null
@@ -1,172 +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
--------------------------------------------------------------------------------
-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::directAMI
-
-Description
-    Direct mapped Arbitrary Mesh Interface (AMI) method
-
-SourceFiles
-    directAMI.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef directAMI_H
-#define directAMI_H
-
-#include "AMIMethod.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                          Class directAMI Declaration
-\*---------------------------------------------------------------------------*/
-
-template<class SourcePatch, class TargetPatch>
-class directAMI
-:
-    public AMIMethod<SourcePatch, TargetPatch>
-{
-
-private:
-
-    // Private Member Functions
-
-        //- No copy construct
-        directAMI(const directAMI&) = delete;
-
-        //- No copy assignment
-        void operator=(const directAMI&) = delete;
-
-        // Marching front
-
-            //- Append to list of src face seed indices
-            void appendToDirectSeeds
-            (
-                labelList& mapFlag,
-                labelList& srcTgtSeed,
-                DynamicList<label>& srcSeeds,
-                DynamicList<label>& nonOverlapFaces,
-                label& srcFacei,
-                label& tgtFacei
-            ) const;
-
-            //- Restart the advancing front - typically happens for
-            //  disconnected regions
-            void restartAdvancingFront
-            (
-                labelList& mapFlag,
-                DynamicList<label>& nonOverlapFaces,
-                label& srcFacei,
-                label& tgtFacei
-            ) const;
-
-
-        // Evaluation
-
-            //- Area of intersection between source and target faces
-            scalar interArea
-            (
-                const label srcFacei,
-                const label tgtFacei
-            ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("directAMI");
-
-
-    // Constructors
-
-        //- Construct from components
-        directAMI
-        (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
-            const bool reverseTarget = false,
-            const bool requireMatch = true
-        );
-
-
-    //- Destructor
-    virtual ~directAMI();
-
-
-    // Member Functions
-
-        // Manipulation
-
-            //- Update addressing and weights
-            virtual void calculate
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label srcFacei = -1,
-                label tgtFacei = -1
-            );
-
-            //- Set the face areas for parallel runs
-            virtual void setMagSf
-            (
-                const TargetPatch& tgtPatch,
-                const mapDistribute& map,
-                scalarList& srcMagSf,
-                scalarList& tgtMagSf
-            ) const;
-
-            //- Normalise the weight. Can optionally subset addressing
-            //  (e.g. for mapNearest)
-            virtual void normaliseWeights
-            (
-                const bool verbose,
-                AMIInterpolation<SourcePatch, TargetPatch>& inter
-            );
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#ifdef NoRepository
-    #include "directAMI.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C
deleted file mode 100644
index 3cbe9cd65f8cedcbb26730f9722640790cef792d..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C
+++ /dev/null
@@ -1,693 +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) 2018 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 "faceAreaWeightAMI.H"
-#include "profiling.H"
-
-// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
-(
-    List<DynamicList<label>>& srcAddr,
-    List<DynamicList<scalar>>& srcWght,
-    List<DynamicList<label>>& tgtAddr,
-    List<DynamicList<scalar>>& tgtWght,
-    label srcFacei,
-    label tgtFacei
-)
-{
-    addProfiling(ami, "faceAreaWeightAMI::calcAddressing");
-
-    // construct weights and addressing
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    label nFacesRemaining = srcAddr.size();
-
-    // list of tgt face neighbour faces
-    DynamicList<label> nbrFaces(10);
-
-    // list of faces currently visited for srcFacei to avoid multiple hits
-    DynamicList<label> visitedFaces(10);
-
-    // list to keep track of tgt faces used to seed src faces
-    labelList seedFaces(nFacesRemaining, -1);
-    seedFaces[srcFacei] = tgtFacei;
-
-    // list to keep track of whether src face can be mapped
-    boolList mapFlag(nFacesRemaining, true);
-
-    // reset starting seed
-    label startSeedi = 0;
-
-    DynamicList<label> nonOverlapFaces;
-    do
-    {
-        // Do advancing front starting from srcFacei,tgtFacei
-        bool faceProcessed = processSourceFace
-        (
-            srcFacei,
-            tgtFacei,
-
-            nbrFaces,
-            visitedFaces,
-
-            srcAddr,
-            srcWght,
-            tgtAddr,
-            tgtWght
-        );
-
-        mapFlag[srcFacei] = false;
-
-        nFacesRemaining--;
-
-        if (!faceProcessed)
-        {
-            nonOverlapFaces.append(srcFacei);
-        }
-
-        // choose new src face from current src face neighbour
-        if (nFacesRemaining > 0)
-        {
-            setNextFaces
-            (
-                startSeedi,
-                srcFacei,
-                tgtFacei,
-                mapFlag,
-                seedFaces,
-                visitedFaces
-            );
-        }
-    } while (nFacesRemaining > 0);
-
-    this->srcNonOverlap_.transfer(nonOverlapFaces);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-bool Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::processSourceFace
-(
-    const label srcFacei,
-    const label tgtStartFacei,
-
-    // list of tgt face neighbour faces
-    DynamicList<label>& nbrFaces,
-    // list of faces currently visited for srcFacei to avoid multiple hits
-    DynamicList<label>& visitedFaces,
-
-    // temporary storage for addressing and weights
-    List<DynamicList<label>>& srcAddr,
-    List<DynamicList<scalar>>& srcWght,
-    List<DynamicList<label>>& tgtAddr,
-    List<DynamicList<scalar>>& tgtWght
-)
-{
-    addProfiling(ami, "faceAreaWeightAMI::processSourceFace");
-
-    if (tgtStartFacei == -1)
-    {
-        return false;
-    }
-
-    nbrFaces.clear();
-    visitedFaces.clear();
-
-    // append initial target face and neighbours
-    nbrFaces.append(tgtStartFacei);
-    this->appendNbrFaces
-    (
-        tgtStartFacei,
-        this->tgtPatch_,
-        visitedFaces,
-        nbrFaces
-    );
-
-    bool faceProcessed = false;
-
-    label maxNeighbourFaces = nbrFaces.size();
-
-    do
-    {
-        // process new target face
-        label tgtFacei = nbrFaces.remove();
-        visitedFaces.append(tgtFacei);
-        scalar area = interArea(srcFacei, tgtFacei);
-
-        // store when intersection fractional area > tolerance
-        if (area/this->srcMagSf_[srcFacei] > faceAreaIntersect::tolerance())
-        {
-            srcAddr[srcFacei].append(tgtFacei);
-            srcWght[srcFacei].append(area);
-
-            tgtAddr[tgtFacei].append(srcFacei);
-            tgtWght[tgtFacei].append(area);
-
-            this->appendNbrFaces
-            (
-                tgtFacei,
-                this->tgtPatch_,
-                visitedFaces,
-                nbrFaces
-            );
-
-            faceProcessed = true;
-
-            maxNeighbourFaces = max(maxNeighbourFaces, nbrFaces.size());
-        }
-
-    } while (nbrFaces.size() > 0);
-
-    if (debug > 1)
-    {
-        DebugVar(maxNeighbourFaces);
-    }
-
-    return faceProcessed;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
-(
-    label& startSeedi,
-    label& srcFacei,
-    label& tgtFacei,
-    const boolList& mapFlag,
-    labelList& seedFaces,
-    const DynamicList<label>& visitedFaces,
-    bool errorOnNotFound
-) const
-{
-    addProfiling(ami, "faceAreaWeightAMI::setNextFaces");
-
-    const labelList& srcNbrFaces = this->srcPatch_.faceFaces()[srcFacei];
-
-    // initialise tgtFacei
-    tgtFacei = -1;
-
-    // set possible seeds for later use
-    bool valuesSet = false;
-    for (label faceS: srcNbrFaces)
-    {
-        if (mapFlag[faceS] && seedFaces[faceS] == -1)
-        {
-            for (label faceT : visitedFaces)
-            {
-                const scalar threshold =
-                    this->srcMagSf_[faceS]*faceAreaIntersect::tolerance();
-
-                // store when intersection fractional area > tolerance
-                // Check that faces have enough overlap for robust walking
-                if (overlaps(faceS, faceT, threshold))
-                {
-                    seedFaces[faceS] = faceT;
-
-                    if (!valuesSet)
-                    {
-                        srcFacei = faceS;
-                        tgtFacei = faceT;
-                        valuesSet = true;
-                    }
-                }
-            }
-        }
-    }
-
-    // set next src and tgt faces if not set above
-    if (valuesSet)
-    {
-        return;
-    }
-    else
-    {
-        // try to use existing seed
-        bool foundNextSeed = false;
-        for (label facei = startSeedi; facei < mapFlag.size(); ++facei)
-        {
-            if (mapFlag[facei])
-            {
-                if (!foundNextSeed)
-                {
-                    startSeedi = facei;
-                    foundNextSeed = true;
-                }
-
-                if (seedFaces[facei] != -1)
-                {
-                    srcFacei = facei;
-                    tgtFacei = seedFaces[facei];
-
-                    return;
-                }
-            }
-        }
-
-        // perform new search to find match
-        if (debug)
-        {
-            Pout<< "Advancing front stalled: searching for new "
-                << "target face" << endl;
-        }
-
-        foundNextSeed = false;
-        for (label facei = startSeedi; facei < mapFlag.size(); ++facei)
-        {
-            if (mapFlag[facei])
-            {
-                if (!foundNextSeed)
-                {
-                    startSeedi = facei + 1;
-                    foundNextSeed = true;
-                }
-
-                srcFacei = facei;
-                tgtFacei = this->findTargetFace(srcFacei);
-
-                if (tgtFacei >= 0)
-                {
-                    return;
-                }
-            }
-        }
-
-        if (errorOnNotFound)
-        {
-            FatalErrorInFunction
-               << "Unable to set source and target faces" << abort(FatalError);
-        }
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::scalar Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::interArea
-(
-    const label srcFacei,
-    const label tgtFacei
-) const
-{
-    addProfiling(ami, "faceAreaWeightAMI::interArea");
-
-    scalar area = 0;
-
-    const pointField& srcPoints = this->srcPatch_.points();
-    const pointField& tgtPoints = this->tgtPatch_.points();
-
-    // references to candidate faces
-    const face& src = this->srcPatch_[srcFacei];
-    const face& tgt = this->tgtPatch_[tgtFacei];
-
-    // quick reject if either face has zero area
-    if
-    (
-        (this->srcMagSf_[srcFacei] < ROOTVSMALL)
-     || (this->tgtMagSf_[tgtFacei] < ROOTVSMALL)
-    )
-    {
-        return area;
-    }
-
-    // create intersection object
-    faceAreaIntersect inter
-    (
-        srcPoints,
-        tgtPoints,
-        this->srcTris_[srcFacei],
-        this->tgtTris_[tgtFacei],
-        this->reverseTarget_,
-        AMIInterpolation<SourcePatch, TargetPatch>::cacheIntersections_
-    );
-
-    // crude resultant norm
-    vector n(-this->srcPatch_.faceNormals()[srcFacei]);
-    if (this->reverseTarget_)
-    {
-        n -= this->tgtPatch_.faceNormals()[tgtFacei];
-    }
-    else
-    {
-        n += this->tgtPatch_.faceNormals()[tgtFacei];
-    }
-    scalar magN = mag(n);
-
-    if (magN > ROOTVSMALL)
-    {
-        area = inter.calc(src, tgt, n/magN);
-    }
-    else
-    {
-        WarningInFunction
-            << "Invalid normal for source face " << srcFacei
-            << " points " << UIndirectList<point>(srcPoints, src)
-            << " target face " << tgtFacei
-            << " points " << UIndirectList<point>(tgtPoints, tgt)
-            << endl;
-    }
-
-
-    if ((debug > 1) && (area > 0))
-    {
-        this->writeIntersectionOBJ(area, src, tgt, srcPoints, tgtPoints);
-    }
-
-    return area;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-bool Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::overlaps
-(
-    const label srcFacei,
-    const label tgtFacei,
-    const scalar threshold
-) const
-{
-    const pointField& srcPoints = this->srcPatch_.points();
-    const pointField& tgtPoints = this->tgtPatch_.points();
-
-    // references to candidate faces
-    const face& src = this->srcPatch_[srcFacei];
-    const face& tgt = this->tgtPatch_[tgtFacei];
-
-    // quick reject if either face has zero area
-    if
-    (
-        (this->srcMagSf_[srcFacei] < ROOTVSMALL)
-     || (this->tgtMagSf_[tgtFacei] < ROOTVSMALL)
-    )
-    {
-        return false;
-    }
-
-    faceAreaIntersect inter
-    (
-        srcPoints,
-        tgtPoints,
-        this->srcTris_[srcFacei],
-        this->tgtTris_[tgtFacei],
-        this->reverseTarget_,
-        AMIInterpolation<SourcePatch, TargetPatch>::cacheIntersections_
-    );
-
-    // crude resultant norm
-    vector n(-this->srcPatch_.faceNormals()[srcFacei]);
-    if (this->reverseTarget_)
-    {
-        n -= this->tgtPatch_.faceNormals()[tgtFacei];
-    }
-    else
-    {
-        n += this->tgtPatch_.faceNormals()[tgtFacei];
-    }
-    scalar magN = mag(n);
-
-    if (magN > ROOTVSMALL)
-    {
-        return inter.overlaps(src, tgt, n/magN, threshold);
-    }
-    else
-    {
-        WarningInFunction
-            << "Invalid normal for source face " << srcFacei
-            << " points " << UIndirectList<point>(srcPoints, src)
-            << " target face " << tgtFacei
-            << " points " << UIndirectList<point>(tgtPoints, tgt)
-            << endl;
-    }
-
-    return false;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::
-restartUncoveredSourceFace
-(
-    List<DynamicList<label>>& srcAddr,
-    List<DynamicList<scalar>>& srcWght,
-    List<DynamicList<label>>& tgtAddr,
-    List<DynamicList<scalar>>& tgtWght
-)
-{
-    addProfiling(ami, "faceAreaWeightAMI::restartUncoveredSourceFace");
-
-    // Collect all src faces with a low weight
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    labelHashSet lowWeightFaces(100);
-    forAll(srcWght, srcFacei)
-    {
-        scalar s = sum(srcWght[srcFacei]);
-        scalar t = s/this->srcMagSf_[srcFacei];
-
-        if (t < 0.5)
-        {
-            lowWeightFaces.insert(srcFacei);
-        }
-    }
-
-    if (debug)
-    {
-        Pout<< "faceAreaWeightAMI: restarting search on "
-            << lowWeightFaces.size() << " faces since sum of weights < 0.5"
-            << endl;
-    }
-
-    if (lowWeightFaces.size() > 0)
-    {
-        // Erase all the lowWeight source faces from the target
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-        DynamicList<label> okSrcFaces(10);
-        DynamicList<scalar> okSrcWeights(10);
-        forAll(tgtAddr, tgtFacei)
-        {
-            okSrcFaces.clear();
-            okSrcWeights.clear();
-            DynamicList<label>& srcFaces = tgtAddr[tgtFacei];
-            DynamicList<scalar>& srcWeights = tgtWght[tgtFacei];
-            forAll(srcFaces, i)
-            {
-                if (!lowWeightFaces.found(srcFaces[i]))
-                {
-                    okSrcFaces.append(srcFaces[i]);
-                    okSrcWeights.append(srcWeights[i]);
-                }
-            }
-            if (okSrcFaces.size() < srcFaces.size())
-            {
-                srcFaces.transfer(okSrcFaces);
-                srcWeights.transfer(okSrcWeights);
-            }
-        }
-
-
-
-        // Restart search from best hit
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-        // list of tgt face neighbour faces
-        DynamicList<label> nbrFaces(10);
-
-        // list of faces currently visited for srcFacei to avoid multiple hits
-        DynamicList<label> visitedFaces(10);
-
-        for (const label srcFacei : lowWeightFaces)
-        {
-            const label tgtFacei = this->findTargetFace(srcFacei);
-            if (tgtFacei != -1)
-            {
-                //bool faceProcessed =
-                processSourceFace
-                (
-                    srcFacei,
-                    tgtFacei,
-
-                    nbrFaces,
-                    visitedFaces,
-
-                    srcAddr,
-                    srcWght,
-                    tgtAddr,
-                    tgtWght
-                );
-                // ? Check faceProcessed to see if restarting has worked.
-            }
-        }
-    }
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::faceAreaWeightAMI
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool reverseTarget,
-    const bool requireMatch,
-    const bool restartUncoveredSourceFace
-)
-:
-    AMIMethod<SourcePatch, TargetPatch>
-    (
-        srcPatch,
-        tgtPatch,
-        triMode,
-        reverseTarget,
-        requireMatch
-    ),
-    restartUncoveredSourceFace_(restartUncoveredSourceFace),
-    srcTris_(),
-    tgtTris_()
-{
-    this->triangulatePatch(srcPatch, srcTris_, this->srcMagSf_);
-    this->triangulatePatch(tgtPatch, tgtTris_, this->tgtMagSf_);
-}
-
-
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::~faceAreaWeightAMI()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
-(
-    labelListList& srcAddress,
-    scalarListList& srcWeights,
-    labelListList& tgtAddress,
-    scalarListList& tgtWeights,
-    label srcFacei,
-    label tgtFacei
-)
-{
-    addProfiling(ami, "faceAreaWeightAMI::calculate");
-
-    bool ok =
-        this->initialise
-        (
-            srcAddress,
-            srcWeights,
-            tgtAddress,
-            tgtWeights,
-            srcFacei,
-            tgtFacei
-        );
-
-    if (!ok)
-    {
-        return;
-    }
-
-    // temporary storage for addressing and weights
-    List<DynamicList<label>> srcAddr(this->srcPatch_.size());
-    List<DynamicList<scalar>> srcWght(srcAddr.size());
-    List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
-    List<DynamicList<scalar>> tgtWght(tgtAddr.size());
-
-    calcAddressing
-    (
-        srcAddr,
-        srcWght,
-        tgtAddr,
-        tgtWght,
-        srcFacei,
-        tgtFacei
-    );
-
-    if (debug && !this->srcNonOverlap_.empty())
-    {
-        Pout<< "    AMI: " << this->srcNonOverlap_.size()
-            << " non-overlap faces identified"
-            << endl;
-    }
-
-
-    // Check for badly covered faces
-    if (restartUncoveredSourceFace_)
-    {
-        restartUncoveredSourceFace
-        (
-            srcAddr,
-            srcWght,
-            tgtAddr,
-            tgtWght
-        );
-    }
-
-
-    // transfer data to persistent storage
-    forAll(srcAddr, i)
-    {
-        srcAddress[i].transfer(srcAddr[i]);
-        srcWeights[i].transfer(srcWght[i]);
-    }
-    forAll(tgtAddr, i)
-    {
-        tgtAddress[i].transfer(tgtAddr[i]);
-        tgtWeights[i].transfer(tgtWght[i]);
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setMagSf
-(
-    const TargetPatch& tgtPatch,
-    const mapDistribute& map,
-    scalarList& srcMagSf,
-    scalarList& tgtMagSf
-) const
-{
-    srcMagSf = std::move(this->srcMagSf_);
-    tgtMagSf = std::move(this->tgtMagSf_);
-    map.reverseDistribute(tgtPatch.size(), tgtMagSf);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::normaliseWeights
-(
-    const bool verbose,
-    AMIInterpolation<SourcePatch, TargetPatch>& inter
-)
-{
-    inter.normaliseWeights(this->conformal(), verbose);
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C
deleted file mode 100644
index 5bfaa347ca68747b5ae725a9fe2c8f63b7761a61..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C
+++ /dev/null
@@ -1,444 +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) 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 "mapNearestAMI.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::mapNearestAMI<SourcePatch, TargetPatch>::findNearestFace
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const label& srcFacei,
-    label& tgtFacei
-) const
-{
-    const vectorField& srcCf = srcPatch.faceCentres();
-    const vectorField& tgtCf = tgtPatch.faceCentres();
-
-    const vector srcP = srcCf[srcFacei];
-
-    DynamicList<label> tgtFaces(10);
-    tgtFaces.append(tgtFacei);
-
-    DynamicList<label> visitedFaces(10);
-
-    scalar d = GREAT;
-
-    do
-    {
-        label tgtI = tgtFaces.remove();
-        visitedFaces.append(tgtI);
-
-        scalar dTest = magSqr(tgtCf[tgtI] - srcP);
-        if (dTest < d)
-        {
-            tgtFacei = tgtI;
-            d = dTest;
-
-            this->appendNbrFaces
-            (
-                tgtFacei,
-                tgtPatch,
-                visitedFaces,
-                tgtFaces
-            );
-        }
-
-    } while (tgtFaces.size() > 0);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::mapNearestAMI<SourcePatch, TargetPatch>::setNextNearestFaces
-(
-    boolList& mapFlag,
-    label& startSeedI,
-    label& srcFacei,
-    label& tgtFacei
-) const
-{
-    const labelList& srcNbr = this->srcPatch_.faceFaces()[srcFacei];
-
-    srcFacei = -1;
-
-    for (const label facei : srcNbr)
-    {
-        if (mapFlag[facei])
-        {
-            srcFacei = facei;
-            startSeedI = facei + 1;
-
-            return;
-        }
-    }
-
-    forAll(mapFlag, facei)
-    {
-        if (mapFlag[facei])
-        {
-            srcFacei = facei;
-            tgtFacei = this->findTargetFace(facei);
-
-            if (tgtFacei == -1)
-            {
-                const vectorField& srcCf = this->srcPatch_.faceCentres();
-
-                FatalErrorInFunction
-                    << "Unable to find target face for source face "
-                    << srcFacei << " with face centre " << srcCf[srcFacei]
-                    << abort(FatalError);
-            }
-
-            break;
-        }
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::label Foam::mapNearestAMI<SourcePatch, TargetPatch>::findMappedSrcFace
-(
-    const label tgtFacei,
-    const List<DynamicList<label>>& tgtToSrc
-) const
-{
-    DynamicList<label> testFaces(10);
-    DynamicList<label> visitedFaces(10);
-
-    testFaces.append(tgtFacei);
-
-    do
-    {
-        // search target tgtFacei neighbours for match with source face
-        label tgtI = testFaces.remove();
-
-        if (!visitedFaces.found(tgtI))
-        {
-            visitedFaces.append(tgtI);
-
-            if (tgtToSrc[tgtI].size())
-            {
-                return tgtToSrc[tgtI][0];
-            }
-            else
-            {
-                const labelList& nbrFaces = this->tgtPatch_.faceFaces()[tgtI];
-
-                for (const label nbrFacei : nbrFaces)
-                {
-                    if (!visitedFaces.found(nbrFacei))
-                    {
-                        testFaces.append(nbrFacei);
-                    }
-                }
-            }
-        }
-    } while (testFaces.size());
-
-    // did not find any match - should not be possible to get here!
-    return -1;
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::mapNearestAMI<SourcePatch, TargetPatch>::mapNearestAMI
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool reverseTarget,
-    const bool requireMatch
-)
-:
-    AMIMethod<SourcePatch, TargetPatch>
-    (
-        srcPatch,
-        tgtPatch,
-        triMode,
-        reverseTarget,
-        requireMatch
-    )
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::mapNearestAMI<SourcePatch, TargetPatch>::~mapNearestAMI()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::mapNearestAMI<SourcePatch, TargetPatch>::calculate
-(
-    labelListList& srcAddress,
-    scalarListList& srcWeights,
-    labelListList& tgtAddress,
-    scalarListList& tgtWeights,
-    label srcFacei,
-    label tgtFacei
-)
-{
-    bool ok =
-        this->initialise
-        (
-            srcAddress,
-            srcWeights,
-            tgtAddress,
-            tgtWeights,
-            srcFacei,
-            tgtFacei
-        );
-
-    if (!ok)
-    {
-        return;
-    }
-
-
-    // temporary storage for addressing and weights
-    List<DynamicList<label>> srcAddr(this->srcPatch_.size());
-    List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
-
-
-    // construct weights and addressing
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    // list to keep track of whether src face can be mapped
-    boolList mapFlag(srcAddr.size(), true);
-
-    // reset starting seed
-    label startSeedI = 0;
-
-    DynamicList<label> nonOverlapFaces;
-    do
-    {
-        findNearestFace(this->srcPatch_, this->tgtPatch_, srcFacei, tgtFacei);
-
-        srcAddr[srcFacei].append(tgtFacei);
-        tgtAddr[tgtFacei].append(srcFacei);
-
-        mapFlag[srcFacei] = false;
-
-        // Do advancing front starting from srcFacei, tgtFacei
-        setNextNearestFaces
-        (
-            mapFlag,
-            startSeedI,
-            srcFacei,
-            tgtFacei
-        );
-    } while (srcFacei >= 0);
-
-
-    // for the case of multiple source faces per target face, select the
-    // nearest source face only and discard the others
-    const vectorField& srcCf = this->srcPatch_.faceCentres();
-    const vectorField& tgtCf = this->tgtPatch_.faceCentres();
-
-    forAll(tgtAddr, targetFacei)
-    {
-        if (tgtAddr[targetFacei].size() > 1)
-        {
-            const vector& tgtC = tgtCf[tgtFacei];
-
-            DynamicList<label>& srcFaces = tgtAddr[targetFacei];
-
-            label srcFacei = srcFaces[0];
-            scalar d = magSqr(tgtC - srcCf[srcFacei]);
-
-            for (label i = 1; i < srcFaces.size(); ++i)
-            {
-                label srcI = srcFaces[i];
-                scalar dNew = magSqr(tgtC - srcCf[srcI]);
-                if (dNew < d)
-                {
-                    d = dNew;
-                    srcFacei = srcI;
-                }
-            }
-
-            srcFaces.clear();
-            srcFaces.append(srcFacei);
-        }
-    }
-
-    // If there are more target faces than source faces, some target faces
-    // might not yet be mapped
-    forAll(tgtAddr, tgtFacei)
-    {
-        if (tgtAddr[tgtFacei].empty())
-        {
-            label srcFacei = findMappedSrcFace(tgtFacei, tgtAddr);
-
-            if (srcFacei >= 0)
-            {
-                // note - reversed search from src->tgt to tgt->src
-                findNearestFace
-                (
-                    this->tgtPatch_,
-                    this->srcPatch_,
-                    tgtFacei,
-                    srcFacei
-                );
-
-                tgtAddr[tgtFacei].append(srcFacei);
-            }
-        }
-    }
-
-
-    // transfer data to persistent storage
-    const pointField& srcFc = this->srcPatch_.faceCentres();
-    const pointField& tgtFc = this->tgtPatch_.faceCentres();
-
-    forAll(srcAddr, srcI)
-    {
-        srcAddress[srcI].transfer(srcAddr[srcI]);
-
-        const labelList& addr = srcAddress[srcI];
-        srcWeights[srcI].setSize(addr.size());
-        const point& srcPt = srcFc[srcI];
-        forAll(addr, i)
-        {
-            srcWeights[srcI][i] = magSqr(srcPt-tgtFc[addr[i]]);
-        }
-    }
-    forAll(tgtAddr, tgtI)
-    {
-        tgtAddress[tgtI].transfer(tgtAddr[tgtI]);
-
-        const labelList& addr = tgtAddress[tgtI];
-        tgtWeights[tgtI].setSize(addr.size());
-        const point& tgtPt = tgtFc[tgtI];
-        forAll(addr, i)
-        {
-            tgtWeights[tgtI][i] = magSqr(tgtPt-srcFc[addr[i]]);
-        }
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::mapNearestAMI<SourcePatch, TargetPatch>::setMagSf
-(
-    const TargetPatch& tgtPatch,
-    const mapDistribute& map,
-    scalarList& srcMagSf,
-    scalarList& tgtMagSf
-) const
-{
-    srcMagSf = std::move(this->srcMagSf_);
-    tgtMagSf = scalarList(tgtPatch.size(), 1.0);
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::mapNearestAMI<SourcePatch, TargetPatch>::normaliseWeights
-(
-    const bool verbose,
-    AMIInterpolation<SourcePatch, TargetPatch>& inter
-)
-{
-    {
-        labelListList& srcAddress = inter.srcAddress();
-        scalarListList& srcWeights = inter.srcWeights();
-
-        forAll(srcAddress, srcI)
-        {
-            labelList& addr = srcAddress[srcI];
-            scalarList& wghts = srcWeights[srcI];
-
-            // Choose one with smallest weight (since calculate above returns
-            // distance)
-            if (addr.size())
-            {
-                label minFaceI = addr[0];
-                scalar minWeight = wghts[0];
-
-                for (label i = 0; i < addr.size(); ++i)
-                {
-                    if (wghts[i] < minWeight)
-                    {
-                        minWeight = wghts[i];
-                        minFaceI = addr[i];
-                    }
-                }
-
-                wghts.setSize(1);
-                wghts[0] = this->srcMagSf_[srcI];
-                addr.setSize(1);
-                addr[0] = minFaceI;
-            }
-        }
-    }
-
-    {
-        labelListList& tgtAddress = inter.tgtAddress();
-        scalarListList& tgtWeights = inter.tgtWeights();
-
-        forAll(tgtAddress, tgtI)
-        {
-            labelList& addr = tgtAddress[tgtI];
-            scalarList& wghts = tgtWeights[tgtI];
-
-            // Choose one with smallest weight (since calculate above returns
-            // distance)
-            if (addr.size())
-            {
-                label minFaceI = addr[0];
-                scalar minWeight = wghts[0];
-
-                forAll(addr, i)
-                {
-                    if (wghts[i] < minWeight)
-                    {
-                        minWeight = wghts[i];
-                        minFaceI = addr[i];
-                    }
-                }
-
-                wghts.setSize(1);
-                wghts[0] = inter.tgtMagSf()[tgtI];
-                addr.setSize(1);
-                addr[0] = minFaceI;
-            }
-        }
-    }
-
-    inter.normaliseWeights(this->conformal(), verbose);
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H
deleted file mode 100644
index b59e0682d3bcc5f21b8d301e561c64371e9b7b25..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H
+++ /dev/null
@@ -1,177 +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) 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/>.
-
-Class
-    Foam::mapNearestAMI
-
-Description
-    Nearest-mapping Arbitrary Mesh Interface (AMI) method
-
-SourceFiles
-    mapNearestAMI.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef mapNearestAMI_H
-#define mapNearestAMI_H
-
-#include "AMIMethod.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                        Class mapNearestAMI Declaration
-\*---------------------------------------------------------------------------*/
-
-template<class SourcePatch, class TargetPatch>
-class mapNearestAMI
-:
-    public AMIMethod<SourcePatch, TargetPatch>
-{
-
-private:
-
-    // Private Member Functions
-
-        //- No copy construct
-        mapNearestAMI(const mapNearestAMI&) = delete;
-
-        //- No copy assignment
-        void operator=(const mapNearestAMI&) = delete;
-
-        // Marching front
-
-            //- Find nearest target face for source face srcFacei
-            void findNearestFace
-            (
-                const SourcePatch& srcPatch,
-                const TargetPatch& tgtPatch,
-                const label& srcFacei,
-                label& tgtFacei
-            ) const;
-
-            //- Determine next source-target face pair
-            void setNextNearestFaces
-            (
-                boolList& mapFlag,
-                label& startSeedI,
-                label& srcFacei,
-                label& tgtFacei
-            ) const;
-
-            //- Find mapped source face
-            label findMappedSrcFace
-            (
-                const label tgtFacei,
-                const List<DynamicList<label>>& tgtToSrc
-            ) const;
-
-
-        // Evaluation
-
-            //- Area of intersection between source and target faces
-            scalar interArea
-            (
-                const label srcFacei,
-                const label tgtFacei
-            ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("mapNearestAMI");
-
-
-    // Constructors
-
-        //- Construct from components
-        mapNearestAMI
-        (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
-            const bool reverseTarget = false,
-            const bool requireMatch = true
-        );
-
-
-    //- Destructor
-    virtual ~mapNearestAMI();
-
-
-    // Member Functions
-
-        // Manipulation
-
-            //- Update addressing and weights
-            virtual void calculate
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label srcFacei = -1,
-                label tgtFacei = -1
-            );
-
-            //- Set the face areas for parallel runs
-            virtual void setMagSf
-            (
-                const TargetPatch& tgtPatch,
-                const mapDistribute& map,
-                scalarList& srcMagSf,
-                scalarList& tgtMagSf
-            ) const;
-
-            //- Normalise the weight. Can optionally subset addressing
-            //- (e.g. for mapNearest)
-            virtual void normaliseWeights
-            (
-                const bool verbose,
-                AMIInterpolation<SourcePatch, TargetPatch>& inter
-            );
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#ifdef NoRepository
-    #include "mapNearestAMI.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
deleted file mode 100644
index 78275e0111c64cf05b958dcfeee5f80f35aea498..0000000000000000000000000000000000000000
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
+++ /dev/null
@@ -1,183 +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
--------------------------------------------------------------------------------
-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"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
-(
-    label& startSeedi,
-    label& srcFacei,
-    label& tgtFacei,
-    const boolList& mapFlag,
-    labelList& seedFaces,
-    const DynamicList<label>& visitedFaces,
-    const bool errorOnNotFound
-) const
-{
-    faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
-    (
-        startSeedi,
-        srcFacei,
-        tgtFacei,
-        mapFlag,
-        seedFaces,
-        visitedFaces,
-        false // no error on not found
-    );
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::
-partialFaceAreaWeightAMI
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
-    const bool reverseTarget,
-    const bool requireMatch
-)
-:
-    faceAreaWeightAMI<SourcePatch, TargetPatch>
-    (
-        srcPatch,
-        tgtPatch,
-        triMode,
-        reverseTarget,
-        requireMatch
-    )
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::
-~partialFaceAreaWeightAMI()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-bool Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::conformal() const
-{
-    return false;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
-(
-    labelListList& srcAddress,
-    scalarListList& srcWeights,
-    labelListList& tgtAddress,
-    scalarListList& tgtWeights,
-    label srcFacei,
-    label tgtFacei
-)
-{
-    bool ok =
-        this->initialise
-        (
-            srcAddress,
-            srcWeights,
-            tgtAddress,
-            tgtWeights,
-            srcFacei,
-            tgtFacei
-        );
-
-    if (!ok)
-    {
-        return;
-    }
-
-    // temporary storage for addressing and weights
-    List<DynamicList<label>> srcAddr(this->srcPatch_.size());
-    List<DynamicList<scalar>> srcWght(srcAddr.size());
-    List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
-    List<DynamicList<scalar>> tgtWght(tgtAddr.size());
-
-    faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
-    (
-        srcAddr,
-        srcWght,
-        tgtAddr,
-        tgtWght,
-        srcFacei,
-        tgtFacei
-    );
-
-    // transfer data to persistent storage
-    forAll(srcAddr, i)
-    {
-        srcAddress[i].transfer(srcAddr[i]);
-        srcWeights[i].transfer(srcWght[i]);
-    }
-    forAll(tgtAddr, i)
-    {
-        tgtAddress[i].transfer(tgtAddr[i]);
-        tgtWeights[i].transfer(tgtWght[i]);
-    }
-}
-
-
-template<class SourcePatch, class TargetPatch>
-void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::setMagSf
-(
-    const TargetPatch& tgtPatch,
-    const mapDistribute& map,
-    scalarList& srcMagSf,
-    scalarList& tgtMagSf
-) const
-{
-    srcMagSf = std::move(this->srcMagSf_);
-
-    scalarList newTgtMagSf(std::move(this->tgtMagSf_));
-    map.reverseDistribute(tgtPatch.size(), newTgtMagSf);
-
-    // 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 = tgtPatch.magFaceAreas();
-
-    for (const labelList& smap : map.subMap())
-    {
-        UIndirectList<scalar>(tgtMagSf, smap) =
-            UIndirectList<scalar>(newTgtMagSf, smap);
-    }
-}
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.H
index c11f82dc3a0f5295149eebd85bfa8e5f8bd7c062..43a8a3e5cc5023531d6d1a1078dbf3e7994cadd7 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.H
@@ -30,14 +30,12 @@ License
 #define AMIPatchToPatchInterpolation_H
 
 #include "AMIInterpolation.H"
-#include "primitivePatch.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    typedef AMIInterpolation<primitivePatch, primitivePatch>
-        AMIPatchToPatchInterpolation;
+    typedef AMIInterpolation AMIPatchToPatchInterpolation;
 }
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
similarity index 52%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C
rename to src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
index 3b1f06d405cf74a363d3ae5066bb522fe4500b7b..dc41c132ea60a990f89c16e796022146944c596d 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2018 OpenCFD Ltd.
+    Copyright (C) 2015-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,20 +26,31 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "AMIMethod.H"
+#include "advancingFrontAMI.H"
 #include "meshTools.H"
 #include "mapDistribute.H"
 #include "unitConversion.H"
 
+#include "findNearestMaskedOp.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(advancingFrontAMI, 0);
+}
+
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIMethod<SourcePatch, TargetPatch>::checkPatches() const
+void Foam::advancingFrontAMI::checkPatches() const
 {
-    if (debug && (!srcPatch_.size() || !tgtPatch_.size()))
+    const auto& src = srcPatch();
+    const auto& tgt = tgtPatch();
+
+    if (debug && (!src.size() || !tgt.size()))
     {
         Pout<< "AMI: Patches not on processor: Source faces = "
-            << srcPatch_.size() << ", target faces = " << tgtPatch_.size()
+            << src.size() << ", target faces = " << tgt.size()
             << endl;
     }
 
@@ -48,9 +59,9 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::checkPatches() const
     {
         const scalar maxBoundsError = 0.05;
 
-        // check bounds of source and target
-        boundBox bbSrc(srcPatch_.points(), srcPatch_.meshPoints(), true);
-        boundBox bbTgt(tgtPatch_.points(), tgtPatch_.meshPoints(), true);
+        // Check bounds of source and target
+        boundBox bbSrc(src.points(), src.meshPoints(), true);
+        boundBox bbTgt(tgt.points(), tgt.meshPoints(), true);
 
         boundBox bbTgtInf(bbTgt);
         bbTgtInf.inflate(maxBoundsError);
@@ -70,49 +81,69 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::checkPatches() const
 }
 
 
-template<class SourcePatch, class TargetPatch>
-bool Foam::AMIMethod<SourcePatch, TargetPatch>::initialise
-(
-    labelListList& srcAddress,
-    scalarListList& srcWeights,
-    labelListList& tgtAddress,
-    scalarListList& tgtWeights,
-    label& srcFacei,
-    label& tgtFacei
-)
+void Foam::advancingFrontAMI::createExtendedTgtPatch()
 {
-    checkPatches();
+    // Create processor map of extended cells. This map gets (possibly
+    // remote) cells from the src mesh such that they (together) cover
+    // all of tgt
+    extendedTgtMapPtr_.reset(calcProcMap(srcPatch0(), tgtPatch0()));
+    const mapDistribute& map = extendedTgtMapPtr_();
+
+    // Original faces from tgtPatch
+    // Note: in globalIndexing since might be remote
+    globalIndex globalTgtFaces(tgtPatch0().size());
+    distributeAndMergePatches
+    (
+        map,
+        tgtPatch0(),
+        globalTgtFaces,
+        extendedTgtFaces_,
+        extendedTgtPoints_,
+        extendedTgtFaceIDs_
+    );
+
+    // Create a representation of the tgt patch that is extended to overlap
+    // the src patch
+    extendedTgtPatchPtr_.reset
+    (
+        autoPtr<primitivePatch>::New
+        (
+            SubList<face>(extendedTgtFaces_),
+            extendedTgtPoints_
+        )
+    );
+}
+
+
+bool Foam::advancingFrontAMI::initialiseWalk(label& srcFacei, label& tgtFacei)
+{
+    const auto& src = this->srcPatch();
+    const auto& tgt = this->tgtPatch();
 
-    // set initial sizes for weights and addressing - must be done even if
-    // returns false below
-    srcAddress.setSize(srcPatch_.size());
-    srcWeights.setSize(srcPatch_.size());
-    tgtAddress.setSize(tgtPatch_.size());
-    tgtWeights.setSize(tgtPatch_.size());
+    bool foundFace = false;
 
-    // check that patch sizes are valid
-    if (!srcPatch_.size())
+    // Check that patch sizes are valid
+    if (!src.size())
     {
-        return false;
+        return foundFace;
     }
-    else if (!tgtPatch_.size())
+    else if (!tgt.size())
     {
         WarningInFunction
-            << srcPatch_.size() << " source faces but no target faces" << endl;
+            << src.size() << " source faces but no target faces" << endl;
 
-        return false;
+        return foundFace;
     }
 
-    // reset the octree
-    resetTree();
+    // Reset the octree
+    treePtr_.reset(createTree(tgt));
 
-    // find initial face match using brute force/octree search
+    // Find initial face match using brute force/octree search
     if ((srcFacei == -1) || (tgtFacei == -1))
     {
         srcFacei = 0;
         tgtFacei = 0;
-        bool foundFace = false;
-        forAll(srcPatch_, facei)
+        forAll(src, facei)
         {
             tgtFacei = findTargetFace(facei);
             if (tgtFacei >= 0)
@@ -132,7 +163,7 @@ bool Foam::AMIMethod<SourcePatch, TargetPatch>::initialise
                     << abort(FatalError);
             }
 
-            return false;
+            return foundFace;
         }
     }
 
@@ -145,8 +176,7 @@ bool Foam::AMIMethod<SourcePatch, TargetPatch>::initialise
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIMethod<SourcePatch, TargetPatch>::writeIntersectionOBJ
+void Foam::advancingFrontAMI::writeIntersectionOBJ
 (
     const scalar area,
     const face& f1,
@@ -197,51 +227,29 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::writeIntersectionOBJ
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIMethod<SourcePatch, TargetPatch>::resetTree()
+Foam::label Foam::advancingFrontAMI::findTargetFace
+(
+    const label srcFacei,
+    const UList<label>& excludeFaces,
+    const label srcFacePti
+) const
 {
-    // Clear the old octree
-    treePtr_.clear();
+    const auto& src = srcPatch();
 
-    treeBoundBox bb(tgtPatch_.points(), tgtPatch_.meshPoints());
-    bb.inflate(0.01);
+    label targetFacei = -1;
 
-    if (!treePtr_.valid())
-    {
-        treePtr_.reset
-        (
-            new indexedOctree<treeType>
-            (
-                treeType
-                (
-                    false,
-                    tgtPatch_,
-                    indexedOctree<treeType>::perturbTol()
-                ),
-                bb,                         // overall search domain
-                8,                          // maxLevel
-                10,                         // leaf size
-                3.0                         // duplicity
-            )
-        );
-    }
-}
+    const pointField& srcPts = src.points();
+    const face& srcFace = src[srcFacei];
 
+    findNearestMaskedOp<primitivePatch> fnOp(*treePtr_, excludeFaces);
 
-template<class SourcePatch, class TargetPatch>
-Foam::label Foam::AMIMethod<SourcePatch, TargetPatch>::findTargetFace
-(
-    const label srcFacei
-) const
-{
-    label targetFacei = -1;
+    const boundBox bb(srcPts, srcFace, false);
 
-    const pointField& srcPts = srcPatch_.points();
-    const face& srcFace = srcPatch_[srcFacei];
-    const point srcPt = srcFace.centre(srcPts);
-    const scalar srcFaceArea = srcMagSf_[srcFacei];
+    const point srcPt =
+        srcFacePti == -1 ? bb.centre() : srcPts[srcFace[srcFacePti]];
 
-    pointIndexHit sample = treePtr_->findNearest(srcPt, 10.0*srcFaceArea);
+    const pointIndexHit sample =
+        treePtr_->findNearest(srcPt, magSqr(bb.max() - bb.centre()), fnOp);
 
     if (sample.hit())
     {
@@ -259,11 +267,10 @@ Foam::label Foam::AMIMethod<SourcePatch, TargetPatch>::findTargetFace
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIMethod<SourcePatch, TargetPatch>::appendNbrFaces
+void Foam::advancingFrontAMI::appendNbrFaces
 (
     const label facei,
-    const TargetPatch& patch,
+    const primitivePatch& patch,
     const DynamicList<label>& visitedFaces,
     DynamicList<label>& faceIDs
 ) const
@@ -272,33 +279,11 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::appendNbrFaces
 
     const labelList& nbrFaces = patch.faceFaces()[facei];
 
-    // filter out faces already visited from face neighbours
+    // Filter out faces already visited from face neighbours
     for (const label nbrFacei : nbrFaces)
     {
-        bool valid = true;
-        for (const label visitedFacei : visitedFaces)
-        {
-            if (nbrFacei == visitedFacei)
-            {
-                valid = false;
-                break;
-            }
-        }
-
-        if (valid)
-        {
-            for (const label testFacei : faceIDs)
-            {
-                if (nbrFacei == testFacei)
-                {
-                    valid = false;
-                    break;
-                }
-            }
-        }
-
-        // prevent addition of face if it is not on the same plane-ish
-        if (valid)
+        // Prevent addition of face if it is not on the same plane-ish
+        if (!visitedFaces.found(nbrFacei) && !faceIDs.found(nbrFacei))
         {
             const vector& n1 = patch.faceNormals()[facei];
             const vector& n2 = patch.faceNormals()[nbrFacei];
@@ -314,11 +299,9 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::appendNbrFaces
 }
 
 
-template<class SourcePatch, class TargetPatch>
-template<class PatchType>
-void Foam::AMIMethod<SourcePatch, TargetPatch>::triangulatePatch
+void Foam::advancingFrontAMI::triangulatePatch
 (
-    const PatchType& patch,
+    const primitivePatch& patch,
     List<DynamicList<face>>& tris,
     List<scalar>& magSf
 ) const
@@ -330,6 +313,8 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::triangulatePatch
     // Using methods that index into existing points
     forAll(patch, facei)
     {
+        tris[facei].clear();
+
         switch (triMode_)
         {
             case faceAreaIntersect::tmFan:
@@ -362,34 +347,114 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::triangulatePatch
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-Foam::AMIMethod<SourcePatch, TargetPatch>::AMIMethod
+Foam::advancingFrontAMI::advancingFrontAMI
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch,
-    const faceAreaIntersect::triangulationMode& triMode,
+    const dictionary& dict,
+    const bool reverseTarget
+)
+:
+    AMIInterpolation(dict, reverseTarget),
+    srcTris_(),
+    tgtTris_(),
+    extendedTgtPatchPtr_(nullptr),
+    extendedTgtFaces_(),
+    extendedTgtPoints_(),
+    extendedTgtFaceIDs_(),
+    extendedTgtMapPtr_(nullptr),
+    srcNonOverlap_(),
+    triMode_
+    (
+        faceAreaIntersect::triangulationModeNames_.getOrDefault
+        (
+            "triMode",
+            dict,
+            faceAreaIntersect::tmMesh
+        )
+    )
+{}
+
+
+Foam::advancingFrontAMI::advancingFrontAMI
+(
+    const bool requireMatch,
     const bool reverseTarget,
-    const bool requireMatch
+    const scalar lowWeightCorrection,
+    const faceAreaIntersect::triangulationMode triMode
 )
 :
-    srcPatch_(srcPatch),
-    tgtPatch_(tgtPatch),
-    reverseTarget_(reverseTarget),
-    requireMatch_(requireMatch),
-    srcMagSf_(srcPatch.size(), 1.0),
-    tgtMagSf_(tgtPatch.size(), 1.0),
+    AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
+    srcTris_(),
+    tgtTris_(),
+    extendedTgtPatchPtr_(nullptr),
+    extendedTgtFaces_(),
+    extendedTgtPoints_(),
+    extendedTgtFaceIDs_(),
+    extendedTgtMapPtr_(nullptr),
     srcNonOverlap_(),
     triMode_(triMode)
-{
-    // Note: setting srcMagSf and tgtMagSf to 1 by default for 1-to-1 methods
-    // - others will need to overwrite as necessary
-}
+{}
+
+
+Foam::advancingFrontAMI::advancingFrontAMI(const advancingFrontAMI& ami)
+:
+    AMIInterpolation(ami),
+    srcTris_(),
+    tgtTris_(),
+    extendedTgtPatchPtr_(nullptr),
+    extendedTgtFaces_(),
+    extendedTgtPoints_(),
+    extendedTgtFaceIDs_(),
+    extendedTgtMapPtr_(nullptr),
+    srcNonOverlap_(),
+    triMode_(ami.triMode_)
+{}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-bool Foam::AMIMethod<SourcePatch, TargetPatch>::conformal() const
+bool Foam::advancingFrontAMI::calculate
+(
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
+    const autoPtr<searchableSurface>& surfPtr
+)
+{
+    if (AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr))
+    {
+        // Create a representation of the target patch that covers the source patch
+        if (distributed())
+        {
+            createExtendedTgtPatch();
+        }
+
+        const auto& src = this->srcPatch();
+        const auto& tgt = this->tgtPatch();
+
+        // Initialise area magnitudes
+        srcMagSf_.setSize(src.size(), 1.0);
+        tgtMagSf_.setSize(tgt.size(), 1.0);
+
+        // Source and target patch triangulations
+        triangulatePatch(src, srcTris_, srcMagSf_);
+        triangulatePatch(tgt, tgtTris_, tgtMagSf_);
+
+        checkPatches();
+
+        // Set initial sizes for weights and addressing - must be done even if
+        // returns false below
+        srcAddress_.setSize(src.size());
+        srcWeights_.setSize(src.size());
+        tgtAddress_.setSize(tgt.size());
+        tgtWeights_.setSize(tgt.size());
+
+        return true;
+    }
+
+    return false;
+}
+
+
+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
new file mode 100644
index 0000000000000000000000000000000000000000..84031daa733f3e53df6d86d6bdaca30a458ee3db
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMI.H
@@ -0,0 +1,271 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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) 2016-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/>.
+
+Class
+    Foam::advancingFrontAMI
+
+Description
+    Base class for Arbitrary Mesh Interface (AMI) methods
+
+SourceFiles
+    advancingFrontAMI.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef advancingFrontAMI_H
+#define advancingFrontAMI_H
+
+#include "className.H"
+#include "DynamicList.H"
+#include "faceAreaIntersect.H"
+#include "pointList.H"
+#include "AMIInterpolation.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class advancingFrontAMI Declaration
+\*---------------------------------------------------------------------------*/
+
+class advancingFrontAMI
+:
+    public AMIInterpolation
+{
+
+private:
+
+    // Private Member Functions
+
+        //- No copy assignment
+        void operator=(const advancingFrontAMI&) = delete;
+
+
+        // Parallel operations
+
+            label calcOverlappingProcs
+            (
+                const List<treeBoundBoxList>& procBb,
+                const treeBoundBox& bb,
+                boolList& overlaps
+            ) const;
+
+            void distributePatches
+            (
+                const mapDistribute& map,
+                const primitivePatch& pp,
+                const globalIndex& gi,
+                List<faceList>& faces,
+                List<pointField>& points,
+                List<labelList>& tgtFaceIDs
+            ) const;
+
+            void distributeAndMergePatches
+            (
+                const mapDistribute& map,
+                const primitivePatch& tgtPatch,
+                const globalIndex& gi,
+                faceList& tgtFaces,
+                pointField& tgtPoints,
+                labelList& tgtFaceIDs
+            ) const;
+
+            autoPtr<mapDistribute> calcProcMap
+            (
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch
+            ) const;
+
+
+protected:
+
+    // Protected data
+
+        //- Storage for src-side triangle decomposition
+        List<DynamicList<face>> srcTris_;
+
+        //- Storage for tgt-side triangle decomposition
+        List<DynamicList<face>> tgtTris_;
+
+        //- Demand-driven extended target mesh (distributed parallel usage)
+        autoPtr<primitivePatch> extendedTgtPatchPtr_;
+
+        //- Extended patch faces
+        faceList extendedTgtFaces_;
+
+        //- Extended patch points
+        pointField extendedTgtPoints_;
+
+        //- Extended patch face IDs
+        labelList extendedTgtFaceIDs_;
+
+        //- Extended patch map
+        autoPtr<mapDistribute> extendedTgtMapPtr_;
+
+        //- Labels of faces that are not overlapped by any target faces
+        //- (should be empty for correct functioning for fully covered AMIs)
+        labelList srcNonOverlap_;
+
+        //- Octree used to find face seeds
+        autoPtr<indexedOctree<treeType>> treePtr_;
+
+        //- Face triangulation mode
+        const faceAreaIntersect::triangulationMode triMode_;
+
+
+    // Protected Member Functions
+
+        // Helper functions
+
+            //- Create a map that extends tgtPatch so that it covers srcPatch
+            void createExtendedTgtPatch();
+
+            //- Check AMI patch coupling
+            void checkPatches() const;
+
+            virtual bool calculate
+            (
+                const primitivePatch& srcPatch,
+                const primitivePatch& tgtPatch,
+                const autoPtr<searchableSurface>& surfPtr = nullptr
+            );
+
+            //- Initialise walk and return true if all ok
+            bool initialiseWalk
+            (
+                label& srcFacei,
+                label& tgtFacei
+            );
+
+            //- Write triangle intersection to OBJ file
+            void writeIntersectionOBJ
+            (
+                const scalar area,
+                const face& f1,
+                const face& f2,
+                const pointField& f1Points,
+                const pointField& f2Points
+            ) const;
+
+
+        // Common AMI method functions
+
+            label findTargetFace
+            (
+                const label srcFacei,
+                const UList<label>& excludeFaces = UList<label>::null(),
+                const label srcFacePti = -1
+            ) const;
+
+            //- Add faces neighbouring facei to the ID list
+            void appendNbrFaces
+            (
+                const label facei,
+                const primitivePatch& patch,
+                const DynamicList<label>& visitedFaces,
+                DynamicList<label>& faceIDs
+            ) const;
+
+            //- Helper function to decompose a patch
+            void triangulatePatch
+            (
+                const primitivePatch& patch,
+                List<DynamicList<face>>& tris,
+                List<scalar>& magSf
+            ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("advancingFrontAMI");
+
+    // Constructors
+
+        //- Construct from components
+        advancingFrontAMI
+        (
+            const dictionary& dict,
+            const bool reverseTarget
+        );
+
+        //- Construct from components
+        advancingFrontAMI
+        (
+            const bool requireMatch = true,
+            const bool reverseTarget = false,
+            const scalar lowWeightCorrection = -1,
+            const faceAreaIntersect::triangulationMode triMode =
+                faceAreaIntersect::tmMesh
+        );
+
+        //- Construct as copy
+        advancingFrontAMI(const advancingFrontAMI& ami);
+
+        //- Construct and return a clone
+        virtual autoPtr<AMIInterpolation> clone() const
+        {
+            return autoPtr<AMIInterpolation>(new advancingFrontAMI(*this));
+        }
+
+
+    //- Destructor
+    virtual ~advancingFrontAMI() = default;
+
+
+    // Member Functions
+
+        //- Return const access to the source patch
+        inline const primitivePatch& srcPatch() const;
+
+        //- Return const access to the target patch
+        inline const primitivePatch& tgtPatch() const;
+
+        //- 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;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "advancingFrontAMII.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.C b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMII.H
similarity index 63%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.C
rename to src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMII.H
index 3ea1622bed68b5104889157962713e26dfe5c318..e0441345383976a94c5f1b2ddf26ff89b6ef8ab2 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIPatchToPatchInterpolation.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMII.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2013 OpenFOAM Foundation
+    Copyright (C) 2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -25,23 +25,40 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "AMIPatchToPatchInterpolation.H"
-#include "AMIMethod.H"
-#include "directAMI.H"
-#include "mapNearestAMI.H"
-#include "faceAreaWeightAMI.H"
-#include "partialFaceAreaWeightAMI.H"
+inline const Foam::primitivePatch& Foam::advancingFrontAMI::srcPatch() const
+{
+    if (!tsrcPatch0_.valid())
+    {
+        FatalErrorInFunction
+            << "tsrcPatch0_ not allocated"
+            << abort(FatalError);
+    }
+
+    return tsrcPatch0_();
+}
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-namespace Foam
+inline const Foam::primitivePatch& Foam::advancingFrontAMI::tgtPatch() const
 {
-    makeAMIMethod(AMIPatchToPatchInterpolation);
+    if (extendedTgtPatchPtr_)
+    {
+        return extendedTgtPatchPtr_();
+    }
+
+    if (!ttgtPatch0_.valid())
+    {
+        FatalErrorInFunction
+            << "srcPatch0Ptr not allocated"
+            << abort(FatalError);
+    }
 
-    makeAMIMethodType(AMIPatchToPatchInterpolation, directAMI);
-    makeAMIMethodType(AMIPatchToPatchInterpolation, mapNearestAMI);
-    makeAMIMethodType(AMIPatchToPatchInterpolation, faceAreaWeightAMI);
-    makeAMIMethodType(AMIPatchToPatchInterpolation, partialFaceAreaWeightAMI);
+    return ttgtPatch0_();
+}
+
+
+inline const Foam::labelList& Foam::advancingFrontAMI::srcNonOverlap() const
+{
+    return srcNonOverlap_;
 }
 
 
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationParallelOps.C b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMIParallelOps.C
similarity index 84%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationParallelOps.C
rename to src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMIParallelOps.C
index ad3a9ec83ad7d43af7c87deaea3155f2eccc4bc3..e67d9e6e400cde827065477af3a849195c9e272e 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationParallelOps.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/advancingFrontAMI/advancingFrontAMIParallelOps.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,62 +26,14 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "AMIInterpolation.H"
+#include "advancingFrontAMI.H"
 #include "mergePoints.H"
 #include "mapDistribute.H"
 #include "AABBTree.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-template<class SourcePatch, class TargetPatch>
-Foam::label Foam::AMIInterpolation<SourcePatch, TargetPatch>::calcDistribution
-(
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch
-) const
-{
-    label proci = 0;
-
-    if (Pstream::parRun())
-    {
-        labelList facesPresentOnProc(Pstream::nProcs(), Zero);
-        if ((srcPatch.size() > 0) || (tgtPatch.size() > 0))
-        {
-            facesPresentOnProc[Pstream::myProcNo()] = 1;
-        }
-        else
-        {
-            facesPresentOnProc[Pstream::myProcNo()] = 0;
-        }
-
-        Pstream::gatherList(facesPresentOnProc);
-        Pstream::scatterList(facesPresentOnProc);
-
-        label nHaveFaces = sum(facesPresentOnProc);
-
-        if (nHaveFaces > 1)
-        {
-            proci = -1;
-            DebugInFunction
-                << "AMI split across multiple processors" << endl;
-        }
-        else if (nHaveFaces == 1)
-        {
-            proci = facesPresentOnProc.find(1);
-            DebugInFunction
-                << "AMI local to processor" << proci << endl;
-        }
-    }
-
-
-    // Either not parallel or no faces on any processor
-    return proci;
-}
-
-
-template<class SourcePatch, class TargetPatch>
-Foam::label
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::calcOverlappingProcs
+Foam::label Foam::advancingFrontAMI::calcOverlappingProcs
 (
     const List<treeBoundBoxList>& procBb,
     const treeBoundBox& bb,
@@ -102,7 +54,7 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::calcOverlappingProcs
             if (tbb.overlaps(bb))
             {
                 overlaps[proci] = true;
-                nOverlaps++;
+                ++nOverlaps;
                 break;
             }
         }
@@ -112,11 +64,10 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::calcOverlappingProcs
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::distributePatches
+void Foam::advancingFrontAMI::distributePatches
 (
     const mapDistribute& map,
-    const TargetPatch& pp,
+    const primitivePatch& pp,
     const globalIndex& gi,
     List<faceList>& faces,
     List<pointField>& points,
@@ -197,12 +148,10 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::distributePatches
 }
 
 
-template<class SourcePatch, class TargetPatch>
-void Foam::AMIInterpolation<SourcePatch, TargetPatch>::
-distributeAndMergePatches
+void Foam::advancingFrontAMI::distributeAndMergePatches
 (
     const mapDistribute& map,
-    const TargetPatch& tgtPatch,
+    const primitivePatch& tgtPatch,
     const globalIndex& gi,
     faceList& tgtFaces,
     pointField& tgtPoints,
@@ -311,12 +260,10 @@ distributeAndMergePatches
 }
 
 
-template<class SourcePatch, class TargetPatch>
-Foam::autoPtr<Foam::mapDistribute>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::calcProcMap
+Foam::autoPtr<Foam::mapDistribute> Foam::advancingFrontAMI::calcProcMap
 (
-    const SourcePatch& srcPatch,
-    const TargetPatch& tgtPatch
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch
 ) const
 {
     // Get decomposition of patch
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
new file mode 100644
index 0000000000000000000000000000000000000000..2be76626bdf415b70290fe4d9f7989a207e40794
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.C
@@ -0,0 +1,801 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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) 2018-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 "faceAreaWeightAMI.H"
+#include "profiling.H"
+#include "OBJstream.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(faceAreaWeightAMI, 0);
+    addToRunTimeSelectionTable(AMIInterpolation, faceAreaWeightAMI, dict);
+    addToRunTimeSelectionTable(AMIInterpolation, faceAreaWeightAMI, component);
+}
+
+// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
+
+/*
+    if (debug)
+    {
+        static label nAMI = 0;
+
+        // Write out triangulated surfaces as OBJ files
+        OBJstream srcTriObj("srcTris_" + Foam::name(nAMI) + ".obj");
+        const pointField& srcPts = src.points();
+        forAll(srcTris_, facei)
+        {
+            const DynamicList<face>& faces = srcTris_[facei];
+            for (const face& f : faces)
+            {
+                srcTriObj.write
+                (
+                    triPointRef(srcPts[f[0]], srcPts[f[1]], srcPts[f[2]])
+                );
+            }
+        }
+
+        OBJstream tgtTriObj("tgtTris_" + Foam::name(nAMI) + ".obj");
+        const pointField& tgtPts = tgt.points();
+        forAll(tgtTris_, facei)
+        {
+            const DynamicList<face>& faces = tgtTris_[facei];
+            for (const face& f : faces)
+            {
+                tgtTriObj.write
+                (
+                    triPointRef(tgtPts[f[0]], tgtPts[f[1]], tgtPts[f[2]])
+                );
+            }
+        }
+
+        ++nAMI;
+    }
+*/
+
+
+void Foam::faceAreaWeightAMI::calcAddressing
+(
+    List<DynamicList<label>>& srcAddr,
+    List<DynamicList<scalar>>& srcWght,
+    List<DynamicList<point>>& srcCtr,
+    List<DynamicList<label>>& tgtAddr,
+    List<DynamicList<scalar>>& tgtWght,
+    label srcFacei,
+    label tgtFacei
+)
+{
+    addProfiling(ami, "faceAreaWeightAMI::calcAddressing");
+
+    // Construct weights and addressing
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    label nFacesRemaining = srcAddr.size();
+
+    // List of tgt face neighbour faces
+    DynamicList<label> nbrFaces(10);
+
+    // List of faces currently visited for srcFacei to avoid multiple hits
+    DynamicList<label> visitedFaces(10);
+
+    // List to keep track of tgt faces used to seed src faces
+    labelList seedFaces(nFacesRemaining, -1);
+    seedFaces[srcFacei] = tgtFacei;
+
+    // List to keep track of whether src face can be mapped
+    bitSet mapFlag(nFacesRemaining, true);
+
+    // Reset starting seed
+    label startSeedi = 0;
+
+    bool continueWalk = true;
+    DynamicList<label> nonOverlapFaces;
+    do
+    {
+        nbrFaces.clear();
+        visitedFaces.clear();
+
+        // Do advancing front starting from srcFacei,tgtFacei
+        bool faceProcessed = processSourceFace
+        (
+            srcFacei,
+            tgtFacei,
+
+            nbrFaces,
+            visitedFaces,
+
+            srcAddr,
+            srcWght,
+            srcCtr,
+            tgtAddr,
+            tgtWght
+        );
+
+        mapFlag.unset(srcFacei);
+
+        if (!faceProcessed)
+        {
+            nonOverlapFaces.append(srcFacei);
+        }
+
+        // Choose new src face from current src face neighbour
+        continueWalk = setNextFaces
+        (
+            startSeedi,
+            srcFacei,
+            tgtFacei,
+            mapFlag,
+            seedFaces,
+            visitedFaces,
+            requireMatch_ && (lowWeightCorrection_ < 0)
+            // pass in nonOverlapFaces for failed tree search?
+        );
+    } while (continueWalk);
+
+    srcNonOverlap_.transfer(nonOverlapFaces);
+}
+
+
+bool Foam::faceAreaWeightAMI::processSourceFace
+(
+    const label srcFacei,
+    const label tgtStartFacei,
+
+    // list of tgt face neighbour faces
+    DynamicList<label>& nbrFaces,
+    // list of faces currently visited for srcFacei to avoid multiple hits
+    DynamicList<label>& visitedFaces,
+
+    // temporary storage for addressing, weights and centroid
+    List<DynamicList<label>>& srcAddr,
+    List<DynamicList<scalar>>& srcWght,
+    List<DynamicList<point>>& srcCtr,
+    List<DynamicList<label>>& tgtAddr,
+    List<DynamicList<scalar>>& tgtWght
+)
+{
+    addProfiling(ami, "faceAreaWeightAMI::processSourceFace");
+
+    if (tgtStartFacei == -1)
+    {
+        return false;
+    }
+
+    const auto& tgtPatch = this->tgtPatch();
+
+    // append initial target face and neighbours
+    nbrFaces.append(tgtStartFacei);
+    appendNbrFaces(tgtStartFacei, tgtPatch, visitedFaces, nbrFaces);
+
+    bool faceProcessed = false;
+
+    label maxNeighbourFaces = nbrFaces.size();
+
+    do
+    {
+        // process new target face
+        label tgtFacei = nbrFaces.remove();
+        visitedFaces.append(tgtFacei);
+
+        scalar interArea = 0;
+        vector interCentroid(Zero);
+        calcInterArea(srcFacei, tgtFacei, interArea, interCentroid);
+
+        // store when intersection fractional area > tolerance
+        if (interArea/srcMagSf_[srcFacei] > faceAreaIntersect::tolerance())
+        {
+            srcAddr[srcFacei].append(tgtFacei);
+            srcWght[srcFacei].append(interArea);
+            srcCtr[srcFacei].append(interCentroid);
+
+            tgtAddr[tgtFacei].append(srcFacei);
+            tgtWght[tgtFacei].append(interArea);
+
+            appendNbrFaces(tgtFacei, tgtPatch, visitedFaces, nbrFaces);
+
+            faceProcessed = true;
+
+            maxNeighbourFaces = max(maxNeighbourFaces, nbrFaces.size());
+        }
+
+    } while (nbrFaces.size() > 0);
+
+    if (debug > 1)
+    {
+        DebugVar(maxNeighbourFaces);
+    }
+
+    return faceProcessed;
+}
+
+
+bool Foam::faceAreaWeightAMI::setNextFaces
+(
+    label& startSeedi,
+    label& srcFacei,
+    label& tgtFacei,
+    const bitSet& mapFlag,
+    labelList& seedFaces,
+    const DynamicList<label>& visitedFaces,
+    const bool errorOnNotFound
+) const
+{
+    addProfiling(ami, "faceAreaWeightAMI::setNextFaces");
+
+    if (mapFlag.count() == 0)
+    {
+        // No more faces to map
+        return false;
+    }
+
+    const labelList& srcNbrFaces = this->srcPatch().faceFaces()[srcFacei];
+
+    // Initialise tgtFacei
+    tgtFacei = -1;
+
+    // Set possible seeds for later use
+    bool valuesSet = false;
+    for (label faceS: srcNbrFaces)
+    {
+        if (mapFlag.test(faceS) && seedFaces[faceS] == -1)
+        {
+            for (label faceT : visitedFaces)
+            {
+                const scalar threshold =
+                    srcMagSf_[faceS]*faceAreaIntersect::tolerance();
+
+                // Store when intersection fractional area > threshold
+                if (overlaps(faceS, faceT, threshold))
+                {
+                    seedFaces[faceS] = faceT;
+
+                    if (!valuesSet)
+                    {
+                        srcFacei = faceS;
+                        tgtFacei = faceT;
+                        valuesSet = true;
+                    }
+                }
+            }
+        }
+    }
+
+    if (valuesSet)
+    {
+        return true;
+    }
+
+    // Set next src and tgt faces if not set above
+    // - try to use existing seed
+    label facei = startSeedi;
+    if (!mapFlag.test(startSeedi))
+    {
+        facei = mapFlag.find_next(facei);
+    }
+    const label startSeedi0 = facei;
+
+    bool foundNextSeed = false;
+    while (facei != -1)
+    {
+        if (!foundNextSeed)
+        {
+            startSeedi = facei;
+            foundNextSeed = true;
+        }
+
+        if (seedFaces[facei] != -1)
+        {
+            srcFacei = facei;
+            tgtFacei = seedFaces[facei];
+
+            return true;
+        }
+
+        facei = mapFlag.find_next(facei);
+    }
+
+    // Perform new search to find match
+    if (debug)
+    {
+        Pout<< "Advancing front stalled: searching for new "
+            << "target face" << endl;
+    }
+
+    facei = startSeedi0;
+    while (facei != -1)
+    {
+        srcFacei = facei;
+        tgtFacei = findTargetFace(srcFacei, visitedFaces);
+
+        if (tgtFacei >= 0)
+        {
+            return true;
+        }
+
+        facei = mapFlag.find_next(facei);
+    }
+
+    if (errorOnNotFound)
+    {
+        FatalErrorInFunction
+            << "Unable to set target face for source face " << srcFacei
+            << abort(FatalError);
+    }
+
+    return false;
+}
+
+
+void Foam::faceAreaWeightAMI::calcInterArea
+(
+    const label srcFacei,
+    const label tgtFacei,
+    scalar& area,
+    vector& centroid
+) const
+{
+    addProfiling(ami, "faceAreaWeightAMI::interArea");
+
+    // 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();
+
+    // Create intersection object
+    faceAreaIntersect inter
+    (
+        srcPoints,
+        tgtPoints,
+        srcTris_[srcFacei],
+        tgtTris_[tgtFacei],
+        reverseTarget_,
+        AMIInterpolation::cacheIntersections_
+    );
+
+    // Crude resultant norm
+    vector n(-srcPatch.faceNormals()[srcFacei]);
+    if (reverseTarget_)
+    {
+        n -= tgtPatch.faceNormals()[tgtFacei];
+    }
+    else
+    {
+        n += tgtPatch.faceNormals()[tgtFacei];
+    }
+    scalar magN = mag(n);
+
+    const face& src = srcPatch[srcFacei];
+    const face& tgt = tgtPatch[tgtFacei];
+
+    if (magN > ROOTVSMALL)
+    {
+        inter.calc(src, tgt, n/magN, area, centroid);
+    }
+    else
+    {
+        WarningInFunction
+            << "Invalid normal for source face " << srcFacei
+            << " points " << UIndirectList<point>(srcPoints, src)
+            << " target face " << tgtFacei
+            << " points " << UIndirectList<point>(tgtPoints, tgt)
+            << endl;
+    }
+
+    if (AMIInterpolation::cacheIntersections_ && debug)
+    {
+        static OBJstream tris("intersectionTris.obj");
+        const auto& triPts = inter.triangles();
+        for (const auto& tp : triPts)
+        {
+            tris.write(triPointRef(tp[0], tp[1], tp[2]), false);
+        }
+    }
+
+    if ((debug > 1) && (area > 0))
+    {
+        writeIntersectionOBJ(area, src, tgt, srcPoints, tgtPoints);
+    }
+}
+
+
+bool Foam::faceAreaWeightAMI::overlaps
+(
+    const label srcFacei,
+    const label tgtFacei,
+    const scalar threshold
+) const
+{
+    // Quick reject if either face has zero area
+    if
+    (
+        (srcMagSf_[srcFacei] < ROOTVSMALL)
+     || (tgtMagSf_[tgtFacei] < ROOTVSMALL)
+    )
+    {
+        return false;
+    }
+
+    const auto& srcPatch = this->srcPatch();
+    const auto& tgtPatch = this->tgtPatch();
+
+    const pointField& srcPoints = srcPatch.points();
+    const pointField& tgtPoints = tgtPatch.points();
+
+    faceAreaIntersect inter
+    (
+        srcPoints,
+        tgtPoints,
+        srcTris_[srcFacei],
+        tgtTris_[tgtFacei],
+        reverseTarget_,
+        AMIInterpolation::cacheIntersections_
+    );
+
+    // Crude resultant norm
+    vector n(-srcPatch.faceNormals()[srcFacei]);
+    if (reverseTarget_)
+    {
+        n -= tgtPatch.faceNormals()[tgtFacei];
+    }
+    else
+    {
+        n += tgtPatch.faceNormals()[tgtFacei];
+    }
+    scalar magN = mag(n);
+
+    const face& src = srcPatch[srcFacei];
+    const face& tgt = tgtPatch[tgtFacei];
+
+    if (magN > ROOTVSMALL)
+    {
+        return inter.overlaps(src, tgt, n/magN, threshold);
+    }
+    else
+    {
+        WarningInFunction
+            << "Invalid normal for source face " << srcFacei
+            << " points " << UIndirectList<point>(srcPoints, src)
+            << " target face " << tgtFacei
+            << " points " << UIndirectList<point>(tgtPoints, tgt)
+            << endl;
+    }
+
+    return false;
+}
+
+
+void Foam::faceAreaWeightAMI::restartUncoveredSourceFace
+(
+    List<DynamicList<label>>& srcAddr,
+    List<DynamicList<scalar>>& srcWght,
+    List<DynamicList<point>>& srcCtr,
+    List<DynamicList<label>>& tgtAddr,
+    List<DynamicList<scalar>>& tgtWght
+)
+{
+    addProfiling(ami, "faceAreaWeightAMI::restartUncoveredSourceFace");
+
+    // Note: exclude faces in srcNonOverlap_ for ACMI?
+
+    label nBelowMinWeight = 0;
+    const scalar minWeight = 0.95;
+
+    // List of tgt face neighbour faces
+    DynamicList<label> nbrFaces(10);
+
+    // List of faces currently visited for srcFacei to avoid multiple hits
+    DynamicList<label> visitedFaces(10);
+
+    const auto& srcPatch = this->srcPatch();
+
+    forAll(srcWght, srcFacei)
+    {
+        const scalar s = sum(srcWght[srcFacei]);
+        const scalar t = s/srcMagSf_[srcFacei];
+
+        if (t < minWeight)
+        {
+            ++nBelowMinWeight;
+
+            const face& f = srcPatch[srcFacei];
+
+            forAll(f, fpi)
+            {
+                const label tgtFacei =
+                    findTargetFace(srcFacei, srcAddr[srcFacei], fpi);
+
+                if (tgtFacei != -1)
+                {
+                    nbrFaces.clear();
+                    visitedFaces = srcAddr[srcFacei];
+
+                    (void)processSourceFace
+                    (
+                        srcFacei,
+                        tgtFacei,
+
+                        nbrFaces,
+                        visitedFaces,
+
+                        srcAddr,
+                        srcWght,
+                        srcCtr,
+                        tgtAddr,
+                        tgtWght
+                    );
+                }
+            }
+        }
+    }
+
+    if (debug && nBelowMinWeight)
+    {
+        WarningInFunction
+            << "Restarted search on " << nBelowMinWeight
+            << " faces since sum of weights < " << minWeight
+            << endl;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::faceAreaWeightAMI::faceAreaWeightAMI
+(
+    const dictionary& dict,
+    const bool reverseTarget
+)
+:
+    advancingFrontAMI(dict, reverseTarget),
+    restartUncoveredSourceFace_
+    (
+        dict.getOrDefault("restartUncoveredSourceFace", true)
+    )
+{}
+
+
+Foam::faceAreaWeightAMI::faceAreaWeightAMI
+(
+    const bool requireMatch,
+    const bool reverseTarget,
+    const scalar lowWeightCorrection,
+    const faceAreaIntersect::triangulationMode triMode,
+    const bool restartUncoveredSourceFace
+)
+:
+    advancingFrontAMI
+    (
+        requireMatch,
+        reverseTarget,
+        lowWeightCorrection,
+        triMode
+    ),
+    restartUncoveredSourceFace_(restartUncoveredSourceFace)
+{}
+
+
+Foam::faceAreaWeightAMI::faceAreaWeightAMI(const faceAreaWeightAMI& ami)
+:
+    advancingFrontAMI(ami),
+    restartUncoveredSourceFace_(ami.restartUncoveredSourceFace_)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::faceAreaWeightAMI::calculate
+(
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
+    const autoPtr<searchableSurface>& surfPtr
+)
+{
+    if (upToDate_)
+    {
+        return false;
+    }
+
+    addProfiling(ami, "faceAreaWeightAMI::calculate");
+
+    advancingFrontAMI::calculate(srcPatch, tgtPatch, surfPtr);
+
+    label srcFacei = 0;
+    label tgtFacei = 0;
+
+    bool ok = initialiseWalk(srcFacei, tgtFacei);
+
+    srcCentroids_.setSize(srcAddress_.size());
+
+    const auto& src = this->srcPatch();
+    const auto& tgt = this->tgtPatch(); // might be the extended patch!
+
+    // Temporary storage for addressing and weights
+    List<DynamicList<label>> srcAddr(src.size());
+    List<DynamicList<scalar>> srcWght(srcAddr.size());
+    List<DynamicList<point>> srcCtr(srcAddr.size());
+    List<DynamicList<label>> tgtAddr(tgt.size());
+    List<DynamicList<scalar>> tgtWght(tgtAddr.size());
+
+    if (ok)
+    {
+        calcAddressing
+        (
+            srcAddr,
+            srcWght,
+            srcCtr,
+            tgtAddr,
+            tgtWght,
+            srcFacei,
+            tgtFacei
+        );
+
+        if (debug && !srcNonOverlap_.empty())
+        {
+            Pout<< "    AMI: " << srcNonOverlap_.size()
+                << " non-overlap faces identified"
+                << endl;
+        }
+
+        // Check for badly covered faces
+        if (restartUncoveredSourceFace_) // && requireMatch_???
+        {
+            restartUncoveredSourceFace
+            (
+                srcAddr,
+                srcWght,
+                srcCtr,
+                tgtAddr,
+                tgtWght
+            );
+        }
+    }
+
+    // Transfer data to persistent storage
+    forAll(srcAddr, i)
+    {
+        srcAddress_[i].transfer(srcAddr[i]);
+        srcWeights_[i].transfer(srcWght[i]);
+        srcCentroids_[i].transfer(srcCtr[i]);
+    }
+
+    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_,
+            ListOps::appendEqOp<label>(),
+            flipOp(),                   // flip operation
+            labelList()
+        );
+
+        mapDistributeBase::distribute
+        (
+            Pstream::commsTypes::nonBlocking,
+            List<labelPair>(),
+            tgtPatch0.size(),
+            extendedTgtMapPtr_->constructMap(),
+            false,
+            extendedTgtMapPtr_->subMap(),
+            false,
+            tgtWeights_,
+            ListOps::appendEqOp<scalar>(),
+            flipOp(),
+            scalarList()
+        );
+
+        // 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(conformal(), true);
+
+    upToDate_ = true;
+
+    return upToDate_;
+}
+
+
+void Foam::faceAreaWeightAMI::write(Ostream& os) const
+{
+    advancingFrontAMI::write(os);
+
+    if (restartUncoveredSourceFace_)
+    {
+        os.writeEntry
+        (
+            "restartUncoveredSourceFace",
+            restartUncoveredSourceFace_
+        );
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
similarity index 70%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H
rename to src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
index 0c6dec88d6ce98d25f95c8efa330720eea418278..818d26ebea8aec4310209e3d46261a90384a3f4d 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/faceAreaWeightAMI/faceAreaWeightAMI.H
@@ -38,7 +38,7 @@ SourceFiles
 #ifndef faceAreaWeightAMI_H
 #define faceAreaWeightAMI_H
 
-#include "AMIMethod.H"
+#include "advancingFrontAMI.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -49,10 +49,9 @@ namespace Foam
                       Class faceAreaWeightAMI Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class SourcePatch, class TargetPatch>
 class faceAreaWeightAMI
 :
-    public AMIMethod<SourcePatch, TargetPatch>
+    public advancingFrontAMI
 {
 private:
 
@@ -61,30 +60,34 @@ private:
         //- Flag to restart uncovered source faces
         const bool restartUncoveredSourceFace_;
 
-        //- Storage for src-side triangle decomposition
-        List<DynamicList<face>> srcTris_;
-
-        //- Storage for tgt-side triangle decomposition
-        List<DynamicList<face>> tgtTris_;
 
 
 protected:
 
     // Protected Member Functions
 
-        //- No copy construct
-        faceAreaWeightAMI(const faceAreaWeightAMI&) = delete;
-
         //- 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
 
-            //- Calculate addressing and weights using temporary storage
+            //- Calculate addressing, weights and centroids using temporary
+            //- storage
             virtual void calcAddressing
             (
                 List<DynamicList<label>>& srcAddress,
                 List<DynamicList<scalar>>& srcWeights,
+                List<DynamicList<point>>& srcCentroids,
                 List<DynamicList<label>>& tgtAddress,
                 List<DynamicList<scalar>>& tgtWeights,
                 label srcFacei,
@@ -100,6 +103,7 @@ protected:
                 DynamicList<label>& visitedFaces,
                 List<DynamicList<label>>& srcAddr,
                 List<DynamicList<scalar>>& srcWght,
+                List<DynamicList<point>>& srcCtr,
                 List<DynamicList<label>>& tgtAddr,
                 List<DynamicList<scalar>>& tgtWght
             );
@@ -109,30 +113,33 @@ protected:
             (
                 List<DynamicList<label>>& srcAddr,
                 List<DynamicList<scalar>>& srcWght,
+                List<DynamicList<point>>& srcCtr,
                 List<DynamicList<label>>& tgtAddr,
                 List<DynamicList<scalar>>& tgtWght
             );
 
             //- Set the source and target seed faces
-            virtual void setNextFaces
+            virtual bool setNextFaces
             (
                 label& startSeedi,
                 label& srcFacei,
                 label& tgtFacei,
-                const boolList& mapFlag,
+                const bitSet& mapFlag,
                 labelList& seedFaces,
                 const DynamicList<label>& visitedFaces,
-                bool errorOnNotFound = true
+                const bool errorOnNotFound = true
             ) const;
 
 
         // Evaluation
 
             //- Area of intersection between source and target faces
-            virtual scalar interArea
+            virtual void calcInterArea
             (
                 const label srcFacei,
-                const label tgtFacei
+                const label tgtFacei,
+                scalar& area,
+                vector& centroid
             ) const;
 
             //- Return true if faces overlap
@@ -152,53 +159,50 @@ public:
 
     // Constructors
 
+        //- Construct from dictionary
+        faceAreaWeightAMI
+        (
+            const dictionary& dict,
+            const bool reverseTarget = false
+        );
+
         //- Construct from components
         faceAreaWeightAMI
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
+            const bool requireMatch,
             const bool reverseTarget = false,
-            const bool requireMatch = true,
+            const scalar lowWeightCorrection = -1,
+            const faceAreaIntersect::triangulationMode triMode =
+                faceAreaIntersect::tmMesh,
             const bool restartUncoveredSourceFace = true
         );
 
+        //- Construct as copy
+        faceAreaWeightAMI(const faceAreaWeightAMI& ami);
 
-    //- Destructor
-    virtual ~faceAreaWeightAMI();
+        //- Construct and return a clone
+        virtual autoPtr<AMIInterpolation> clone() const
+        {
+            return autoPtr<AMIInterpolation>(new faceAreaWeightAMI(*this));
+        }
 
 
-    // Member Functions
+    //- Destructor
+    virtual ~faceAreaWeightAMI() = default;
 
-        // Manipulation
 
-            //- Update addressing and weights
-            virtual void calculate
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label srcFacei = -1,
-                label tgtFacei = -1
-            );
+    // Member Functions
 
-            //- Set the face areas for parallel runs
-            virtual void setMagSf
-            (
-                const TargetPatch& tgtPatch,
-                const mapDistribute& map,
-                scalarList& srcMagSf,
-                scalarList& tgtMagSf
-            ) const;
+        //- Update addressing, weights and (optional) centroids
+        virtual bool calculate
+        (
+            const primitivePatch& srcPatch,
+            const primitivePatch& tgtPatch,
+            const autoPtr<searchableSurface>& surfPtr = nullptr
+        );
 
-            //- Normalise the weight. Can optionally subset addressing
-            //- (e.g. for mapNearest)
-            virtual void normaliseWeights
-            (
-                const bool verbose,
-                AMIInterpolation<SourcePatch, TargetPatch>& inter
-            );
+        //- Write
+        virtual void write(Ostream& os) const;
 };
 
 
@@ -208,12 +212,6 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-#ifdef NoRepository
-    #include "faceAreaWeightAMI.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
 #endif
 
 // ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/findNearestMaskedOp.H b/src/meshTools/AMIInterpolation/AMIInterpolation/findNearestMaskedOp.H
new file mode 100644
index 0000000000000000000000000000000000000000..4bc2507cc998b85706a7b372031a32402704034f
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/findNearestMaskedOp.H
@@ -0,0 +1,62 @@
+#include "indexedOctree.H"
+#include "treeDataPrimitivePatch.H"
+
+namespace Foam
+{
+
+template<class PatchType>
+class findNearestMaskedOp
+{
+    const indexedOctree<treeDataPrimitivePatch<PatchType>>& tree_;
+    const labelUList& excludeIndices_;
+
+public:
+
+    findNearestMaskedOp
+    (
+        const indexedOctree<treeDataPrimitivePatch<PatchType>>& tree,
+        const labelUList& excludeIndices
+    )
+    :
+        tree_(tree),
+        excludeIndices_(excludeIndices)
+    {}
+
+    void operator()
+    (
+        const labelUList& indices,
+        const point& sample,
+
+        scalar& nearestDistSqr,
+        label& minIndex,
+        point& nearestPoint
+    ) const
+    {
+        const treeDataPrimitivePatch<PatchType>& shape = tree_.shapes();
+        const PatchType& patch = shape.patch();
+
+        const pointField& points = patch.points();
+
+        forAll(indices, i)
+        {
+            const label index = indices[i];
+
+            if (!excludeIndices_.found(index))
+            {
+                const typename PatchType::FaceType& f = patch[index];
+
+                pointHit nearHit = f.nearestPoint(sample, points);
+                scalar distSqr = sqr(nearHit.distance());
+
+                if (distSqr < nearestDistSqr)
+                {
+                    nearestDistSqr = distSqr;
+                    minIndex = index;
+                    nearestPoint = nearHit.rawPoint();
+                }
+            }
+        }
+    }
+};
+
+} // End namespace Foam
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.C
new file mode 100644
index 0000000000000000000000000000000000000000..9108ddbca89f3131065442b65e65c527ad2ffc65
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.C
@@ -0,0 +1,408 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 "nearestFaceAMI.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(nearestFaceAMI, 0);
+    addToRunTimeSelectionTable(AMIInterpolation, nearestFaceAMI, dict);
+    addToRunTimeSelectionTable(AMIInterpolation, nearestFaceAMI, component);
+}
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcFaceMap
+(
+    const List<nearestAndDist>& localInfo,
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch
+) const
+{
+    // Generate the list of processor bounding boxes for tgtPatch
+    List<boundBox> procBbs(Pstream::nProcs());
+    procBbs[Pstream::myProcNo()] =
+        boundBox(tgtPatch.points(), tgtPatch.meshPoints(), true);
+    Pstream::gatherList(procBbs);
+    Pstream::scatterList(procBbs);
+
+    // Identify which of my local src faces intersect with each processor
+    // tgtPatch bb within the current match's search distance
+    const pointField& srcCcs = srcPatch.faceCentres();
+    List<DynamicList<label>> dynSendMap(Pstream::nProcs());
+
+    forAll(localInfo, srcFacei)
+    {
+        // Test local srcPatch face centres against remote processor tgtPatch bb
+        // using distance from initial pass
+
+        const scalar r2 = localInfo[srcFacei].second();
+
+        forAll(procBbs, proci)
+        {
+            if (proci != Pstream::myProcNo())
+            {
+                if (procBbs[proci].overlaps(srcCcs[srcFacei], r2))
+                {
+                    dynSendMap[proci].append(srcFacei);
+                }
+            }
+        }
+    }
+
+    // Convert dynamicList to labelList
+    labelListList sendMap(Pstream::nProcs());
+    forAll(sendMap, proci)
+    {
+        dynSendMap[proci].shrink();
+        sendMap[proci].transfer(dynSendMap[proci]);
+
+        if (debug)
+        {
+            Pout<< "send map - to proc " << proci << " sending "
+                << sendMap[proci].size() << " elements" << endl;
+        }
+    }
+
+    return autoPtr<mapDistribute>::New(std::move(sendMap));
+}
+
+
+Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcDistributed
+(
+    const primitivePatch& src,
+    const primitivePatch& tgt,
+    labelListList& srcToTgtAddr,
+    scalarListList& srcToTgtWght
+) const
+{
+    autoPtr<indexedOctree<treeType>> tgtTreePtr;
+    if (tgt.size())
+    {
+        tgtTreePtr = this->createTree(tgt);
+    }
+
+    // Create global indexing for tgtPatch
+    globalIndex globalTgtCells(tgt.size());
+
+
+    // First pass
+    // ==========
+    // For each srcPatch face, determine local match on tgtPatch
+
+    // Identify local nearest matches
+    pointField srcCcs(src.faceCentres());
+
+    List<nearestAndDist> localInfo(src.size());
+    if (tgt.size())
+    {
+        const auto& tgtTree = tgtTreePtr();
+
+        forAll(srcCcs, srcCelli)
+        {
+            const point& srcCc = srcCcs[srcCelli];
+
+            pointIndexHit& test = localInfo[srcCelli].first();
+            test = tgtTree.findNearest(srcCc, GREAT);
+
+            if (test.hit())
+            {
+                // With a search radius2 of GREAT all cells should receive a hit
+                localInfo[srcCelli].second() = magSqr(srcCc - test.hitPoint());
+                test.setIndex(globalTgtCells.toGlobal(test.index()));
+            }
+        }
+    }
+    else
+    {
+        // No local tgtPatch faces - initialise nearest distance for all
+        // srcPatch faces to GREAT so that they [should be] set by remote
+        // tgtPatch faces
+        for (auto& info : localInfo)
+        {
+            info.second() = GREAT;
+        }
+    }
+
+
+    // Second pass
+    // ===========
+    // Determine remote matches
+
+    // Map returns labels of src patch faces to send to each proc
+    autoPtr<mapDistribute> mapPtr = calcFaceMap(localInfo, src, tgt);
+    mapDistribute& map = mapPtr();
+
+    List<nearestAndDist> remoteInfo(localInfo);
+    map.distribute(remoteInfo);
+
+    // Note: re-using srcCcs
+    map.distribute(srcCcs);
+
+    if (tgt.size())
+    {
+        const auto& tgtTree = tgtTreePtr();
+
+        // Test remote srcPatch faces against local tgtPatch faces
+        nearestAndDist testInfo;
+        pointIndexHit& test = testInfo.first();
+        forAll(remoteInfo, i)
+        {
+            test = tgtTree.findNearest(srcCcs[i], remoteInfo[i].second());
+            if (test.hit())
+            {
+                test.setIndex(globalTgtCells.toGlobal(test.index()));
+                testInfo.second() = magSqr(test.hitPoint() - srcCcs[i]);
+                nearestEqOp()(remoteInfo[i], testInfo);
+            }
+        }
+    }
+
+    // Send back to originating processor. Choose best if sent to multiple
+    // processors. Note that afterwards all unused entries have the unique
+    // value nearestZero (distance < 0). This is used later on to see if
+    // the sample was sent to any processor.
+    const nearestAndDist nearestZero(pointIndexHit(), -GREAT);
+    mapDistributeBase::distribute
+    (
+        Pstream::commsTypes::nonBlocking,
+        List<labelPair>(0),
+        src.size(),
+        map.constructMap(),
+        map.constructHasFlip(),
+        map.subMap(),
+        map.subHasFlip(),
+        remoteInfo,
+        nearestEqOp(),
+        noOp(),             // no flipping
+        nearestZero
+    );
+
+
+    // Third pass
+    // ==========
+    // Combine local and remote info and filter out any connections that are
+    // further away than threshold distance squared
+
+    srcToTgtAddr.setSize(src.size());
+    srcToTgtWght.setSize(src.size());
+    forAll(srcToTgtAddr, srcFacei)
+    {
+        nearestEqOp()(localInfo[srcFacei], remoteInfo[srcFacei]);
+        if (localInfo[srcFacei].second() < maxDistance2_)
+        {
+            const label tgtFacei = localInfo[srcFacei].first().index();
+            srcToTgtAddr[srcFacei] = labelList(1, tgtFacei);
+            srcToTgtWght[srcFacei] = scalarList(1, 1.0);
+        }
+    }
+
+    List<Map<label>> cMap;
+    return autoPtr<mapDistribute>::New(globalTgtCells, srcToTgtAddr, cMap);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::nearestFaceAMI::nearestFaceAMI
+(
+    const dictionary& dict,
+    const bool reverseTarget
+)
+:
+    AMIInterpolation(dict, reverseTarget),
+    maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
+{}
+
+
+Foam::nearestFaceAMI::nearestFaceAMI
+(
+    const bool requireMatch,
+    const bool reverseTarget,
+    const scalar lowWeightCorrection
+)
+:
+    AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
+    maxDistance2_(GREAT)
+{}
+
+
+Foam::nearestFaceAMI::nearestFaceAMI(const nearestFaceAMI& ami)
+:
+    AMIInterpolation(ami),
+    maxDistance2_(ami.maxDistance2_)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::nearestFaceAMI::calculate
+(
+    const primitivePatch& srcPatch,
+    const primitivePatch& tgtPatch,
+    const autoPtr<searchableSurface>& surfPtr
+)
+{
+    if (upToDate_)
+    {
+        return false;
+    }
+
+    AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr);
+
+    const auto& src = this->srcPatch0();
+    const auto& tgt = this->tgtPatch0();
+
+    // Set face area magnitudes
+    srcMagSf_ = mag(src.faceAreas());
+    tgtMagSf_ = mag(tgt.faceAreas());
+
+    // TODO: implement symmetric calculation controls; assume yes for now
+    bool symmetric_ = true;
+
+    if (this->distributed())
+    {
+        tgtMapPtr_ =
+            calcDistributed
+            (
+                src,
+                tgt,
+                srcAddress_,
+                srcWeights_
+            );
+
+        if (symmetric_)
+        {
+            srcMapPtr_ =
+                calcDistributed
+                (
+                    tgt,
+                    src,
+                    tgtAddress_,
+                    tgtWeights_
+                );
+            }
+    }
+    else
+    {
+        srcAddress_.setSize(src.size());
+        srcWeights_.setSize(src.size());
+
+        if (symmetric_)
+        {
+            tgtAddress_.setSize(tgt.size());
+            tgtWeights_.setSize(tgt.size());
+        }
+
+        const pointField& srcCcs = src.faceCentres();
+        const pointField& tgtCcs = tgt.faceCentres();
+
+        const auto tgtTreePtr = this->createTree(tgtPatch);
+        const auto& tgtTree = tgtTreePtr();
+
+        forAll(srcCcs, srcFacei)
+        {
+            const point& srcCc = srcCcs[srcFacei];
+            const pointIndexHit hit = tgtTree.findNearest(srcCc, GREAT);
+
+            if
+            (
+                hit.hit()
+             && (magSqr(srcCc - tgtCcs[hit.index()]) < maxDistance2_)
+            )
+            {
+                label tgtFacei = hit.index();
+                srcAddress_[srcFacei] = labelList(1, tgtFacei);
+                srcWeights_[srcFacei] = scalarList(1, 1.0);
+
+                if (symmetric_)
+                {
+                    tgtAddress_[tgtFacei] = labelList(1, srcFacei);
+                    tgtWeights_[tgtFacei] = scalarList(1, 1.0);
+                }
+            }
+            else
+            {
+                if (debug)
+                {
+                    WarningInFunction
+                        << "Unable to find target face for source face "
+                        << srcFacei << endl;
+                }
+            }
+        }
+
+        if (symmetric_)
+        {
+            const auto srcTreePtr = this->createTree(srcPatch);
+            const auto& srcTree = srcTreePtr();
+
+            // Check that all source cells have connections and populate any
+            // missing entries
+            forAll(tgtWeights_, tgtCelli)
+            {
+                if (tgtAddress_[tgtCelli].empty())
+                {
+                    const point& tgtCc = tgtCcs[tgtCelli];
+                    pointIndexHit hit = srcTree.findNearest(tgtCc, GREAT);
+
+                    if
+                    (
+                        hit.hit()
+                     && (magSqr(tgtCc - srcCcs[hit.index()]) < maxDistance2_)
+                    )
+                    {
+                        tgtAddress_[tgtCelli] = labelList(1, hit.index());
+                        tgtWeights_[tgtCelli] = scalarList(1, 1.0);
+                    }
+                }
+                else
+                {
+                    if (debug)
+                    {
+                        WarningInFunction
+                            << "Unable to find source face for target face "
+                            << tgtCelli << endl;
+                    }
+                }
+            }
+        }
+    }
+
+    srcWeightsSum_.setSize(srcWeights_.size(), 1);
+    tgtWeightsSum_.setSize(tgtWeights_.size(), 1);
+
+    upToDate_ = true;
+
+    return upToDate_;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.H
new file mode 100644
index 0000000000000000000000000000000000000000..85938ecf4d817422cf1aee4646756949ccefd527
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/nearestFaceAMI/nearestFaceAMI.H
@@ -0,0 +1,169 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Class
+    Foam::nearestFaceAMI
+
+Description
+    Nearest-face Arbitrary Mesh Interface (AMI) method
+
+SourceFiles
+    nearestFaceAMI.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef nearestFaceAMI_H
+#define nearestFaceAMI_H
+
+#include "AMIInterpolation.H"
+#include "pointIndexHit.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class nearestFaceAMI Declaration
+\*---------------------------------------------------------------------------*/
+
+class nearestFaceAMI
+:
+    public AMIInterpolation
+{
+public:
+
+    typedef Tuple2<pointIndexHit, scalar> nearestAndDist;
+
+    //- Helper class for finding nearest
+    class nearestEqOp
+    {
+
+    public:
+
+        void operator()(nearestAndDist& x, const nearestAndDist& y) const
+        {
+            if (y.first().hit())
+            {
+                if (!x.first().hit())
+                {
+                    x = y;
+                }
+                else if (y.second() < x.second())
+                {
+                    x = y;
+                }
+            }
+        }
+    };
+
+
+private:
+
+    // Private Data
+
+        //- Maximum squared distance
+        scalar maxDistance2_;
+
+
+    // Private Member Functions
+
+        //- No copy assignment
+        void operator=(const nearestFaceAMI&) = delete;
+
+        autoPtr<mapDistribute> calcFaceMap
+        (
+            const List<nearestAndDist>& localInfo,
+            const primitivePatch& srcPatch,
+            const primitivePatch& tgtPatch
+        ) const;
+
+        autoPtr<mapDistribute> calcDistributed
+        (
+            const primitivePatch& src,
+            const primitivePatch& tgt,
+            labelListList& srcToTgtAddr,
+            scalarListList& srcToTgtWght
+        ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("nearestFaceAMI");
+
+
+    // Constructors
+
+        //- Construct from dictionary
+        nearestFaceAMI
+        (
+            const dictionary& dict,
+            const bool reverseTarget = false
+        );
+
+        //- Construct from components
+        nearestFaceAMI
+        (
+            const bool requireMatch = true,
+            const bool reverseTarget = false,
+            const scalar lowWeightCorrection = -1
+        );
+
+        //- Construct as copy
+        nearestFaceAMI(const nearestFaceAMI& ami);
+
+        //- Construct and return a clone
+        virtual autoPtr<AMIInterpolation> clone() const
+        {
+            return autoPtr<AMIInterpolation>(new nearestFaceAMI(*this));
+        }
+
+
+    //- Destructor
+    virtual ~nearestFaceAMI() = default;
+
+
+    // Member Functions
+
+        //- Update addressing and weights
+        virtual bool calculate
+        (
+            const primitivePatch& srcPatch,
+            const primitivePatch& tgtPatch,
+            const autoPtr<searchableSurface>& surfPtr = nullptr
+        );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
new file mode 100644
index 0000000000000000000000000000000000000000..61b4197f2b0b5354f02bfa7c3dada538d4a46555
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
@@ -0,0 +1,159 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
similarity index 59%
rename from src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
rename to src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
index becb1c69fb4f3e1cf08f68db6e40c8850c54f5a1..f03f8d155dd4281eb33555170e4b0b1328f48206 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2016 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -49,35 +49,34 @@ namespace Foam
                   Class partialFaceAreaWeightAMI Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class SourcePatch, class TargetPatch>
 class partialFaceAreaWeightAMI
 :
-    public faceAreaWeightAMI<SourcePatch, TargetPatch>
+    public faceAreaWeightAMI
 {
 
 private:
 
     // Private Member Functions
 
-        //- No copy construct
-        partialFaceAreaWeightAMI(const partialFaceAreaWeightAMI&) = delete;
-
         //- No copy assignment
         void operator=(const partialFaceAreaWeightAMI&) = delete;
 
-        // Marching front
 
-            //- Set the source and target seed faces
-            virtual void setNextFaces
-            (
-                label& startSeedi,
-                label& srcFacei,
-                label& tgtFacei,
-                const boolList& mapFlag,
-                labelList& seedFaces,
-                const DynamicList<label>& visitedFaces,
-                bool errorOnNotFound = true
-            ) const;
+protected:
+
+    // Protected Member Functions
+
+        //- Set the source and target seed faces
+        virtual bool setNextFaces
+        (
+            label& startSeedi,
+            label& srcFacei,
+            label& tgtFacei,
+            const bitSet& mapFlag,
+            labelList& seedFaces,
+            const DynamicList<label>& visitedFaces,
+            const bool errorOnNotFound = true
+        ) const;
 
 
 public:
@@ -88,50 +87,51 @@ public:
 
     // Constructors
 
+        //- Construct from dictionary
+        partialFaceAreaWeightAMI
+        (
+            const dictionary& dict,
+            const bool reverseTarget = false
+        );
+
         //- Construct from components
         partialFaceAreaWeightAMI
         (
-            const SourcePatch& srcPatch,
-            const TargetPatch& tgtPatch,
-            const faceAreaIntersect::triangulationMode& triMode,
+            const bool requireMatch = false,
             const bool reverseTarget = false,
-            const bool requireMatch = true
+            const scalar lowWeightCorrection = -1,
+            const faceAreaIntersect::triangulationMode triMode =
+                faceAreaIntersect::tmMesh,
+            const bool restartUncoveredSourceFace = true
         );
 
+        //- Construct as copy
+        partialFaceAreaWeightAMI(const partialFaceAreaWeightAMI& ami);
+
+        //- Construct and return a clone
+        virtual autoPtr<AMIInterpolation> clone() const
+        {
+            return
+                autoPtr<AMIInterpolation>(new partialFaceAreaWeightAMI(*this));
+        }
+
 
     //- Destructor
-    virtual ~partialFaceAreaWeightAMI();
+    virtual ~partialFaceAreaWeightAMI() = default;
 
 
     // Member Functions
 
-        // Access
-
-            //- Flag to indicate that interpolation patches are conformal
-            virtual bool conformal() const;
-
-
-        // Manipulation
-
-            //- Update addressing and weights
-            virtual void calculate
-            (
-                labelListList& srcAddress,
-                scalarListList& srcWeights,
-                labelListList& tgtAddress,
-                scalarListList& tgtWeights,
-                label srcFacei = -1,
-                label tgtFacei = -1
-            );
-
-            //- Set the face areas for parallel runs
-            virtual void setMagSf
-            (
-                const TargetPatch& tgtPatch,
-                const mapDistribute& map,
-                scalarList& srcMagSf,
-                scalarList& tgtMagSf
-            ) const;
+        //- Flag to indicate that interpolation patches are conformal
+        virtual bool conformal() const;
+
+        //- Update addressing, weights and (optional) centroids
+        virtual bool calculate
+        (
+            const primitivePatch& srcPatch,
+            const primitivePatch& tgtPatch,
+            const autoPtr<searchableSurface>& surfPtr = nullptr
+        );
 };
 
 
@@ -141,12 +141,6 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-#ifdef NoRepository
-    #include "partialFaceAreaWeightAMI.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
 #endif
 
 // ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.C b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.C
index 97428ec0e3bf66b9772c21e025ec297a8d54455d..8976e57b586dc365a0b6dd4366d35a19fc567a82 100644
--- a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.C
+++ b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.C
@@ -219,13 +219,15 @@ void Foam::faceAreaIntersect::triSliceWithPlane
 }
 
 
-Foam::scalar Foam::faceAreaIntersect::triangleIntersect
+void Foam::faceAreaIntersect::triangleIntersect
 (
     const triPoints& src,
     const point& tgt0,
     const point& tgt1,
     const point& tgt2,
-    const vector& n
+    const vector& n,
+    scalar& area,
+    vector& centroid
 ) const
 {
     // Work storage
@@ -241,7 +243,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
     const scalar srcArea(triArea(src));
     if (srcArea < ROOTVSMALL)
     {
-        return 0.0;
+        return;
     }
 
     // Typical length scale
@@ -255,7 +257,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
         scalar s = mag(tgt1 - tgt0);
         if (s < ROOTVSMALL)
         {
-            return 0.0;
+            return;
         }
 
         // Note: outer product with n pre-scaled with edge length. This is
@@ -268,7 +270,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
             // Triangle either zero edge length (s == 0) or
             // perpendicular to face normal n. In either case zero
             // overlap area
-            return 0.0;
+            return;
         }
         else
         {
@@ -279,7 +281,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
 
     if (nWorkTris1 == 0)
     {
-        return 0.0;
+        return;
     }
 
     // Edge 1
@@ -290,7 +292,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
         scalar s = mag(tgt2 - tgt1);
         if (s < ROOTVSMALL)
         {
-            return 0.0;
+            return;
         }
         const vector n1((tgt1 - tgt2)^(-s*n));
         const scalar magSqrN1(magSqr(n1));
@@ -300,7 +302,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
             // Triangle either zero edge length (s == 0) or
             // perpendicular to face normal n. In either case zero
             // overlap area
-            return 0.0;
+            return;
         }
         else
         {
@@ -315,7 +317,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
 
             if (nWorkTris2 == 0)
             {
-                return 0.0;
+                return;
             }
         }
     }
@@ -328,7 +330,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
         scalar s = mag(tgt2 - tgt0);
         if (s < ROOTVSMALL)
         {
-            return 0.0;
+            return;
         }
         const vector n2((tgt2 - tgt0)^(-s*n));
         const scalar magSqrN2(magSqr(n2));
@@ -338,7 +340,7 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
             // Triangle either zero edge length (s == 0) or
             // perpendicular to face normal n. In either case zero
             // overlap area
-            return 0.0;
+            return;
         }
         else
         {
@@ -353,23 +355,25 @@ Foam::scalar Foam::faceAreaIntersect::triangleIntersect
 
             if (nWorkTris1 == 0)
             {
-                return 0.0;
+                return;
             }
             else
             {
                 // Calculate area of sub-triangles
-                scalar area = 0.0;
                 for (label i = 0; i < nWorkTris1; ++i)
                 {
-                    area += triArea(workTris1[i]);
+                    // Area of intersection
+                    const scalar currArea = triArea(workTris1[i]);
+                    area += currArea;
+
+                    // Area-weighted centroid of intersection
+                    centroid += currArea*triCentroid(workTris1[i]);
 
                     if (cacheTriangulation_)
                     {
                         triangles_.append(workTris1[i]);
                     }
                 }
-
-                return area;
             }
         }
     }
@@ -443,12 +447,13 @@ void Foam::faceAreaIntersect::triangulate
 }
 
 
-
-Foam::scalar Foam::faceAreaIntersect::calc
+void Foam::faceAreaIntersect::calc
 (
     const face& faceA,
     const face& faceB,
-    const vector& n
+    const vector& n,
+    scalar& area,
+    vector& centroid
 ) const
 {
     if (cacheTriangulation_)
@@ -456,8 +461,10 @@ Foam::scalar Foam::faceAreaIntersect::calc
         triangles_.clear();
     }
 
+    area = 0.0;
+    centroid = vector::zero;
+
     // Intersect triangles
-    scalar totalArea = 0.0;
     for (const face& triA : trisA_)
     {
         triPoints tpA = getTriPoints(pointsA_, triA, false);
@@ -466,32 +473,38 @@ Foam::scalar Foam::faceAreaIntersect::calc
         {
             if (reverseB_)
             {
-                totalArea +=
-                    triangleIntersect
-                    (
-                        tpA,
-                        pointsB_[triB[0]],
-                        pointsB_[triB[1]],
-                        pointsB_[triB[2]],
-                        n
-                    );
+                triangleIntersect
+                (
+                    tpA,
+                    pointsB_[triB[0]],
+                    pointsB_[triB[1]],
+                    pointsB_[triB[2]],
+                    n,
+                    area,
+                    centroid
+                );
             }
             else
             {
-                totalArea +=
-                    triangleIntersect
-                    (
-                        tpA,
-                        pointsB_[triB[2]],
-                        pointsB_[triB[1]],
-                        pointsB_[triB[0]],
-                        n
-                    );
+                triangleIntersect
+                (
+                    tpA,
+                    pointsB_[triB[2]],
+                    pointsB_[triB[1]],
+                    pointsB_[triB[0]],
+                    n,
+                    area,
+                    centroid
+                );
             }
         }
     }
 
-    return totalArea;
+    // Area weighed centroid
+    if (area > 0)
+    {
+        centroid /= area;
+    }
 }
 
 
@@ -503,8 +516,10 @@ bool Foam::faceAreaIntersect::overlaps
     const scalar threshold
 ) const
 {
+    scalar area = 0.0;
+    vector centroid(Zero);
+
     // Intersect triangles
-    scalar totalArea = 0.0;
     for (const face& triA : trisA_)
     {
         const triPoints tpA = getTriPoints(pointsA_, triA, false);
@@ -513,30 +528,32 @@ bool Foam::faceAreaIntersect::overlaps
         {
             if (reverseB_)
             {
-                totalArea +=
-                    triangleIntersect
-                    (
-                        tpA,
-                        pointsB_[triB[0]],
-                        pointsB_[triB[1]],
-                        pointsB_[triB[2]],
-                        n
-                    );
+                triangleIntersect
+                (
+                    tpA,
+                    pointsB_[triB[0]],
+                    pointsB_[triB[1]],
+                    pointsB_[triB[2]],
+                    n,
+                    area,
+                    centroid
+                );
             }
             else
             {
-                totalArea +=
-                    triangleIntersect
-                    (
-                        tpA,
-                        pointsB_[triB[2]],
-                        pointsB_[triB[1]],
-                        pointsB_[triB[0]],
-                        n
-                    );
+                triangleIntersect
+                (
+                    tpA,
+                    pointsB_[triB[2]],
+                    pointsB_[triB[1]],
+                    pointsB_[triB[0]],
+                    n,
+                    area,
+                    centroid
+                );
             }
 
-            if (totalArea > threshold)
+            if (area > threshold)
             {
                 return true;
             }
diff --git a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H
index f0ba9b3e92510c4ad9a11f1b8ed8116982b6b817..d79327de31ddc44d3b331a2e0dcbd2212af0f8e8 100644
--- a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H
+++ b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H
@@ -46,6 +46,7 @@ SourceFiles
 #include "face.H"
 #include "triPoints.H"
 #include "Enum.H"
+#include "searchableSurface.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -97,6 +98,7 @@ private:
 
     // Static data members
 
+        //- Tolerance
         static scalar tol;
 
 
@@ -132,6 +134,9 @@ private:
         //- Return triangle area
         inline scalar triArea(const triPoints& t) const;
 
+        //- Return triangle centre
+        inline vector triCentroid(const triPoints& t) const;
+
         //- Slice triangle with plane and generate new cut sub-triangles
         void triSliceWithPlane
         (
@@ -143,13 +148,15 @@ private:
         ) const;
 
         //- Return area of intersection of triangles src and tgt
-        scalar triangleIntersect
+        void triangleIntersect
         (
             const triPoints& src,
             const point& tgt0,
             const point& tgt1,
             const point& tgt2,
-            const vector& n
+            const vector& n,
+            scalar& area,
+            vector& centroid
         ) const;
 
 
@@ -199,12 +206,15 @@ public:
             DynamicList<face>& faces
         );
 
-        //- Return area of intersection of faceA with faceB
-        scalar calc
+        //- Return area of intersection of faceA with faceB and effective
+        //- face centre
+        void calc
         (
             const face& faceA,
             const face& faceB,
-            const vector& n
+            const vector& n,
+            scalar& area,
+            vector& centroid
         ) const;
 
         //- Return area of intersection of faceA with faceB
diff --git a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersectI.H b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersectI.H
index 12113a594d6cc71da2ad12ad5c0a85b7417dc46b..836b87d76200a29f5ec519dfca87d7559d1a50ad 100644
--- a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersectI.H
+++ b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersectI.H
@@ -113,6 +113,15 @@ inline Foam::scalar Foam::faceAreaIntersect::triArea(const triPoints& t) const
 }
 
 
+inline Foam::vector Foam::faceAreaIntersect::triCentroid
+(
+    const triPoints& t
+) const
+{
+    return (t[0] + t[1] + t[2])/3;
+}
+
+
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
 Foam::scalar& Foam::faceAreaIntersect::tolerance()
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.C b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.C
index 3cd26ef2b82f8069e73f5bb0215a4719d4968ad4..4e994a6f236185043af6457fa7b607da60537c64 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.C
+++ b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2018 OpenCFD Ltd.
+    Copyright (C) 2017-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,221 +41,234 @@ namespace Foam
     addToRunTimeSelectionTable(polyPatch, cyclicACMIPolyPatch, dictionary);
 }
 
-const Foam::scalar Foam::cyclicACMIPolyPatch::tolerance_ = 1e-10;
-
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
-void Foam::cyclicACMIPolyPatch::resetAMI
+void Foam::cyclicACMIPolyPatch::reportCoverage
 (
-    const AMIPatchToPatchInterpolation::interpolationMethod&
+    const word& name,
+    const scalarField& weightSum
 ) const
 {
-    if (owner())
+    label nUncovered = 0;
+    label nCovered = 0;
+    for (const scalar sum : weightSum)
     {
-        const polyPatch& nonOverlapPatch = this->nonOverlapPatch();
-
-        if (debug)
+        if (sum < tolerance_)
         {
-            Pout<< "cyclicACMIPolyPatch::resetAMI : recalculating weights"
-                << " for " << name() << " and " << nonOverlapPatch.name()
-                << endl;
+            ++nUncovered;
         }
-
-        if (boundaryMesh().mesh().hasCellCentres())
+        else if (sum > scalar(1) - tolerance_)
         {
-            if (debug)
-            {
-                Pout<< "cyclicACMIPolyPatch::resetAMI : clearing cellCentres"
-                    << " for " << name() << " and " << nonOverlapPatch.name()
-                    << endl;
-            }
-
-            //WarningInFunction
-            //    << "The mesh already has cellCentres calculated when"
-            //    << " resetting ACMI " << name() << "." << endl
-            //    << "This is a problem since ACMI adapts the face areas"
-            //    << " (to close cells) so this has" << endl
-            //    << "to be done before cell centre calculation." << endl
-            //    << "This can happen if e.g. the cyclicACMI is after"
-            //    << " any processor patches in the boundary." << endl;
-            const_cast<polyMesh&>
-            (
-                boundaryMesh().mesh()
-            ).primitiveMesh::clearGeom();
+            ++nCovered;
         }
+    }
+    reduce(nUncovered, sumOp<label>());
+    reduce(nCovered, sumOp<label>());
+    label nTotal = returnReduce(weightSum.size(), sumOp<label>());
 
+    Info<< "ACMI: Patch " << name << " uncovered/blended/covered = "
+        << nUncovered << ", " << nTotal-nUncovered-nCovered
+        << ", " << nCovered << endl;
+}
 
-        // Trigger re-building of faceAreas
-        (void)boundaryMesh().mesh().faceAreas();
 
+void Foam::cyclicACMIPolyPatch::resetAMI() const
+{
+    resetAMI(boundaryMesh().mesh().points());
+}
 
-        // Calculate the AMI using partial face-area-weighted. This leaves
-        // the weights as fractions of local areas (sum(weights) = 1 means
-        // face is fully covered)
-        cyclicAMIPolyPatch::resetAMI
-        (
-            AMIPatchToPatchInterpolation::imPartialFaceAreaWeight
-        );
 
-        AMIPatchToPatchInterpolation& AMI =
-            const_cast<AMIPatchToPatchInterpolation&>(this->AMI());
+void Foam::cyclicACMIPolyPatch::resetAMI(const UList<point>& points) const
+{
+    if (!owner())
+    {
+        return;
+    }
 
-        // Output some stats. AMIInterpolation will have already output the
-        // average weights ("sum(weights) min:1 max:1 average:1")
-        {
-            const scalarField& wghtsSum = AMI.srcWeightsSum();
+    const polyPatch& nonOverlapPatch = this->nonOverlapPatch();
 
-            label nUncovered = 0;
-            label nCovered = 0;
-            forAll(wghtsSum, facei)
-            {
-                scalar sum = wghtsSum[facei];
-                if (sum < tolerance_)
-                {
-                    nUncovered++;
-                }
-                else if (sum > scalar(1)-tolerance_)
-                {
-                    nCovered++;
-                }
-            }
-            reduce(nUncovered, sumOp<label>());
-            reduce(nCovered, sumOp<label>());
-            label nTotal = returnReduce(wghtsSum.size(), sumOp<label>());
+    DebugPout
+        << "cyclicACMIPolyPatch::resetAMI : recalculating weights"
+        << " for " << name() << " and " << nonOverlapPatch.name()
+        << endl;
 
-            Info<< "ACMI: Patch source uncovered/blended/covered = "
-                << nUncovered << ", " << nTotal-nUncovered-nCovered
-                << ", " << nCovered << endl;
-        }
-        {
-            const scalarField& wghtsSum = AMI.tgtWeightsSum();
+    const polyMesh& mesh = boundaryMesh().mesh();
 
-            label nUncovered = 0;
-            label nCovered = 0;
-            forAll(wghtsSum, facei)
-            {
-                scalar sum = wghtsSum[facei];
-                if (sum < tolerance_)
-                {
-                    nUncovered++;
-                }
-                else if (sum > scalar(1)-tolerance_)
-                {
-                    nCovered++;
-                }
-            }
-            reduce(nUncovered, sumOp<label>());
-            reduce(nCovered, sumOp<label>());
-            label nTotal = returnReduce(wghtsSum.size(), sumOp<label>());
+    if (!createAMIFaces_ && mesh.hasCellCentres())
+    {
+        DebugPout
+            << "cyclicACMIPolyPatch::resetAMI : clearing cellCentres"
+            << " for " << name() << " and " << nonOverlapPatch.name()
+            << endl;
+
+        WarningInFunction
+           << "The mesh already has cellCentres calculated when"
+           << " resetting ACMI " << name() << "." << nl
+           << "This is a problem since ACMI adapts the face areas"
+           << " (to close cells) so this has" << nl
+           << "to be done before cell centre calculation." << nl
+           << "This can happen if e.g. the cyclicACMI is after"
+           << " any processor patches in the boundary." << endl;
+        const_cast<polyMesh&>(mesh).primitiveMesh::clearGeom();
+    }
 
-            Info<< "ACMI: Patch target uncovered/blended/covered = "
-                << nUncovered << ", " << nTotal-nUncovered-nCovered
-                << ", " << nCovered << endl;
-        }
 
-        srcMask_ =
-            min(scalar(1) - tolerance_, max(tolerance_, AMI.srcWeightsSum()));
+    // Trigger re-building of faceAreas
+    (void)mesh.faceAreas();
 
-        tgtMask_ =
-            min(scalar(1) - tolerance_, max(tolerance_, AMI.tgtWeightsSum()));
 
+    // Calculate the AMI using partial face-area-weighted. This leaves
+    // the weights as fractions of local areas (sum(weights) = 1 means
+    // face is fully covered)
+    cyclicAMIPolyPatch::resetAMI(points);
 
-        // Adapt owner side areas. Note that in uncoupled situations (e.g.
-        // decomposePar) srcMask, tgtMask can be zero size.
-        if (srcMask_.size())
-        {
-            vectorField::subField Sf = faceAreas();
-            vectorField::subField noSf = nonOverlapPatch.faceAreas();
+    const AMIPatchToPatchInterpolation& AMI = this->AMI();
 
-            forAll(Sf, facei)
-            {
-                Sf[facei] *= srcMask_[facei];
-                noSf[facei] *= 1.0 - srcMask_[facei];
-            }
+    // Output some statistics
+    reportCoverage("source", AMI.srcWeightsSum());
+    reportCoverage("target", AMI.tgtWeightsSum());
+
+    // Set the mask fields
+    // Note:
+    // - assumes that the non-overlap patches are decomposed using the same
+    //   decomposition as the coupled patches (per side)
+    srcMask_ = min(scalar(1), max(scalar(0), AMI.srcWeightsSum()));
+    tgtMask_ = min(scalar(1), max(scalar(0), AMI.tgtWeightsSum()));
+
+    if (debug)
+    {
+        Pout<< "resetAMI" << endl;
+        {
+            const cyclicACMIPolyPatch& patch = *this;
+            Pout<< "patch:" << patch.name() << " size:" << patch.size()
+                << " non-overlap patch: " << patch.nonOverlapPatch().name()
+                << " size:" << patch.nonOverlapPatch().size()
+                << " mask size:" << patch.srcMask().size() << endl;
         }
-        // Adapt slave side areas
-        if (tgtMask_.size())
         {
-            const cyclicACMIPolyPatch& cp =
-                refCast<const cyclicACMIPolyPatch>(this->neighbPatch());
-            const polyPatch& pp = cp.nonOverlapPatch();
+            const cyclicACMIPolyPatch& patch = this->neighbPatch();
+            Pout<< "patch:" << patch.name() << " size:" << patch.size()
+                << " non-overlap patch: " << patch.nonOverlapPatch().name()
+                << " size:" << patch.nonOverlapPatch().size()
+                << " mask size:" << patch.neighbPatch().tgtMask().size()
+                << endl;
+        }
+    }
+}
 
-            vectorField::subField Sf = cp.faceAreas();
-            vectorField::subField noSf = pp.faceAreas();
 
-            forAll(Sf, facei)
-            {
-                Sf[facei] *= tgtMask_[facei];
-                noSf[facei] *= 1.0 - tgtMask_[facei];
-            }
+void Foam::cyclicACMIPolyPatch::scalePatchFaceAreas()
+{
+    if (!owner() || !canResetAMI())
+    {
+        return;
+    }
+
+    scalePatchFaceAreas(*this);
+    scalePatchFaceAreas(this->neighbPatch());
+}
+
+
+void Foam::cyclicACMIPolyPatch::scalePatchFaceAreas
+(
+    const cyclicACMIPolyPatch& acmipp
+)
+{
+    // Primitive patch face areas have been cleared/reset based on the raw
+    // points - need to reset to avoid double-accounting of face areas
+
+    const scalar maxTol = scalar(1) - tolerance_;
+    const scalarField& mask = acmipp.mask();
+
+    const polyPatch& nonOverlapPatch = acmipp.nonOverlapPatch();
+    vectorField::subField noSf = nonOverlapPatch.faceAreas();
+
+    DebugPout
+        << "rescaling non-overlap patch areas for: " << nonOverlapPatch.name()
+        << endl;
+
+
+    if (mask.size() != noSf.size())
+    {
+        WarningInFunction
+            << "Inconsistent sizes for patch: " << acmipp.name()
+            << " - not manipulating patches" << nl
+            << " - size: " << size() << nl
+            << " - non-overlap patch size: " << noSf.size() << nl
+            << " - mask size: " << mask.size() << nl
+            << "This is OK for decomposition but should be considered fatal "
+            << "at run-time" << endl;
+
+        return;
+    }
+
+    forAll(noSf, facei)
+    {
+        const scalar w = min(maxTol, max(tolerance_, mask[facei]));
+        noSf[facei] *= scalar(1) - w;
+    }
+
+    if (!createAMIFaces_)
+    {
+        // Note: for topological update (createAMIFaces_ = true)
+        // AMI coupled patch face areas are updated as part of the topological
+        // updates, e.g. by the calls to cyclicAMIPolyPatch's setTopology and
+        // initMovePoints
+        DebugPout
+            << "scaling coupled patch areas for: " << acmipp.name() << endl;
+
+        // Scale the coupled patch face areas
+        vectorField::subField Sf = acmipp.faceAreas();
+
+        forAll(Sf, facei)
+        {
+            Sf[facei] *= max(tolerance_, mask[facei]);
         }
 
         // Re-normalise the weights since the effect of overlap is already
-        // accounted for in the area.
+        // accounted for in the area
+        auto& weights = const_cast<scalarListList&>(acmipp.weights());
+        auto& weightsSum = const_cast<scalarField&>(acmipp.weightsSum());
+        forAll(weights, i)
         {
-            scalarListList& srcWeights = AMI.srcWeights();
-            scalarField& srcWeightsSum = AMI.srcWeightsSum();
-            forAll(srcWeights, i)
+            scalarList& wghts = weights[i];
+            if (wghts.size())
             {
-                scalarList& wghts = srcWeights[i];
-                if (wghts.size())
-                {
-                    scalar& sum = srcWeightsSum[i];
+                scalar& sum = weightsSum[i];
 
-                    forAll(wghts, j)
-                    {
-                        wghts[j] /= sum;
-                    }
-                    sum = 1.0;
-                }
-            }
-        }
-        {
-            scalarListList& tgtWeights = AMI.tgtWeights();
-            scalarField& tgtWeightsSum = AMI.tgtWeightsSum();
-            forAll(tgtWeights, i)
-            {
-                scalarList& wghts = tgtWeights[i];
-                if (wghts.size())
+                forAll(wghts, j)
                 {
-                    scalar& sum = tgtWeightsSum[i];
-                    forAll(wghts, j)
-                    {
-                        wghts[j] /= sum;
-                    }
-                    sum = 1.0;
+                    wghts[j] /= sum;
                 }
+                sum = 1.0;
             }
         }
-
-        // Set the updated flag
-        updated_ = true;
     }
 }
 
 
 void Foam::cyclicACMIPolyPatch::initGeometry(PstreamBuffers& pBufs)
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::initGeometry : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::initGeometry : " << name() << endl;
 
     // Note: calculates transformation and triggers face centre calculation
     cyclicAMIPolyPatch::initGeometry(pBufs);
 
     // Initialise the AMI early to make sure we adapt the face areas before the
     // cell centre calculation gets triggered.
-    resetAMI();
+    if (!createAMIFaces_ && canResetAMI())
+    {
+        resetAMI();
+    }
+
+    scalePatchFaceAreas();
 }
 
 
 void Foam::cyclicACMIPolyPatch::calcGeometry(PstreamBuffers& pBufs)
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::calcGeometry : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::calcGeometry : " << name() << endl;
+
     cyclicAMIPolyPatch::calcGeometry(pBufs);
 }
 
@@ -266,16 +279,13 @@ void Foam::cyclicACMIPolyPatch::initMovePoints
     const pointField& p
 )
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::initMovePoints : " << name() << endl;
-    }
+    DebugPout<< "cyclicACMIPolyPatch::initMovePoints : " << name() << endl;
 
     // Note: calculates transformation and triggers face centre calculation
+    // - Note: resetAMI called by cyclicAMIPolyPatch::initMovePoints
     cyclicAMIPolyPatch::initMovePoints(pBufs, p);
 
-    // Initialise the AMI early. See initGeometry.
-    resetAMI();
+    scalePatchFaceAreas();
 }
 
 
@@ -285,40 +295,33 @@ void Foam::cyclicACMIPolyPatch::movePoints
     const pointField& p
 )
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::movePoints : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::movePoints : " << name() << endl;
+
+    // When topology is changing, this will scale the duplicate AMI faces
     cyclicAMIPolyPatch::movePoints(pBufs, p);
 }
 
 
 void Foam::cyclicACMIPolyPatch::initUpdateMesh(PstreamBuffers& pBufs)
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::initUpdateMesh : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::initUpdateMesh : " << name() << endl;
+
     cyclicAMIPolyPatch::initUpdateMesh(pBufs);
 }
 
 
 void Foam::cyclicACMIPolyPatch::updateMesh(PstreamBuffers& pBufs)
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::updateMesh : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::updateMesh : " << name() << endl;
+
     cyclicAMIPolyPatch::updateMesh(pBufs);
 }
 
 
 void Foam::cyclicACMIPolyPatch::clearGeom()
 {
-    if (debug)
-    {
-        Pout<< "cyclicACMIPolyPatch::clearGeom : " << name() << endl;
-    }
+    DebugPout << "cyclicACMIPolyPatch::clearGeom : " << name() << endl;
+
     cyclicAMIPolyPatch::clearGeom();
 }
 
@@ -345,17 +348,27 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     const label index,
     const polyBoundaryMesh& bm,
     const word& patchType,
-    const transformType transform
+    const transformType transform,
+    const word& defaultAMIMethod
 )
 :
-    cyclicAMIPolyPatch(name, size, start, index, bm, patchType, transform),
+    cyclicAMIPolyPatch
+    (
+        name,
+        size,
+        start,
+        index,
+        bm,
+        patchType,
+        transform,
+        defaultAMIMethod
+    ),
     nonOverlapPatchName_(word::null),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
-    AMIRequireMatch_ = false;
+    AMIPtr_->setRequireMatch(false);
 
     // Non-overlapping patch might not be valid yet so cannot determine
     // associated patchID
@@ -368,17 +381,17 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     const dictionary& dict,
     const label index,
     const polyBoundaryMesh& bm,
-    const word& patchType
+    const word& patchType,
+    const word& defaultAMIMethod
 )
 :
-    cyclicAMIPolyPatch(name, dict, index, bm, patchType),
-    nonOverlapPatchName_(dict.lookup("nonOverlapPatch")),
+    cyclicAMIPolyPatch(name, dict, index, bm, patchType, defaultAMIMethod),
+    nonOverlapPatchName_(dict.get<word>("nonOverlapPatch")),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
-    AMIRequireMatch_ = false;
+    AMIPtr_->setRequireMatch(false);
 
     if (nonOverlapPatchName_ == name)
     {
@@ -403,10 +416,9 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(pp.nonOverlapPatchName_),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
-    AMIRequireMatch_ = false;
+    AMIPtr_->setRequireMatch(false);
 
     // Non-overlapping patch might not be valid yet so cannot determine
     // associated patchID
@@ -428,10 +440,9 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(nonOverlapPatchName),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
-    AMIRequireMatch_ = false;
+    AMIPtr_->setRequireMatch(false);
 
     if (nonOverlapPatchName_ == name())
     {
@@ -459,19 +470,12 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(pp.nonOverlapPatchName_),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
-    AMIRequireMatch_ = false;
+    AMIPtr_->setRequireMatch(false);
 }
 
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::cyclicACMIPolyPatch::~cyclicACMIPolyPatch()
-{}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 const Foam::cyclicACMIPolyPatch& Foam::cyclicACMIPolyPatch::neighbPatch() const
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatch.H
index 5f2bd9dfad989c9a68b8e1d394ce0263c165fcca..51b0f40b5c3d2e92fcd1eb1a7476ceba05ee4e0b 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 OpenCFD Ltd.
+    Copyright (C) 2018-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,6 +41,7 @@ SourceFiles
 #include "cyclicAMIPolyPatch.H"
 #include "AMIPatchToPatchInterpolation.H"
 #include "polyBoundaryMesh.H"
+#include "partialFaceAreaWeightAMI.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -60,9 +61,6 @@ private:
 
     // Private data
 
-        //- Fraction of face area below which face is considered disconnected
-        static const scalar tolerance_;
-
         //- Name of non-overlapping patch
         const word nonOverlapPatchName_;
 
@@ -75,21 +73,31 @@ private:
         //- Mask/weighting for target patch
         mutable scalarField tgtMask_;
 
-        //- Flag to indicate that AMI has been updated
-        mutable bool updated_;
-
 
 protected:
 
     // Protected Member Functions
 
-        //- Reset the AMI interpolator
-        virtual void resetAMI
+        //- Report coverage statics, e.g. number of uncovered/blended/covered
+        //- faces
+        void reportCoverage
         (
-            const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
-                AMIPatchToPatchInterpolation::imFaceAreaWeight
+            const word& name,
+            const scalarField& weightSum
         ) const;
 
+        //- Reset the AMI interpolator, supply patch points
+        virtual void resetAMI(const UList<point>& points) const;
+
+        //-  Reset the AMI interpolator, use current patch points
+        virtual void resetAMI() const;
+
+        //- Scale patch face areas to maintain physical area
+        virtual void scalePatchFaceAreas();
+
+        //- Scale patch face areas to maintain physical area
+        virtual void scalePatchFaceAreas(const cyclicACMIPolyPatch& acmipp);
+
         //- Initialise the calculation of the patch geometry
         virtual void initGeometry(PstreamBuffers&);
 
@@ -111,12 +119,6 @@ protected:
         //- Clear geometry
         virtual void clearGeom();
 
-        //- Return the mask/weighting for the source patch
-        virtual const scalarField& srcMask() const;
-
-        //- Return the mask/weighting for the target patch
-        virtual const scalarField& tgtMask() const;
-
 
 public:
 
@@ -135,7 +137,8 @@ public:
             const label index,
             const polyBoundaryMesh& bm,
             const word& patchType,
-            const transformType transform = UNKNOWN
+            const transformType transform = UNKNOWN,
+            const word& defaultAMIMethod = partialFaceAreaWeightAMI::typeName
         );
 
         //- Construct from dictionary
@@ -145,7 +148,8 @@ public:
             const dictionary& dict,
             const label index,
             const polyBoundaryMesh& bm,
-            const word& patchType
+            const word& patchType,
+            const word& defaultAMIMethod = partialFaceAreaWeightAMI::typeName
         );
 
         //- Construct as copy, resetting the boundary mesh
@@ -235,19 +239,13 @@ public:
 
 
     //- Destructor
-    virtual ~cyclicACMIPolyPatch();
+    virtual ~cyclicACMIPolyPatch() = default;
 
 
     // Member Functions
 
         // Access
 
-            //- Reset the updated flag
-            inline void setUpdated(bool flag) const;
-
-            //- Return access to the updated flag
-            inline bool updated() const;
-
             //- Return a reference to the neighbour patch
             virtual const cyclicACMIPolyPatch& neighbPatch() const;
 
@@ -263,9 +261,15 @@ public:
             //- Return a reference to the non-overlapping patch
             inline polyPatch& nonOverlapPatch();
 
-            //- Mask field where 1 = overlap, 0 = no-overlap
+            //- Mask field where 1 = overlap(coupled), 0 = no-overlap
             inline const scalarField& mask() const;
 
+            //- Return the mask/weighting for the source patch
+            virtual const scalarField& srcMask() const;
+
+            //- Return the mask/weighting for the target patch
+            virtual const scalarField& tgtMask() const;
+
             //- Overlap tolerance
             inline static scalar tolerance();
 
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatchI.H b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatchI.H
index 42b41196673b4f316e4bcb331b14a4a93b3ea646..50f4022ed63e2ce5204ebda9f9b33b1f653218fa 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatchI.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicACMI/cyclicACMIPolyPatch/cyclicACMIPolyPatchI.H
@@ -27,18 +27,6 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-inline void Foam::cyclicACMIPolyPatch::setUpdated(const bool flag) const
-{
-    updated_ = flag;
-}
-
-
-inline bool Foam::cyclicACMIPolyPatch::updated() const
-{
-    return updated_;
-}
-
-
 inline const Foam::word& Foam::cyclicACMIPolyPatch::nonOverlapPatchName() const
 {
     return nonOverlapPatchName_;
@@ -69,10 +57,8 @@ inline const Foam::scalarField& Foam::cyclicACMIPolyPatch::mask() const
     {
         return srcMask_;
     }
-    else
-    {
-        return neighbPatch().tgtMask();
-    }
+
+    return neighbPatch().tgtMask();
 }
 
 
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
index 51f14ccb5ed67a11988c894ce82fe7259b128cdd..85f6170f7185e854219d3992f863303992ac0c42 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
@@ -27,13 +27,12 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cyclicAMIPolyPatch.H"
-#include "transformField.H"
 #include "SubField.H"
-#include "polyMesh.H"
 #include "Time.H"
+#include "unitConversion.H"
+#include "OFstream.H"
+#include "meshTools.H"
 #include "addToRunTimeSelectionTable.H"
-#include "faceAreaIntersect.H"
-#include "ops.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -45,6 +44,7 @@ namespace Foam
     addToRunTimeSelectionTable(polyPatch, cyclicAMIPolyPatch, dictionary);
 }
 
+const Foam::scalar Foam::cyclicAMIPolyPatch::tolerance_ = 1e-10;
 
 // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
 
@@ -61,14 +61,12 @@ Foam::vector Foam::cyclicAMIPolyPatch::findFaceNormalMaxRadius
 
     label facei = findMax(magRadSqr);
 
-    if (debug)
-    {
-        Info<< "findFaceMaxRadius(const pointField&) : patch: " << name() << nl
-            << "    rotFace  = " << facei << nl
-            << "    point    = " << faceCentres[facei] << nl
-            << "    distance = " << Foam::sqrt(magRadSqr[facei])
-            << endl;
-    }
+    DebugInFunction
+        << "Patch: " << name() << nl
+        << "    rotFace  = " << facei << nl
+        << "    point    = " << faceCentres[facei] << nl
+        << "    distance = " << Foam::sqrt(magRadSqr[facei])
+        << endl;
 
     return n[facei];
 }
@@ -290,82 +288,119 @@ void Foam::cyclicAMIPolyPatch::calcTransforms
 
 // * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * * //
 
-void Foam::cyclicAMIPolyPatch::resetAMI
-(
-    const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
-) const
+const Foam::autoPtr<Foam::searchableSurface>&
+Foam::cyclicAMIPolyPatch::surfPtr() const
 {
-    if (owner())
-    {
-        AMIPtr_.clear();
+    const word surfType(surfDict_.getOrDefault<word>("type", "none"));
 
-        const polyPatch& nbr = neighbPatch();
-        pointField nbrPoints
-        (
-            neighbPatch().boundaryMesh().mesh().points(),
-            neighbPatch().meshPoints()
-        );
+    if (!surfPtr_ && owner() && surfType != "none")
+    {
+        word surfName(surfDict_.getOrDefault("name", name()));
 
-        if (debug)
-        {
-            const Time& t = boundaryMesh().mesh().time();
-            OFstream os(t.path()/name() + "_neighbourPatch-org.obj");
-            meshTools::writeOBJ(os, neighbPatch().localFaces(), nbrPoints);
-        }
+        const polyMesh& mesh = boundaryMesh().mesh();
 
-        // Transform neighbour patch to local system
-        transformPosition(nbrPoints);
-        primitivePatch nbrPatch0
-        (
-            SubList<face>
+        surfPtr_ =
+            searchableSurface::New
             (
-                nbr.localFaces(),
-                nbr.size()
-            ),
-            nbrPoints
-        );
+                surfType,
+                IOobject
+                (
+                    surfName,
+                    mesh.time().constant(),
+                    "triSurface",
+                    mesh,
+                    IOobject::MUST_READ,
+                    IOobject::NO_WRITE
+                ),
+                surfDict_
+            );
+    }
 
-        if (debug)
-        {
-            const Time& t = boundaryMesh().mesh().time();
-            OFstream osN(t.path()/name() + "_neighbourPatch-trans.obj");
-            meshTools::writeOBJ(osN, nbrPatch0.localFaces(), nbrPoints);
+    return surfPtr_;
+}
 
-            OFstream osO(t.path()/name() + "_ownerPatch.obj");
-            meshTools::writeOBJ(osO, this->localFaces(), localPoints());
-        }
 
-        // Construct/apply AMI interpolation to determine addressing and weights
-        AMIPtr_.reset
-        (
-            new AMIPatchToPatchInterpolation
-            (
-                *this,
-                nbrPatch0,
-                surfPtr(),
-                faceAreaIntersect::tmMesh,
-                AMIRequireMatch_,
-                AMIMethod,
-                AMILowWeightCorrection_,
-                AMIReverse_
-            )
-        );
+void Foam::cyclicAMIPolyPatch::resetAMI() const
+{
+    resetAMI(boundaryMesh().mesh().points());
+}
+
+
+void Foam::cyclicAMIPolyPatch::resetAMI(const UList<point>& points) const
+{
+    DebugInFunction << endl;
+
+    if (!owner())
+    {
+        return;
+    }
+
+    const cyclicAMIPolyPatch& nbr = neighbPatch();
+    pointField srcPoints(localPoints());
+    pointField nbrPoints(nbr.localPoints());
+
+    if (debug)
+    {
+        const Time& t = boundaryMesh().mesh().time();
+        OFstream os(t.path()/name() + "_neighbourPatch-org.obj");
+        meshTools::writeOBJ(os, neighbPatch().localFaces(), nbrPoints);
+    }
+
+    label patchSize0 = size();
+    label nbrPatchSize0 = nbr.size();
 
-        if (debug)
+    if (createAMIFaces_)
+    {
+        // AMI is created based on the original patch faces (non-extended patch)
+        if (srcFaceIDs_.size())
+        {
+            patchSize0 = srcFaceIDs_.size();
+        }
+        if (tgtFaceIDs_.size())
         {
-            Pout<< "cyclicAMIPolyPatch : " << name()
-                << " constructed AMI with " << nl
-                << "    " << "srcAddress:" << AMIPtr_().srcAddress().size()
-                << nl
-                << "    " << "tgAddress :" << AMIPtr_().tgtAddress().size()
-                << nl << endl;
+            nbrPatchSize0 = tgtFaceIDs_.size();
         }
     }
+
+    // Transform neighbour patch to local system
+    transformPosition(nbrPoints);
+    primitivePatch nbrPatch0
+    (
+        SubList<face>(nbr.localFaces(), nbrPatchSize0),
+        nbrPoints
+    );
+    primitivePatch patch0
+    (
+        SubList<face>(localFaces(), patchSize0),
+        srcPoints
+    );
+
+
+    if (debug)
+    {
+        const Time& t = boundaryMesh().mesh().time();
+        OFstream osN(t.path()/name() + "_neighbourPatch-trans.obj");
+        meshTools::writeOBJ(osN, nbrPatch0.localFaces(), nbrPoints);
+
+        OFstream osO(t.path()/name() + "_ownerPatch.obj");
+        meshTools::writeOBJ(osO, this->localFaces(), localPoints());
+    }
+
+    // Construct/apply AMI interpolation to determine addressing and weights
+    AMIPtr_->upToDate() = false;
+    AMIPtr_->calculate(patch0, nbrPatch0, surfPtr());
+
+    if (debug)
+    {
+        AMIPtr_->checkSymmetricWeights(true);
+    }
 }
 
 
 void Foam::cyclicAMIPolyPatch::calcTransforms()
 {
+    DebugInFunction << endl;
+
     const cyclicAMIPolyPatch& half0 = *this;
     vectorField half0Areas(half0.size());
     forAll(half0, facei)
@@ -389,35 +424,35 @@ void Foam::cyclicAMIPolyPatch::calcTransforms()
         half1Areas
     );
 
-    if (debug)
-    {
-        Pout<< "calcTransforms() : patch: " << name() << nl
-            << "    forwardT = " << forwardT() << nl
-            << "    reverseT = " << reverseT() << nl
-            << "    separation = " << separation() << nl
-            << "    collocated = " << collocated() << nl << endl;
-    }
+    DebugPout
+        << "calcTransforms() : patch: " << name() << nl
+        << "    forwardT = " << forwardT() << nl
+        << "    reverseT = " << reverseT() << nl
+        << "    separation = " << separation() << nl
+        << "    collocated = " << collocated() << nl << endl;
 }
 
 
 void Foam::cyclicAMIPolyPatch::initGeometry(PstreamBuffers& pBufs)
 {
-    // The AMI is no longer valid. Leave it up to demand-driven calculation
-    AMIPtr_.clear();
+    DebugInFunction << endl;
+
+    // Flag AMI as needing update
+    AMIPtr_->upToDate() = false;
 
     polyPatch::initGeometry(pBufs);
 
     // Early calculation of transforms so e.g. cyclicACMI can use them.
     // Note: also triggers primitiveMesh face centre. Note that cell
     // centres should -not- be calculated
-    // since e.g. cyclicACMI override face areas
+    // since e.g. cyclicACMI overrides face areas
     calcTransforms();
 }
 
 
 void Foam::cyclicAMIPolyPatch::calcGeometry(PstreamBuffers& pBufs)
 {
-    // All geometry done inside initGeometry
+    DebugInFunction << endl;
 }
 
 
@@ -427,14 +462,31 @@ void Foam::cyclicAMIPolyPatch::initMovePoints
     const pointField& p
 )
 {
-    // The AMI is no longer valid. Leave it up to demand-driven calculation
-    AMIPtr_.clear();
-
-    polyPatch::initMovePoints(pBufs, p);
+    DebugInFunction << endl;
 
     // See below. Clear out any local geometry
     primitivePatch::movePoints(p);
 
+    // Note: processorPolyPatch::initMovePoints calls
+    // processorPolyPatch::initGeometry which will trigger calculation of
+    // patch faceCentres() and cell volumes...
+
+    if (createAMIFaces_)
+    {
+        // Note: AMI should have been updated in setTopology
+
+        // faceAreas() and faceCentres() have been reset and will be
+        // recalculated on-demand using the mesh points and no longer
+        // correspond to the scaled areas!
+        restoreScaledGeometry();
+
+        // deltas need to be recalculated to use new face centres!
+    }
+    else
+    {
+        AMIPtr_->upToDate() = false;
+    }
+
     // Early calculation of transforms. See above.
     calcTransforms();
 }
@@ -446,30 +498,55 @@ void Foam::cyclicAMIPolyPatch::movePoints
     const pointField& p
 )
 {
-    polyPatch::movePoints(pBufs, p);
-
-    // All transformation tensors already done in initMovePoints
+    DebugInFunction << endl;
+
+//    Note: not calling movePoints since this will undo our manipulations!
+//    polyPatch::movePoints(pBufs, p);
+/*
+    polyPatch::movePoints
+     -> primitivePatch::movePoints
+        -> primitivePatch::clearGeom:
+    deleteDemandDrivenData(localPointsPtr_);
+    deleteDemandDrivenData(faceCentresPtr_);
+    deleteDemandDrivenData(faceAreasPtr_);
+    deleteDemandDrivenData(magFaceAreasPtr_);
+    deleteDemandDrivenData(faceNormalsPtr_);
+    deleteDemandDrivenData(pointNormalsPtr_);
+*/
 }
 
 
 void Foam::cyclicAMIPolyPatch::initUpdateMesh(PstreamBuffers& pBufs)
 {
-    // The AMI is no longer valid. Leave it up to demand-driven calculation
-    AMIPtr_.clear();
+    DebugInFunction << endl;
 
     polyPatch::initUpdateMesh(pBufs);
+
+    if (createAMIFaces_ && boundaryMesh().mesh().topoChanging() && owner())
+    {
+        setAMIFaces();
+    }
 }
 
 
 void Foam::cyclicAMIPolyPatch::updateMesh(PstreamBuffers& pBufs)
 {
+    DebugInFunction << endl;
+
+    // Note: this clears out cellCentres(), faceCentres() and faceAreas()
     polyPatch::updateMesh(pBufs);
 }
 
 
 void Foam::cyclicAMIPolyPatch::clearGeom()
 {
-    AMIPtr_.clear();
+    DebugInFunction << endl;
+
+    if (!updatingAMI_)
+    {
+        AMIPtr_->upToDate() = false;
+    }
+
     polyPatch::clearGeom();
 }
 
@@ -484,7 +561,8 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     const label index,
     const polyBoundaryMesh& bm,
     const word& patchType,
-    const transformType transform
+    const transformType transform,
+    const word& defaultAMIMethod
 )
 :
     coupledPolyPatch(name, size, start, index, bm, patchType, transform),
@@ -495,13 +573,16 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     rotationAngleDefined_(false),
     rotationAngle_(0.0),
     separationVector_(Zero),
-    AMIPtr_(nullptr),
-    AMIMethod_(AMIPatchToPatchInterpolation::imFaceAreaWeight),
-    AMIReverse_(false),
-    AMIRequireMatch_(true),
-    AMILowWeightCorrection_(-1.0),
+    AMIPtr_(AMIInterpolation::New(defaultAMIMethod)),
+    surfDict_(fileName("surface")),
     surfPtr_(nullptr),
-    surfDict_(fileName("surface"))
+    createAMIFaces_(false),
+    moveFaceCentres_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     // Neighbour patch might not be valid yet so no transformation
     // calculation possible
@@ -514,11 +595,12 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     const dictionary& dict,
     const label index,
     const polyBoundaryMesh& bm,
-    const word& patchType
+    const word& patchType,
+    const word& defaultAMIMethod
 )
 :
     coupledPolyPatch(name, dict, index, bm, patchType),
-    nbrPatchName_(dict.getOrDefault<word>("neighbourPatch", "")),
+    nbrPatchName_(dict.getOrDefault<word>("neighbourPatch", word::null)),
     coupleGroup_(dict),
     nbrPatchID_(-1),
     rotationAxis_(Zero),
@@ -526,26 +608,24 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     rotationAngleDefined_(false),
     rotationAngle_(0.0),
     separationVector_(Zero),
-    AMIPtr_(nullptr),
-    AMIMethod_
+    AMIPtr_
     (
-        AMIPatchToPatchInterpolation::interpolationMethodNames_
-        [
-            dict.getOrDefault
-            (
-                "method",
-                AMIPatchToPatchInterpolation::interpolationMethodNames_
-                [
-                    AMIPatchToPatchInterpolation::imFaceAreaWeight
-                ]
-            )
-        ]
+        AMIInterpolation::New
+        (
+            dict.getOrDefault<word>("AMIMethod", defaultAMIMethod),
+            dict,
+            dict.getOrDefault("flipNormals", false)
+        )
     ),
-    AMIReverse_(dict.getOrDefault("flipNormals", false)),
-    AMIRequireMatch_(true),
-    AMILowWeightCorrection_(dict.getOrDefault("lowWeightCorrection", -1.0)),
+    surfDict_(dict.subOrEmptyDict("surface")),
     surfPtr_(nullptr),
-    surfDict_(dict.subOrEmptyDict("surface"))
+    createAMIFaces_(dict.getOrDefault("createAMIFaces", false)),
+    moveFaceCentres_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     if (nbrPatchName_ == word::null && !coupleGroup_.valid())
     {
@@ -605,6 +685,15 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 
     // Neighbour patch might not be valid yet so no transformation
     // calculation possible
+
+    // If topology change, recover the sizes of the original patches and
+    // read additional controls
+    if (createAMIFaces_)
+    {
+        srcFaceIDs_.setSize(dict.get<label>("srcSize"));
+        tgtFaceIDs_.setSize(dict.get<label>("tgtSize"));
+        moveFaceCentres_ = dict.getOrDefault("moveFaceCentres", true);
+    }
 }
 
 
@@ -623,13 +712,16 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     rotationAngleDefined_(pp.rotationAngleDefined_),
     rotationAngle_(pp.rotationAngle_),
     separationVector_(pp.separationVector_),
-    AMIPtr_(nullptr),
-    AMIMethod_(pp.AMIMethod_),
-    AMIReverse_(pp.AMIReverse_),
-    AMIRequireMatch_(pp.AMIRequireMatch_),
-    AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
+    AMIPtr_(pp.AMIPtr_->clone()),
+    surfDict_(pp.surfDict_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
+    createAMIFaces_(pp.createAMIFaces_),
+    moveFaceCentres_(pp.moveFaceCentres_),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     // Neighbour patch might not be valid yet so no transformation
     // calculation possible
@@ -655,13 +747,16 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     rotationAngleDefined_(pp.rotationAngleDefined_),
     rotationAngle_(pp.rotationAngle_),
     separationVector_(pp.separationVector_),
-    AMIPtr_(nullptr),
-    AMIMethod_(pp.AMIMethod_),
-    AMIReverse_(pp.AMIReverse_),
-    AMIRequireMatch_(pp.AMIRequireMatch_),
-    AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
+    AMIPtr_(pp.AMIPtr_->clone()),
+    surfDict_(pp.surfDict_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
+    createAMIFaces_(pp.createAMIFaces_),
+    moveFaceCentres_(pp.moveFaceCentres_),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     if (nbrPatchName_ == name())
     {
@@ -694,19 +789,16 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     rotationAngleDefined_(pp.rotationAngleDefined_),
     rotationAngle_(pp.rotationAngle_),
     separationVector_(pp.separationVector_),
-    AMIPtr_(nullptr),
-    AMIMethod_(pp.AMIMethod_),
-    AMIReverse_(pp.AMIReverse_),
-    AMIRequireMatch_(pp.AMIRequireMatch_),
-    AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
+    AMIPtr_(pp.AMIPtr_->clone()),
+    surfDict_(pp.surfDict_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::cyclicAMIPolyPatch::~cyclicAMIPolyPatch()
+    createAMIFaces_(pp.createAMIFaces_),
+    moveFaceCentres_(pp.moveFaceCentres_),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {}
 
 
@@ -761,38 +853,6 @@ const Foam::cyclicAMIPolyPatch& Foam::cyclicAMIPolyPatch::neighbPatch() const
 }
 
 
-const Foam::autoPtr<Foam::searchableSurface>&
-Foam::cyclicAMIPolyPatch::surfPtr() const
-{
-    const word surfType(surfDict_.getOrDefault<word>("type", "none"));
-
-    if (!surfPtr_.valid() && owner() && surfType != "none")
-    {
-        word surfName(surfDict_.getOrDefault("name", name()));
-
-        const polyMesh& mesh = boundaryMesh().mesh();
-
-        surfPtr_ =
-            searchableSurface::New
-            (
-                surfType,
-                IOobject
-                (
-                    surfName,
-                    mesh.time().constant(),
-                    "triSurface",
-                    mesh,
-                    IOobject::MUST_READ,
-                    IOobject::NO_WRITE
-                ),
-                surfDict_
-            );
-    }
-
-    return surfPtr_;
-}
-
-
 const Foam::AMIPatchToPatchInterpolation& Foam::cyclicAMIPolyPatch::AMI() const
 {
     if (!owner())
@@ -802,9 +862,9 @@ const Foam::AMIPatchToPatchInterpolation& Foam::cyclicAMIPolyPatch::AMI() const
             << abort(FatalError);
     }
 
-    if (!AMIPtr_.valid())
+    if (!AMIPtr_->upToDate())
     {
-        resetAMI(AMIMethod_);
+        resetAMI();
     }
 
     return *AMIPtr_;
@@ -1079,31 +1139,19 @@ void Foam::cyclicAMIPolyPatch::write(Ostream& os) const
         }
     }
 
-    if (AMIMethod_ != AMIPatchToPatchInterpolation::imFaceAreaWeight)
-    {
-        os.writeEntry
-        (
-            "method",
-            AMIPatchToPatchInterpolation::interpolationMethodNames_
-            [
-                AMIMethod_
-            ]
-        );
-    }
-
-    if (AMIReverse_)
-    {
-        os.writeEntry("flipNormals", AMIReverse_);
-    }
+    AMIPtr_->write(os);
 
-    if (AMILowWeightCorrection_ > 0)
+    if (!surfDict_.empty())
     {
-        os.writeEntry("lowWeightCorrection", AMILowWeightCorrection_);
+        surfDict_.writeEntry(surfDict_.dictName(), os);
     }
 
-    if (!surfDict_.empty())
+    if (createAMIFaces_)
     {
-        surfDict_.writeEntry(surfDict_.dictName(), os);
+        os.writeEntry("createAMIFaces", createAMIFaces_);
+        os.writeEntry("srcSize", srcFaceIDs_.size());
+        os.writeEntry("tgtSize", tgtFaceIDs_.size());
+        os.writeEntry("moveFaceCentres", moveFaceCentres_);
     }
 }
 
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
index 25259ffacdfc8dff92aa2ae82363658b16f99f5e..2d0f0b393d8350f68c8fa0fd83aec84f46ea1aae 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2018-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,6 +30,18 @@ Class
 Description
     Cyclic patch for Arbitrary Mesh Interface (AMI)
 
+    Includes provision for updating the patch topology to enforce a 1-to-1
+    face match across the interface, based on the \c createAMIFaces flag.
+
+    The manipulations are based on the reference:
+
+    \verbatim
+    H.J. Aguerre, S. Márquez Damián, J.M. Gimenez, N.M.Nigro, Conservative
+    handling of arbitrary non-conformal interfaces using an efficient
+    supermesh, Journal of Computational Physics 335(15) 21-49. 2017.
+    https://doi.org/10.1016/j.jcp.2017.01.018.
+    \endverbatim
+
 SourceFiles
     cyclicAMIPolyPatch.C
 
@@ -41,6 +54,7 @@ SourceFiles
 #include "AMIPatchToPatchInterpolation.H"
 #include "polyBoundaryMesh.H"
 #include "coupleGroupIdentifier.H"
+#include "faceAreaWeightAMI.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -110,33 +124,61 @@ protected:
         //- AMI interpolation class
         mutable autoPtr<AMIPatchToPatchInterpolation> AMIPtr_;
 
-        //- AMI method
-        const AMIPatchToPatchInterpolation::interpolationMethod AMIMethod_;
+        //- Dictionary used during projection surface construction
+        const dictionary surfDict_;
 
-        //- Flag to indicate that slave patch should be reversed for AMI
-        const bool AMIReverse_;
+        //- Projection surface
+        mutable autoPtr<searchableSurface> surfPtr_;
 
-        //- Flag to indicate that patches should match/overlap
-        bool AMIRequireMatch_;
 
-        //- Low weight correction threshold for AMI
-        const scalar AMILowWeightCorrection_;
+        // Change of topology as AMI is updated
 
-        //- Projection surface
-        mutable autoPtr<searchableSurface> surfPtr_;
+            //- Flag to indicate that new AMI faces will created
+            //  Set by the call to changeTopology
+            mutable bool createAMIFaces_;
 
-        //- Dictionary used during projection surface construction
-        const dictionary surfDict_;
+            //- Move face centres (default = no)
+            bool moveFaceCentres_;
+
+            mutable bool updatingAMI_;
+
+            labelListList srcFaceIDs_;
+
+            labelListList tgtFaceIDs_;
+
+            //- Temporary storage for AMI face areas
+            mutable vectorField faceAreas0_;
+
+            //- Temporary storage for AMI face centres
+            mutable vectorField faceCentres0_;
 
 
     // Protected Member Functions
 
-        //- Reset the AMI interpolator
-        virtual void resetAMI
-        (
-            const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
-                AMIPatchToPatchInterpolation::imFaceAreaWeight
-        ) const;
+        // Topology change
+
+            //- Collect faces to remove in the topoChange container
+            virtual bool removeAMIFaces(polyTopoChange& topoChange);
+
+            //- Collect faces to add in the topoChange container
+            virtual bool addAMIFaces(polyTopoChange& topoChange);
+
+            //- Set properties of newly inserted faces after topological changes
+            virtual void setAMIFaces();
+
+            //- Helper to re-apply the geometric scaling lost during mesh
+            //- updates
+            virtual void restoreScaledGeometry();
+
+
+        //- Create and return pointer to the projection surface
+        const autoPtr<searchableSurface>& surfPtr() const;
+
+        //- Reset the AMI interpolator, supply patch points
+        virtual void resetAMI(const UList<point>& points) const;
+
+        //-  Reset the AMI interpolator, use current patch points
+        virtual void resetAMI() const;
 
         //- Recalculate the transformation tensors
         virtual void calcTransforms();
@@ -180,7 +222,8 @@ public:
             const label index,
             const polyBoundaryMesh& bm,
             const word& patchType,
-            const transformType transform = UNKNOWN
+            const transformType transform = UNKNOWN,
+            const word& defaultAMIMethod = faceAreaWeightAMI::typeName
         );
 
         //- Construct from dictionary
@@ -190,7 +233,8 @@ public:
             const dictionary& dict,
             const label index,
             const polyBoundaryMesh& bm,
-            const word& patchType
+            const word& patchType,
+            const word& defaultAMIMethod = faceAreaWeightAMI::typeName
         );
 
         //- Construct as copy, resetting the boundary mesh
@@ -274,15 +318,34 @@ public:
 
 
     //- Destructor
-    virtual ~cyclicAMIPolyPatch();
+    virtual ~cyclicAMIPolyPatch() = default;
 
 
     // Member Functions
 
         // Access
 
+            //- Tolerance used e.g. for area calculations/limits
+            static const scalar tolerance_;
+
+            //- Flag to indicate whether the AMI can be reset
+            inline bool canResetAMI() const;
+
+            //- Return access to the createAMIFaces flag
+            inline bool createAMIFaces() const;
+
+            //- Return access to the updated flag
+            inline bool updatingAMI() const;
+
+            //- Return true if this patch changes the mesh topology
+            //  True when createAMIFaces is true
+            virtual bool changeTopology() const;
+
+            //- Set topology changes in the polyTopoChange object
+            virtual bool setTopology(polyTopoChange& topoChange);
+
             //- Is patch 'coupled'. Note that on AMI the geometry is not
-            //  coupled but the fields are!
+            //- coupled but the fields are!
             virtual bool coupled() const
             {
                 return false;
@@ -300,15 +363,26 @@ public:
             //- Return a reference to the neighbour patch
             virtual const cyclicAMIPolyPatch& neighbPatch() const;
 
-            //- Return a reference to the projection surface
-            const autoPtr<searchableSurface>& surfPtr() const;
-
             //- Return a reference to the AMI interpolator
             const AMIPatchToPatchInterpolation& AMI() const;
 
+            //- Helper function to return the weights
+            inline const scalarListList& weights() const;
+
+            //- Helper function to return the weights sum
+            inline const scalarField& weightsSum() const;
+
             //- Return true if applying the low weight correction
             bool applyLowWeightCorrection() const;
 
+            //- Return access to the initial face areas
+            //  Used for topology change
+            inline vectorField& faceAreas0() const;
+
+            //- Return access to the initial face centres
+            //  Used for topology change
+            inline vectorField& faceCentres0() const;
+
 
             // Transformations
 
@@ -388,7 +462,7 @@ public:
         );
 
         //- Initialize ordering for primitivePatch. Does not
-        //  refer to *this (except for name() and type() etc.)
+        //- refer to *this (except for name() and type() etc.)
         virtual void initOrder
         (
             PstreamBuffers&,
@@ -409,7 +483,7 @@ public:
         ) const;
 
         //- Return face index on neighbour patch which shares point p
-        //  following trajectory vector n
+        //- following trajectory vector n
         label pointFace
         (
             const label facei,
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchI.H b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchI.H
index ccc36fc3007d3fdc7f7768c85c61edc63250ba42..f4c640d7c95356ef10562c585c1f6ad7cb91a51c 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchI.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchI.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
+    Copyright (C) 2019-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,6 +28,24 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+inline bool Foam::cyclicAMIPolyPatch::canResetAMI() const
+{
+    return Pstream::parRun() || !boundaryMesh().mesh().time().processorCase();
+}
+
+
+inline bool Foam::cyclicAMIPolyPatch::createAMIFaces() const
+{
+    return createAMIFaces_;
+}
+
+
+inline bool Foam::cyclicAMIPolyPatch::updatingAMI() const
+{
+    return updatingAMI_;
+}
+
+
 inline const Foam::word& Foam::cyclicAMIPolyPatch::neighbPatchName() const
 {
     if (nbrPatchName_.empty())
@@ -39,6 +58,38 @@ inline const Foam::word& Foam::cyclicAMIPolyPatch::neighbPatchName() const
     return nbrPatchName_;
 }
 
+inline const Foam::scalarListList& Foam::cyclicAMIPolyPatch::weights() const
+{
+    if (owner())
+    {
+        return AMI().srcWeights();
+    }
+
+    return neighbPatch().AMI().tgtWeights();
+}
+
+
+inline const Foam::scalarField& Foam::cyclicAMIPolyPatch::weightsSum() const
+{
+    if (owner())
+    {
+        return AMI().srcWeightsSum();
+    }
+
+    return neighbPatch().AMI().tgtWeightsSum();
+}
+
+
+inline Foam::vectorField& Foam::cyclicAMIPolyPatch::faceAreas0() const
+{
+    return faceAreas0_;
+}
+
+
+inline Foam::vectorField& Foam::cyclicAMIPolyPatch::faceCentres0() const
+{
+    return faceCentres0_;
+}
 
 inline const Foam::vector& Foam::cyclicAMIPolyPatch::rotationAxis() const
 {
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchTopologyChange.C b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchTopologyChange.C
new file mode 100644
index 0000000000000000000000000000000000000000..a1c6634e2831b47a34adaefe0889cba00797afdf
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchTopologyChange.C
@@ -0,0 +1,678 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2019-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 "cyclicAMIPolyPatch.H"
+#include "SubField.H"
+#include "vectorList.H"
+#include "polyTopoChange.H"
+
+// * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * * //
+
+void Foam::cyclicAMIPolyPatch::restoreScaledGeometry()
+{
+    DebugInFunction << endl;
+
+    // Note: only used for topology update (createAMIFaces_ flag)
+    if (!createAMIFaces_)
+    {
+        FatalErrorInFunction
+            << "Attempted to perform topology update when createAMIFaces_ "
+            << "flag is set to false"
+            << abort(FatalError);
+    }
+
+    if (boundaryMesh().mesh().hasCellVolumes())
+    {
+        WarningInFunction
+            << "Mesh already has volumes set!"
+            << endl;
+    }
+
+    vectorField::subField faceAreas = this->faceAreas();
+    vectorField::subField faceCentres = this->faceCentres();
+
+    DebugInfo
+        << "Patch:" << name() << " before: sum(mag(faceAreas)):"
+        << gSum(mag(faceAreas)) << nl
+        << "Patch:" << name() << " before: sum(mag(faceAreas0)):"
+        << gSum(mag(faceAreas0_)) << endl;
+
+    faceAreas = faceAreas0_;
+    if (moveFaceCentres_)
+    {
+        DebugInfo << "Moving face centres" << endl;
+        faceCentres = faceCentres0_;
+    }
+
+    faceAreas0_.clear();
+    faceCentres0_.clear();
+
+    DebugInfo
+        << "Patch:" << name() << " after: sum(mag(faceAreas)):"
+        << gSum(mag(faceAreas)) << nl
+        << "Patch:" << name() << " after: sum(mag(faceAreas0)):"
+        << gSum(mag(faceAreas0_)) << endl;
+}
+
+
+bool Foam::cyclicAMIPolyPatch::removeAMIFaces(polyTopoChange& topoChange)
+{
+    DebugInFunction << endl;
+
+    // Note: only used for topology update (createAMIFaces_ flag)
+    if (!createAMIFaces_)
+    {
+        FatalErrorInFunction
+            << "Attempted to perform topology update when createAMIFaces_ "
+            << "flag is set to false"
+            << abort(FatalError);
+    }
+
+    if (!owner())
+    {
+        return false;
+    }
+
+    bool changeRequired = false;
+
+    // Remove any faces that we inserted to create the 1-to-1 match...
+
+    const cyclicAMIPolyPatch& nbr = neighbPatch();
+
+    const label newSrcFaceStart = srcFaceIDs_.size();
+
+    if (newSrcFaceStart != 0)
+    {
+        for (label facei = newSrcFaceStart; facei < size(); ++facei)
+        {
+            changeRequired = true;
+            label meshFacei = start() + facei;
+            topoChange.removeFace(meshFacei, -1);
+        }
+    }
+
+    const label newTgtFaceStart = tgtFaceIDs_.size();
+
+    if (newTgtFaceStart != 0)
+    {
+        for (label facei = newTgtFaceStart; facei < nbr.size(); ++facei)
+        {
+            changeRequired = true;
+            label meshFacei = nbr.start() + facei;
+            topoChange.removeFace(meshFacei, -1);
+        }
+    }
+
+    srcFaceIDs_.clear();
+    tgtFaceIDs_.clear();
+
+    return changeRequired;
+}
+
+
+bool Foam::cyclicAMIPolyPatch::addAMIFaces(polyTopoChange& topoChange)
+{
+    DebugInFunction << endl;
+
+    // Note: only used for topology update (createAMIFaces_ flag = true)
+    if (!createAMIFaces_)
+    {
+        FatalErrorInFunction
+            << "Attempted to perform topology update when createAMIFaces_ "
+            << "flag is set to false"
+            << abort(FatalError);
+    }
+
+    bool changedFaces = false;
+    const cyclicAMIPolyPatch& nbr = neighbPatch();
+
+    polyMesh& mesh = const_cast<polyMesh&>(boundaryMesh().mesh());
+    const faceZoneMesh& faceZones = mesh.faceZones();
+
+    // First face address and weight are used to manipulate the
+    // original face - all other addresses and weights are used to
+    // create additional faces
+    const labelListList& srcToTgtAddr = AMI().srcAddress();
+    const labelListList& tgtToSrcAddr = AMI().tgtAddress();
+
+    const label nSrcFace = srcToTgtAddr.size();
+    const label nTgtFace = tgtToSrcAddr.size();
+
+    srcFaceIDs_.setSize(nSrcFace);
+    tgtFaceIDs_.setSize(nTgtFace);
+
+    label nNewSrcFaces = 0;
+    forAll(srcToTgtAddr, srcFacei)
+    {
+        const labelList& tgtAddr = srcToTgtAddr[srcFacei];
+
+        // No tgt faces linked to srcFacei (ACMI)
+        if (tgtAddr.empty()) continue;
+
+        srcFaceIDs_[srcFacei].setSize(tgtAddr.size());
+        srcFaceIDs_[srcFacei][0] = srcFacei;
+
+        const label meshFacei = start() + srcFacei;
+        for (label addri = 1; addri < tgtAddr.size(); ++addri)
+        {
+            changedFaces = true;
+
+            // Note: new faces reuse originating face points
+            // - but areas are scaled by the weights (later)
+
+            // New source face for each target face address
+            srcFaceIDs_[srcFacei][addri] = nNewSrcFaces + nSrcFace;
+            ++nNewSrcFaces;
+            (void)topoChange.addFace
+            (
+                mesh.faces()[meshFacei],        // modified face
+                mesh.faceOwner()[meshFacei],    // owner
+                -1,                             // neighbour
+                -1,                             // master point
+                -1,                             // master edge
+                meshFacei,                      // master face
+                false,                          // face flip
+                index(),                        // patch for face
+                faceZones.whichZone(meshFacei), // zone for original face
+                false                           // face flip in zone
+            );
+        }
+    }
+
+    label nNewTgtFaces = 0;
+    forAll(tgtToSrcAddr, tgtFacei)
+    {
+        const labelList& srcAddr = tgtToSrcAddr[tgtFacei];
+
+        // No src faces linked to tgtFacei (ACMI)
+        if (srcAddr.empty()) continue;
+
+        tgtFaceIDs_[tgtFacei].setSize(srcAddr.size());
+        tgtFaceIDs_[tgtFacei][0] = tgtFacei;
+
+        const label meshFacei = nbr.start() + tgtFacei;
+        for (label addri = 1; addri < srcAddr.size(); ++addri)
+        {
+            changedFaces = true;
+
+            // Note: new faces reuse originating face points
+            // - but areas are scaled by the weights (later)
+
+            // New target face for each source face address
+            tgtFaceIDs_[tgtFacei][addri] = nNewTgtFaces + nTgtFace;
+            ++nNewTgtFaces;
+
+            (void)topoChange.addFace
+            (
+                mesh.faces()[meshFacei],        // modified face
+                mesh.faceOwner()[meshFacei],    // owner
+                -1,                             // neighbour
+                -1,                             // master point
+                -1,                             // master edge
+                meshFacei,                      // master face
+                false,                          // face flip
+                nbr.index(),                    // patch for face
+                faceZones.whichZone(meshFacei), // zone for original face
+                false                           // face flip in zone
+            );
+        }
+    }
+
+    Info<< "AMI: Patch " << name() << " additional faces: "
+        << returnReduce(nNewSrcFaces, sumOp<label>()) << nl
+        << "AMI: Patch " << nbr.name() << " additional faces: "
+        << returnReduce(nNewTgtFaces, sumOp<label>())
+        << endl;
+
+    if (debug)
+    {
+        Pout<< "New faces - " << name() << ": " << nNewSrcFaces
+            << " "  << nbr.name() << ": " << nNewTgtFaces << endl;
+    }
+
+    return returnReduce(changedFaces, orOp<bool>());
+}
+
+
+void Foam::cyclicAMIPolyPatch::setAMIFaces()
+{
+    // Create new mesh faces so that there is a 1-to-1 correspondence
+    // between faces on each side of the AMI
+
+    // Note: only used for topology update (createAMIFaces_ flag)
+    if (!createAMIFaces_)
+    {
+        FatalErrorInFunction
+            << "Attempted to perform topology update when createAMIFaces_ "
+            << "flag is set to false"
+            << abort(FatalError);
+    }
+
+
+    DebugInFunction << endl;
+
+    if (!owner())
+    {
+        return;
+    }
+
+    const cyclicAMIPolyPatch& nbr = neighbPatch();
+
+    vectorField& nbrFaceAreas0 = nbr.faceAreas0();
+    vectorField& nbrFaceCentres0 = nbr.faceCentres0();
+
+    // Scale the new face areas and set the centroids
+    // Note:
+    // - storing local copies so that they can be re-applied after the call to
+    //   movePoints that will reset any changes to the areas and centroids
+    //
+    // - For AMI, src and tgt patches should be the same
+    // - For ACMI they are likely to be different!
+    faceAreas0_ = faceAreas();
+    faceCentres0_ = faceCentres();
+    nbrFaceAreas0 = nbr.faceAreas();
+    nbrFaceCentres0 = nbr.faceCentres();
+
+    // Original AMI info (based on the mesh state when the AMI was evaluated)
+    const labelListList& srcToTgtAddr0 = AMIPtr_->srcAddress();
+    const labelListList& tgtToSrcAddr0 = AMIPtr_->tgtAddress();
+    const pointListList& srcCtr0 = AMIPtr_->srcCentroids();
+    const scalarListList& srcToTgtWght0 = AMIPtr_->srcWeights();
+
+    // New addressing on new mesh (extended by polyTopoChange)
+    labelListList srcToTgtAddr1(size(), labelList());
+    labelListList tgtToSrcAddr1(nbr.size(), labelList());
+
+    // Need to calc new parallel maps (mesh has changed since AMI was computed)
+    autoPtr<mapDistribute> srcToTgtMap1;
+    autoPtr<mapDistribute> tgtToSrcMap1;
+
+    if (AMIPtr_->singlePatchProc() == -1)
+    {
+       // Parallel running
+
+        // Global index based on old patch sizes (when AMI was computed)
+        globalIndex globalSrcFaces0(srcToTgtAddr0.size());
+        globalIndex globalTgtFaces0(tgtToSrcAddr0.size());
+
+        // Global index based on new patch sizes
+        globalIndex globalSrcFaces1(size());
+        globalIndex globalTgtFaces1(nbr.size());
+
+
+        // Gather source side info
+        // =======================
+
+        // Note: using new global index for addressing, and distributed using
+        // the old AMI map
+        labelListList newTgtGlobalFaces(tgtFaceIDs_);
+        forAll(newTgtGlobalFaces, tgtFacei)
+        {
+            globalTgtFaces1.inplaceToGlobal(newTgtGlobalFaces[tgtFacei]);
+        }
+        AMIPtr_->tgtMap().distribute(newTgtGlobalFaces);
+
+        // Now have new tgt face indices for each src face
+
+        labelList globalSrcFaceIDs(identity(srcToTgtAddr0.size()));
+        globalSrcFaces0.inplaceToGlobal(globalSrcFaceIDs);
+        AMIPtr_->srcMap().distribute(globalSrcFaceIDs);
+        // globalSrcFaceIDs now has remote data for each srcFacei0 known to the
+        // tgt patch
+
+        List<List<point>> globalSrcCtrs0(srcCtr0);
+        AMIPtr_->srcMap().distribute(globalSrcCtrs0);
+
+        labelList globalTgtFaceIDs(identity(tgtToSrcAddr0.size()));
+        globalTgtFaces0.inplaceToGlobal(globalTgtFaceIDs);
+        AMIPtr_->tgtMap().distribute(globalTgtFaceIDs);
+        // globalTgtFaceIDs now has remote data for each tgtFacei0 known to the
+        // src patch
+
+        // For debug - send tgt face centres and compare against mapped src
+        // face centres
+        //List<List<point>> globalTgtCtrs0(tgtCtr0);
+        //AMIPtr_->tgtMap().distribute(globalTgtCtrs0);
+
+        labelListList globalTgtToSrcAddr(tgtToSrcAddr0);
+        forAll(tgtToSrcAddr0, tgtFacei0)
+        {
+            forAll(tgtToSrcAddr0[tgtFacei0], addri)
+            {
+                const label globalSrcFacei =
+                    globalSrcFaceIDs[tgtToSrcAddr0[tgtFacei0][addri]];
+                globalTgtToSrcAddr[tgtFacei0][addri] = globalSrcFacei;
+            }
+        }
+        AMIPtr_->tgtMap().distribute(globalTgtToSrcAddr);
+
+        labelListList globalSrcToTgtAddr(srcToTgtAddr0);
+        forAll(srcToTgtAddr0, srcFacei0)
+        {
+            forAll(srcToTgtAddr0[srcFacei0], addri)
+            {
+                const label globalTgtFacei =
+                    globalTgtFaceIDs[srcToTgtAddr0[srcFacei0][addri]];
+                globalSrcToTgtAddr[srcFacei0][addri] = globalTgtFacei;
+            }
+        }
+        AMIPtr_->srcMap().distribute(globalSrcToTgtAddr);
+
+        label nError = 0;
+        forAll(srcToTgtAddr0, srcFacei0)
+        {
+            const labelList& newSrcFaces = srcFaceIDs_[srcFacei0];
+
+            forAll(newSrcFaces, i)
+            {
+                const label srcFacei1 = newSrcFaces[i];
+
+                // What index did srcFacei0 appear in tgtToSrc0 list?
+                // - if first index, all ok
+                // - else tgt face has been moved to according to tgtFaceIDs_
+                const label tgtFacei0 = srcToTgtAddr0[srcFacei0][i];
+                const label addri =
+                    globalTgtToSrcAddr[tgtFacei0].find
+                    (
+                        globalSrcFaceIDs[srcFacei0]
+                    );
+
+                if (addri == -1)
+                {
+                    ++nError;
+                    continue;
+
+                    if (debug)
+                    {
+                        Pout<< "Unable to find global source face "
+                            << globalSrcFaceIDs[srcFacei0]
+                            << " in globalTgtToSrcAddr[" << tgtFacei0 << "]: "
+                            << globalTgtToSrcAddr[tgtFacei0]
+                            << endl;
+                    }
+                }
+
+                const label tgtFacei1 = newTgtGlobalFaces[tgtFacei0][addri];
+
+                // Sanity check to see that we've picked the correct face
+                // point tgtCtr0(globalTgtCtrs0[tgtFacei0][addri]);
+                // Pout<< "srcCtr:" << srcCtr0[srcFacei0][i]
+                //     << " tgtCtr:" << tgtCtr0 << endl;
+
+                srcToTgtAddr1[srcFacei1] = labelList(1, tgtFacei1);
+                faceAreas0_[srcFacei1] *= srcToTgtWght0[srcFacei0][i];
+                faceCentres0_[srcFacei1] = srcCtr0[srcFacei0][i];
+            }
+        }
+
+        if (nError)
+        {
+            FatalErrorInFunction
+                << "Unable to find " << nError << " global source faces"
+                << abort(FatalError);
+        }
+
+
+        // Gather Target side info
+        // =======================
+
+        labelListList newSrcGlobalFaces(srcFaceIDs_);
+        forAll(newSrcGlobalFaces, srcFacei)
+        {
+            globalSrcFaces1.inplaceToGlobal(newSrcGlobalFaces[srcFacei]);
+        }
+
+        AMIPtr_->srcMap().distribute(newSrcGlobalFaces);
+
+        // Now have new src face indices for each tgt face
+        forAll(tgtToSrcAddr0, tgtFacei0)
+        {
+            const labelList& newTgtFaces = tgtFaceIDs_[tgtFacei0];
+            forAll(newTgtFaces, i)
+            {
+                const label srcFacei0 = tgtToSrcAddr0[tgtFacei0][i];
+
+                const label addri =
+                    globalSrcToTgtAddr[srcFacei0].find
+                    (
+                        globalTgtFaceIDs[tgtFacei0]
+                    );
+
+                if (addri == -1)
+                {
+                    ++nError;
+                    continue;
+
+                    if (debug)
+                    {
+                        Pout<< "Unable to find global target face "
+                            << globalTgtFaceIDs[tgtFacei0]
+                            << " in globalSrcToTgtAddr[" << srcFacei0 << "]: "
+                            << globalSrcToTgtAddr[srcFacei0]
+                            << endl;
+                    }
+                }
+
+                const label srcFacei1 = newSrcGlobalFaces[srcFacei0][addri];
+
+                // Sanity check to see that we've picked the correct face
+                point srcCtr0(globalSrcCtrs0[srcFacei0][addri]);
+                reverseTransformPosition(srcCtr0, srcFacei0);
+
+                const label tgtFacei1 = newTgtFaces[i];
+                tgtToSrcAddr1[tgtFacei1] = labelList(1, srcFacei1);
+                nbrFaceCentres0[tgtFacei1] = srcCtr0;
+            }
+        }
+
+        if (nError)
+        {
+            FatalErrorInFunction
+                << "Unable to find " << nError << " global target faces"
+                << abort(FatalError);
+        }
+
+        // Update the maps
+        {
+            List<Map<label>> cMap;
+            srcToTgtMap1.reset
+            (
+                new mapDistribute(globalSrcFaces1, tgtToSrcAddr1, cMap)
+            );
+        }
+        {
+            List<Map<label>> cMap;
+            tgtToSrcMap1.reset
+            (
+                new mapDistribute(globalTgtFaces1, srcToTgtAddr1, cMap)
+            );
+        }
+
+        // Reset tgt patch areas using the new map
+        vectorList newSrcGlobalFaceAreas(faceAreas0_);
+
+        srcToTgtMap1->distribute(newSrcGlobalFaceAreas);
+        forAll(nbrFaceAreas0, tgtFacei)
+        {
+            if (!tgtToSrcAddr1[tgtFacei].empty())
+            {
+                const label srcFacei = tgtToSrcAddr1[tgtFacei][0];
+                nbrFaceAreas0[tgtFacei] = -newSrcGlobalFaceAreas[srcFacei];
+            }
+        }
+    }
+    else
+    {
+        label nError = 0;
+        forAll(srcToTgtAddr0, srcFacei0)
+        {
+            const labelList& srcFaceTgtAddr0 = srcToTgtAddr0[srcFacei0];
+            const scalarList& srcFaceTgtWght0 = srcToTgtWght0[srcFacei0];
+            const pointList& srcFaceTgtCtr0 = srcCtr0[srcFacei0];
+            forAll(srcFaceTgtAddr0, addri)
+            {
+                const label srcFacei1 = srcFaceIDs_[srcFacei0][addri];
+
+                // Find which slot srcFacei0 appears in tgt->src addressing
+                const label tgtFacei0 = srcFaceTgtAddr0[addri];
+                const label tgtAddri0 =
+                    tgtToSrcAddr0[tgtFacei0].find(srcFacei0);
+
+                if (tgtAddri0 == -1)
+                {
+                    ++nError;
+                    continue;
+
+                    if (debug)
+                    {
+                        Pout<< "Unable to find source face " << srcFacei0
+                            << " in tgtToSrcAddr0[" << tgtFacei0 << "]: "
+                            << tgtToSrcAddr0[tgtFacei0]
+                            << endl;
+                    }
+                }
+
+                const label tgtFacei1 = tgtFaceIDs_[tgtFacei0][tgtAddri0];
+
+                faceAreas0_[srcFacei1] *= srcFaceTgtWght0[addri];
+                nbrFaceAreas0[tgtFacei1] = -faceAreas0_[srcFacei1];
+
+                point pt(srcFaceTgtCtr0[addri]);
+                faceCentres0_[srcFacei1] = pt;
+                reverseTransformPosition(pt, srcFacei0);
+                nbrFaceCentres0[tgtFacei1] = pt;
+
+                // SANITY CHECK
+                // Info<< "srcPt:" << srcFaceCentres[srcFacei1]
+                //     << " tgtPt:" << tgtFaceCentres[tgtFacei1] << endl;
+
+                srcToTgtAddr1[srcFacei1] = labelList(1, tgtFacei1);
+                tgtToSrcAddr1[tgtFacei1] = labelList(1, srcFacei1);
+            }
+        }
+
+        if (nError)
+        {
+            FatalErrorInFunction
+                << "Unable to find " << nError
+                << " source faces in tgtToSrcAddr0"
+                << abort(FatalError);
+        }
+    }
+
+    scalarListList newSrcToTgtWeights(srcToTgtAddr1.size());
+    forAll(srcToTgtAddr1, facei)
+    {
+        if (srcToTgtAddr1[facei].size())
+        {
+            newSrcToTgtWeights[facei] = scalarList(1, scalar(1));
+        }
+        else
+        {
+            // No connection - effect of face removed by setting area to a
+            // a small value
+            faceAreas0_[facei] *= tolerance_;
+        }
+    }
+
+    scalarListList newTgtToSrcWeights(tgtToSrcAddr1.size());
+    forAll(tgtToSrcAddr1, facei)
+    {
+        if (tgtToSrcAddr1[facei].size())
+        {
+            newTgtToSrcWeights[facei] = scalarList(1, scalar(1));
+        }
+        else
+        {
+            // No connection - effect of face removed by setting area to a
+            // a small value
+            nbrFaceAreas0[facei] *= tolerance_;
+        }
+    }
+
+    // Reset the AMI addressing and weights to reflect the new 1-to-1
+    // correspondence
+    AMIPtr_->reset
+    (
+        std::move(srcToTgtMap1),
+        std::move(tgtToSrcMap1),
+        std::move(srcToTgtAddr1),
+        std::move(newSrcToTgtWeights),
+        std::move(tgtToSrcAddr1),
+        std::move(newTgtToSrcWeights)
+    );
+
+    // Need to set areas, e.g. for agglomeration to (re-)normalisation weights
+    AMIPtr_->srcMagSf() = mag(faceAreas0_);
+    AMIPtr_->tgtMagSf() = mag(nbrFaceAreas0);
+
+    if (debug)
+    {
+        Pout<< "cyclicAMIPolyPatch : " << name()
+            << " constructed AMI with " << nl
+            << "    " << "srcAddress:" << AMIPtr_().srcAddress().size()
+            << nl
+            << "    " << "tgAddress :" << AMIPtr_().tgtAddress().size()
+            << nl << endl;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::cyclicAMIPolyPatch::changeTopology() const
+{
+    DebugInFunction << endl;
+
+    createAMIFaces_ = true;
+
+    return true;
+}
+
+
+bool Foam::cyclicAMIPolyPatch::setTopology(polyTopoChange& topoChange)
+{
+    DebugInFunction << endl;
+
+    if (createAMIFaces_ && owner())
+    {
+        // Calculate the AMI using the new points
+        // Note: mesh still has old points
+        resetAMI(topoChange.points());
+
+        removeAMIFaces(topoChange);
+
+        addAMIFaces(topoChange);
+
+        return true;
+    }
+
+    return false;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
index 28c6b98f6d48420a7e1bfe3bd21928ca86eeb78c..d8ade6e0cc2e1538157ee023229f0c64522e44f5 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
+++ b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.C
@@ -26,6 +26,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cyclicPeriodicAMIPolyPatch.H"
+#include "partialFaceAreaWeightAMI.H"
 #include "addToRunTimeSelectionTable.H"
 
 // For debugging
@@ -228,10 +229,7 @@ void Foam::cyclicPeriodicAMIPolyPatch::writeOBJ
 }
 
 
-void Foam::cyclicPeriodicAMIPolyPatch::resetAMI
-(
-    const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
-) const
+void Foam::cyclicPeriodicAMIPolyPatch::resetAMI() const
 {
     if (owner())
     {
@@ -326,20 +324,8 @@ void Foam::cyclicPeriodicAMIPolyPatch::resetAMI
         );
 
         // Construct a new AMI interpolation between the initial patch locations
-        AMIPtr_.reset
-        (
-            new AMIPatchToPatchInterpolation
-            (
-                thisPatch0,
-                nbrPatch0,
-                surfPtr(),
-                faceAreaIntersect::tmMesh,
-                false,
-                AMIPatchToPatchInterpolation::imPartialFaceAreaWeight,
-                AMILowWeightCorrection_,
-                AMIReverse_
-            )
-        );
+        AMIPtr_->setRequireMatch(false);
+        AMIPtr_->calculate(thisPatch0, nbrPatch0, surfPtr());
 
         // Number of geometry replications
         label iter(0);
@@ -358,7 +344,6 @@ void Foam::cyclicPeriodicAMIPolyPatch::resetAMI
         // Weight sum averages
         scalar srcSum(gAverage(AMIPtr_->srcWeightsSum()));
         scalar tgtSum(gAverage(AMIPtr_->tgtWeightsSum()));
-
         // Direction (or rather side of AMI : this or nbr patch) of
         // geometry replication
         bool direction = nTransforms_ >= 0;
@@ -520,17 +505,13 @@ void Foam::cyclicPeriodicAMIPolyPatch::resetAMI
                 << "The current matchTolerance : " << matchTolerance()
                 << ", sum of owner weights : " << srcSum
                 << ", sum of neighbour weights : " << tgtSum
-                 << "." << nl
+                << "." << nl
                 << "This is only acceptable during post-processing"
                 << "; not during running. Improve your mesh or increase"
                 << " the 'matchTolerance' setting in the patch specification."
                 << endl;
         }
 
-        // Normalise the weights. Disable printing since weights are
-        // still areas.
-        AMIPtr_->normaliseWeights(true, false);
-
         // Print some statistics
         const label nFace = returnReduce(size(), sumOp<label>());
 
@@ -577,7 +558,17 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
     const transformType transform
 )
 :
-    cyclicAMIPolyPatch(name, size, start, index, bm, patchType, transform),
+    cyclicAMIPolyPatch
+    (
+        name,
+        size,
+        start,
+        index,
+        bm,
+        patchType,
+        transform,
+        partialFaceAreaWeightAMI::typeName
+    ),
     periodicPatchName_(word::null),
     periodicPatchID_(-1),
     nTransforms_(0),
@@ -595,7 +586,15 @@ Foam::cyclicPeriodicAMIPolyPatch::cyclicPeriodicAMIPolyPatch
     const word& patchType
 )
 :
-    cyclicAMIPolyPatch(name, dict, index, bm, patchType),
+    cyclicAMIPolyPatch
+    (
+        name,
+        dict,
+        index,
+        bm,
+        patchType,
+        partialFaceAreaWeightAMI::typeName
+    ),
     periodicPatchName_(dict.lookup("periodicPatch")),
     periodicPatchID_(-1),
     nTransforms_(dict.getOrDefault<label>("nTransforms", 0)),
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.H b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.H
index 22fbc088455d3b3f30373dcd552e1c769d7e59bf..297c5c3189310d8ef90faf64244c4b0332005075 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicPeriodicAMI/cyclicPeriodicAMIPolyPatch/cyclicPeriodicAMIPolyPatch.H
@@ -83,11 +83,7 @@ private:
         void writeOBJ(const primitivePatch& p, OBJstream& str) const;
 
         //- Reset the AMI interpolator
-        virtual void resetAMI
-        (
-            const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
-                AMIPatchToPatchInterpolation::imFaceAreaWeight
-        ) const;
+        virtual void resetAMI() const;
 
 
 public:
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 22b12ee488cfd3e4ba2d8395e20ddb4ff3c4d0cd..e428fc63dcbdf3ce93c549d8e2c35b97e428872e 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -259,8 +259,13 @@ processorLOD/cellBox/cellBox.C
 processorLOD/faceBox/faceBox.C
 
 AMI=AMIInterpolation
-$(AMI)/AMIInterpolation/AMIInterpolationName.C
-$(AMI)/AMIInterpolation/AMIPatchToPatchInterpolation.C
+$(AMI)/AMIInterpolation/AMIInterpolation.C
+$(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
 $(AMI)/GAMG/interfaceFields/cyclicAMIGAMGInterfaceField/cyclicAMIGAMGInterfaceField.C
@@ -271,6 +276,7 @@ AMICycPatches=$(AMI)/patches/cyclicAMI
 $(AMICycPatches)/cyclicAMILduInterfaceField/cyclicAMILduInterface.C
 $(AMICycPatches)/cyclicAMILduInterfaceField/cyclicAMILduInterfaceField.C
 $(AMICycPatches)/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
+$(AMICycPatches)/cyclicAMIPolyPatch/cyclicAMIPolyPatchTopologyChange.C
 $(AMICycPatches)/cyclicAMIPointPatch/cyclicAMIPointPatch.C
 $(AMICycPatches)/cyclicAMIPointPatchField/cyclicAMIPointPatchFields.C
 
diff --git a/src/meshTools/Make/options b/src/meshTools/Make/options
index 9ee5884e5908ccdb6b1ba3acf323b8c524ec996a..5b4f9ec84181988556c25393c12e10eb3e3b4531 100644
--- a/src/meshTools/Make/options
+++ b/src/meshTools/Make/options
@@ -1,6 +1,7 @@
 EXE_INC = \
     -I$(LIB_SRC)/fileFormats/lnInclude \
-    -I$(LIB_SRC)/surfMesh/lnInclude
+    -I$(LIB_SRC)/surfMesh/lnInclude \
+    -I$(LIB_SRC)/dynamicMesh/lnInclude
 
 LIB_LIBS = \
     -lfileFormats \
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
index 8dac4e801c5cc156b8fb66c6617ed3241dd70da3..cef6859a6f9d70181524113f896a5bb342e86f47 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
@@ -45,6 +45,7 @@ License
 #include "syncTools.H"
 #include "treeDataCell.H"
 #include "DynamicField.H"
+#include "faceAreaWeightAMI.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -791,14 +792,14 @@ const
 
 void Foam::mappedPatchBase::calcAMI() const
 {
-    if (AMIPtr_.valid())
+    if (AMIPtr_->upToDate())
     {
-        FatalErrorInFunction
-            << "AMI already calculated" << exit(FatalError);
-    }
-
-    AMIPtr_.clear();
+        DebugInFunction
+            << "AMI already up-to-date"
+            << endl;
 
+        return;
+    }
 
     const polyPatch& nbr = samplePolyPatch();
 
@@ -829,29 +830,13 @@ void Foam::mappedPatchBase::calcAMI() const
     }
 
     // Construct/apply AMI interpolation to determine addressing and weights
-    AMIPtr_.reset
-    (
-        new AMIPatchToPatchInterpolation
-        (
-            patch_,
-            nbrPatch0,
-            surfPtr(),
-            faceAreaIntersect::tmMesh,
-            true,
-            AMIPatchToPatchInterpolation::imFaceAreaWeight,
-            -1,
-            AMIReverse_
-        )
-    );
+    AMIPtr_->calculate(patch_, nbrPatch0, surfPtr());
 }
 
 
 // * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * * * * //
 
-Foam::mappedPatchBase::mappedPatchBase
-(
-    const polyPatch& pp
-)
+Foam::mappedPatchBase::mappedPatchBase(const polyPatch& pp)
 :
     patch_(pp),
     sampleRegion_(patch_.boundaryMesh().mesh().name()),
@@ -864,8 +849,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(false),
+    AMIPtr_(new faceAreaWeightAMI(true, AMIReverse_)),
     surfPtr_(nullptr),
     surfDict_(fileName("surface"))
 {}
@@ -891,8 +876,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(false),
+    AMIPtr_(new faceAreaWeightAMI(true, AMIReverse_)),
     surfPtr_(nullptr),
     surfDict_(fileName("surface"))
 {}
@@ -918,8 +903,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(false),
+    AMIPtr_(new faceAreaWeightAMI(true, AMIReverse_)),
     surfPtr_(nullptr),
     surfDict_(fileName("surface"))
 {}
@@ -945,8 +930,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(distance),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(false),
+    AMIPtr_(new faceAreaWeightAMI(true, AMIReverse_)),
     surfPtr_(nullptr),
     surfDict_(fileName("surface"))
 {}
@@ -969,8 +954,16 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(dict.getOrDefault("flipNormals", false)),
+    AMIPtr_
+    (
+        AMIInterpolation::New
+        (
+            dict.getOrDefault("AMIMethod", faceAreaWeightAMI::typeName),
+            dict,
+            AMIReverse_
+        )
+    ),
     surfPtr_(nullptr),
     surfDict_(dict.subOrEmptyDict("surface"))
 {
@@ -1044,8 +1037,16 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(dict.getOrDefault("flipNormals", false)),
+    AMIPtr_
+    (
+        AMIInterpolation::New
+        (
+            dict.getOrDefault("AMIMethod", faceAreaWeightAMI::typeName),
+            dict,
+            AMIReverse_
+        )
+    ),
     surfPtr_(nullptr),
     surfDict_(dict.subOrEmptyDict("surface"))
 {
@@ -1089,8 +1090,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(mpb.distance_),
     sameRegion_(mpb.sameRegion_),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(mpb.AMIReverse_),
+    AMIPtr_(mpb.AMIPtr_->clone()),
     surfPtr_(nullptr),
     surfDict_(mpb.surfDict_)
 {}
@@ -1119,8 +1120,8 @@ Foam::mappedPatchBase::mappedPatchBase
     distance_(mpb.distance_),
     sameRegion_(mpb.sameRegion_),
     mapPtr_(nullptr),
-    AMIPtr_(nullptr),
     AMIReverse_(mpb.AMIReverse_),
+    AMIPtr_(mpb.AMIPtr_->clone()),
     surfPtr_(nullptr),
     surfDict_(mpb.surfDict_)
 {}
@@ -1137,8 +1138,8 @@ Foam::mappedPatchBase::~mappedPatchBase()
 void Foam::mappedPatchBase::clearOut()
 {
     mapPtr_.clear();
-    AMIPtr_.clear();
     surfPtr_.clear();
+    AMIPtr_->upToDate() = false;
 }
 
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
index ac4f6c7fb514960d71a0d9d195482878a8660445..d6b76023dba3cc5da7cf434398d2819e71a740f2 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
@@ -231,12 +231,12 @@ protected:
 
         // AMI interpolator (only for NEARESTPATCHFACEAMI)
 
-            //- Pointer to AMI interpolator
-            mutable autoPtr<AMIPatchToPatchInterpolation> AMIPtr_;
-
             //- Flag to indicate that slave patch should be reversed for AMI
             const bool AMIReverse_;
 
+            //- Pointer to AMI interpolator
+            mutable autoPtr<AMIPatchToPatchInterpolation> AMIPtr_;
+
             //- Pointer to projection surface employed by AMI interpolator
             mutable autoPtr<searchableSurface> surfPtr_;
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
index 93e8c7c7de87a4aeb12a7c84ac88a60df3ad6edb..dc507b64e6c4c5e8cb411eb7958e89a368c8aff5 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
@@ -172,13 +172,10 @@ inline const Foam::AMIPatchToPatchInterpolation& Foam::mappedPatchBase::AMI
 
     if (topoChange || forceUpdate)
     {
-        AMIPtr_.clear();
+        AMIPtr_->upToDate() = false;
     }
 
-    if (AMIPtr_.empty())
-    {
-        calcAMI();
-    }
+    calcAMI();
 
     return *AMIPtr_;
 }
diff --git a/src/meshTools/polyTopoChange/polyTopoChange.H b/src/meshTools/polyTopoChange/polyTopoChange.H
index d861a2d7da259af1f9cfc462ecf6663ca794c0ef..613f6d398f3bafb16e051d0ca40b2d44c46da63d 100644
--- a/src/meshTools/polyTopoChange/polyTopoChange.H
+++ b/src/meshTools/polyTopoChange/polyTopoChange.H
@@ -94,7 +94,7 @@ class IOobject;
 template<class T, class Container> class CompactListList;
 
 /*---------------------------------------------------------------------------*\
-                           Class polyTopoChange Declaration
+                       Class polyTopoChange Declaration
 \*---------------------------------------------------------------------------*/
 
 class polyTopoChange
@@ -121,7 +121,7 @@ class polyTopoChange
             DynamicList<label> pointMap_;
 
             //- For all original and added points contains new point label.
-            //  (used to map return value of addPoint to new mesh point)
+            //- (used to map return value of addPoint to new mesh point)
             DynamicList<label> reversePointMap_;
 
             //- Zone of point
@@ -324,10 +324,10 @@ class polyTopoChange
         );
 
         //- Remove all unused/removed points/faces/cells and update
-        //  face ordering (always), cell ordering (bandcompression,
-        //  orderCells=true),
-        //  point ordering (sorted into internal and boundary points,
-        //  orderPoints=true)
+        //- face ordering (always), cell ordering (bandcompression,
+        //- orderCells=true),
+        //- point ordering (sorted into internal and boundary points,
+        //- orderPoints=true)
         void compact
         (
             const bool orderCells,
@@ -433,7 +433,7 @@ public:
     // Constructors
 
         //- Construct without mesh. Either specify nPatches or use
-        //  setNumPatches before trying to make a mesh (makeMesh, changeMesh)
+        //- setNumPatches before trying to make a mesh (makeMesh, changeMesh)
         polyTopoChange(const label nPatches, const bool strict = true);
 
         //- Construct from mesh. Adds all points/face/cells from mesh.
@@ -471,15 +471,15 @@ public:
             }
 
             //- Is point removed?
-            //  Considered removed if point is GREAT.
+            //- Considered removed if point is GREAT.
             inline bool pointRemoved(const label pointi) const;
 
             //- Is face removed?
-            //  Considered removed if face is empty
+            //- Considered removed if face is empty
             inline bool faceRemoved(const label facei) const;
 
             //- Is cell removed?
-            //  Considered removed if the cellMap is -2
+            //- Considered removed if the cellMap is -2
             inline bool cellRemoved(const label celli) const;
 
 
@@ -489,7 +489,7 @@ public:
             void clear();
 
             //- Add all points/faces/cells of mesh. Additional offset for patch
-            //  or zone ids.
+            //- or zone ids.
             void addMesh
             (
                 const polyMesh& mesh,
@@ -500,7 +500,7 @@ public:
             );
 
             //- Explicitly pre-size the dynamic storage for expected mesh
-            //  size for if construct-without-mesh
+            //- size for if construct-without-mesh
             void setCapacity
             (
                 const label nPoints,
@@ -590,7 +590,7 @@ public:
             void removeCell(const label celli, const label mergeCelli);
 
             //- Explicitly set the number of patches if construct-without-mesh
-            //  used.
+            //- used.
             inline void setNumPatches(const label nPatches);
 
         // Other
@@ -628,7 +628,6 @@ public:
                 const bool orderCells = false,
                 const bool orderPoints = false
             );
-
 };
 
 
diff --git a/src/regionModels/regionModel/regionModel/regionModel.C b/src/regionModels/regionModel/regionModel/regionModel.C
index 0885dd53f6fa50c9dfaf5ebd1cc4ef4fca664b73..d92a4521725ea60801fe915138df9f7cbe0c26a6 100644
--- a/src/regionModels/regionModel/regionModel/regionModel.C
+++ b/src/regionModels/regionModel/regionModel/regionModel.C
@@ -31,6 +31,7 @@ License
 #include "Time.H"
 #include "mappedWallPolyPatch.H"
 #include "zeroGradientFvPatchFields.H"
+#include "faceAreaWeightAMI.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -209,18 +210,16 @@ Foam::regionModels::regionModel::interRegionAMI
             interRegionAMI_[nbrRegionID].set
             (
                 regionPatchi,
-                new AMIPatchToPatchInterpolation
+                AMIInterpolation::New
                 (
-                    p,
-                    nbrP,
-                    faceAreaIntersect::tmMesh,
-                    true,
-                    AMIPatchToPatchInterpolation::imFaceAreaWeight,
-                    -1,
+                    faceAreaWeightAMI::typeName,
+                    true, // requireMatch
                     flip
                 )
             );
 
+            interRegionAMI_[nbrRegionID][regionPatchi].calculate(p, nbrP);
+
             UPstream::msgType() = oldTag;
         }
 
@@ -252,18 +251,16 @@ Foam::regionModels::regionModel::interRegionAMI
         interRegionAMI_[nbrRegionID].set
         (
             regionPatchi,
-            new AMIPatchToPatchInterpolation
+            AMIInterpolation::New
             (
-                p,
-                nbrP,
-                faceAreaIntersect::tmMesh,
-                true,
-                AMIPatchToPatchInterpolation::imFaceAreaWeight,
-                -1,
-                flip
+                faceAreaWeightAMI::typeName,
+                true, // requireMatch
+                flip // reverse
             )
         );
 
+        interRegionAMI_[nbrRegionID][regionPatchi].calculate(p, nbrP);
+
         UPstream::msgType() = oldTag;
 
         return interRegionAMI_[nbrRegionID][regionPatchi];
diff --git a/src/sampling/meshToMesh/meshToMesh.C b/src/sampling/meshToMesh/meshToMesh.C
index 591f67fc5e18db7baa5d8b47bf4d9dbc64d8d034..90d7b8b4d5279f8e197fc8a554e693992957a947 100644
--- a/src/sampling/meshToMesh/meshToMesh.C
+++ b/src/sampling/meshToMesh/meshToMesh.C
@@ -30,6 +30,8 @@ License
 #include "Time.H"
 #include "globalIndex.H"
 #include "meshToMeshMethod.H"
+#include "nearestFaceAMI.H"
+#include "faceAreaWeightAMI.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -633,25 +635,27 @@ void Foam::meshToMesh::calculate(const word& methodName, const bool normalise)
 }
 
 
-Foam::AMIPatchToPatchInterpolation::interpolationMethod
-Foam::meshToMesh::interpolationMethodAMI(const interpolationMethod method)
+Foam::word Foam::meshToMesh::interpolationMethodAMI
+(
+    const interpolationMethod method
+)
 {
     switch (method)
     {
         case interpolationMethod::imDirect:
         {
-            return AMIPatchToPatchInterpolation::imDirect;
+            return nearestFaceAMI::typeName;
             break;
         }
         case interpolationMethod::imMapNearest:
         {
-            return AMIPatchToPatchInterpolation::imMapNearest;
+            return nearestFaceAMI::typeName;
             break;
         }
         case interpolationMethod::imCellVolumeWeight:
         case interpolationMethod::imCorrectedCellVolumeWeight:
         {
-            return AMIPatchToPatchInterpolation::imFaceAreaWeight;
+            return faceAreaWeightAMI::typeName;
             break;
         }
         default:
@@ -662,7 +666,7 @@ Foam::meshToMesh::interpolationMethodAMI(const interpolationMethod method)
         }
     }
 
-    return AMIPatchToPatchInterpolation::imDirect;
+    return nearestFaceAMI::typeName;
 }
 
 
@@ -695,18 +699,17 @@ void Foam::meshToMesh::calculatePatchAMIs(const word& AMIMethodName)
         patchAMIs_.set
         (
             i,
-            new AMIPatchToPatchInterpolation
+            AMIInterpolation::New
             (
-                srcPP,
-                tgtPP,
-                faceAreaIntersect::tmMesh,
-                false,
                 AMIMethodName,
-                -1,
-                true // flip target patch since patch normals are aligned
+                false, // requireMatch
+                true,  // flip target patch since patch normals are aligned
+                -1     // low weight correction
             )
         );
 
+        patchAMIs_[i].calculate(srcPP, tgtPP);
+
         Info<< decrIndent;
     }
 }
@@ -862,10 +865,7 @@ Foam::meshToMesh::meshToMesh
     constructNoCuttingPatches
     (
         interpolationMethodNames_[method],
-        AMIPatchToPatchInterpolation::interpolationMethodNames_
-        [
-            interpolationMethodAMI(method)
-        ],
+        interpolationMethodAMI(method),
         interpAllPatches
     );
 }
@@ -933,10 +933,7 @@ Foam::meshToMesh::meshToMesh
     constructFromCuttingPatches
     (
         interpolationMethodNames_[method],
-        AMIPatchToPatchInterpolation::interpolationMethodNames_
-        [
-            interpolationMethodAMI(method)
-        ],
+        interpolationMethodAMI(method),
         patchMap,
         cuttingPatches,
         normalise
diff --git a/src/sampling/meshToMesh/meshToMesh.H b/src/sampling/meshToMesh/meshToMesh.H
index f3c64fa647f9065dcb8e87bb5641113617abb871..877259617de75a1ab10d4395109b0aee5f65aaf7 100644
--- a/src/sampling/meshToMesh/meshToMesh.H
+++ b/src/sampling/meshToMesh/meshToMesh.H
@@ -394,8 +394,7 @@ public:
             inline scalar V() const;
 
             //- Conversion between mesh and patch interpolation methods
-            static AMIPatchToPatchInterpolation::interpolationMethod
-            interpolationMethodAMI
+            static word interpolationMethodAMI
             (
                 const interpolationMethod method
             );