diff --git a/applications/utilities/parallelProcessing/decomposePar/distributeCells.C b/applications/utilities/parallelProcessing/decomposePar/distributeCells.C
index 8477a7d0870252fe3ed06d8790969ac7d48683f5..ef7a613a116c94fbbd4efba18df469927bf40ad8 100644
--- a/applications/utilities/parallelProcessing/decomposePar/distributeCells.C
+++ b/applications/utilities/parallelProcessing/decomposePar/distributeCells.C
@@ -28,6 +28,8 @@ License
 #include "decompositionMethod.H"
 #include "cpuTime.H"
 #include "cyclicPolyPatch.H"
+#include "cellSet.H"
+#include "regionSplit.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -127,117 +129,34 @@ void domainDecomposition::distributeCells()
     }
     else
     {
-        
-
-        // Work the faces whose neighbours need to be kept together into an
-        // agglomeration.
-
-        // Per cell the region/agglomeration it is in
-        labelList cellToRegion(nCells(), -1);
-
-        // Current region
-        label regionI = 0;
-
-        labelHashSet freeRegions;
+        // Faces where owner and neighbour are not 'connected' (= all except
+        // sameProcFaces)
+        boolList blockedFace(nFaces(), true);
 
         forAllConstIter(labelHashSet, sameProcFaces, iter)
         {
-            label patchI = boundaryMesh().whichPatch(iter.key());
-
-            label own = faceOwner()[iter.key()];
-            label nei = -1;
-
-            if (patchI == -1)
-            {
-                nei = faceNeighbour()[iter.key()];
-            }
-            else if (isA<cyclicPolyPatch>(boundaryMesh()[patchI]))
-            {
-                const cyclicPolyPatch& pp =
-                    refCast<const cyclicPolyPatch>(boundaryMesh()[patchI]);
-
-                nei = faceOwner()[pp.transformGlobalFace(iter.key())];
-            }
-
-            if (nei != -1)
-            {
-                label ownRegion = cellToRegion[own];
-                label neiRegion = cellToRegion[nei];
-
-                if (ownRegion == -1 && neiRegion == -1)
-                {
-                    // Allocate new agglomeration
-                    cellToRegion[own] = regionI;
-                    cellToRegion[nei] = regionI;
-                    regionI++;
-                }
-                else if (ownRegion != -1)
-                {
-                    // Owner already part of agglomeration. Add nei to it.
-                    cellToRegion[nei] = ownRegion;
-                }
-                else if (neiRegion != -1)
-                {
-                    // nei already part of agglomeration. Add own to it.
-                    cellToRegion[own] = neiRegion;
-                }
-                else if (ownRegion < neiRegion)
-                {
-                    // Renumber neiRegion
-                    forAll(cellToRegion, cellI)
-                    {
-                        if (cellToRegion[cellI] == neiRegion)
-                        {
-                            cellToRegion[cellI] = ownRegion;
-                        }
-                    }
-                    freeRegions.insert(neiRegion);
-                }
-                else if (ownRegion > neiRegion)
-                {
-                    // Renumber ownRegion
-                    forAll(cellToRegion, cellI)
-                    {
-                        if (cellToRegion[cellI] == ownRegion)
-                        {
-                            cellToRegion[cellI] = neiRegion;
-                        }
-                    }
-                    freeRegions.insert(ownRegion);
-                }
-            }
-        }
-
-
-        // Do all other cells
-        forAll(cellToRegion, cellI)
-        {
-            if (cellToRegion[cellI] == -1)
-            {
-                cellToRegion[cellI] = regionI++;
-            }
+            blockedFace[iter.key()] = false;
         }
 
+        // Connect coupled boundary faces
+        const polyBoundaryMesh& patches =  boundaryMesh();
 
-        // Compact out freeRegions
-        // ~~~~~~~~~~~~~~~~~~~~~~~
-
+        forAll(patches, patchI)
         {
-            labelList compactRegion(regionI, -1);
-
-            regionI = 0;
+            const polyPatch& pp = patches[patchI];
 
-            forAll(compactRegion, i)
+            if (pp.coupled())
             {
-                if (!freeRegions.found(compactRegion[i]))
+                forAll(pp, i)
                 {
-                    compactRegion[i] = regionI++;
+                    blockedFace[pp.start()+i] = false;
                 }
             }
-
-            inplaceRenumber(compactRegion, cellToRegion);
         }
 
+        // Determine global regions, separated by blockedFaces
+        regionSplit globalRegion(*this, blockedFace);
+
 
         // Determine region cell centres
         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -249,11 +168,11 @@ void domainDecomposition::distributeCells()
 
         const point greatPoint(GREAT, GREAT, GREAT);
 
-        pointField regionCentres(regionI, greatPoint);
+        pointField regionCentres(globalRegion.nRegions(), greatPoint);
 
-        forAll(cellToRegion, cellI)
+        forAll(globalRegion, cellI)
         {
-            label regionI = cellToRegion[cellI];
+            label regionI = globalRegion[cellI];
 
             if (regionCentres[regionI] == greatPoint)
             {
@@ -261,10 +180,9 @@ void domainDecomposition::distributeCells()
             }
         }
 
-
         // Do decomposition on agglomeration
         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-        cellToProc_ = decomposePtr().decompose(cellToRegion, regionCentres);
+        cellToProc_ = decomposePtr().decompose(globalRegion, regionCentres);
     }
 
     Info<< "\nFinished decomposition in "
diff --git a/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.C b/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.C
index 5d8453a1a22058b0a26edef48c6a47afbaf1d890..b2a507ce42a7b6e33f015c2d4c0a1eb00c5a3d72 100644
--- a/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.C
+++ b/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.C
@@ -125,4 +125,50 @@ Foam::labelList Foam::decompositionMethod::decompose
 }
 
 
+void Foam::decompositionMethod::calcCellCells
+(
+    const polyMesh& mesh,
+    const labelList& fineToCoarse,
+    const label nCoarse,
+    labelListList& cellCells
+)
+{
+    if (fineToCoarse.size() != mesh.nCells())
+    {
+        FatalErrorIn
+        (
+            "decompositionMethod::calcCellCells"
+            "(const labelList&, labelListList&) const"
+        )   << "Only valid for mesh agglomeration." << exit(FatalError);
+    }
+
+    List<DynamicList<label> > dynCellCells(nCoarse);
+
+    forAll(mesh.faceNeighbour(), faceI)
+    {
+        label own = fineToCoarse[mesh.faceOwner()[faceI]];
+        label nei = fineToCoarse[mesh.faceNeighbour()[faceI]];
+
+        if (own != nei)
+        {
+            if (findIndex(dynCellCells[own], nei) == -1)
+            {
+                dynCellCells[own].append(nei);
+            }
+            if (findIndex(dynCellCells[nei], own) == -1)
+            {
+                dynCellCells[nei].append(own);
+            }
+        }
+    }
+
+    cellCells.setSize(dynCellCells.size());
+    forAll(dynCellCells, coarseI)
+    {
+        cellCells[coarseI].transfer(dynCellCells[coarseI].shrink());
+        dynCellCells[coarseI].clear();
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.H b/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.H
index 7f2804fe838b10e39c80dc612c00c3cc94da8226..9d2095da44fdae95f7b06871a88933bb3d69a006 100644
--- a/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.H
+++ b/src/decompositionAgglomeration/decompositionMethods/decompositionMethod/decompositionMethod.H
@@ -36,9 +36,6 @@ SourceFiles
 #ifndef decompositionMethod_H
 #define decompositionMethod_H
 
-#include "typeInfo.H"
-#include "runTimeSelectionTables.H"
-#include "dictionary.H"
 #include "polyMesh.H"
 #include "pointField.H"
 
@@ -60,6 +57,15 @@ protected:
         label nProcessors_;
 
 
+        //- Helper: determine (non-parallel) cellCells from mesh agglomeration.
+        static void calcCellCells
+        (
+            const polyMesh& mesh,
+            const labelList& agglom,
+            const label nCoarse,
+            labelListList& cellCells
+        );
+
 private:
 
     // Private Member Functions
@@ -103,13 +109,13 @@ public:
 
     // Selectors
 
-        //- Return a reference to the selected turbulence model
+        //- Return a reference to the selected decomposition method
         static autoPtr<decompositionMethod> New
         (
             const dictionary& decompositionDict
         );
 
-        //- Return a reference to the selected turbulence model
+        //- Return a reference to the selected decomposition method
         static autoPtr<decompositionMethod> New
         (
             const dictionary& decompositionDict,
@@ -142,18 +148,35 @@ public:
         //  proc boundaries)
         virtual bool parallelAware() const = 0;
 
-        //- Return for every coordinate the wanted processor number
+        //- Return for every coordinate the wanted processor number. Use the
+        //  mesh connectivity (if needed)
         virtual labelList decompose(const pointField&) = 0;
 
         //- Return for every coordinate the wanted processor number. Gets
         //  passed agglomeration map (from fine to coarse cells) and coarse cell
         //  location. Can be overridden by decomposers that provide this
-        //  functionality natively.
+        //  functionality natively. Coarse cells are local to the processor
+        //  (if in parallel). If you want to have coarse cells spanning
+        //  processors use the next function below instead.
         virtual labelList decompose
         (
-            const labelList& agglom,
-            const pointField&
+            const labelList& cellToRegion,
+            const pointField& regionPoints
         );
+
+        //- Return for every coordinate the wanted processor number. Explicitly
+        //  provided connectivity - does not use mesh_.
+        //  The connectivity is equal to mesh.cellCells() except for
+        //  - in parallel the cell numbers are global cell numbers (starting
+        //    from 0 at processor0 and then incrementing all through the
+        //    processors)
+        //  - the connections are across coupled patches
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        ) = 0;
+
 };
 
 
diff --git a/src/decompositionAgglomeration/decompositionMethods/hierarchGeomDecomp/hierarchGeomDecomp.H b/src/decompositionAgglomeration/decompositionMethods/hierarchGeomDecomp/hierarchGeomDecomp.H
index 7b1a3953cc4af5a81eac0566e7348b1466f32ad9..f7a3798f5fe929fae93e255a99a35a21f71bdded 100644
--- a/src/decompositionAgglomeration/decompositionMethods/hierarchGeomDecomp/hierarchGeomDecomp.H
+++ b/src/decompositionAgglomeration/decompositionMethods/hierarchGeomDecomp/hierarchGeomDecomp.H
@@ -154,7 +154,25 @@ public:
             return true;
         }
 
+        //- Return for every coordinate the wanted processor number. Use the
+        //  mesh connectivity (if needed)
         virtual labelList decompose(const pointField&);
+
+        //- Return for every coordinate the wanted processor number. Explicitly
+        //  provided connectivity - does not use mesh_.
+        //  The connectivity is equal to mesh.cellCells() except for
+        //  - in parallel the cell numbers are global cell numbers (starting
+        //    from 0 at processor0 and then incrementing all through the
+        //    processors)
+        //  - the connections are across coupled patches
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        )
+        {
+            return decompose(cc);
+        }
 };
 
 
diff --git a/src/decompositionAgglomeration/decompositionMethods/manualDecomp/manualDecomp.H b/src/decompositionAgglomeration/decompositionMethods/manualDecomp/manualDecomp.H
index 28bafc10074e655eded6f61a57b36fdc857f8e21..d10888762a319f91fd90856646de2bafda810e33 100644
--- a/src/decompositionAgglomeration/decompositionMethods/manualDecomp/manualDecomp.H
+++ b/src/decompositionAgglomeration/decompositionMethods/manualDecomp/manualDecomp.H
@@ -97,7 +97,25 @@ public:
             return true;
         }
 
+        //- Return for every coordinate the wanted processor number. Use the
+        //  mesh connectivity (if needed)
         virtual labelList decompose(const pointField&);
+
+        //- Return for every coordinate the wanted processor number. Explicitly
+        //  provided connectivity - does not use mesh_.
+        //  The connectivity is equal to mesh.cellCells() except for
+        //  - in parallel the cell numbers are global cell numbers (starting
+        //    from 0 at processor0 and then incrementing all through the
+        //    processors)
+        //  - the connections are across coupled patches
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        )
+        {
+            return decompose(cc);
+        }
 };
 
 
diff --git a/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.C b/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.C
index 884542e25b11d740f88bcee925e814fefc9fe466..ce44affbaa6f17ae046c3418a78208df70287b32 100644
--- a/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.C
+++ b/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.C
@@ -29,7 +29,7 @@ License
 #include "floatScalar.H"
 #include "IFstream.H"
 #include "Time.H"
-#include "coupledPolyPatch.H"
+#include "cyclicPolyPatch.H"
 
 extern "C"
 {
@@ -52,113 +52,18 @@ namespace Foam
     );
 }
 
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::metisDecomp::metisDecomp
-(
-    const dictionary& decompositionDict,
-    const polyMesh& mesh
-)
-:
-    decompositionMethod(decompositionDict),
-    mesh_(mesh)
-{}
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
+// Call Metis with options from dictionary.
+Foam::label Foam::metisDecomp::decompose
+(
+    const List<int>& adjncy,
+    const List<int>& xadj,
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
+    List<int>& finalDecomp
+)
 {
-    // Make Metis CSR (Compressed Storage Format) storage
-    //   adjncy      : contains neighbours (= edges in graph)
-    //   xadj(celli) : start of information in adjncy for celli
-
-    List<int> xadj(mesh_.nCells()+1);
-
-    // Initialise the number of internal faces of the cells to twice the
-    // number of internal faces
-    label nInternalFaces = 2*mesh_.nInternalFaces();
-
-    // Check the boundary for coupled patches and add to the number of 
-    // internal faces
-    const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
-
-    forAll(pbm, patchi)
-    {
-        if (isA<coupledPolyPatch>(pbm[patchi]))
-        {
-            nInternalFaces += pbm[patchi].size();
-        }
-    }
-
-    // Create the adjncy array the size of the total number of internal and
-    // coupled faces
-    List<int> adjncy(nInternalFaces);
-
-    // Fill in xadj
-    // ~~~~~~~~~~~~
-    label freeAdj = 0;
-
-    for (label cellI = 0; cellI < mesh_.nCells(); cellI++)
-    {
-        xadj[cellI] = freeAdj;
-
-        const labelList& cFaces = mesh_.cells()[cellI];
-
-        forAll(cFaces, i)
-        {
-            label faceI = cFaces[i];
-
-            if
-            (
-                mesh_.isInternalFace(faceI)
-             || isA<coupledPolyPatch>
-                (pbm[pbm.whichPatch(faceI)])
-            )
-            {
-                freeAdj++;
-            }
-        }
-    }
-    xadj[mesh_.nCells()] = freeAdj;
-
-
-    // Fill in adjncy
-    // ~~~~~~~~~~~~~~
-
-    labelList nFacesPerCell(mesh_.nCells(), 0);
-
-    // Internal faces
-    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
-    {
-        label own = mesh_.faceOwner()[faceI];
-        label nei = mesh_.faceNeighbour()[faceI];
-
-        adjncy[xadj[own] + nFacesPerCell[own]++] = nei;
-        adjncy[xadj[nei] + nFacesPerCell[nei]++] = own;
-    }
-
-    // Coupled faces
-    forAll(pbm, patchi)
-    {
-        if (isA<coupledPolyPatch>(pbm[patchi]))
-        {
-            const unallocLabelList& faceCells = pbm[patchi].faceCells();
-
-            label sizeby2 = faceCells.size()/2;
-
-            for (label facei=0; facei<sizeby2; facei++)
-            {
-                label own = faceCells[facei];
-                label nei = faceCells[facei + sizeby2];
-
-                adjncy[xadj[own] + nFacesPerCell[own]++] = nei;
-                adjncy[xadj[nei] + nFacesPerCell[nei]++] = own;
-            }
-        }
-    }
-
-
     // C style numbering
     int numFlag = 0;
 
@@ -259,68 +164,69 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
             );
             cellWeights.transfer(cellIOWeights);
 
-            if (cellWeights.size() != mesh_.nCells())
+            if (cellWeights.size() != xadj.size()-1)
             {
                 FatalErrorIn("metisDecomp::decompose(const pointField&)")
                     << "Number of cell weights " << cellWeights.size()
-                    << " does not equal number of cells " << mesh_.nCells()
+                    << " does not equal number of cells " << xadj.size()-1
                     << exit(FatalError);
             }
         }
 
-        if (metisDecompCoeffs.found("faceWeightsFile"))
-        {
-            Info<< "metisDecomp : Using face-based weights." << endl;
-
-            word faceWeightsFile
-            (
-                metisDecompCoeffs.lookup("faceWeightsFile")
-            );
-
-            IOList<int> weights
-            (
-                IOobject
-                (
-                    faceWeightsFile,
-                    mesh_.time().timeName(),
-                    mesh_,
-                    IOobject::MUST_READ,
-                    IOobject::AUTO_WRITE
-                )
-            );
-
-            if (weights.size() != mesh_.nInternalFaces())
-            {
-                FatalErrorIn("metisDecomp::decompose(const pointField&)")
-                    << "Number of face weights " << weights.size()
-                    << " does not equal number of internal faces "
-                    << mesh_.nInternalFaces()
-                    << exit(FatalError);
-            }
-
-            // Assume symmetric weights. Keep same ordering as adjncy.
-            faceWeights.setSize(2*mesh_.nInternalFaces());
-
-            labelList nFacesPerCell(mesh_.nCells(), 0);
-
-            for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
-            {
-                label w = weights[faceI];
-
-                label own = mesh_.faceOwner()[faceI];
-                label nei = mesh_.faceNeighbour()[faceI];
-
-                faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
-                faceWeights[xadj[nei] + nFacesPerCell[nei]++] = w;
-            }
-        }
+        //- faceWeights disabled. Only makes sense for cellCells from mesh.
+        //if (metisDecompCoeffs.found("faceWeightsFile"))
+        //{
+        //    Info<< "metisDecomp : Using face-based weights." << endl;
+        //
+        //    word faceWeightsFile
+        //    (
+        //        metisDecompCoeffs.lookup("faceWeightsFile")
+        //    );
+        //
+        //    IOList<int> weights
+        //    (
+        //        IOobject
+        //        (
+        //            faceWeightsFile,
+        //            mesh_.time().timeName(),
+        //            mesh_,
+        //            IOobject::MUST_READ,
+        //            IOobject::AUTO_WRITE
+        //        )
+        //    );
+        //
+        //    if (weights.size() != adjncy.size()/2)
+        //    {
+        //        FatalErrorIn("metisDecomp::decompose(const pointField&)")
+        //            << "Number of face weights " << weights.size()
+        //            << " does not equal number of internal faces "
+        //            << adjncy.size()/2
+        //            << exit(FatalError);
+        //    }
+        //
+        //    // Assume symmetric weights. Keep same ordering as adjncy.
+        //    faceWeights.setSize(adjncy.size());
+        //
+        //    labelList nFacesPerCell(mesh_.nCells(), 0);
+        //
+        //    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
+        //    {
+        //        label w = weights[faceI];
+        //
+        //        label own = mesh_.faceOwner()[faceI];
+        //        label nei = mesh_.faceNeighbour()[faceI];
+        //
+        //        faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
+        //        faceWeights[xadj[nei] + nFacesPerCell[nei]++] = w;
+        //    }
+        //}
     }
 
-    int numCells = mesh_.nCells();
+    int numCells = xadj.size()-1;
     int nProcs = nProcessors_;
 
     // output: cell -> processor addressing
-    List<int> finalDecomp(mesh_.nCells());
+    finalDecomp.setSize(numCells);
 
     // output: number of cut edges
     int edgeCut = 0;
@@ -348,8 +254,8 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
             METIS_WPartGraphRecursive
             (
                 &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
+                const_cast<List<int>&>(xadj).begin(),   // indexing into adjncy
+                const_cast<List<int>&>(adjncy).begin(), // neighbour info
                 vwgtPtr,           // vertexweights
                 adjwgtPtr,         // no edgeweights
                 &wgtFlag,
@@ -366,8 +272,8 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
             METIS_PartGraphRecursive
             (
                 &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
+                const_cast<List<int>&>(xadj).begin(),   // indexing into adjncy
+                const_cast<List<int>&>(adjncy).begin(), // neighbour info
                 vwgtPtr,           // vertexweights
                 adjwgtPtr,         // no edgeweights
                 &wgtFlag,
@@ -386,8 +292,8 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
             METIS_WPartGraphKway
             (
                 &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
+                const_cast<List<int>&>(xadj).begin(),   // indexing into adjncy
+                const_cast<List<int>&>(adjncy).begin(), // neighbour info
                 vwgtPtr,           // vertexweights
                 adjwgtPtr,         // no edgeweights
                 &wgtFlag,
@@ -404,8 +310,8 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
             METIS_PartGraphKway
             (
                 &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
+                const_cast<List<int>&>(xadj).begin(),   // indexing into adjncy
+                const_cast<List<int>&>(adjncy).begin(), // neighbour info
                 vwgtPtr,           // vertexweights
                 adjwgtPtr,         // no edgeweights
                 &wgtFlag,
@@ -418,77 +324,162 @@ Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
         }
     }
 
-    labelList decomp(finalDecomp.size());
-    forAll(decomp, i)
-    {
-        decomp[i] = finalDecomp[i];
-    }
-    return decomp;
+    return edgeCut;
 }
 
 
-Foam::labelList Foam::metisDecomp::decompose
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::metisDecomp::metisDecomp
 (
-    const labelList& agglom,
-    const pointField& agglomPoints
+    const dictionary& decompositionDict,
+    const polyMesh& mesh
 )
+:
+    decompositionMethod(decompositionDict),
+    mesh_(mesh)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::labelList Foam::metisDecomp::decompose(const pointField& points)
 {
+    if (points.size() != mesh_.nCells())
+    {
+        FatalErrorIn("metisDecomp::decompose(const pointField&)")
+            << "Can use this decomposition method only for the whole mesh"
+            << endl
+            << "and supply one coordinate (cellCentre) for every cell." << endl
+            << "The number of coordinates " << points.size() << endl
+            << "The number of cells in the mesh " << mesh_.nCells()
+            << exit(FatalError);
+    }
+
     // Make Metis CSR (Compressed Storage Format) storage
     //   adjncy      : contains neighbours (= edges in graph)
     //   xadj(celli) : start of information in adjncy for celli
 
-    List<int> xadj(agglomPoints.size()+1);
+    List<int> xadj(mesh_.nCells()+1);
+
+    // Initialise the number of internal faces of the cells to twice the
+    // number of internal faces
+    label nInternalFaces = 2*mesh_.nInternalFaces();
+
+    // Check the boundary for coupled patches and add to the number of 
+    // internal faces
+    const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
+
+    forAll(pbm, patchi)
+    {
+        if (isA<cyclicPolyPatch>(pbm[patchi]))
+        {
+            nInternalFaces += pbm[patchi].size();
+        }
+    }
+
+    // Create the adjncy array the size of the total number of internal and
+    // coupled faces
+    List<int> adjncy(nInternalFaces);
+
+    // Fill in xadj
+    // ~~~~~~~~~~~~
+    label freeAdj = 0;
 
-    // Get cellCells on coarse mesh.
-    labelListList cellCells(agglomPoints.size());
+    for (label cellI = 0; cellI < mesh_.nCells(); cellI++)
     {
-        List<DynamicList<label> > dynCellCells(cellCells.size());
+        xadj[cellI] = freeAdj;
 
-        forAll(mesh_.faceNeighbour(), faceI)
+        const labelList& cFaces = mesh_.cells()[cellI];
+
+        forAll(cFaces, i)
         {
-            label own = agglom[mesh_.faceOwner()[faceI]];
-            label nei = agglom[mesh_.faceNeighbour()[faceI]];
+            label faceI = cFaces[i];
 
-            if (own != nei)
+            if
+            (
+                mesh_.isInternalFace(faceI)
+             || isA<cyclicPolyPatch>(pbm[pbm.whichPatch(faceI)])
+            )
             {
-                if (findIndex(dynCellCells[own], nei) == -1)
-                {
-                    dynCellCells[own].append(nei);
-                }
-                if (findIndex(dynCellCells[nei], own) == -1)
-                {
-                    dynCellCells[nei].append(own);
-                }
+                freeAdj++;
             }
         }
-
-        forAll(dynCellCells, coarseI)
-        {
-            cellCells[coarseI].transfer(dynCellCells[coarseI].shrink());
-            dynCellCells[coarseI].clear();
-        }
     }
+    xadj[mesh_.nCells()] = freeAdj;
 
 
-    // Count number of internal faces
-    label nInternalFaces = 0;
+    // Fill in adjncy
+    // ~~~~~~~~~~~~~~
 
-    forAll(cellCells, coarseI)
+    labelList nFacesPerCell(mesh_.nCells(), 0);
+
+    // Internal faces
+    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
     {
-        const labelList& cCells = cellCells[coarseI];
+        label own = mesh_.faceOwner()[faceI];
+        label nei = mesh_.faceNeighbour()[faceI];
 
-        forAll(cCells, i)
+        adjncy[xadj[own] + nFacesPerCell[own]++] = nei;
+        adjncy[xadj[nei] + nFacesPerCell[nei]++] = own;
+    }
+
+    // Coupled faces. Only cyclics done.
+    forAll(pbm, patchi)
+    {
+        if (isA<cyclicPolyPatch>(pbm[patchi]))
         {
-            if (cCells[i] > coarseI)
+            const unallocLabelList& faceCells = pbm[patchi].faceCells();
+
+            label sizeby2 = faceCells.size()/2;
+
+            for (label facei=0; facei<sizeby2; facei++)
             {
-                nInternalFaces++;
+                label own = faceCells[facei];
+                label nei = faceCells[facei + sizeby2];
+
+                adjncy[xadj[own] + nFacesPerCell[own]++] = nei;
+                adjncy[xadj[nei] + nFacesPerCell[nei]++] = own;
             }
         }
     }
 
+    // Decompose using default weights
+    List<int> finalDecomp;
+    decompose(adjncy, xadj, finalDecomp);
+
+    // Copy back to labelList
+    labelList decomp(finalDecomp.size());
+    forAll(decomp, i)
+    {
+        decomp[i] = finalDecomp[i];
+    }
+    return decomp;
+}
+
+
+// From cell-cell connections to Metis format (like CompactListList)
+void Foam::metisDecomp::calcMetisCSR
+(
+    const labelListList& cellCells,
+    List<int>& adjncy,
+    List<int>& xadj
+)
+{
+    // Count number of internal faces
+    label nConnections = 0;
+
+    forAll(cellCells, coarseI)
+    {
+        nConnections += cellCells[coarseI].size();
+    }
+
     // Create the adjncy array as twice the size of the total number of
     // internal faces
-    List<int> adjncy(2*nInternalFaces);
+    adjncy.setSize(nConnections);
+
+    xadj.setSize(cellCells.size()+1);
+
 
     // Fill in xadj
     // ~~~~~~~~~~~~
@@ -506,222 +497,98 @@ Foam::labelList Foam::metisDecomp::decompose
         }
     }
     xadj[cellCells.size()] = freeAdj;
+}
 
 
-    // C style numbering
-    int numFlag = 0;
-
-    // Method of decomposition
-    // recursive: multi-level recursive bisection (default)
-    // k-way: multi-level k-way 
-    word method("k-way");
-
-    // decomposition options. 0 = use defaults
-    List<int> options(5, 0);
-
-    // processor weights initialised with no size, only used if specified in
-    // a file
-    Field<floatScalar> processorWeights;
-
-    // cell weights (so on the vertices of the dual)
-    List<int> cellWeights;
+Foam::labelList Foam::metisDecomp::decompose
+(
+    const labelList& agglom,
+    const pointField& agglomPoints
+)
+{
+    if (agglom.size() != mesh_.nCells())
+    {
+        FatalErrorIn
+        (
+            "parMetisDecomp::decompose(const labelList&, const pointField&)"
+        )   << "Size of cell-to-coarse map " << agglom.size()
+            << " differs from number of cells in mesh " << mesh_.nCells()
+            << exit(FatalError);
+    }
 
-    // Check for user supplied weights and decomp options
-    if (decompositionDict_.found("metisCoeffs"))
+    // Make Metis CSR (Compressed Storage Format) storage
+    //   adjncy      : contains neighbours (= edges in graph)
+    //   xadj(celli) : start of information in adjncy for celli
+    List<int> adjncy;
+    List<int> xadj;
     {
-        dictionary metisDecompCoeffs
+        // Get cellCells on coarse mesh.
+        labelListList cellCells;
+        calcCellCells
         (
-            decompositionDict_.subDict("metisCoeffs")
+            mesh_,
+            agglom,
+            agglomPoints.size(),
+            cellCells
         );
 
-        if (metisDecompCoeffs.found("method"))
-        {
-            metisDecompCoeffs.lookup("method") >> method;
-
-            if (method != "recursive" && method != "k-way")
-            {
-                FatalErrorIn("metisDecomp::decompose()")
-                    << "Method " << method << " in metisCoeffs in dictionary : "
-                    << decompositionDict_.name()
-                    << " should be 'recursive' or 'k-way'"
-                    << exit(FatalError);
-            }
-
-            Info<< "metisDecomp : Using Metis options     " << options
-                << endl << endl;
-        }
-
-        if (metisDecompCoeffs.found("options"))
-        {
-            metisDecompCoeffs.lookup("options") >> options;
-
-            if (options.size() != 5)
-            {
-                FatalErrorIn("metisDecomp::decompose()")
-                    << "Number of options in metisCoeffs in dictionary : "
-                    << decompositionDict_.name()
-                    << " should be 5"
-                    << exit(FatalError);
-            }
-
-            Info<< "metisDecomp : Using Metis options     " << options
-                << endl << endl;
-        }
-
-        if (metisDecompCoeffs.found("processorWeights"))
-        {
-            metisDecompCoeffs.lookup("processorWeights") >> processorWeights;
-            processorWeights /= sum(processorWeights);
-
-            if (processorWeights.size() != nProcessors_)
-            {
-                FatalErrorIn("metisDecomp::decompose(const pointField&)")
-                    << "Number of processor weights "
-                    << processorWeights.size()
-                    << " does not equal number of domains " << nProcessors_
-                    << exit(FatalError);
-            }
-        }
+        calcMetisCSR(cellCells, adjncy, xadj);
+    }
 
-        if (metisDecompCoeffs.found("cellWeightsFile"))
-        {
-            Info<< "metisDecomp : Using cell-based weights." << endl;
+    // Decompose using default weights
+    List<int> finalDecomp;
+    decompose(adjncy, xadj, finalDecomp);
 
-            word cellWeightsFile
-            (
-                metisDecompCoeffs.lookup("cellWeightsFile")
-            );
 
-            IOList<int> cellIOWeights
-            (
-                IOobject
-                (
-                    cellWeightsFile,
-                    mesh_.time().timeName(),
-                    mesh_,
-                    IOobject::MUST_READ,
-                    IOobject::AUTO_WRITE
-                )
-            );
-            cellWeights.transfer(cellIOWeights);
+    // Rework back into decomposition for original mesh_
+    labelList fineDistribution(agglom.size());
 
-            if (cellWeights.size() != cellCells.size())
-            {
-                FatalErrorIn("metisDecomp::decompose(const pointField&)")
-                    << "Number of cell weights " << cellWeights.size()
-                    << " does not equal number of agglomerated cells "
-                    << cellCells.size() << exit(FatalError);
-            }
-        }
+    forAll(fineDistribution, i)
+    {
+        fineDistribution[i] = finalDecomp[agglom[i]];
     }
 
-    int numCells = cellCells.size();
-    int nProcs = nProcessors_;
-
-    // output: cell -> processor addressing
-    List<int> finalDecomp(cellCells.size());
-
-    // output: number of cut edges
-    int edgeCut = 0;
+    return fineDistribution;
+}
 
-    // Vertex weight info
-    int wgtFlag = 0;
-    int* vwgtPtr = NULL;
-    int* adjwgtPtr = NULL;
 
-    if (cellWeights.size() > 0)
+Foam::labelList Foam::metisDecomp::decompose
+(
+    const labelListList& globalCellCells,
+    const pointField& cellCentres
+)
+{
+    if (cellCentres.size() != globalCellCells.size())
     {
-        vwgtPtr = cellWeights.begin();
-        wgtFlag += 2;       // Weights on vertices
+        FatalErrorIn
+        (
+            "metisDecomp::decompose(const pointField&, const labelListList&)"
+        )   << "Inconsistent number of cells (" << globalCellCells.size()
+            << ") and number of cell centres (" << cellCentres.size()
+            << ")." << exit(FatalError);
     }
 
-    if (method == "recursive")
-    {
-        if (processorWeights.size())
-        {
-            METIS_WPartGraphRecursive
-            (
-                &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
-                vwgtPtr,           // vertexweights
-                adjwgtPtr,         // no edgeweights
-                &wgtFlag,
-                &numFlag,
-                &nProcs,
-                processorWeights.begin(),
-                options.begin(),
-                &edgeCut,
-                finalDecomp.begin()
-            );
-        }
-        else
-        {
-            METIS_PartGraphRecursive
-            (
-                &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
-                vwgtPtr,           // vertexweights
-                adjwgtPtr,         // no edgeweights
-                &wgtFlag,
-                &numFlag,
-                &nProcs,
-                options.begin(),
-                &edgeCut,
-                finalDecomp.begin()
-            );
-        }
-    }
-    else
-    {
-        if (processorWeights.size())
-        {
-            METIS_WPartGraphKway
-            (
-                &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
-                vwgtPtr,           // vertexweights
-                adjwgtPtr,         // no edgeweights
-                &wgtFlag,
-                &numFlag,
-                &nProcs,
-                processorWeights.begin(),
-                options.begin(),
-                &edgeCut,
-                finalDecomp.begin()
-            );
-        }
-        else
-        {
-            METIS_PartGraphKway
-            (
-                &numCells,         // num vertices in graph
-                xadj.begin(),      // indexing into adjncy
-                adjncy.begin(),    // neighbour info
-                vwgtPtr,           // vertexweights
-                adjwgtPtr,         // no edgeweights
-                &wgtFlag,
-                &numFlag,
-                &nProcs,
-                options.begin(),
-                &edgeCut,
-                finalDecomp.begin()
-            );
-        }
-    }
 
+    // Make Metis CSR (Compressed Storage Format) storage
+    //   adjncy      : contains neighbours (= edges in graph)
+    //   xadj(celli) : start of information in adjncy for celli
+
+    List<int> adjncy;
+    List<int> xadj;
+    calcMetisCSR(globalCellCells, adjncy, xadj);
 
-    // Rework back into decomposition for original mesh_
-    labelList fineDistribution(agglom.size());
 
-    forAll(fineDistribution, i)
+    // Decompose using default weights
+    List<int> finalDecomp;
+    decompose(adjncy, xadj, finalDecomp);
+
+    // Copy back to labelList
+    labelList decomp(finalDecomp.size());
+    forAll(decomp, i)
     {
-        fineDistribution[i] = finalDecomp[agglom[i]];
+        decomp[i] = finalDecomp[i];
     }
-
-    return fineDistribution;
+    return decomp;
 }
 
 
diff --git a/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.H b/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.H
index b4aaef7a92906af89d1ef8472351200168822ad1..0dea688dc1dbc3f91a3a7ee2a7780edc9218416e 100644
--- a/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.H
+++ b/src/decompositionAgglomeration/decompositionMethods/metisDecomp/metisDecomp.H
@@ -55,6 +55,13 @@ class metisDecomp
 
     // Private Member Functions
 
+        label decompose
+        (
+            const List<int>& adjncy,
+            const List<int>& xadj,
+            List<int>& finalDecomp
+        );
+
         //- Disallow default bitwise copy construct and assignment
         void operator=(const metisDecomp&);
         metisDecomp(const metisDecomp&);
@@ -90,9 +97,40 @@ public:
             return false;
         }
 
+        //- Return for every coordinate the wanted processor number. Use the
+        //  mesh connectivity (if needed)
         virtual labelList decompose(const pointField&);
 
-        virtual labelList decompose(const labelList& agglom, const pointField&);
+        //- Return for every coordinate the wanted processor number. Gets
+        //  passed agglomeration map (from fine to coarse cells) and coarse cell
+        //  location. Can be overridden by decomposers that provide this
+        //  functionality natively.
+        virtual labelList decompose
+        (
+            const labelList& agglom,
+            const pointField&
+        );
+
+        //- Return for every coordinate the wanted processor number. Explicitly
+        //  provided mesh connectivity.
+        //  The connectivity is equal to mesh.cellCells() except for
+        //  - in parallel the cell numbers are global cell numbers (starting
+        //    from 0 at processor0 and then incrementing all through the
+        //    processors)
+        //  - the connections are across coupled patches
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        );
+
+        //- Helper to convert cellcells into Metis storage
+        static void calcMetisCSR
+        (
+            const labelListList& globalCellCells,
+            List<int>& adjncy,
+            List<int>& xadj
+        );
 
 };
 
diff --git a/src/decompositionAgglomeration/decompositionMethods/simpleGeomDecomp/simpleGeomDecomp.H b/src/decompositionAgglomeration/decompositionMethods/simpleGeomDecomp/simpleGeomDecomp.H
index 6e26f502571809113f51757335fb1de4ec21e466..f807edaf3233d4477f9f27bd766a4041995f773a 100644
--- a/src/decompositionAgglomeration/decompositionMethods/simpleGeomDecomp/simpleGeomDecomp.H
+++ b/src/decompositionAgglomeration/decompositionMethods/simpleGeomDecomp/simpleGeomDecomp.H
@@ -92,6 +92,16 @@ public:
         }
 
         virtual labelList decompose(const pointField&);
+
+        //- Explicitly provided connectivity
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        )
+        {
+            return decompose(cc);
+        }
 };
 
 
diff --git a/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.C b/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.C
index 155f4a7b6d6690b74a328146a5b716fc9fcfaaf9..3178356cd187120201c4bb4d054d062efceb5d59 100644
--- a/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.C
+++ b/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.C
@@ -32,6 +32,7 @@ License
 #include "polyMesh.H"
 #include "Time.H"
 #include "labelIOField.H"
+#include "globalIndex.H"
 
 #include <mpi.h>
 
@@ -56,6 +57,267 @@ namespace Foam
 }
 
 
+//- Does prevention of 0 cell domains and calls parmetis.
+Foam::label Foam::parMetisDecomp::decompose
+(
+    Field<int>& xadj,
+    Field<int>& adjncy,
+    const pointField& cellCentres,
+    Field<int>& cellWeights,
+    Field<int>& faceWeights,
+    const List<int>& options,
+
+    List<int>& finalDecomp
+)
+{
+    // C style numbering
+    int numFlag = 0;
+
+    // Number of dimensions
+    int nDims = 3;
+
+    // Get number of cells on all processors
+    List<int> nLocalCells(Pstream::nProcs());
+    nLocalCells[Pstream::myProcNo()] = xadj.size()-1;
+    Pstream::gatherList(nLocalCells);
+    Pstream::scatterList(nLocalCells);
+
+    // Get cell offsets.
+    List<int> cellOffsets(Pstream::nProcs()+1);
+    int nGlobalCells = 0;
+    forAll(nLocalCells, procI)
+    {
+        cellOffsets[procI] = nGlobalCells;
+        nGlobalCells += nLocalCells[procI];
+    }
+    cellOffsets[Pstream::nProcs()] = nGlobalCells;
+
+    // Convert pointField into float
+    Field<floatScalar> xyz(3*cellCentres.size());
+    int compI = 0;
+    forAll(cellCentres, cellI)
+    {
+        const point& cc = cellCentres[cellI];
+        xyz[compI++] = float(cc.x());
+        xyz[compI++] = float(cc.y());
+        xyz[compI++] = float(cc.z());
+    }
+
+    // Make sure every domain has at least one cell
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    // (Metis falls over with zero sized domains)
+    // Trickle cells from processors that have them down to those that
+    // don't.
+
+
+    // Number of cells to send down (is same as number of cells next processor
+    // has to receive)
+    List<int> nSendCells(Pstream::nProcs(), 0);
+
+    for (label procI = nLocalCells.size()-1; procI >=1; procI--)
+    {
+        if (nLocalCells[procI]-nSendCells[procI] < 1)
+        {
+            nSendCells[procI-1] = nSendCells[procI]-nLocalCells[procI]+1;
+        }
+    }
+
+    // First receive (so increasing the sizes of all arrays)
+
+    if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
+    {
+        // Receive cells from previous processor
+        IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
+
+        Field<int> prevXadj(fromPrevProc);
+        Field<int> prevAdjncy(fromPrevProc);
+        Field<floatScalar> prevXyz(fromPrevProc);
+        Field<int> prevCellWeights(fromPrevProc);
+        Field<int> prevFaceWeights(fromPrevProc);
+
+        // Insert adjncy
+        prepend(prevAdjncy, adjncy);
+        // Adapt offsets and prepend xadj
+        xadj += prevAdjncy.size();
+        prepend(prevXadj, xadj);
+        // Coords
+        prepend(prevXyz, xyz);
+        // Weights
+        prepend(prevCellWeights, cellWeights);
+        prepend(prevFaceWeights, faceWeights);
+    }
+
+
+    // Send to my next processor
+
+    if (nSendCells[Pstream::myProcNo()] > 0)
+    {
+        // Send cells to next processor
+        OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1);
+
+        int nCells = nSendCells[Pstream::myProcNo()];
+        int startCell = xadj.size()-1 - nCells;
+        int startFace = xadj[startCell];
+        int nFaces = adjncy.size()-startFace;
+
+        // Send for all cell data: last nCells elements
+        // Send for all face data: last nFaces elements
+        toNextProc
+            << Field<int>::subField(xadj, nCells, startCell)-startFace
+            << Field<int>::subField(adjncy, nFaces, startFace)
+            << SubField<floatScalar>(xyz, nDims*nCells, nDims*startCell)
+            <<
+            (
+                (cellWeights.size() > 0)
+              ? static_cast<const Field<int>&>
+                (
+                    Field<int>::subField(cellWeights, nCells, startCell)
+                )
+              : Field<int>(0)
+            )
+            <<
+            (
+                (faceWeights.size() > 0)
+              ? static_cast<const Field<int>&>
+                (
+                    Field<int>::subField(faceWeights, nFaces, startFace)
+                )
+              : Field<int>(0)
+            );
+
+        // Remove data that has been sent
+        if (faceWeights.size() > 0)
+        {
+            faceWeights.setSize(faceWeights.size()-nFaces);
+        }
+        if (cellWeights.size() > 0)
+        {
+            cellWeights.setSize(cellWeights.size()-nCells);
+        }
+        xyz.setSize(xyz.size()-nDims*nCells);
+        adjncy.setSize(adjncy.size()-nFaces);
+        xadj.setSize(xadj.size() - nCells);
+    }
+
+
+
+    // Adapt number of cells
+    forAll(nSendCells, procI)
+    {
+        // Sent cells
+        nLocalCells[procI] -= nSendCells[procI];
+
+        if (procI >= 1)
+        {
+            // Received cells
+            nLocalCells[procI] += nSendCells[procI-1];
+        }
+    }
+    // Adapt cellOffsets
+    nGlobalCells = 0;
+    forAll(nLocalCells, procI)
+    {
+        cellOffsets[procI] = nGlobalCells;
+        nGlobalCells += nLocalCells[procI];
+    }
+
+
+    // Weight info
+    int wgtFlag = 0;
+    int* vwgtPtr = NULL;
+    int* adjwgtPtr = NULL;
+
+    if (cellWeights.size() > 0)
+    {
+        vwgtPtr = cellWeights.begin();
+        wgtFlag += 2;       // Weights on vertices
+    }
+    if (faceWeights.size() > 0)
+    {
+        adjwgtPtr = faceWeights.begin();
+        wgtFlag += 1;       // Weights on edges
+    }
+
+
+    // Number of weights or balance constraints
+    int nCon = 1;
+    // Per processor, per constraint the weight
+    Field<floatScalar> tpwgts(nCon*nProcessors_, 1./nProcessors_);
+    // Imbalance tolerance
+    Field<floatScalar> ubvec(nCon, 1.02);
+    if (nProcessors_ == 1)
+    {
+        // If only one processor there is no imbalance.
+        ubvec[0] = 1;
+    }
+
+    MPI_Comm comm = MPI_COMM_WORLD;
+
+    // output: cell -> processor addressing
+    finalDecomp.setSize(nLocalCells[Pstream::myProcNo()]);
+
+    // output: number of cut edges
+    int edgeCut = 0;
+
+
+    ParMETIS_V3_PartGeomKway
+    (
+        cellOffsets.begin(),    // vtxDist
+        xadj.begin(),
+        adjncy.begin(),
+        vwgtPtr,                // vertexweights
+        adjwgtPtr,              // edgeweights
+        &wgtFlag,
+        &numFlag,
+        &nDims,
+        xyz.begin(),
+        &nCon,
+        &nProcessors_,          // nParts
+        tpwgts.begin(),
+        ubvec.begin(),
+        const_cast<List<int>&>(options).begin(),
+        &edgeCut,
+        finalDecomp.begin(),
+        &comm
+    );
+
+
+    // If we sent cells across make sure we undo it
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    // Receive back from next processor if I sent something
+    if (nSendCells[Pstream::myProcNo()] > 0)
+    {
+        IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1);
+
+        List<int> nextFinalDecomp(fromNextProc);
+
+        append(nextFinalDecomp, finalDecomp);
+    }
+
+    // Send back to previous processor.
+    if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
+    {
+        OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
+
+        int nToPrevious = nSendCells[Pstream::myProcNo()-1];
+
+        toPrevProc <<
+            SubList<int>
+            (
+                finalDecomp,
+                nToPrevious,
+                finalDecomp.size()-nToPrevious
+            );
+
+        // Remove locally what has been sent
+        finalDecomp.setSize(finalDecomp.size()-nToPrevious);
+    }
+
+    return edgeCut;
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::parMetisDecomp::parMetisDecomp
@@ -73,30 +335,35 @@ Foam::parMetisDecomp::parMetisDecomp
 
 Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
 {
+    if (points.size() != mesh_.nCells())
+    {
+        FatalErrorIn("parMetisDecomp::decompose(const pointField&)")
+            << "Can use this decomposition method only for the whole mesh"
+            << endl
+            << "and supply one coordinate (cellCentre) for every cell." << endl
+            << "The number of coordinates " << points.size() << endl
+            << "The number of cells in the mesh " << mesh_.nCells()
+            << exit(FatalError);
+    }
+
     // For running sequential ...
     if (Pstream::nProcs() <= 1)
     {
         return metisDecomp(decompositionDict_, mesh_).decompose(points);
     }
 
-    //
-    // Make Metis Distributed CSR (Compressed Storage Format) storage
-    //   adjncy      : contains cellCells (= edges in graph)
-    //   xadj(celli) : start of information in adjncy for celli
-    //
-
     // Create global cell numbers
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     // Get number of cells on all processors
-    labelList nLocalCells(Pstream::nProcs());
+    List<int> nLocalCells(Pstream::nProcs());
     nLocalCells[Pstream::myProcNo()] = mesh_.nCells();
     Pstream::gatherList(nLocalCells);
     Pstream::scatterList(nLocalCells);
 
     // Get cell offsets.
-    labelList cellOffsets(Pstream::nProcs()+1);
-    label nGlobalCells = 0;
+    List<int> cellOffsets(Pstream::nProcs()+1);
+    int nGlobalCells = 0;
     forAll(nLocalCells, procI)
     {
         cellOffsets[procI] = nGlobalCells;
@@ -104,7 +371,14 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
     }
     cellOffsets[Pstream::nProcs()] = nGlobalCells;
 
-    label myOffset = cellOffsets[Pstream::myProcNo()];
+    int myOffset = cellOffsets[Pstream::myProcNo()];
+
+
+    //
+    // Make Metis Distributed CSR (Compressed Storage Format) storage
+    //   adjncy      : contains cellCells (= edges in graph)
+    //   xadj(celli) : start of information in adjncy for celli
+    //
 
 
 
@@ -116,7 +390,7 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
     // Get renumbered owner on other side of coupled faces
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    labelList globalNeighbour(mesh_.nFaces()-mesh_.nInternalFaces());
+    List<int> globalNeighbour(mesh_.nFaces()-mesh_.nInternalFaces());
 
     forAll(patches, patchI)
     {
@@ -142,7 +416,7 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     // Number of faces per cell
-    labelList nFacesPerCell(mesh_.nCells(), 0);
+    List<int> nFacesPerCell(mesh_.nCells(), 0);
 
     // Number of coupled faces
     label nCoupledFaces = 0;
@@ -167,15 +441,15 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
                 nFacesPerCell[faceOwner[faceI++]]++;
             }
         }
-    }            
+    }
 
 
     // Fill in xadj
     // ~~~~~~~~~~~~
 
-    labelField xadj(mesh_.nCells()+1, -1);
+    Field<int> xadj(mesh_.nCells()+1, -1);
 
-    label freeAdj = 0;
+    int freeAdj = 0;
 
     for (label cellI = 0; cellI < mesh_.nCells(); cellI++)
     {
@@ -190,7 +464,7 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
     // Fill in adjncy
     // ~~~~~~~~~~~~~~
 
-    labelField adjncy(2*mesh_.nInternalFaces() + nCoupledFaces, -1);
+    Field<int> adjncy(2*mesh_.nInternalFaces() + nCoupledFaces, -1);
 
     nFacesPerCell = 0;
 
@@ -223,41 +497,21 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
                 bFaceI++;
             }
         }
-    }            
-
-
-
-    // C style numbering
-    int numFlag = 0;
-
-    // Number of dimensions
-    int nDims = 3;
-
-    // cell centres
-    Field<floatScalar> xyz(nDims*mesh_.nCells());
-    const pointField& cellCentres = mesh_.cellCentres();
-    label compI = 0;
-    forAll(cellCentres, cellI)
-    {
-        const point& cc = cellCentres[cellI];
-        xyz[compI++] = float(cc.x());
-        xyz[compI++] = float(cc.y());
-        xyz[compI++] = float(cc.z());
     }
 
 
+
     // decomposition options. 0 = use defaults
-    labelList options(3, 0);
+    List<int> options(3, 0);
     //options[0] = 1;     // don't use defaults but use values below
     //options[1] = -1;    // full debug info
     //options[2] = 15;    // random number seed
 
-
     // cell weights (so on the vertices of the dual)
-    labelField cellWeights;
+    Field<int> cellWeights;
 
     // face weights (so on the edges of the dual)
-    labelField faceWeights;
+    Field<int> faceWeights;
 
     // Check for user supplied weights and decomp options
     if (decompositionDict_.found("metisCoeffs"))
@@ -364,7 +618,7 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
                         faceI++;
                     }
                 }
-            }            
+            }
         }
 
         if (parMetisDecompCoeffs.found("options"))
@@ -384,219 +638,353 @@ Foam::labelList Foam::parMetisDecomp::decompose(const pointField& points)
     }
 
 
+    // Do actual decomposition
+    List<int> finalDecomp;
+    decompose
+    (
+        xadj,
+        adjncy,
+        points,
+        cellWeights,
+        faceWeights,
+        options,
+
+        finalDecomp
+    );
 
-    // Make sure every domain has at least one cell
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    // (Metis falls over with zero sized domains)
-    // Trickle cells from processors that have them down to those that
-    // don't.
+    // Copy back to labelList
+    labelList decomp(finalDecomp.size());
+    forAll(decomp, i)
+    {
+        decomp[i] = finalDecomp[i];
+    }
+    return decomp;
+}
 
 
-    // Number of cells to send down (is same as number of cells next processor
-    // has to receive)
-    labelList nSendCells(Pstream::nProcs(), 0);
+Foam::labelList Foam::parMetisDecomp::decompose
+(
+    const labelList& cellToRegion,
+    const pointField& regionPoints
+)
+{
+    const labelList& faceOwner = mesh_.faceOwner();
+    const labelList& faceNeighbour = mesh_.faceNeighbour();
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
 
-    for (label procI = nLocalCells.size()-1; procI >=1; procI--)
+    if (cellToRegion.size() != mesh_.nCells())
     {
-        if (nLocalCells[procI]-nSendCells[procI] < 1)
-        {
-            nSendCells[procI-1] = nSendCells[procI]-nLocalCells[procI]+1;
-        }
+        FatalErrorIn
+        (
+            "parMetisDecomp::decompose(const labelList&, const pointField&)"
+        )   << "Size of cell-to-coarse map " << cellToRegion.size()
+            << " differs from number of cells in mesh " << mesh_.nCells()
+            << exit(FatalError);
     }
 
-    // First receive (so increasing the sizes of all arrays)
 
-    if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
+    // Global region numbering engine
+    globalIndex globalRegions(regionPoints.size());
+
+
+    // Get renumbered owner region on other side of coupled faces
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    List<int> globalNeighbour(mesh_.nFaces()-mesh_.nInternalFaces());
+
+    forAll(patches, patchI)
     {
-        // Receive cells from previous processor
-        IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
+        const polyPatch& pp = patches[patchI];
 
-        labelField prevXadj(fromPrevProc);
-        labelField prevAdjncy(fromPrevProc);
-        Field<floatScalar> prevXyz(fromPrevProc);
-        labelField prevCellWeights(fromPrevProc);
-        labelField prevFaceWeights(fromPrevProc);
+        if (pp.coupled())
+        {
+            label faceI = pp.start();
+            label bFaceI = pp.start() - mesh_.nInternalFaces();
 
-        // Insert adjncy
-        prepend(prevAdjncy, adjncy);
-        // Adapt offsets and prepend xadj
-        xadj += prevAdjncy.size();
-        prepend(prevXadj, xadj);
-        // Coords
-        prepend(prevXyz, xyz);
-        // Weights
-        prepend(prevCellWeights, cellWeights);
-        prepend(prevFaceWeights, faceWeights);
+            forAll(pp, i)
+            {
+                label ownRegion = cellToRegion[faceOwner[faceI]];
+                globalNeighbour[bFaceI++] = globalRegions.toGlobal(ownRegion);
+                faceI++;
+            }
+        }
     }
 
+    // Get the cell on the other side of coupled patches
+    syncTools::swapBoundaryFaceList(mesh_, globalNeighbour, false);
 
-    // Send to my next processor
 
-    if (nSendCells[Pstream::myProcNo()] > 0)
+    // Get globalCellCells on coarse mesh
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    labelListList globalRegionRegions;
     {
-        // Send cells to next processor
-        OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1);
+        List<DynamicList<label> > dynRegionRegions(regionPoints.size());
 
-        label nCells = nSendCells[Pstream::myProcNo()];
-        label startCell = xadj.size()-1 - nCells;
-        label startFace = xadj[startCell];
-        label nFaces = adjncy.size()-startFace;
+        // Internal faces first
+        forAll(faceNeighbour, faceI)
+        {
+            label ownRegion = cellToRegion[faceOwner[faceI]];
+            label neiRegion = cellToRegion[faceNeighbour[faceI]];
 
-        // Send for all cell data: last nCells elements
-        // Send for all face data: last nFaces elements
-        toNextProc
-            << labelField::subField(xadj, nCells, startCell)-startFace
-            << labelField::subField(adjncy, nFaces, startFace)
-            << SubField<floatScalar>(xyz, nDims*nCells, nDims*startCell)
-            <<
-            (
-                (cellWeights.size() > 0)
-              ? static_cast<const labelField&>
-                (
-                    labelField::subField(cellWeights, nCells, startCell)
-                )
-              : labelField(0)
-            )
-            <<
-            (
-                (faceWeights.size() > 0)
-              ? static_cast<const labelField&>
-                (
-                    labelField::subField(faceWeights, nFaces, startFace)
-                )
-              : labelField(0)
-            );
+            if (ownRegion != neiRegion)
+            {
+                label globalOwn = globalRegions.toGlobal(ownRegion);
+                label globalNei = globalRegions.toGlobal(neiRegion);
 
-        // Remove data that has been sent
-        if (faceWeights.size() > 0)
-        {
-            faceWeights.setSize(faceWeights.size()-nFaces);
+                if (findIndex(dynRegionRegions[ownRegion], globalNei) == -1)
+                {
+                    dynRegionRegions[ownRegion].append(globalNei);
+                }
+                if (findIndex(dynRegionRegions[neiRegion], globalOwn) == -1)
+                {
+                    dynRegionRegions[neiRegion].append(globalOwn);
+                }
+            }
         }
-        if (cellWeights.size() > 0)
+
+        // Coupled boundary faces
+        forAll(patches, patchI)
         {
-            cellWeights.setSize(cellWeights.size()-nCells);
-        }
-        xyz.setSize(xyz.size()-nDims*nCells);
-        adjncy.setSize(adjncy.size()-nFaces);
-        xadj.setSize(xadj.size() - nCells);
-    }
+            const polyPatch& pp = patches[patchI];
 
+            if (pp.coupled())
+            {
+                label faceI = pp.start();
+                label bFaceI = pp.start() - mesh_.nInternalFaces();
 
+                forAll(pp, i)
+                {
+                    label ownRegion = cellToRegion[faceOwner[faceI]];
+                    label globalNei = globalNeighbour[bFaceI++];
+                    faceI++;
 
-    // Adapt number of cells
-    forAll(nSendCells, procI)
-    {
-        // Sent cells
-        nLocalCells[procI] -= nSendCells[procI];
+                    if (findIndex(dynRegionRegions[ownRegion], globalNei) == -1)
+                    {
+                        dynRegionRegions[ownRegion].append(globalNei);
+                    }
+                }
+            }
+        }
 
-        if (procI >= 1)
+        globalRegionRegions.setSize(dynRegionRegions.size());
+        forAll(dynRegionRegions, i)
         {
-            // Received cells
-            nLocalCells[procI] += nSendCells[procI-1];
+            globalRegionRegions[i].transfer(dynRegionRegions[i].shrink());
+            dynRegionRegions[i].clear();
         }
     }
-    // Adapt cellOffsets
-    nGlobalCells = 0;
-    forAll(nLocalCells, procI)
-    {
-        cellOffsets[procI] = nGlobalCells;
-        nGlobalCells += nLocalCells[procI];
-    }
 
+    labelList regionDecomp(decompose(globalRegionRegions, regionPoints));
 
-    // Weight info
-    int wgtFlag = 0;
-    label* vwgtPtr = NULL;
-    label* adjwgtPtr = NULL;
+    // Rework back into decomposition for original mesh_
+    labelList cellDistribution(cellToRegion.size());
 
-    if (cellWeights.size() > 0)
+    forAll(cellDistribution, cellI)
     {
-        vwgtPtr = cellWeights.begin();
-        wgtFlag += 2;       // Weights on vertices
-    }
-    if (faceWeights.size() > 0)
-    {
-        adjwgtPtr = faceWeights.begin();
-        wgtFlag += 1;       // Weights on edges
+        cellDistribution[cellI] = regionDecomp[cellToRegion[cellI]];
     }
+    return cellDistribution;
+}
 
 
-    // Number of weights or balance constraints
-    int nCon = 1;
-    // Per processor, per constraint the weight
-    Field<floatScalar> tpwgts(nCon*nProcessors_, 1./nProcessors_);
-    // Imbalance tolerance
-    Field<floatScalar> ubvec(nCon, 1.02);
-    if (nProcessors_ == 1)
+Foam::labelList Foam::parMetisDecomp::decompose
+(
+    const labelListList& globalCellCells,
+    const pointField& cellCentres
+)
+{
+    if (cellCentres.size() != globalCellCells.size())
     {
-        // If only one processor there is no imbalance.
-        ubvec[0] = 1;
+        FatalErrorIn
+        (
+            "parMetisDecomp::decompose(const labelListList&, const pointField&)"
+        )   << "Inconsistent number of cells (" << globalCellCells.size()
+            << ") and number of cell centres (" << cellCentres.size()
+            << ")." << exit(FatalError);
     }
 
-    MPI_Comm comm = MPI_COMM_WORLD;
+    // For running sequential ...
+    if (Pstream::nProcs() <= 1)
+    {
+        return metisDecomp(decompositionDict_, mesh_)
+            .decompose(globalCellCells, cellCentres);
+    }
 
-    // output: cell -> processor addressing
-    labelList finalDecomp(nLocalCells[Pstream::myProcNo()]);
 
-    // output: number of cut edges
-    int edgeCut = 0;
+    // Make Metis Distributed CSR (Compressed Storage Format) storage
 
+    // Connections
+    Field<int> adjncy;
+    // Offsets into adjncy
+    Field<int> xadj;
+    metisDecomp::calcMetisCSR(globalCellCells, adjncy, xadj);
 
-    ParMETIS_V3_PartGeomKway
-    (
-        cellOffsets.begin(),    // vtxDist
-        xadj.begin(),
-        adjncy.begin(),
-        vwgtPtr,                // vertexweights
-        adjwgtPtr,              // edgeweights
-        &wgtFlag,
-        &numFlag,
-        &nDims,
-        xyz.begin(),
-        &nCon,
-        &nProcessors_,          // nParts
-        tpwgts.begin(),
-        ubvec.begin(),
-        options.begin(),
-        &edgeCut,
-        finalDecomp.begin(),
-        &comm
-    );
+    // decomposition options. 0 = use defaults
+    List<int> options(3, 0);
+    //options[0] = 1;     // don't use defaults but use values below
+    //options[1] = -1;    // full debug info
+    //options[2] = 15;    // random number seed
 
+    // cell weights (so on the vertices of the dual)
+    Field<int> cellWeights;
 
-    // If we sent cells across make sure we undo it
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    // face weights (so on the edges of the dual)
+    Field<int> faceWeights;
 
-    // Receive back from next processor if I sent something
-    if (nSendCells[Pstream::myProcNo()] > 0)
+    // Check for user supplied weights and decomp options
+    if (decompositionDict_.found("metisCoeffs"))
     {
-        IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1);
-
-        labelList nextFinalDecomp(fromNextProc);
-
-        append(nextFinalDecomp, finalDecomp);
-    }
+        dictionary parMetisDecompCoeffs
+        (
+            decompositionDict_.subDict("metisCoeffs")
+        );
 
-    // Send back to previous processor.
-    if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0)
-    {
-        OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1);
+        if (parMetisDecompCoeffs.found("cellWeightsFile"))
+        {
+            word cellWeightsFile
+            (
+                parMetisDecompCoeffs.lookup("cellWeightsFile")
+            );
 
-        label nToPrevious = nSendCells[Pstream::myProcNo()-1];
+            Info<< "parMetisDecomp : Using cell-based weights read from "
+                << cellWeightsFile << endl;
 
-        toPrevProc <<
-            SubList<label>
+            labelIOField cellIOWeights
             (
-                finalDecomp,
-                nToPrevious,
-                finalDecomp.size()-nToPrevious
+                IOobject
+                (
+                    cellWeightsFile,
+                    mesh_.time().timeName(),
+                    mesh_,
+                    IOobject::MUST_READ,
+                    IOobject::AUTO_WRITE
+                )
             );
+            cellWeights.transfer(cellIOWeights);
 
-        // Remove locally what has been sent
-        finalDecomp.setSize(finalDecomp.size()-nToPrevious);
+            if (cellWeights.size() != cellCentres.size())
+            {
+                FatalErrorIn
+                (
+                    "parMetisDecomp::decompose"
+                    "(const labelListList&, const pointField&)"
+                )   << "Number of cell weights " << cellWeights.size()
+                    << " read from " << cellIOWeights.objectPath()
+                    << " does not equal number of cells " << cellCentres.size()
+                    << exit(FatalError);
+            }
+        }
+
+        //- faceWeights disabled. Only makes sense for cellCells from mesh.
+        //if (parMetisDecompCoeffs.found("faceWeightsFile"))
+        //{
+        //    word faceWeightsFile
+        //    (
+        //        parMetisDecompCoeffs.lookup("faceWeightsFile")
+        //    );
+        //
+        //    Info<< "parMetisDecomp : Using face-based weights read from "
+        //        << faceWeightsFile << endl;
+        //
+        //    labelIOField weights
+        //    (
+        //        IOobject
+        //        (
+        //            faceWeightsFile,
+        //            mesh_.time().timeName(),
+        //            mesh_,
+        //            IOobject::MUST_READ,
+        //            IOobject::AUTO_WRITE
+        //        )
+        //    );
+        //
+        //    if (weights.size() != mesh_.nFaces())
+        //    {
+        //        FatalErrorIn("parMetisDecomp::decompose(const pointField&)")
+        //            << "Number of face weights " << weights.size()
+        //            << " does not equal number of internal and boundary faces "
+        //            << mesh_.nFaces()
+        //            << exit(FatalError);
+        //    }
+        //
+        //    faceWeights.setSize(2*mesh_.nInternalFaces()+nCoupledFaces);
+        //
+        //    // Assume symmetric weights. Keep same ordering as adjncy.
+        //    nFacesPerCell = 0;
+        //
+        //    // Handle internal faces
+        //    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
+        //    {
+        //        label w = weights[faceI];
+        //
+        //        label own = faceOwner[faceI];
+        //        label nei = faceNeighbour[faceI];
+        //
+        //        faceWeights[xadj[own] + nFacesPerCell[own]++] = w;
+        //        faceWeights[xadj[nei] + nFacesPerCell[nei]++] = w;
+        //    }
+        //    // Coupled boundary faces
+        //    forAll(patches, patchI)
+        //    {
+        //        const polyPatch& pp = patches[patchI];
+        //
+        //        if (pp.coupled())
+        //        {
+        //            label faceI = pp.start();
+        //
+        //            forAll(pp, i)
+        //            {
+        //                label w = weights[faceI];
+        //                label own = faceOwner[faceI];
+        //                adjncy[xadj[own] + nFacesPerCell[own]++] = w;
+        //                faceI++;
+        //            }
+        //        }
+        //    }
+        //}
+
+        if (parMetisDecompCoeffs.found("options"))
+        {
+            parMetisDecompCoeffs.lookup("options") >> options;
+
+            Info<< "Using Metis options     " << options
+                << endl << endl;
+
+            if (options.size() != 3)
+            {
+                FatalErrorIn
+                (
+                    "parMetisDecomp::decompose"
+                    "(const labelListList&, const pointField&)"
+                )   << "Number of options " << options.size()
+                    << " should be three." << exit(FatalError);
+            }
+        }
     }
 
-    return finalDecomp;
+
+    // Do actual decomposition
+    List<int> finalDecomp;
+    decompose
+    (
+        xadj,
+        adjncy,
+        cellCentres,
+        cellWeights,
+        faceWeights,
+        options,
+
+        finalDecomp
+    );
+
+    // Copy back to labelList
+    labelList decomp(finalDecomp.size());
+    forAll(decomp, i)
+    {
+        decomp[i] = finalDecomp[i];
+    }
+    return decomp;
 }
 
 
diff --git a/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.H b/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.H
index 739339fcc4ec84f167da5a8db89ef5bcc7d9faf4..89af153b0002ac7c42a0eed553b5135c72b97d95 100644
--- a/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.H
+++ b/src/decompositionAgglomeration/parMetisDecomp/parMetisDecomp.H
@@ -62,6 +62,18 @@ class parMetisDecomp
         template<class Type>
         static void append(const UList<Type>&, List<Type>&);
 
+        label decompose
+        (
+            Field<int>& xadj,
+            Field<int>& adjncy,
+            const pointField& cellCentres,
+            Field<int>& cellWeights,
+            Field<int>& faceWeights,
+            const List<int>& options,
+
+            List<int>& finalDecomp
+        );
+
 
         //- Disallow default bitwise copy construct and assignment
         void operator=(const parMetisDecomp&);
@@ -93,12 +105,37 @@ public:
     // Member Functions
 
         //- parMetis handles Foam processor boundaries
-        bool parallelAware() const
+        virtual bool parallelAware() const
         {
             return true;
         }
 
-        labelList decompose(const pointField&);
+        //- Return for every coordinate the wanted processor number. Use the
+        //  mesh connectivity (if needed)
+        virtual labelList decompose(const pointField&);
+
+        //- Return for every coordinate the wanted processor number. Gets
+        //  passed agglomeration map (from fine to coarse cells) and coarse cell
+        //  location. Can be overridden by decomposers that provide this
+        //  functionality natively.
+        virtual labelList decompose
+        (
+            const labelList& agglom,
+            const pointField&
+        );
+
+        //- Return for every coordinate the wanted processor number. Explicitly
+        //  provided mesh connectivity.
+        //  The connectivity is equal to mesh.cellCells() except for
+        //  - in parallel the cell numbers are global cell numbers (starting
+        //    from 0 at processor0 and then incrementing all through the
+        //    processors)
+        //  - the connections are across coupled patches
+        virtual labelList decompose
+        (
+            const labelListList& globalCellCells,
+            const pointField& cc
+        );
 };