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/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..5de3713a53f959125d81181f24c78d6a7596c78d
--- /dev/null
+++ b/src/dynamicFvMesh/dynamicMotionSolverFvMeshAMI/dynamicMotionSolverFvMeshAMI.C
@@ -0,0 +1,216 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 "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();
+    }
+
+    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/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..23a12796ad1d9a2c682e398edeb62f53fae10fda 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,13 @@ 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<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 +70,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,7 +117,6 @@ Foam::tmp<Foam::vectorField> Foam::cyclicACMIFvPatch::delta() const
 
         vectorField nbrPatchD(interpolate(nbrPatch.coupledFvPatch::delta()));
 
-
         tmp<vectorField> tpdv(new vectorField(patchD.size()));
         vectorField& pdv = tpdv.ref();
 
@@ -201,4 +170,94 @@ 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()];
+
+    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 = cyclicACMIPolyPatch_.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(cyclicACMIPolyPatch_.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()];
+
+    forAll(nbrPhip, facei)
+    {
+        if (newTgtAddr[facei].empty())
+        {
+            nbrPhip[facei] = 0.0;
+        }
+        else
+        {
+            const face& fAMI = nbrACMI.patch().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(nbrACMI.patch().localPoints());
+            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..a9c03e340c24d43d29e28aea662dc733344e10f2 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.C
@@ -29,6 +29,7 @@ License
 #include "addToRunTimeSelectionTable.H"
 #include "fvMesh.H"
 #include "transform.H"
+#include "surfaceFields.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -82,8 +83,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);
         }
@@ -174,4 +176,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..4245dd4be83c232b14262ca5840853d8c5b3ac28 100644
--- a/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H
+++ b/src/finiteVolume/fvMesh/fvPatches/constraint/cyclicAMI/cyclicAMIFvPatch.H
@@ -68,6 +68,9 @@ protected:
         //- Make patch weighting factors
         void makeWeights(scalarField&) const;
 
+        //- Correct patches after moving points
+        virtual void movePoints();
+
 
 public:
 
diff --git a/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C b/src/finiteVolume/interpolation/surfaceInterpolation/surfaceInterpolation/surfaceInterpolation.C
index 2b31e5c66bded5a82ef8aef4f05d36121a1e54ff..c716e1456a060ee1a870bf5af17121018cd7a778 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.
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
index b7c297829447d505c46765b0afa9119056d508fa..090a3812a2f6105a93362af93ecb9a0bcf7ebc97 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.C
@@ -33,6 +33,8 @@ License
 #include "flipOp.H"
 #include "profiling.H"
 
+#define DEBUGAMI(msg){Pout<< "[" << __FILE__ << ":" << __LINE__ << "]: " << #msg << "=" << msg << endl;}
+
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -49,10 +51,12 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolationMethodNames_
     { 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>
@@ -264,7 +268,6 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::agglomerate
         }
     }
 
-
     // Agglomerate weights and indices
     if (targetMapPtr.valid())
     {
@@ -298,7 +301,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());
 
@@ -616,10 +619,12 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcCentroids_(),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
+    tgtCentroids_(),
     triMode_(triMode),
     srcMapPtr_(nullptr),
     tgtMapPtr_(nullptr)
@@ -649,10 +654,12 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcCentroids_(),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
+    tgtCentroids_(),
     triMode_(triMode),
     srcMapPtr_(nullptr),
     tgtMapPtr_(nullptr)
@@ -683,10 +690,12 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcCentroids_(),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
+    tgtCentroids_(),
     triMode_(triMode),
     srcMapPtr_(nullptr),
     tgtMapPtr_(nullptr)
@@ -717,10 +726,12 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
     srcAddress_(),
     srcWeights_(),
     srcWeightsSum_(),
+    srcCentroids_(),
     tgtMagSf_(),
     tgtAddress_(),
     tgtWeights_(),
     tgtWeightsSum_(),
+    tgtCentroids_(),
     triMode_(triMode),
     srcMapPtr_(nullptr),
     tgtMapPtr_(nullptr)
@@ -834,13 +845,6 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::AMIInterpolation
 }
 
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::AMIInterpolation<SourcePatch, TargetPatch>::~AMIInterpolation()
-{}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -932,6 +936,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
         (
             srcAddress_,
             srcWeights_,
+            srcCentroids_,
             tgtAddress_,
             tgtWeights_
         );
@@ -1009,9 +1014,16 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
         AMIPtr->normaliseWeights(true, *this);
 
         // Cache maps and reset addresses
-        List<Map<label>> cMap;
-        srcMapPtr_.reset(new mapDistribute(globalSrcFaces, tgtAddress_, cMap));
-        tgtMapPtr_.reset(new mapDistribute(globalTgtFaces, srcAddress_, cMap));
+        List<Map<label>> cMapSrc;
+        srcMapPtr_.reset
+        (
+            new mapDistribute(globalSrcFaces, tgtAddress_, cMapSrc)
+        );
+        List<Map<label>> cMapTgt;
+        tgtMapPtr_.reset
+        (
+            new mapDistribute(globalTgtFaces, srcAddress_, cMapTgt)
+        );
     }
     else
     {
@@ -1033,6 +1045,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
         (
             srcAddress_,
             srcWeights_,
+            srcCentroids_,
             tgtAddress_,
             tgtWeights_
         );
@@ -1056,6 +1069,42 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
 }
 
 
+template<class SourcePatch, class TargetPatch>
+void Foam::AMIInterpolation<SourcePatch, TargetPatch>::update
+(
+    autoPtr<mapDistribute>&& srcToTgtMap,
+    autoPtr<mapDistribute>&& tgtToSrcMap,
+    labelListList&& srcAddress,
+    scalarListList&& srcWeights,
+    labelListList&& tgtAddress,
+    scalarListList&& tgtWeights
+)
+{
+    DebugInFunction<< endl;
+
+    srcAddress_.transfer(srcAddress);
+    srcWeights_.transfer(srcWeights);
+    tgtAddress_.transfer(tgtAddress);
+    tgtWeights_.transfer(tgtWeights);
+
+    // Reset the sums of the weights
+    srcWeightsSum_.setSize(srcWeights_.size());
+    forAll(srcWeights_, facei)
+    {
+        srcWeightsSum_[facei] = sum(srcWeights_[facei]);
+    }
+
+    tgtWeightsSum_.setSize(tgtWeights_.size());
+    forAll(tgtWeights_, facei)
+    {
+        tgtWeightsSum_[facei] = sum(tgtWeights_[facei]);
+    }
+
+    srcMapPtr_ = srcToTgtMap;
+    tgtMapPtr_ = tgtToSrcMap;
+}
+
+
 template<class SourcePatch, class TargetPatch>
 void Foam::AMIInterpolation<SourcePatch, TargetPatch>::append
 (
@@ -1397,7 +1446,7 @@ void Foam::AMIInterpolation<SourcePatch, TargetPatch>::interpolateToSource
             FatalErrorInFunction
                 << "Employing default values when sum of weights falls below "
                 << lowWeightCorrection_
-                << " but supplied default field size is not equal to target "
+                << " but supplied default field size is not equal to source "
                 << "patch size" << nl
                 << "    default values = " << defaultValues.size() << nl
                 << "    source patch   = " << srcAddress_.size() << nl
@@ -1693,6 +1742,73 @@ const
 }
 
 
+template<class SourcePatch, class TargetPatch>
+bool Foam::AMIInterpolation<SourcePatch, TargetPatch>::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;
+}
+
+
 template<class SourcePatch, class TargetPatch>
 void Foam::AMIInterpolation<SourcePatch, TargetPatch>::writeFaceConnectivity
 (
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
index fcf418ff6bf2b67087a6c2cfbcfed0ca3c3fd8fb..2e57a3c7dd3da042b11e146eacdacbe8ae2f7f9d 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolation.H
@@ -61,6 +61,7 @@ SourceFiles
 #include "globalIndex.H"
 #include "ops.H"
 #include "Enum.H"
+#include "pointList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -146,6 +147,9 @@ private:
             //- Sum of weights of target faces per source face
             scalarField srcWeightsSum_;
 
+            //- Centroid of target faces per source face
+            pointListList srcCentroids_;
+
 
         // Target patch
 
@@ -161,6 +165,9 @@ 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_;
@@ -347,7 +354,7 @@ public:
 
 
     //- Destructor
-    ~AMIInterpolation();
+    ~AMIInterpolation() = default;
 
     // Typedef to SourcePatch type this AMIInterpolation is instantiated on
     typedef SourcePatch sourcePatchType;
@@ -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
@@ -448,6 +461,22 @@ public:
                 const TargetPatch& tgtPatch
             );
 
+            void update
+            (
+                autoPtr<mapDistribute>&& srcToTgtMap,
+                autoPtr<mapDistribute>&& tgtToSrcMap,
+                labelListList&& srcAddress,
+                scalarListList&& srcWeights,
+                labelListList&& tgtAddress,
+                scalarListList&& tgtWeights
+            );
+
+            void setAreas(const scalarList& srcMagSf, const scalarList& tgtMagSf)
+            {
+                srcMagSf_ = srcMagSf;
+                tgtMagSf_ = tgtMagSf;
+            }
+
             //- Append additional addressing and weights
             void append
             (
@@ -582,6 +611,10 @@ 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
             (
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
index 9cd5e51ff1dce34480e328ddd6cd7532e31c5fff..027a504521d7bf285941b0e4fe8a414ac7f9a72b 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIInterpolationI.H
@@ -115,6 +115,22 @@ Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcWeightsSum()
 }
 
 
+template<class SourcePatch, class TargetPatch>
+inline const Foam::pointListList&
+Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcCentroids() const
+{
+    return srcCentroids_;
+}
+
+
+template<class SourcePatch, class TargetPatch>
+inline Foam::pointListList&
+Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcCentroids()
+{
+    return srcCentroids_;
+}
+
+
 template<class SourcePatch, class TargetPatch>
 inline const Foam::mapDistribute&
 Foam::AMIInterpolation<SourcePatch, TargetPatch>::srcMap() const
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C
index 3b1f06d405cf74a363d3ae5066bb522fe4500b7b..5a8f11ec74b6d925f1fa0ec55ec074b517a27ceb 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.C
@@ -31,6 +31,8 @@ License
 #include "mapDistribute.H"
 #include "unitConversion.H"
 
+#include "findNearestMaskedOp.H"
+
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -231,17 +233,25 @@ void Foam::AMIMethod<SourcePatch, TargetPatch>::resetTree()
 template<class SourcePatch, class TargetPatch>
 Foam::label Foam::AMIMethod<SourcePatch, TargetPatch>::findTargetFace
 (
-    const label srcFacei
+    const label srcFacei,
+    const UList<label>& excludeFaces,
+    const label srcFacePti
 ) const
 {
     label targetFacei = -1;
 
     const pointField& srcPts = srcPatch_.points();
     const face& srcFace = srcPatch_[srcFacei];
-    const point srcPt = srcFace.centre(srcPts);
-    const scalar srcFaceArea = srcMagSf_[srcFacei];
 
-    pointIndexHit sample = treePtr_->findNearest(srcPt, 10.0*srcFaceArea);
+    findNearestMaskedOp<TargetPatch> fnOp(*treePtr_, excludeFaces);
+
+    boundBox bb(srcPts, srcFace, false);
+
+    const point srcPt =
+        srcFacePti == -1 ? bb.centre() : srcPts[srcFace[srcFacePti]];
+
+    const pointIndexHit sample =
+        treePtr_->findNearest(srcPt, magSqr(bb.max() - bb.centre()), fnOp);
 
     if (sample.hit())
     {
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H
index bf569aad560a8e392ed97e9277573397b55bead0..e019c384cea9d5997738eeaf75ce2b69046d8199 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/AMIMethod/AMIMethod.H
@@ -45,6 +45,7 @@ SourceFiles
 #include "treeDataPrimitivePatch.H"
 #include "treeBoundBoxList.H"
 #include "runTimeSelectionTables.H"
+#include "pointList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -101,7 +102,7 @@ protected:
         List<scalar> tgtMagSf_;
 
         //- Labels of faces that are not overlapped by any target faces
-        //- (should be empty for correct functioning)
+        //- (should be empty for correct functioning for fully covered AMIs)
         labelList srcNonOverlap_;
 
         //- Octree used to find face seeds
@@ -145,8 +146,12 @@ protected:
             //- 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;
+            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
@@ -251,6 +256,7 @@ public:
             (
                 labelListList& srcAddress,
                 scalarListList& srcWeights,
+                pointListList& srcCentroids,
                 labelListList& tgtAddress,
                 scalarListList& tgtWeights,
                 label srcFacei = -1,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C
index 894b33fe0f63d5f0292d48e3a46495ca7ced0d30..14989fdfbed58aa035df160d765e266136da3f37 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.C
@@ -205,13 +205,6 @@ Foam::directAMI<SourcePatch, TargetPatch>::directAMI
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::directAMI<SourcePatch, TargetPatch>::~directAMI()
-{}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -219,6 +212,7 @@ void Foam::directAMI<SourcePatch, TargetPatch>::calculate
 (
     labelListList& srcAddress,
     scalarListList& srcWeights,
+    pointListList& srcCentroids,
     labelListList& tgtAddress,
     scalarListList& tgtWeights,
     label srcFacei,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H
index a451cad7da2c5e5c3bb4a15ea42b31eab12245be..a07aa4f617b08122b838da3f1722e1112ef9417f 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/directAMI/directAMI.H
@@ -118,7 +118,7 @@ public:
 
 
     //- Destructor
-    virtual ~directAMI();
+    virtual ~directAMI() = default;
 
 
     // Member Functions
@@ -130,6 +130,7 @@ public:
             (
                 labelListList& srcAddress,
                 scalarListList& srcWeights,
+                pointListList& srcCentroids,
                 labelListList& tgtAddress,
                 scalarListList& tgtWeights,
                 label srcFacei = -1,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C
index 3cbe9cd65f8cedcbb26730f9722640790cef792d..f770dfbb58e99f7fef51ee95ab427f15d29aaf7b 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.C
@@ -28,6 +28,7 @@ License
 
 #include "faceAreaWeightAMI.H"
 #include "profiling.H"
+#include "OBJstream.H"
 
 // * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
 
@@ -36,6 +37,7 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
 (
     List<DynamicList<label>>& srcAddr,
     List<DynamicList<scalar>>& srcWght,
+    List<DynamicList<point>>& srcCtr,
     List<DynamicList<label>>& tgtAddr,
     List<DynamicList<scalar>>& tgtWght,
     label srcFacei,
@@ -60,7 +62,7 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
     seedFaces[srcFacei] = tgtFacei;
 
     // list to keep track of whether src face can be mapped
-    boolList mapFlag(nFacesRemaining, true);
+    bitSet mapFlag(nFacesRemaining, true);
 
     // reset starting seed
     label startSeedi = 0;
@@ -68,6 +70,9 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
     DynamicList<label> nonOverlapFaces;
     do
     {
+        nbrFaces.clear();
+        visitedFaces.clear();
+
         // Do advancing front starting from srcFacei,tgtFacei
         bool faceProcessed = processSourceFace
         (
@@ -79,11 +84,12 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcAddressing
 
             srcAddr,
             srcWght,
+            srcCtr,
             tgtAddr,
             tgtWght
         );
 
-        mapFlag[srcFacei] = false;
+        mapFlag.unset(srcFacei);
 
         nFacesRemaining--;
 
@@ -122,9 +128,10 @@ bool Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::processSourceFace
     // list of faces currently visited for srcFacei to avoid multiple hits
     DynamicList<label>& visitedFaces,
 
-    // temporary storage for addressing and weights
+    // 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
 )
@@ -136,9 +143,6 @@ bool Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::processSourceFace
         return false;
     }
 
-    nbrFaces.clear();
-    visitedFaces.clear();
-
     // append initial target face and neighbours
     nbrFaces.append(tgtStartFacei);
     this->appendNbrFaces
@@ -158,16 +162,24 @@ bool Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::processSourceFace
         // process new target face
         label tgtFacei = nbrFaces.remove();
         visitedFaces.append(tgtFacei);
-        scalar area = interArea(srcFacei, tgtFacei);
+
+        scalar interArea = 0;
+        vector interCentroid(Zero);
+        calcInterArea(srcFacei, tgtFacei, interArea, interCentroid);
 
         // store when intersection fractional area > tolerance
-        if (area/this->srcMagSf_[srcFacei] > faceAreaIntersect::tolerance())
+        if
+        (
+            interArea/this->srcMagSf_[srcFacei]
+          > faceAreaIntersect::tolerance()
+        )
         {
             srcAddr[srcFacei].append(tgtFacei);
-            srcWght[srcFacei].append(area);
+            srcWght[srcFacei].append(interArea);
+            srcCtr[srcFacei].append(interCentroid);
 
             tgtAddr[tgtFacei].append(srcFacei);
-            tgtWght[tgtFacei].append(area);
+            tgtWght[tgtFacei].append(interArea);
 
             this->appendNbrFaces
             (
@@ -199,10 +211,10 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
     label& startSeedi,
     label& srcFacei,
     label& tgtFacei,
-    const boolList& mapFlag,
+    const bitSet& mapFlag,
     labelList& seedFaces,
     const DynamicList<label>& visitedFaces,
-    bool errorOnNotFound
+    const bool errorOnNotFound
 ) const
 {
     addProfiling(ami, "faceAreaWeightAMI::setNextFaces");
@@ -216,15 +228,14 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
     bool valuesSet = false;
     for (label faceS: srcNbrFaces)
     {
-        if (mapFlag[faceS] && seedFaces[faceS] == -1)
+        if (mapFlag.test(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
+                // Store when intersection fractional area > threshold
                 if (overlaps(faceS, faceT, threshold))
                 {
                     seedFaces[faceS] = faceT;
@@ -248,25 +259,31 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
     else
     {
         // try to use existing seed
+        label facei = startSeedi;
+        if (!mapFlag.test(startSeedi))
+        {
+            facei = mapFlag.find_next(facei);
+        }
+        const label startSeedi0 = facei;
+
         bool foundNextSeed = false;
-        for (label facei = startSeedi; facei < mapFlag.size(); ++facei)
+        while (facei != -1)
         {
-            if (mapFlag[facei])
+            if (!foundNextSeed)
             {
-                if (!foundNextSeed)
-                {
-                    startSeedi = facei;
-                    foundNextSeed = true;
-                }
+                startSeedi = facei;
+                foundNextSeed = true;
+            }
 
-                if (seedFaces[facei] != -1)
-                {
-                    srcFacei = facei;
-                    tgtFacei = seedFaces[facei];
+            if (seedFaces[facei] != -1)
+            {
+                srcFacei = facei;
+                tgtFacei = seedFaces[facei];
 
-                    return;
-                }
+                return;
             }
+
+            facei = mapFlag.find_next(facei);
         }
 
         // perform new search to find match
@@ -276,25 +293,18 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
                 << "target face" << endl;
         }
 
-        foundNextSeed = false;
-        for (label facei = startSeedi; facei < mapFlag.size(); ++facei)
+        facei = startSeedi0;
+        while (facei != -1)
         {
-            if (mapFlag[facei])
-            {
-                if (!foundNextSeed)
-                {
-                    startSeedi = facei + 1;
-                    foundNextSeed = true;
-                }
-
-                srcFacei = facei;
-                tgtFacei = this->findTargetFace(srcFacei);
+            srcFacei = facei;
+            tgtFacei = this->findTargetFace(srcFacei, visitedFaces);
 
-                if (tgtFacei >= 0)
-                {
-                    return;
-                }
+            if (tgtFacei >= 0)
+            {
+                return;
             }
+
+            facei = mapFlag.find_next(facei);
         }
 
         if (errorOnNotFound)
@@ -307,16 +317,16 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
 
 
 template<class SourcePatch, class TargetPatch>
-Foam::scalar Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::interArea
+void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calcInterArea
 (
     const label srcFacei,
-    const label tgtFacei
+    const label tgtFacei,
+    scalar& area,
+    vector& centroid
 ) const
 {
     addProfiling(ami, "faceAreaWeightAMI::interArea");
 
-    scalar area = 0;
-
     const pointField& srcPoints = this->srcPatch_.points();
     const pointField& tgtPoints = this->tgtPatch_.points();
 
@@ -331,7 +341,7 @@ Foam::scalar Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::interArea
      || (this->tgtMagSf_[tgtFacei] < ROOTVSMALL)
     )
     {
-        return area;
+        return;
     }
 
     // create intersection object
@@ -359,7 +369,7 @@ Foam::scalar Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::interArea
 
     if (magN > ROOTVSMALL)
     {
-        area = inter.calc(src, tgt, n/magN);
+        inter.calc(src, tgt, n/magN, area, centroid);
     }
     else
     {
@@ -371,13 +381,24 @@ Foam::scalar Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::interArea
             << endl;
     }
 
+    if
+    (
+        AMIInterpolation<SourcePatch, TargetPatch>::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))
     {
         this->writeIntersectionOBJ(area, src, tgt, srcPoints, tgtPoints);
     }
-
-    return area;
 }
 
 
@@ -452,96 +473,71 @@ 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");
 
-    // Collect all src faces with a low weight
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    // Note: exclude faces in srcNonOverlap_ for ACMI?
 
-    labelHashSet lowWeightFaces(100);
-    forAll(srcWght, srcFacei)
-    {
-        scalar s = sum(srcWght[srcFacei]);
-        scalar t = s/this->srcMagSf_[srcFacei];
+    label nBelowMinWeight = 0;
+    const scalar minWeight = 0.95;
 
-        if (t < 0.5)
-        {
-            lowWeightFaces.insert(srcFacei);
-        }
-    }
+    // list of tgt face neighbour faces
+    DynamicList<label> nbrFaces(10);
 
-    if (debug)
-    {
-        Pout<< "faceAreaWeightAMI: restarting search on "
-            << lowWeightFaces.size() << " faces since sum of weights < 0.5"
-            << endl;
-    }
+    // list of faces currently visited for srcFacei to avoid multiple hits
+    DynamicList<label> visitedFaces(10);
 
-    if (lowWeightFaces.size() > 0)
+    forAll(srcWght, srcFacei)
     {
-        // Erase all the lowWeight source faces from the target
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        const scalar s = sum(srcWght[srcFacei]);
+        const scalar t = s/this->srcMagSf_[srcFacei];
 
-        DynamicList<label> okSrcFaces(10);
-        DynamicList<scalar> okSrcWeights(10);
-        forAll(tgtAddr, tgtFacei)
+        if (t < minWeight)
         {
-            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);
-            }
-        }
-
-
+            ++nBelowMinWeight;
 
-        // Restart search from best hit
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+            const face& f = this->srcPatch_[srcFacei];
 
-        // 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)
+            forAll(f, fpi)
             {
-                //bool faceProcessed =
-                processSourceFace
-                (
-                    srcFacei,
-                    tgtFacei,
-
-                    nbrFaces,
-                    visitedFaces,
+                const label tgtFacei =
+                    this->findTargetFace(srcFacei, srcAddr[srcFacei], fpi);
 
-                    srcAddr,
-                    srcWght,
-                    tgtAddr,
-                    tgtWght
-                );
-                // ? Check faceProcessed to see if restarting has worked.
+                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;
+    }
 }
 
 
@@ -572,14 +568,43 @@ Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::faceAreaWeightAMI
 {
     this->triangulatePatch(srcPatch, srcTris_, this->srcMagSf_);
     this->triangulatePatch(tgtPatch, tgtTris_, this->tgtMagSf_);
-}
 
+    if (debug)
+    {
+        static label nAMI = 0;
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
+        // Write out triangulated surfaces as OBJ files
+        OBJstream srcTriObj("srcTris_" + Foam::name(nAMI) + ".obj");
+        const pointField& srcPts = srcPatch.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]])
+                );
+            }
+        }
 
-template<class SourcePatch, class TargetPatch>
-Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::~faceAreaWeightAMI()
-{}
+        OBJstream tgtTriObj("tgtTris_" + Foam::name(nAMI) + ".obj");
+        const pointField& tgtPts = tgtPatch.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;
+    }
+}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
@@ -589,6 +614,7 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
 (
     labelListList& srcAddress,
     scalarListList& srcWeights,
+    pointListList& srcCentroids,
     labelListList& tgtAddress,
     scalarListList& tgtWeights,
     label srcFacei,
@@ -608,6 +634,9 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
             tgtFacei
         );
 
+    srcCentroids.setSize(srcAddress.size());
+
+
     if (!ok)
     {
         return;
@@ -616,6 +645,7 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
     // temporary storage for addressing and weights
     List<DynamicList<label>> srcAddr(this->srcPatch_.size());
     List<DynamicList<scalar>> srcWght(srcAddr.size());
+    List<DynamicList<point>> srcCtr(srcAddr.size());
     List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
     List<DynamicList<scalar>> tgtWght(tgtAddr.size());
 
@@ -623,6 +653,7 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
     (
         srcAddr,
         srcWght,
+        srcCtr,
         tgtAddr,
         tgtWght,
         srcFacei,
@@ -644,17 +675,18 @@ void Foam::faceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
         (
             srcAddr,
             srcWght,
+            srcCtr,
             tgtAddr,
             tgtWght
         );
     }
 
-
-    // transfer data to persistent storage
+    // 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)
     {
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H
index 0c6dec88d6ce98d25f95c8efa330720eea418278..10622d727a13580a49d044f5d0264c91242d45e9 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/faceAreaWeightAMI/faceAreaWeightAMI.H
@@ -80,11 +80,13 @@ protected:
 
         // 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 +102,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,6 +112,7 @@ protected:
             (
                 List<DynamicList<label>>& srcAddr,
                 List<DynamicList<scalar>>& srcWght,
+                List<DynamicList<point>>& srcCtr,
                 List<DynamicList<label>>& tgtAddr,
                 List<DynamicList<scalar>>& tgtWght
             );
@@ -119,20 +123,22 @@ protected:
                 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
@@ -165,18 +171,19 @@ public:
 
 
     //- Destructor
-    virtual ~faceAreaWeightAMI();
+    virtual ~faceAreaWeightAMI() = default;
 
 
     // Member Functions
 
         // Manipulation
 
-            //- Update addressing and weights
+            //- Update addressing, weights and centroids
             virtual void calculate
             (
                 labelListList& srcAddress,
                 scalarListList& srcWeights,
+                pointListList& srcCentroids,
                 labelListList& tgtAddress,
                 scalarListList& tgtWeights,
                 label srcFacei = -1,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C
index 5bfaa347ca68747b5ae725a9fe2c8f63b7761a61..a159fae7a0ba328d906ee66fce64556855ae0472 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.C
@@ -190,13 +190,6 @@ Foam::mapNearestAMI<SourcePatch, TargetPatch>::mapNearestAMI
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::mapNearestAMI<SourcePatch, TargetPatch>::~mapNearestAMI()
-{}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -204,6 +197,7 @@ void Foam::mapNearestAMI<SourcePatch, TargetPatch>::calculate
 (
     labelListList& srcAddress,
     scalarListList& srcWeights,
+    pointListList& srcCentroids,
     labelListList& tgtAddress,
     scalarListList& tgtWeights,
     label srcFacei,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H
index b59e0682d3bcc5f21b8d301e561c64371e9b7b25..f347fcc7676455e875d77ee1a0ff82d3cc8cef3f 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/mapNearestAMI/mapNearestAMI.H
@@ -123,7 +123,7 @@ public:
 
 
     //- Destructor
-    virtual ~mapNearestAMI();
+    virtual ~mapNearestAMI() = default;
 
 
     // Member Functions
@@ -135,6 +135,7 @@ public:
             (
                 labelListList& srcAddress,
                 scalarListList& srcWeights,
+                pointListList& srcCentroids,
                 labelListList& tgtAddress,
                 scalarListList& tgtWeights,
                 label srcFacei = -1,
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
index 78275e0111c64cf05b958dcfeee5f80f35aea498..5692a656baaf96b738d1c99b6bf2235694aea5e6 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.C
@@ -35,7 +35,7 @@ void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::setNextFaces
     label& startSeedi,
     label& srcFacei,
     label& tgtFacei,
-    const boolList& mapFlag,
+    const bitSet& mapFlag,
     labelList& seedFaces,
     const DynamicList<label>& visitedFaces,
     const bool errorOnNotFound
@@ -78,14 +78,6 @@ partialFaceAreaWeightAMI
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
-
-template<class SourcePatch, class TargetPatch>
-Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::
-~partialFaceAreaWeightAMI()
-{}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 template<class SourcePatch, class TargetPatch>
@@ -100,6 +92,7 @@ void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
 (
     labelListList& srcAddress,
     scalarListList& srcWeights,
+    pointListList& srcCentroids,
     labelListList& tgtAddress,
     scalarListList& tgtWeights,
     label srcFacei,
@@ -122,9 +115,12 @@ void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
         return;
     }
 
+    srcCentroids.setSize(srcAddress.size());
+
     // temporary storage for addressing and weights
     List<DynamicList<label>> srcAddr(this->srcPatch_.size());
     List<DynamicList<scalar>> srcWght(srcAddr.size());
+    List<DynamicList<point>> srcCtr(srcAddr.size());
     List<DynamicList<label>> tgtAddr(this->tgtPatch_.size());
     List<DynamicList<scalar>> tgtWght(tgtAddr.size());
 
@@ -132,6 +128,7 @@ void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
     (
         srcAddr,
         srcWght,
+        srcCtr,
         tgtAddr,
         tgtWght,
         srcFacei,
@@ -143,6 +140,7 @@ void Foam::partialFaceAreaWeightAMI<SourcePatch, TargetPatch>::calculate
     {
         srcAddress[i].transfer(srcAddr[i]);
         srcWeights[i].transfer(srcWght[i]);
+        srcCentroids[i].transfer(srcCtr[i]);
     }
     forAll(tgtAddr, i)
     {
diff --git a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
index becb1c69fb4f3e1cf08f68db6e40c8850c54f5a1..2d0255d6dcb4ca659736e23f0c6756dfda3e1891 100644
--- a/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
+++ b/src/meshTools/AMIInterpolation/AMIInterpolation/AMIMethod/partialFaceAreaWeightAMI/partialFaceAreaWeightAMI.H
@@ -65,19 +65,22 @@ private:
         //- 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 void setNextFaces
+        (
+            label& startSeedi,
+            label& srcFacei,
+            label& tgtFacei,
+            const bitSet& mapFlag,
+            labelList& seedFaces,
+            const DynamicList<label>& visitedFaces,
+            const bool errorOnNotFound = true
+        ) const;
 
 
 public:
@@ -100,7 +103,7 @@ public:
 
 
     //- Destructor
-    virtual ~partialFaceAreaWeightAMI();
+    virtual ~partialFaceAreaWeightAMI() = default;
 
 
     // Member Functions
@@ -118,6 +121,7 @@ public:
             (
                 labelListList& srcAddress,
                 scalarListList& srcWeights,
+                pointListList& srcCentroids,
                 labelListList& tgtAddress,
                 scalarListList& tgtWeights,
                 label srcFacei = -1,
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/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..916d40e36f7cf3d02504b8ac97eedc30f5260b12 100644
--- a/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H
+++ b/src/meshTools/AMIInterpolation/faceAreaIntersect/faceAreaIntersect.H
@@ -97,6 +97,7 @@ private:
 
     // Static data members
 
+        //- Tolerance
         static scalar tol;
 
 
@@ -132,6 +133,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 +147,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 +205,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..cbba50fb341b641aeee43b308a25d338107b6c38 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-2019 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,195 +41,225 @@ namespace Foam
     addToRunTimeSelectionTable(polyPatch, cyclicACMIPolyPatch, dictionary);
 }
 
-const Foam::scalar Foam::cyclicACMIPolyPatch::tolerance_ = 1e-10;
-
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
+void Foam::cyclicACMIPolyPatch::reportCoverage
+(
+    const word& name,
+    const scalarField& weightSum
+) const
+{
+    label nUncovered = 0;
+    label nCovered = 0;
+    for (const scalar sum : weightSum)
+    {
+        if (sum < tolerance_)
+        {
+            ++nUncovered;
+        }
+        else if (sum > scalar(1) - tolerance_)
+        {
+            ++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;
+}
+
+
+void Foam::cyclicACMIPolyPatch::resetAMI
+(
+    const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
+) const
+{
+    resetAMI(boundaryMesh().mesh().points(), AMIMethod);
+}
+
+
 void Foam::cyclicACMIPolyPatch::resetAMI
 (
-    const AMIPatchToPatchInterpolation::interpolationMethod&
+    const UList<point>& points,
+    const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
 ) const
 {
-    if (owner())
+    if (!owner())
     {
-        const polyPatch& nonOverlapPatch = this->nonOverlapPatch();
+        return;
+    }
+
+    const polyPatch& nonOverlapPatch = this->nonOverlapPatch();
 
+    if (debug)
+    {
+        Pout<< "cyclicACMIPolyPatch::resetAMI : recalculating weights"
+            << " for " << name() << " and " << nonOverlapPatch.name()
+            << endl;
+    }
+
+
+WarningInFunction<< "DEACTIVATED clearGeom()" << endl;
+//    if (boundaryMesh().mesh().hasCellCentres())
+    if (0 && boundaryMesh().mesh().hasCellCentres())
+    {
         if (debug)
         {
-            Pout<< "cyclicACMIPolyPatch::resetAMI : recalculating weights"
+            Pout<< "cyclicACMIPolyPatch::resetAMI : clearing cellCentres"
                 << " for " << name() << " and " << nonOverlapPatch.name()
                 << endl;
         }
 
-        if (boundaryMesh().mesh().hasCellCentres())
-        {
-            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();
-        }
+        //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();
+    }
 
 
-        // Trigger re-building of faceAreas
-        (void)boundaryMesh().mesh().faceAreas();
+    // Trigger re-building of faceAreas
+    (void)boundaryMesh().mesh().faceAreas();
 
 
-        // 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
-        );
+    // 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,
+        AMIPatchToPatchInterpolation::imPartialFaceAreaWeight
+    );
 
-        AMIPatchToPatchInterpolation& AMI =
-            const_cast<AMIPatchToPatchInterpolation&>(this->AMI());
+    const AMIPatchToPatchInterpolation& AMI = this->AMI();
 
-        // Output some stats. AMIInterpolation will have already output the
-        // average weights ("sum(weights) min:1 max:1 average:1")
-        {
-            const scalarField& wghtsSum = AMI.srcWeightsSum();
+    // Output some statistics
+    reportCoverage("source", AMI.srcWeightsSum());
+    reportCoverage("target", AMI.tgtWeightsSum());
 
-            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>());
+    // 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()));
 
-            Info<< "ACMI: Patch source uncovered/blended/covered = "
-                << nUncovered << ", " << nTotal-nUncovered-nCovered
-                << ", " << nCovered << endl;
+    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;
         }
         {
-            const scalarField& wghtsSum = AMI.tgtWeightsSum();
+            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;
+        }
+    }
+}
 
-            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>());
 
-            Info<< "ACMI: Patch target uncovered/blended/covered = "
-                << nUncovered << ", " << nTotal-nUncovered-nCovered
-                << ", " << nCovered << endl;
-        }
+void Foam::cyclicACMIPolyPatch::scalePatchFaceAreas()
+{
+    if (!owner() || !canResetAMI())
+    {
+        return;
+    }
 
-        srcMask_ =
-            min(scalar(1) - tolerance_, max(tolerance_, AMI.srcWeightsSum()));
+    scalePatchFaceAreas(*this);
+    scalePatchFaceAreas(this->neighbPatch());
+}
 
-        tgtMask_ =
-            min(scalar(1) - tolerance_, max(tolerance_, AMI.tgtWeightsSum()));
 
+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
 
-        // 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();
+    DebugPout
+        << "rescaling non-overlap patch areas" << endl;
 
-            forAll(Sf, facei)
-            {
-                Sf[facei] *= srcMask_[facei];
-                noSf[facei] *= 1.0 - srcMask_[facei];
-            }
-        }
-        // Adapt slave side areas
-        if (tgtMask_.size())
-        {
-            const cyclicACMIPolyPatch& cp =
-                refCast<const cyclicACMIPolyPatch>(this->neighbPatch());
-            const polyPatch& pp = cp.nonOverlapPatch();
+    const scalar maxTol = scalar(1) - tolerance_;
+    const scalarField& mask = acmipp.mask();
 
-            vectorField::subField Sf = cp.faceAreas();
-            vectorField::subField noSf = pp.faceAreas();
+    const polyPatch& nonOverlapPatch = acmipp.nonOverlapPatch();
+    vectorField::subField noSf = nonOverlapPatch.faceAreas();
 
-            forAll(Sf, facei)
-            {
-                Sf[facei] *= tgtMask_[facei];
-                noSf[facei] *= 1.0 - tgtMask_[facei];
-            }
+    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" << 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;
     }
 }
 
@@ -244,9 +274,9 @@ void Foam::cyclicACMIPolyPatch::initGeometry(PstreamBuffers& pBufs)
     // 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();
+    // On start-up there are no topological updates so scale the face areas
+    // - Note: resetAMI called by cyclicAMIPolyPatch::initGeometry
+    scalePatchFaceAreas();
 }
 
 
@@ -272,10 +302,10 @@ void Foam::cyclicACMIPolyPatch::initMovePoints
     }
 
     // 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();
 }
 
 
@@ -289,6 +319,8 @@ void Foam::cyclicACMIPolyPatch::movePoints
     {
         Pout<< "cyclicACMIPolyPatch::movePoints : " << name() << endl;
     }
+
+    // When topology is changing, this will scale the duplicate AMI faces
     cyclicAMIPolyPatch::movePoints(pBufs, p);
 }
 
@@ -352,8 +384,7 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(word::null),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
     AMIRequireMatch_ = false;
 
@@ -375,8 +406,7 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(dict.lookup("nonOverlapPatch")),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
     AMIRequireMatch_ = false;
 
@@ -403,8 +433,7 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(pp.nonOverlapPatchName_),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
     AMIRequireMatch_ = false;
 
@@ -428,8 +457,7 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(nonOverlapPatchName),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
     AMIRequireMatch_ = false;
 
@@ -459,19 +487,12 @@ Foam::cyclicACMIPolyPatch::cyclicACMIPolyPatch
     nonOverlapPatchName_(pp.nonOverlapPatchName_),
     nonOverlapPatchID_(-1),
     srcMask_(),
-    tgtMask_(),
-    updated_(false)
+    tgtMask_()
 {
     AMIRequireMatch_ = 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..c858c14e0485d103ab3930621c137ff830a9d987 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-2019 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -60,9 +60,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 +72,40 @@ 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
+        //- Report coverage statics, e.g. number of uncovered/blended/covered
+        //- faces
+        void reportCoverage
+        (
+            const word& name,
+            const scalarField& weightSum
+        ) const;
+
+        //- Reset the AMI interpolator, supply patch points
+        virtual void resetAMI
+        (
+            const UList<point>& points,
+            const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
+                AMIPatchToPatchInterpolation::imFaceAreaWeight
+        ) const;
+
+        //-  Reset the AMI interpolator, use current patch points
         virtual void resetAMI
         (
             const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
                 AMIPatchToPatchInterpolation::imFaceAreaWeight
         ) 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 +127,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:
 
@@ -235,19 +245,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 +267,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..04cb5b6bb3f63d06358d959f03f0e17184122a68 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.C
@@ -27,13 +27,10 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cyclicAMIPolyPatch.H"
-#include "transformField.H"
 #include "SubField.H"
-#include "polyMesh.H"
 #include "Time.H"
-#include "addToRunTimeSelectionTable.H"
 #include "faceAreaIntersect.H"
-#include "ops.H"
+#include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -45,6 +42,7 @@ namespace Foam
     addToRunTimeSelectionTable(polyPatch, cyclicAMIPolyPatch, dictionary);
 }
 
+const Foam::scalar Foam::cyclicAMIPolyPatch::tolerance_ = 1e-10;
 
 // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
 
@@ -295,77 +293,114 @@ void Foam::cyclicAMIPolyPatch::resetAMI
     const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
 ) const
 {
-    if (owner())
+    resetAMI(boundaryMesh().mesh().points(), AMIMethod);
+}
+
+
+void Foam::cyclicAMIPolyPatch::resetAMI
+(
+    const UList<point>& points,
+    const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod
+) const
+{
+    DebugInFunction << endl;
+
+    if (!owner())
     {
-        AMIPtr_.clear();
+        return;
+    }
 
-        const polyPatch& nbr = neighbPatch();
-        pointField nbrPoints
-        (
-            neighbPatch().boundaryMesh().mesh().points(),
-            neighbPatch().meshPoints()
-        );
+    AMIPtr_.clear();
+
+    const cyclicAMIPolyPatch& nbr = neighbPatch();
+    pointField srcPoints(points, meshPoints());
+    pointField nbrPoints(points, neighbPatch().meshPoints());
+
+    if (debug)
+    {
+        const Time& t = boundaryMesh().mesh().time();
+        OFstream os(t.path()/name() + "_neighbourPatch-org.obj");
+        meshTools::writeOBJ(os, neighbPatch().localFaces(), nbrPoints);
+    }
 
-        if (debug)
+    label patchSize0 = size();
+    label nbrPatchSize0 = nbr.size();
+
+    if (createAMIFaces_)
+    {
+        // AMI is created based on the original patch faces (non-extended patch)
+        if (srcFaceIDs_.size())
+        {
+            patchSize0 = srcFaceIDs_.size();
+        }
+        if (tgtFaceIDs_.size())
         {
-            const Time& t = boundaryMesh().mesh().time();
-            OFstream os(t.path()/name() + "_neighbourPatch-org.obj");
-            meshTools::writeOBJ(os, neighbPatch().localFaces(), nbrPoints);
+            nbrPatchSize0 = tgtFaceIDs_.size();
         }
+    }
 
-        // Transform neighbour patch to local system
-        transformPosition(nbrPoints);
-        primitivePatch nbrPatch0
+    // Transform neighbour patch to local system
+    transformPosition(nbrPoints);
+    primitivePatch nbrPatch0
+    (
+        SubList<face>
         (
-            SubList<face>
-            (
-                nbr.localFaces(),
-                nbr.size()
-            ),
-            nbrPoints
-        );
+            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());
-        }
+    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_.reset
+    // Construct/apply AMI interpolation to determine addressing and weights
+    AMIPtr_.reset
+    (
+        new AMIPatchToPatchInterpolation
         (
-            new AMIPatchToPatchInterpolation
-            (
-                *this,
-                nbrPatch0,
-                surfPtr(),
-                faceAreaIntersect::tmMesh,
-                AMIRequireMatch_,
-                AMIMethod,
-                AMILowWeightCorrection_,
-                AMIReverse_
-            )
-        );
+            patch0, // *this,
+            nbrPatch0,
+            surfPtr(),
+            faceAreaIntersect::tmMesh,
+            AMIRequireMatch_,
+            AMIMethod,
+            AMILowWeightCorrection_,
+            AMIReverse_
+        )
+    );
 
-        if (debug)
-        {
-            Pout<< "cyclicAMIPolyPatch : " << name()
-                << " constructed AMI with " << nl
-                << "    " << "srcAddress:" << AMIPtr_().srcAddress().size()
-                << nl
-                << "    " << "tgAddress :" << AMIPtr_().tgtAddress().size()
-                << nl << endl;
-        }
+    // Set the updated flag
+    updated_ = true;
+
+    if (debug)
+    {
+        AMIPtr_->checkSymmetricWeights(true);
     }
 }
 
 
 void Foam::cyclicAMIPolyPatch::calcTransforms()
 {
+    DebugInFunction << endl;
+
     const cyclicAMIPolyPatch& half0 = *this;
     vectorField half0Areas(half0.size());
     forAll(half0, facei)
@@ -402,22 +437,29 @@ void Foam::cyclicAMIPolyPatch::calcTransforms()
 
 void Foam::cyclicAMIPolyPatch::initGeometry(PstreamBuffers& pBufs)
 {
-    // The AMI is no longer valid. Leave it up to demand-driven calculation
-    AMIPtr_.clear();
+    DebugInFunction << endl;
+
+    // Only resetting the AMI if not creating additional AMI faces
+    // - When creating additional faces the AMI is updated externally by the
+    //   dynamic mesh via the setTopology function.
+    if (!createAMIFaces_ && canResetAMI())
+    {
+        resetAMI(AMIMethod_);
+    }
 
     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 +469,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
+    {
+        resetAMI(p, AMIMethod_);
+    }
+
     // Early calculation of transforms. See above.
     calcTransforms();
 }
@@ -446,30 +505,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_)
+    {
+//DEBUG("*** CLEARING AMI ***");
+        AMIPtr_.clear();
+    }
     polyPatch::clearGeom();
 }
 
@@ -488,6 +572,7 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 )
 :
     coupledPolyPatch(name, size, start, index, bm, patchType, transform),
+    updated_(false),
     nbrPatchName_(word::null),
     nbrPatchID_(-1),
     rotationAxis_(Zero),
@@ -501,7 +586,13 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     AMIRequireMatch_(true),
     AMILowWeightCorrection_(-1.0),
     surfPtr_(nullptr),
-    surfDict_(fileName("surface"))
+    surfDict_(fileName("surface")),
+    createAMIFaces_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     // Neighbour patch might not be valid yet so no transformation
     // calculation possible
@@ -518,6 +609,7 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 )
 :
     coupledPolyPatch(name, dict, index, bm, patchType),
+    updated_(false),
     nbrPatchName_(dict.getOrDefault<word>("neighbourPatch", "")),
     coupleGroup_(dict),
     nbrPatchID_(-1),
@@ -545,7 +637,13 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     AMIRequireMatch_(true),
     AMILowWeightCorrection_(dict.getOrDefault("lowWeightCorrection", -1.0)),
     surfPtr_(nullptr),
-    surfDict_(dict.subOrEmptyDict("surface"))
+    surfDict_(dict.subOrEmptyDict("surface")),
+    createAMIFaces_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     if (nbrPatchName_ == word::null && !coupleGroup_.valid())
     {
@@ -605,6 +703,20 @@ 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
+    label srcSize0 = 0;
+    if (dict.readIfPresent("srcSize", srcSize0))
+    {
+        srcFaceIDs_.setSize(srcSize0);
+        createAMIFaces_ = true;
+    }
+    label tgtSize0 = 0;
+    if (dict.readIfPresent("tgtSize", tgtSize0))
+    {
+        tgtFaceIDs_.setSize(tgtSize0);
+        createAMIFaces_ = true;
+    }
 }
 
 
@@ -615,6 +727,7 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 )
 :
     coupledPolyPatch(pp, bm),
+    updated_(false),
     nbrPatchName_(pp.nbrPatchName_),
     coupleGroup_(pp.coupleGroup_),
     nbrPatchID_(-1),
@@ -629,7 +742,13 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     AMIRequireMatch_(pp.AMIRequireMatch_),
     AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
+    surfDict_(pp.surfDict_),
+    createAMIFaces_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     // Neighbour patch might not be valid yet so no transformation
     // calculation possible
@@ -647,6 +766,7 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 )
 :
     coupledPolyPatch(pp, bm, index, newSize, newStart),
+    updated_(false),
     nbrPatchName_(nbrPatchName),
     coupleGroup_(pp.coupleGroup_),
     nbrPatchID_(-1),
@@ -661,7 +781,13 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     AMIRequireMatch_(pp.AMIRequireMatch_),
     AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
+    surfDict_(pp.surfDict_),
+    createAMIFaces_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {
     if (nbrPatchName_ == name())
     {
@@ -686,6 +812,7 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
 )
 :
     coupledPolyPatch(pp, bm, index, mapAddressing, newStart),
+    updated_(false),
     nbrPatchName_(pp.nbrPatchName_),
     coupleGroup_(pp.coupleGroup_),
     nbrPatchID_(-1),
@@ -700,13 +827,13 @@ Foam::cyclicAMIPolyPatch::cyclicAMIPolyPatch
     AMIRequireMatch_(pp.AMIRequireMatch_),
     AMILowWeightCorrection_(pp.AMILowWeightCorrection_),
     surfPtr_(nullptr),
-    surfDict_(pp.surfDict_)
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::cyclicAMIPolyPatch::~cyclicAMIPolyPatch()
+    surfDict_(pp.surfDict_),
+    createAMIFaces_(false),
+    updatingAMI_(true),
+    srcFaceIDs_(),
+    tgtFaceIDs_(),
+    faceAreas0_(),
+    faceCentres0_()
 {}
 
 
@@ -1105,6 +1232,12 @@ void Foam::cyclicAMIPolyPatch::write(Ostream& os) const
     {
         surfDict_.writeEntry(surfDict_.dictName(), os);
     }
+
+    if (createAMIFaces_)
+    {
+        os.writeEntry("srcSize", srcFaceIDs_.size());
+        os.writeEntry("tgtSize", tgtFaceIDs_.size());
+    }
 }
 
 
diff --git a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
index 25259ffacdfc8dff92aa2ae82363658b16f99f5e..84fab30b99bae4209b920ab3b154c18723092cc4 100644
--- a/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatch.H
@@ -74,6 +74,9 @@ protected:
 
     // Protected data
 
+        //- Flag to indicate that AMI has been updated
+        mutable bool updated_;
+
         //- Name of other half
         mutable word nbrPatchName_;
 
@@ -129,9 +132,47 @@ protected:
         const dictionary surfDict_;
 
 
+        // Change of topology as AMI is updated
+
+            //- Flag to indicate that new AMI faces will created
+            //  Set by the call to changeTopology
+            mutable bool createAMIFaces_;
+
+            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
+        // Topology change
+
+            virtual bool removeAMIFaces(polyTopoChange& topoChange);
+
+            virtual bool addAMIFaces(polyTopoChange& topoChange);
+
+            virtual void setAMIFaces();
+
+            virtual void restoreScaledGeometry();
+
+
+        //- Reset the AMI interpolator, supply patch points
+        virtual void resetAMI
+        (
+            const UList<point>& points,
+            const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
+                AMIPatchToPatchInterpolation::imFaceAreaWeight
+        ) const;
+
+        //-  Reset the AMI interpolator, use current patch points
         virtual void resetAMI
         (
             const AMIPatchToPatchInterpolation::interpolationMethod& AMIMethod =
@@ -274,15 +315,45 @@ 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;
+
+             //- Reset the updated flag
+            inline void setUpdated(bool flag) const;
+
+            //- Return access to the updated flag
+            inline bool updated() const;
+
+            //- Return access to the createAMIFaces flag
+            inline bool createAMIFaces() const;
+
+            //- Return access to the updated flag
+            inline bool updatingAMI() const;
+
+void clearAMI() const
+{
+    AMIPtr_.clear();
+}
+
+            //- 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;
@@ -306,9 +377,23 @@ public:
             //- 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 +473,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 +494,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..6d55bf1841dce5b9711ab772ee2f7d55e82e2f1e 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 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,6 +28,36 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+inline bool Foam::cyclicAMIPolyPatch::canResetAMI() const
+{
+    return Pstream::parRun() || !boundaryMesh().mesh().time().processorCase();
+}
+
+
+inline void Foam::cyclicAMIPolyPatch::setUpdated(const bool flag) const
+{
+    updated_ = flag;
+}
+
+
+inline bool Foam::cyclicAMIPolyPatch::updated() const
+{
+    return updated_;
+}
+
+
+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 +70,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..f7e33910da639eb3b5bb1dabb87360aba8f9fde3
--- /dev/null
+++ b/src/meshTools/AMIInterpolation/patches/cyclicAMI/cyclicAMIPolyPatch/cyclicAMIPolyPatchTopologyChange.C
@@ -0,0 +1,676 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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();
+
+    if (debug)
+    {
+        Info<< "Patch:" << name() << " before: sum(mag(faceAreas)):"
+            << gSum(mag(faceAreas)) << endl;
+        Info<< "Patch:" << name() << " before: sum(mag(faceAreas0)):"
+            << gSum(mag(faceAreas0_)) << endl;
+    }
+
+    faceAreas = faceAreas0_;
+    Info<< "WARNING: ==============================" << endl;
+    Info<< "WARNING: Not updating face cell centres" << endl;
+    Info<< "WARNING: ==============================" << endl;
+    //faceCentres = faceCentres0_;
+    faceAreas0_.clear();
+    faceCentres0_.clear();
+
+    if (debug)
+    {
+        Info<< "Patch:" << name() << " after: sum(mag(faceAreas)):"
+            << gSum(mag(faceAreas)) << endl;
+        Info<< "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_;
+        }
+    }
+
+    // Update the AMI addressing and weights to reflect the new 1-to-1
+    // correspondence
+    AMIPtr_->update
+    (
+        std::move(srcToTgtMap1),
+        std::move(tgtToSrcMap1),
+        std::move(srcToTgtAddr1),
+        std::move(newSrcToTgtWeights),
+        std::move(tgtToSrcAddr1),
+        std::move(newTgtToSrcWeights)
+    );
+
+    AMIPtr_->setAreas(mag(faceAreas0_), 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(), AMIMethod_);
+
+        removeAMIFaces(topoChange);
+
+        addAMIFaces(topoChange);
+
+        return true;
+    }
+
+    return false;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 22b12ee488cfd3e4ba2d8395e20ddb4ff3c4d0cd..c89a1afb64f04ff8dc8dda1d62905eedb4807436 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -271,6 +271,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/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
             );
-
 };