diff --git a/applications/utilities/mesh/manipulation/createBaffles/createBaffles.C b/applications/utilities/mesh/manipulation/createBaffles/createBaffles.C
index fb53d4fef43eee2bb2521bb725cd66eb3c6a5f23..bc0e78a67f847cfe5d670f89ffc4142ffa96dbca 100644
--- a/applications/utilities/mesh/manipulation/createBaffles/createBaffles.C
+++ b/applications/utilities/mesh/manipulation/createBaffles/createBaffles.C
@@ -54,6 +54,57 @@ using namespace Foam;
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+label addPatch
+(
+    fvMesh& mesh,
+    const word& patchName,
+    const word& groupName,
+    const dictionary& patchDict
+)
+{
+    const polyBoundaryMesh& pbm = mesh.boundaryMesh();
+
+    if (pbm.findPatchID(patchName) == -1)
+    {
+        autoPtr<polyPatch> ppPtr
+        (
+            polyPatch::New
+            (
+                patchName,
+                patchDict,
+                0,
+                pbm
+            )
+        );
+        polyPatch& pp = ppPtr();
+
+        if (!groupName.empty() && !pp.inGroup(groupName))
+        {
+            pp.inGroups().append(groupName);
+        }
+
+        // Add patch, create calculated everywhere
+        fvMeshTools::addPatch
+        (
+            mesh,
+            pp,
+            dictionary(),   // do not set specialised patchFields
+            calculatedFvPatchField<scalar>::typeName,
+            true            // parallel sync'ed addition
+        );
+    }
+    else
+    {
+        Info<< "Patch '" << patchName
+            << "' already exists.  Only "
+            << "moving patch faces - type will remain the same"
+            << endl;
+    }
+
+    return pbm.findPatchID(patchName);
+}
+
+
 void modifyOrAddFace
 (
     polyTopoChange& meshMod,
@@ -111,6 +162,185 @@ void modifyOrAddFace
 }
 
 
+// Create faces for fZone faces. Usually newMasterPatches, newSlavePatches
+// only size one but can be more for duplicate baffle sets
+void createFaces
+(
+    const bool internalFacesOnly,
+    const fvMesh& mesh,
+    const faceZone& fZone,
+    const labelList& newMasterPatches,
+    const labelList& newSlavePatches,
+    polyTopoChange& meshMod,
+    PackedBoolList& modifiedFace,
+    label& nModified
+)
+{
+    const polyBoundaryMesh& pbm = mesh.boundaryMesh();
+
+    forAll(newMasterPatches, i)
+    {
+        // Pass 1. Do selected side of zone
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
+        {
+            label zoneFaceI = fZone.whichFace(faceI);
+
+            if (zoneFaceI != -1)
+            {
+                if (!fZone.flipMap()[zoneFaceI])
+                {
+                    // Use owner side of face
+                    modifyOrAddFace
+                    (
+                        meshMod,
+                        mesh.faces()[faceI],    // modified face
+                        faceI,                  // label of face
+                        mesh.faceOwner()[faceI],// owner
+                        false,                  // face flip
+                        newMasterPatches[i],    // patch for face
+                        fZone.index(),          // zone for face
+                        false,                  // face flip in zone
+                        modifiedFace            // modify or add status
+                    );
+                }
+                else
+                {
+                    // Use neighbour side of face
+                    modifyOrAddFace
+                    (
+                        meshMod,
+                        mesh.faces()[faceI].reverseFace(),  // modified face
+                        faceI,                      // label of face
+                        mesh.faceNeighbour()[faceI],// owner
+                        true,                       // face flip
+                        newMasterPatches[i],        // patch for face
+                        fZone.index(),              // zone for face
+                        true,                       // face flip in zone
+                        modifiedFace                // modify or add status
+                    );
+                }
+
+                nModified++;
+            }
+        }
+
+
+        // Pass 2. Do other side of zone
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
+        {
+            label zoneFaceI = fZone.whichFace(faceI);
+
+            if (zoneFaceI != -1)
+            {
+                if (!fZone.flipMap()[zoneFaceI])
+                {
+                    // Use neighbour side of face
+                    modifyOrAddFace
+                    (
+                        meshMod,
+                        mesh.faces()[faceI].reverseFace(),  // modified face
+                        faceI,                          // label of face
+                        mesh.faceNeighbour()[faceI],    // owner
+                        true,                           // face flip
+                        newSlavePatches[i],             // patch for face
+                        fZone.index(),                  // zone for face
+                        true,                           // face flip in zone
+                        modifiedFace                    // modify or add
+                    );
+                }
+                else
+                {
+                    // Use owner side of face
+                    modifyOrAddFace
+                    (
+                        meshMod,
+                        mesh.faces()[faceI],    // modified face
+                        faceI,                  // label of face
+                        mesh.faceOwner()[faceI],// owner
+                        false,                  // face flip
+                        newSlavePatches[i],     // patch for face
+                        fZone.index(),          // zone for face
+                        false,                  // face flip in zone
+                        modifiedFace            // modify or add status
+                    );
+                }
+            }
+        }
+
+
+        // Modify any boundary faces
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        // Normal boundary:
+        // - move to new patch. Might already be back-to-back baffle
+        // you want to add cyclic to. Do warn though.
+        //
+        // Processor boundary:
+        // - do not move to cyclic
+        // - add normal patches though.
+
+        // For warning once per patch.
+        labelHashSet patchWarned;
+
+        forAll(pbm, patchI)
+        {
+            const polyPatch& pp = pbm[patchI];
+
+            label newPatchI = newMasterPatches[i];
+
+            if (pp.coupled() && pbm[newPatchI].coupled())
+            {
+                // Do not allow coupled faces to be moved to different
+                // coupled patches.
+            }
+            else if (pp.coupled() || !internalFacesOnly)
+            {
+                forAll(pp, i)
+                {
+                    label faceI = pp.start()+i;
+
+                    label zoneFaceI = fZone.whichFace(faceI);
+
+                    if (zoneFaceI != -1)
+                    {
+                        if (patchWarned.insert(patchI))
+                        {
+                            WarningIn("createFaces(..)")
+                                << "Found boundary face (in patch "
+                                << pp.name()
+                                << ") in faceZone " << fZone.name()
+                                << " to convert to baffle patch "
+                                << pbm[newPatchI].name()
+                                << endl
+                                << "    Run with -internalFacesOnly option"
+                                << " if you don't wish to convert"
+                                << " boundary faces." << endl;
+                        }
+
+                        modifyOrAddFace
+                        (
+                            meshMod,
+                            mesh.faces()[faceI],        // modified face
+                            faceI,                      // label of face
+                            mesh.faceOwner()[faceI],    // owner
+                            false,                      // face flip
+                            newPatchI,                  // patch for face
+                            fZone.index(),              // zone for face
+                            fZone.flipMap()[zoneFaceI], // face flip in zone
+                            modifiedFace                // modify or add
+                        );
+                        nModified++;
+                    }
+                }
+            }
+        }
+    }
+}
+
 
 int main(int argc, char *argv[])
 {
@@ -220,8 +450,8 @@ int main(int argc, char *argv[])
 
 
 
-    // Creating (if necessary) baffles
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    // Creating (if necessary) faceZones
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     forAll(selectors, selectorI)
     {
@@ -308,21 +538,28 @@ int main(int argc, char *argv[])
     {
         forAll(selectors, selectorI)
         {
-            const dictionary& patchSources
-            (
-                selectors[selectorI].dict().subDict("patches")
-            );
-            forAllConstIter(dictionary, patchSources, iter)
+            const dictionary& dict = selectors[selectorI].dict();
+
+            if (dict.found("patches"))
             {
-                //const word& patchName = iter().keyword();
-                const word patchName(iter().dict()["name"]);
-                bafflePatches.insert(patchName);
+                const dictionary& patchSources = dict.subDict("patches");
+                forAllConstIter(dictionary, patchSources, iter)
+                {
+                    const word patchName(iter().dict()["name"]);
+                    bafflePatches.insert(patchName);
+                }
+            }
+            else
+            {
+                const word masterName = selectors[selectorI].name() + "_master";
+                bafflePatches.insert(masterName);
+                const word slaveName = selectors[selectorI].name() + "_slave";
+                bafflePatches.insert(slaveName);
             }
         }
     }
 
 
-
     // Create baffles
     // ~~~~~~~~~~~~~~
     // Is done in multiple steps
@@ -344,56 +581,52 @@ int main(int argc, char *argv[])
         const polyBoundaryMesh& pbm = mesh.boundaryMesh();
         forAll(selectors, selectorI)
         {
-            const dictionary& patchSources
-            (
-                selectors[selectorI].dict().subDict("patches")
-            );
-            forAllConstIter(dictionary, patchSources, iter)
-            {
-                //const word& patchName = iter().keyword();
-                const word patchName(iter().dict()["name"]);
-
-                label destPatchI = pbm.findPatchID(patchName);
+            const dictionary& dict = selectors[selectorI].dict();
+            const word& groupName = selectors[selectorI].name();
 
-                if (destPatchI == -1)
+            if (dict.found("patches"))
+            {
+                const dictionary& patchSources = dict.subDict("patches");
+                forAllConstIter(dictionary, patchSources, iter)
                 {
-                    dictionary patchDict = iter().dict();
-                    patchDict.set("nFaces", 0);
-                    patchDict.set("startFace", 0);
+                    const word patchName(iter().dict()["name"]);
 
-                    Info<< "Adding new patch " << patchName
-                        << " from " << patchDict << endl;
+                    if (pbm.findPatchID(patchName) == -1)
+                    {
+                        dictionary patchDict = iter().dict();
+                        patchDict.set("nFaces", 0);
+                        patchDict.set("startFace", 0);
 
-                    autoPtr<polyPatch> ppPtr
-                    (
-                        polyPatch::New
-                        (
-                            patchName,
-                            patchDict,
-                            0,
-                            pbm
-                        )
-                    );
+                        // Note: do not set coupleGroup if constructed from
+                        //       baffles so you have freedom specifying it
+                        //       yourself.
+                        //patchDict.set("coupleGroup", groupName);
 
-                    // Add patch, create calculated everywhere
-                    fvMeshTools::addPatch
-                    (
-                        mesh,
-                        ppPtr(),
-                        dictionary(),   // do not set specialised patchFields
-                        calculatedFvPatchField<scalar>::typeName,
-                        true            // parallel sync'ed addition
-                    );
-
-                    //addedPatches.insert(patchName);
-                }
-                else
-                {
-                    Info<< "Patch '" << patchName << "' already exists.  Only "
-                        << "moving patch faces - type will remain the same"
-                        << endl;
+                        addPatch(mesh, patchName, groupName, patchDict);
+                    }
+                    else
+                    {
+                        Info<< "Patch '" << patchName
+                            << "' already exists.  Only "
+                            << "moving patch faces - type will remain the same"
+                            << endl;
+                    }
                 }
             }
+            else
+            {
+                const dictionary& patchSource = dict.subDict("patchPairs");
+                const word masterName = groupName + "_master";
+                const word slaveName = groupName + "_slave";
+
+                dictionary patchDict = patchSource;
+                patchDict.set("nFaces", 0);
+                patchDict.set("startFace", 0);
+                patchDict.set("coupleGroup", groupName);
+
+                addPatch(mesh, masterName, groupName, patchDict);
+                addPatch(mesh, slaveName, groupName, patchDict);
+            }
         }
     }
 
@@ -428,195 +661,53 @@ int main(int argc, char *argv[])
         label zoneID = mesh.faceZones().findZoneID(name);
         const faceZone& fZone = mesh.faceZones()[zoneID];
 
-        const dictionary& patchSources
-        (
-            selectors[selectorI].dict().subDict("patches")
-        );
-
-        DynamicList<label> newMasterPatches(patchSources.size());
-        DynamicList<label> newSlavePatches(patchSources.size());
+        const dictionary& dict = selectors[selectorI].dict();
 
-        bool master = true;
+        DynamicList<label> newMasterPatches;
+        DynamicList<label> newSlavePatches;
 
-        forAllConstIter(dictionary, patchSources, iter)
+        if (dict.found("patches"))
         {
-            //const word& patchName = iter().keyword();
-            const word patchName(iter().dict()["name"]);
-            label patchI = pbm.findPatchID(patchName);
-            if (master)
-            {
-                newMasterPatches.append(patchI);
-            }
-            else
-            {
-                newSlavePatches.append(patchI);
-            }
-            master = !master;
-        }
-
+            const dictionary& patchSources = dict.subDict("patches");
 
-
-        forAll(newMasterPatches, i)
-        {
-            // Pass 1. Do selected side of zone
-            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-            for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
+            bool master = true;
+            forAllConstIter(dictionary, patchSources, iter)
             {
-                label zoneFaceI = fZone.whichFace(faceI);
-
-                if (zoneFaceI != -1)
+                const word patchName(iter().dict()["name"]);
+                label patchI = pbm.findPatchID(patchName);
+                if (master)
                 {
-                    if (!fZone.flipMap()[zoneFaceI])
-                    {
-                        // Use owner side of face
-                        modifyOrAddFace
-                        (
-                            meshMod,
-                            mesh.faces()[faceI],    // modified face
-                            faceI,                  // label of face
-                            mesh.faceOwner()[faceI],// owner
-                            false,                  // face flip
-                            newMasterPatches[i],    // patch for face
-                            fZone.index(),          // zone for face
-                            false,                  // face flip in zone
-                            modifiedFace            // modify or add status
-                        );
-                    }
-                    else
-                    {
-                        // Use neighbour side of face
-                        modifyOrAddFace
-                        (
-                            meshMod,
-                            mesh.faces()[faceI].reverseFace(),  // modified face
-                            faceI,                      // label of face
-                            mesh.faceNeighbour()[faceI],// owner
-                            true,                       // face flip
-                            newMasterPatches[i],        // patch for face
-                            fZone.index(),              // zone for face
-                            true,                       // face flip in zone
-                            modifiedFace                // modify or add status
-                        );
-                    }
-
-                    nModified++;
+                    newMasterPatches.append(patchI);
                 }
-            }
-
-
-            // Pass 2. Do other side of zone
-            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-            for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
-            {
-                label zoneFaceI = fZone.whichFace(faceI);
-
-                if (zoneFaceI != -1)
+                else
                 {
-                    if (!fZone.flipMap()[zoneFaceI])
-                    {
-                        // Use neighbour side of face
-                        modifyOrAddFace
-                        (
-                            meshMod,
-                            mesh.faces()[faceI].reverseFace(),  // modified face
-                            faceI,                          // label of face
-                            mesh.faceNeighbour()[faceI],    // owner
-                            true,                           // face flip
-                            newSlavePatches[i],             // patch for face
-                            fZone.index(),                  // zone for face
-                            true,                           // face flip in zone
-                            modifiedFace                    // modify or add
-                        );
-                    }
-                    else
-                    {
-                        // Use owner side of face
-                        modifyOrAddFace
-                        (
-                            meshMod,
-                            mesh.faces()[faceI],    // modified face
-                            faceI,                  // label of face
-                            mesh.faceOwner()[faceI],// owner
-                            false,                  // face flip
-                            newSlavePatches[i],     // patch for face
-                            fZone.index(),          // zone for face
-                            false,                  // face flip in zone
-                            modifiedFace            // modify or add status
-                        );
-                    }
+                    newSlavePatches.append(patchI);
                 }
+                master = !master;
             }
+        }
+        else
+        {
+            const word masterName = selectors[selectorI].name() + "_master";
+            newMasterPatches.append(pbm.findPatchID(masterName));
 
+            const word slaveName = selectors[selectorI].name() + "_slave";
+            newSlavePatches.append(pbm.findPatchID(slaveName));
+        }
 
-            // Modify any boundary faces
-            // ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-            // Normal boundary:
-            // - move to new patch. Might already be back-to-back baffle
-            // you want to add cyclic to. Do warn though.
-            //
-            // Processor boundary:
-            // - do not move to cyclic
-            // - add normal patches though.
-
-            // For warning once per patch.
-            labelHashSet patchWarned;
-
-            forAll(pbm, patchI)
-            {
-                const polyPatch& pp = pbm[patchI];
-
-                label newPatchI = newMasterPatches[i];
-
-                if (pp.coupled() && pbm[newPatchI].coupled())
-                {
-                    // Do not allow coupled faces to be moved to different
-                    // coupled patches.
-                }
-                else if (pp.coupled() || !internalFacesOnly)
-                {
-                    forAll(pp, i)
-                    {
-                        label faceI = pp.start()+i;
 
-                        label zoneFaceI = fZone.whichFace(faceI);
 
-                        if (zoneFaceI != -1)
-                        {
-                            if (patchWarned.insert(patchI))
-                            {
-                                WarningIn(args.executable())
-                                    << "Found boundary face (in patch "
-                                    << pp.name()
-                                    << ") in faceZone " << fZone.name()
-                                    << " to convert to baffle patch "
-                                    << pbm[newPatchI].name()
-                                    << endl
-                                    << "    Run with -internalFacesOnly option"
-                                    << " if you don't wish to convert"
-                                    << " boundary faces." << endl;
-                            }
-
-                            modifyOrAddFace
-                            (
-                                meshMod,
-                                mesh.faces()[faceI],        // modified face
-                                faceI,                      // label of face
-                                mesh.faceOwner()[faceI],    // owner
-                                false,                      // face flip
-                                newPatchI,                  // patch for face
-                                fZone.index(),              // zone for face
-                                fZone.flipMap()[zoneFaceI], // face flip in zone
-                                modifiedFace                // modify or add
-                            );
-                            nModified++;
-                        }
-                    }
-                }
-            }
-        }
+        createFaces
+        (
+            internalFacesOnly,
+            mesh,
+            fZone,
+            newMasterPatches,
+            newSlavePatches,
+            meshMod,
+            modifiedFace,
+            nModified
+        );
     }
 
 
@@ -672,29 +763,64 @@ int main(int argc, char *argv[])
         const polyBoundaryMesh& pbm = mesh.boundaryMesh();
         forAll(selectors, selectorI)
         {
-            const dictionary& patchSources
-            (
-                selectors[selectorI].dict().subDict("patches")
-            );
-            forAllConstIter(dictionary, patchSources, iter)
+            const dictionary& dict = selectors[selectorI].dict();
+            if (dict.found("patches"))
             {
-                //const word& patchName = iter().keyword();
-                const word patchName(iter().dict()["name"]);
-                label patchI = pbm.findPatchID(patchName);
+                const dictionary& patchSources = dict.subDict("patches");
 
-                if (iter().dict().found("patchFields"))
+                forAllConstIter(dictionary, patchSources, iter)
                 {
-                    const dictionary& patchFieldsDict = iter().dict().subDict
+                    const word patchName(iter().dict()["name"]);
+                    label patchI = pbm.findPatchID(patchName);
+
+                    if (iter().dict().found("patchFields"))
+                    {
+                        const dictionary& patchFieldsDict =
+                            iter().dict().subDict
+                            (
+                                "patchFields"
+                            );
+
+                        fvMeshTools::setPatchFields
+                        (
+                            mesh,
+                            patchI,
+                            patchFieldsDict
+                        );
+                    }
+                }
+            }
+            else
+            {
+                const dictionary& patchSource = dict.subDict("patchPairs");
+                const word& groupName = selectors[selectorI].name();
+
+                if (patchSource.found("patchFields"))
+                {
+                    dictionary patchFieldsDict = patchSource.subDict
                     (
                         "patchFields"
                     );
+                    // Add coupleGroup to all entries
+                    forAllIter(dictionary, patchFieldsDict, iter)
+                    {
+                        if (iter().isDict())
+                        {
+                            dictionary& dict = iter().dict();
+                            dict.set("coupleGroup", groupName);
+                        }
+                    }
 
-                    fvMeshTools::setPatchFields
-                    (
-                        mesh,
-                        patchI,
-                        patchFieldsDict
-                    );
+                    const labelList& patchIDs = pbm.groupPatchIDs()[groupName];
+                    forAll(patchIDs, i)
+                    {
+                        fvMeshTools::setPatchFields
+                        (
+                            mesh,
+                            patchIDs[i],
+                            patchFieldsDict
+                        );
+                    }
                 }
             }
         }
diff --git a/applications/utilities/mesh/manipulation/createBaffles/createBafflesDict b/applications/utilities/mesh/manipulation/createBaffles/createBafflesDict
index ef2eb96227f804d67ccc9b686455c4e6833b442e..27ff9d287e27e84e62c95be51d4bcf266642e0ba 100644
--- a/applications/utilities/mesh/manipulation/createBaffles/createBafflesDict
+++ b/applications/utilities/mesh/manipulation/createBaffles/createBafflesDict
@@ -40,123 +40,47 @@ internalFacesOnly true;
 // Baffles to create.
 baffles
 {
-    baffleFaces
+    baffle1
     {
-        //- Use predefined faceZone to select faces and orientation.
-        type        faceZone;
-        zoneName    baffleFaces;
-
-
+        //- Use surface to select faces and orientation.
+        type        searchableSurface;
+        surface     triSurfaceMesh;
+        name        baffle1D.stl;
         //- Optional flip
         //flip        false;
 
-        patches
-        {
-            master
-            {
-                //- Master side patch
-                name            baffles;
-                type            wall;
 
-                //- Optional override of added patchfields. If not specified
-                //  any added patchfields are of type calculated.
-                patchFields
-                {
-                    epsilon
-                    {
-                        type            epsilonWallFunction;
-                        Cmu             0.09;
-                        kappa           0.41;
-                        E               9.8;
-                        value           uniform 0;
-                    }
-                    k
-                    {
-                        type            kqRWallFunction;
-                        value           uniform 0;
-                    }
-                    nut
-                    {
-                        type            nutkWallFunction;
-                        Cmu             0.09;
-                        kappa           0.41;
-                        E               9.8;
-                        value           uniform 0;
-                    }
-                    nuTilda
-                    {
-                        type            zeroGradient;
-                    }
-                    p
-                    {
-                        type            zeroGradient;
-                    }
-                    U
-                    {
-                        type            fixedValue;
-                        value           uniform (0 0 0);
-                    }
-                }
-            }
-            slave
+        // Generate patchGroup baffle1 with two patches:
+        //  - baffle1_master
+        //  - baffle1_slave
+        patchPairs
+        {
+            type            wall;
+            //- Optional override of added patchfields. If not specified
+            //  any added patchfields are of type calculated.
+            patchFields
             {
-                //- Slave side patch
-                name            baffles;
-                type            wall;
-
-                patchFields
+                U
                 {
-                    epsilon
-                    {
-                        type            epsilonWallFunction;
-                        Cmu             0.09;
-                        kappa           0.41;
-                        E               9.8;
-                        value           uniform 0;
-                    }
-                    k
-                    {
-                        type            kqRWallFunction;
-                        value           uniform 0;
-                    }
-                    nut
-                    {
-                        type            nutkWallFunction;
-                        Cmu             0.09;
-                        kappa           0.41;
-                        E               9.8;
-                        value           uniform 0;
-                    }
-                    nuTilda
-                    {
-                        type            zeroGradient;
-                    }
-                    p
-                    {
-                        type            zeroGradient;
-                    }
-                    U
-                    {
-                        type            fixedValue;
-                        value           uniform (0 0 0);
-                    }
+                    type            fixedValue;
+                    value           uniform (0 0 0);
                 }
             }
         }
     }
 
 
-
     cyclicFaces
     {
         //- Select faces and orientation through a searchableSurface
         type        searchableSurface;
         surface     searchablePlate;
-        //name sphere.stl;  // name if surface=triSurfaceMesh
 
         origin      (0.099 -0.006 0.004);
         span        (0 0.012 0.012);
 
+
+        // Generate patches explicitly
         patches
         {
             master
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 6b52b27068e611261b59ba1e90bbe2aedfdd9df1..2adea5c4e113bb20cbf8c0e5b0d6f8a8219648aa 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -199,6 +199,7 @@ mappedPatches/mappedPolyPatch/mappedPatchBase.C
 mappedPatches/mappedPolyPatch/mappedPolyPatch.C
 mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C
 mappedPatches/mappedPolyPatch/mappedVariableThicknessWallPolyPatch.C
+mappedPatches/mappedPolyPatch/coupleGroupIdentifier.C
 
 mappedPatches/mappedPointPatch/mappedPointPatch.C
 mappedPatches/mappedPointPatch/mappedWallPointPatch.C
diff --git a/src/meshTools/cellDist/wallPoint/wallPointI.H b/src/meshTools/cellDist/wallPoint/wallPointI.H
index 95f0f02cdb3372cb463efa529f276a691aad2f73..306a274984be67e220f86fd089fdd2cc934ef6ea 100644
--- a/src/meshTools/cellDist/wallPoint/wallPointI.H
+++ b/src/meshTools/cellDist/wallPoint/wallPointI.H
@@ -144,8 +144,7 @@ inline bool Foam::wallPoint::sameGeometry
     const wallPoint& w2,
     const scalar tol,
     TrackingData& td
-)
- const
+) const
 {
     scalar diff = mag(distSqr() - w2.distSqr());
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.C b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.C
new file mode 100644
index 0000000000000000000000000000000000000000..e520c539616bec12be3f1327b3ba18e6a860f398
--- /dev/null
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.C
@@ -0,0 +1,260 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2013 OpenFOAM Foundation
+     \\/     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 "coupleGroupIdentifier.H"
+#include "polyMesh.H"
+#include "Time.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::label Foam::coupleGroupIdentifier::findOtherPatchID
+(
+    const polyMesh& mesh,
+    const polyPatch& thisPatch
+) const
+{
+    const polyBoundaryMesh& pbm = mesh.boundaryMesh();
+
+    if (!valid())
+    {
+        FatalErrorIn
+        (
+            "coupleGroupIdentifier::findOtherPatchID(const polyPatch&) const"
+        )   << "Invalid coupleGroup patch group"
+            << " on patch " << thisPatch.name()
+            << " in region " << pbm.mesh().name()
+            << exit(FatalError);
+    }
+
+    HashTable<labelList, word>::const_iterator fnd =
+        pbm.groupPatchIDs().find(name());
+
+    if (fnd == pbm.groupPatchIDs().end())
+    {
+        if (&mesh == &thisPatch.boundaryMesh().mesh())
+        {
+            // thisPatch should be in patchGroup
+            FatalErrorIn
+            (
+                "coupleGroupIdentifier::findOtherPatchID"
+                "(const polyMesh&, const polyPatch&) const"
+            )   << "Patch " << thisPatch.name()
+                << " should be in patchGroup " << name()
+                << " in region " << pbm.mesh().name()
+                << exit(FatalError);
+        }
+
+        return -1;
+    }
+
+    // Mesh has patch group
+    const labelList& patchIDs = fnd();
+
+    if (&mesh == &thisPatch.boundaryMesh().mesh())
+    {
+        if (patchIDs.size() > 2 || patchIDs.size() == 0)
+        {
+            FatalErrorIn
+            (
+                "coupleGroupIdentifier::findOtherPatchID"
+                "(const polyMesh&, const polyPatch&) const"
+            )   << "Couple patchGroup " << name()
+                << " with contents " << patchIDs
+                << " not of size < 2"
+                << " on patch " << thisPatch.name()
+                << " region " << thisPatch.boundaryMesh().mesh().name()
+                << exit(FatalError);
+
+            return -1;
+        }
+
+        label index = findIndex(patchIDs, thisPatch.index());
+
+        if (index == -1)
+        {
+            FatalErrorIn
+            (
+                "coupleGroupIdentifier::findOtherPatchID"
+                "(const polyMesh&, const polyPatch&) const"
+            )   << "Couple patchGroup " << name()
+                << " with contents " << patchIDs
+                << " does not contain patch " << thisPatch.name()
+                << " in region " << pbm.mesh().name()
+                << exit(FatalError);
+
+            return -1;
+        }
+
+
+        if (patchIDs.size() == 2)
+        {
+            // Return the other patch
+            return patchIDs[1-index];
+        }
+        else    // size == 1
+        {
+            return -1;
+        }
+    }
+    else
+    {
+        if (patchIDs.size() != 1)
+        {
+            FatalErrorIn
+            (
+                "coupleGroupIdentifier::findOtherPatchID"
+                "(const polyMesh&, const polyPatch&) const"
+            )   << "Couple patchGroup " << name()
+                << " with contents " << patchIDs
+                << " in region " << mesh.name()
+                << " should only contain a single patch"
+                << " when matching patch " << thisPatch.name()
+                << " in region " << pbm.mesh().name()
+                << exit(FatalError);
+        }
+
+        return patchIDs[0];
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::coupleGroupIdentifier::coupleGroupIdentifier()
+:
+    name_()
+{}
+
+
+Foam::coupleGroupIdentifier::coupleGroupIdentifier(const word& name)
+:
+    name_(name)
+{}
+
+
+Foam::coupleGroupIdentifier::coupleGroupIdentifier(const dictionary& dict)
+:
+    name_(dict.lookupOrDefault<word>("coupleGroup", ""))
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+Foam::label Foam::coupleGroupIdentifier::findOtherPatchID
+(
+    const polyPatch& thisPatch
+) const
+{
+    const polyBoundaryMesh& pbm = thisPatch.boundaryMesh();
+
+    return findOtherPatchID(pbm.mesh(), thisPatch);
+}
+
+
+Foam::label Foam::coupleGroupIdentifier::findOtherPatchID
+(
+    const polyPatch& thisPatch,
+    word& otherRegion
+) const
+{
+    const polyBoundaryMesh& pbm = thisPatch.boundaryMesh();
+    const polyMesh& thisMesh = pbm.mesh();
+    const Time& runTime = thisMesh.time();
+
+
+    // Loop over all regions to find other patch in coupleGroup
+    HashTable<const polyMesh*> meshSet = runTime.lookupClass<polyMesh>();
+
+    label otherPatchID = -1;
+
+    forAllConstIter(HashTable<const polyMesh*>, meshSet, iter)
+    {
+        const polyMesh& mesh = *iter();
+
+        label patchID = findOtherPatchID(mesh, thisPatch);
+
+        if (patchID != -1)
+        {
+            if (otherPatchID != -1)
+            {
+                FatalErrorIn
+                (
+                    "coupleGroupIdentifier::findOtherPatchID"
+                    "(const polyPatch&, word&) const"
+                )   << "Couple patchGroup " << name()
+                    << " should be present on only two patches"
+                    << " in any of the meshes in " << meshSet.sortedToc()
+                    << endl
+                    << "    It seems to be present on patch "
+                    << thisPatch.name()
+                    << " in region " << thisMesh.name()
+                    << ", on patch " << otherPatchID
+                    << " in region " << otherRegion
+                    << " and on patch " << patchID
+                    << " in region " << mesh.name()
+                    << exit(FatalError);
+            }
+            otherPatchID = patchID;
+            otherRegion = mesh.name();
+        }
+    }
+
+    if (otherPatchID == -1)
+    {
+        FatalErrorIn
+        (
+            "coupleGroupIdentifier::findOtherPatchID"
+            "(const polyPatch&, word&) const"
+        )   << "Couple patchGroup " << name()
+            << " not found in any of the other meshes " << meshSet.sortedToc()
+            << " on patch " << thisPatch.name()
+            << " region " << thisMesh.name()
+            << exit(FatalError);
+    }
+
+    return otherPatchID;
+}
+
+
+void Foam::coupleGroupIdentifier::write(Ostream& os) const
+{
+    if (valid())
+    {
+        os.writeKeyword("coupleGroup") << name() << token::END_STATEMENT << nl;
+    }
+}
+
+
+// * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const coupleGroupIdentifier& p)
+{
+    p.write(os);
+    os.check("Ostream& operator<<(Ostream& os, const coupleGroupIdentifier& p");
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.H b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.H
new file mode 100644
index 0000000000000000000000000000000000000000..895dcf53ffabc0fa5fa5cd81dc17f091ce9027a8
--- /dev/null
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifier.H
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2013 OpenFOAM Foundation
+     \\/     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/>.
+
+Class
+    Foam::coupleGroupIdentifier
+
+Description
+    Encapsulates using patchGroups to specify coupled patch
+
+SourceFiles
+    coupleGroupIdentifierI.H
+    coupleGroupIdentifier.C
+    coupleGroupIdentifierIO.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef coupleGroupIdentifier_H
+#define coupleGroupIdentifier_H
+
+#include "word.H"
+#include "label.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class dictionary;
+class polyMesh;
+class polyPatch;
+class Ostream;
+
+// Forward declaration of friend functions and operators
+class coupleGroupIdentifier;
+Ostream& operator<<(Ostream&, const coupleGroupIdentifier&);
+
+
+/*---------------------------------------------------------------------------*\
+                         Class coupleGroupIdentifier Declaration
+\*---------------------------------------------------------------------------*/
+
+class coupleGroupIdentifier
+{
+    // Private data
+
+        //- Name of patchGroup
+        word name_;
+
+
+    // Private Member Functions
+
+        //- Find other patch in specified mesh. Returns index of patch or -1.
+        label findOtherPatchID(const polyMesh&, const polyPatch&) const;
+
+
+public:
+
+    // Constructors
+
+        //- Construct null
+        coupleGroupIdentifier();
+
+        //- Construct from components
+        coupleGroupIdentifier(const word& patchGroupName);
+
+        //- Construct from dictionary
+        coupleGroupIdentifier(const dictionary&);
+
+
+    // Member Functions
+
+        //- Name of patchGroup
+        inline const word& name() const;
+
+        //- Is a valid patchGroup
+        inline bool valid() const;
+
+        //- Find other patch in same region. Returns index of patch or -1.
+        label findOtherPatchID(const polyPatch&) const;
+
+        //- Find other patch and region. Returns index of patch and sets
+        //  otherRegion to name of region. Fatal error if patch not found
+        label findOtherPatchID(const polyPatch&, word&) const;
+
+        //- Write the data as a dictionary
+        void write(Ostream&) const;
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<(Ostream&, const coupleGroupIdentifier&);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "coupleGroupIdentifierI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifierI.H b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifierI.H
new file mode 100644
index 0000000000000000000000000000000000000000..ee6503514c190fb639a267e1a0ee9c928e72f062
--- /dev/null
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/coupleGroupIdentifierI.H
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2013 OpenFOAM Foundation
+     \\/     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 "coupleGroupIdentifier.H"
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const Foam::word& Foam::coupleGroupIdentifier::name() const
+{
+    return name_;
+}
+
+
+bool Foam::coupleGroupIdentifier::valid() const
+{
+    return !name_.empty();
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
index 129445ef80d70eb12665e15b8aa23b8e9822cda4..30edacf93cf6d52e65ed5d5c113979e4a65d99d7 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
@@ -40,6 +40,7 @@ License
 #include "SubField.H"
 #include "triPointRef.H"
 #include "syncTools.H"
+#include "treeDataCell.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -202,7 +203,7 @@ void Foam::mappedPatchBase::findSamples
     {
         case NEARESTCELL:
         {
-            if (samplePatch_.size() && samplePatch_ != "none")
+            if (samplePatch().size() && samplePatch() != "none")
             {
                 FatalErrorIn
                 (
@@ -213,14 +214,13 @@ void Foam::mappedPatchBase::findSamples
             }
 
             //- Note: face-diagonal decomposition
-            const meshSearchMeshObject& meshSearchEngine =
-                meshSearchMeshObject::New(mesh);
+            const indexedOctree<Foam::treeDataCell>& tree = mesh.cellTree();
 
             forAll(samples, sampleI)
             {
                 const point& sample = samples[sampleI];
 
-                label cellI = meshSearchEngine.findCell(sample);
+                label cellI = tree.findInside(sample);
 
                 if (cellI == -1)
                 {
@@ -391,7 +391,7 @@ void Foam::mappedPatchBase::findSamples
 
         case NEARESTFACE:
         {
-            if (samplePatch_.size() && samplePatch_ != "none")
+            if (samplePatch().size() && samplePatch() != "none")
             {
                 FatalErrorIn
                 (
@@ -454,7 +454,7 @@ void Foam::mappedPatchBase::findSamples
 
     if (debug)
     {
-        Info<< "mappedPatchBase::findSamples on mesh " << sampleRegion_
+        Info<< "mappedPatchBase::findSamples on mesh " << sampleRegion()
             << " : " << endl;
         forAll(nearest, sampleI)
         {
@@ -516,8 +516,8 @@ void Foam::mappedPatchBase::calcMapping() const
     bool sampleMyself =
     (
         mode_ == NEARESTPATCHFACE
-     && sampleRegion_ == patch_.boundaryMesh().mesh().name()
-     && samplePatch_ == patch_.name()
+     && sampleRegion() == patch_.boundaryMesh().mesh().name()
+     && samplePatch() == patch_.name()
     );
 
     // Check offset
@@ -544,9 +544,9 @@ void Foam::mappedPatchBase::calcMapping() const
             << " will find the faces themselves which does not make sense"
             << " for anything but testing." << endl
             << "patch_:" << patch_.name() << endl
-            << "sampleRegion_:" << sampleRegion_ << endl
+            << "sampleRegion_:" << sampleRegion() << endl
             << "mode_:" << sampleModeNames_[mode_] << endl
-            << "samplePatch_:" << samplePatch_ << endl
+            << "samplePatch_:" << samplePatch() << endl
             << "offsetMode_:" << offsetModeNames_[offsetMode_] << endl;
     }
 
@@ -604,12 +604,10 @@ void Foam::mappedPatchBase::calcMapping() const
                     << " out of " << sampleProcs.size() << " total samples."
                     << " Sampling these on owner cell centre instead." << endl
                     << "On patch " << patch_.name()
-                    << " on region " << sampleRegion_
+                    << " on region " << sampleRegion()
                     << " in mode " << sampleModeNames_[mode_] << endl
-                    << "whilst sampling patch " << samplePatch_ << endl
-                    << " with offset mode " << offsetModeNames_[offsetMode_]
-                    << endl
-                    << "Suppressing further warnings from " << type() << endl;
+                    << "with offset mode " << offsetModeNames_[offsetMode_]
+                    << ". Suppressing further warnings from " << type() << endl;
 
                 hasWarned = true;
             }
@@ -657,7 +655,7 @@ void Foam::mappedPatchBase::calcMapping() const
     //        << " for proc:" << patchFaceProcs[i]
     //        << " face:" << patchFaces[i]
     //        << " at:" << patchFc[i] << endl
-    //        << "Found data in region " << sampleRegion_
+    //        << "Found data in region " << sampleRegion()
     //        << " at proc:" << sampleProcs[i]
     //        << " face:" << sampleIndices[i]
     //        << " at:" << sampleLocations[i]
@@ -942,7 +940,8 @@ Foam::mappedPatchBase::mappedPatchBase
     patch_(pp),
     sampleRegion_(patch_.boundaryMesh().mesh().name()),
     mode_(NEARESTPATCHFACE),
-    samplePatch_("none"),
+    samplePatch_(""),
+    coupleGroup_(),
     offsetMode_(UNIFORM),
     offset_(vector::zero),
     offsets_(pp.size(), offset_),
@@ -969,6 +968,7 @@ Foam::mappedPatchBase::mappedPatchBase
     sampleRegion_(sampleRegion),
     mode_(mode),
     samplePatch_(samplePatch),
+    coupleGroup_(),
     offsetMode_(NONUNIFORM),
     offset_(vector::zero),
     offsets_(offsets),
@@ -995,6 +995,7 @@ Foam::mappedPatchBase::mappedPatchBase
     sampleRegion_(sampleRegion),
     mode_(mode),
     samplePatch_(samplePatch),
+    coupleGroup_(),
     offsetMode_(UNIFORM),
     offset_(offset),
     offsets_(0),
@@ -1021,6 +1022,7 @@ Foam::mappedPatchBase::mappedPatchBase
     sampleRegion_(sampleRegion),
     mode_(mode),
     samplePatch_(samplePatch),
+    coupleGroup_(),
     offsetMode_(NORMAL),
     offset_(vector::zero),
     offsets_(0),
@@ -1041,16 +1043,10 @@ Foam::mappedPatchBase::mappedPatchBase
 )
 :
     patch_(pp),
-    sampleRegion_
-    (
-        dict.lookupOrDefault
-        (
-            "sampleRegion",
-            patch_.boundaryMesh().mesh().name()
-        )
-    ),
+    sampleRegion_(dict.lookupOrDefault<word>("sampleRegion", "")),
     mode_(sampleModeNames_.read(dict.lookup("sampleMode"))),
-    samplePatch_(dict.lookup("samplePatch")),
+    samplePatch_(dict.lookupOrDefault<word>("samplePatch", "")),
+    coupleGroup_(dict),
     offsetMode_(UNIFORM),
     offset_(vector::zero),
     offsets_(0),
@@ -1062,6 +1058,16 @@ Foam::mappedPatchBase::mappedPatchBase
     surfPtr_(NULL),
     surfDict_(dict.subOrEmptyDict("surface"))
 {
+    if (!coupleGroup_.valid())
+    {
+        if (sampleRegion_.empty())
+        {
+            // If no coupleGroup and no sampleRegion assume local region
+            sampleRegion_ = patch_.boundaryMesh().mesh().name();
+            sameRegion_ = true;
+        }
+    }
+
     if (dict.found("offsetMode"))
     {
         offsetMode_ = offsetModeNames_.read(dict.lookup("offsetMode"));
@@ -1099,7 +1105,7 @@ Foam::mappedPatchBase::mappedPatchBase
         //offsets_ = pointField(dict.lookup("offsets"));
         offsets_ = readListOrField("offsets", dict, patch_.size());
     }
-    else
+    else if (mode_ != NEARESTPATCHFACE && mode_ != NEARESTPATCHFACEAMI)
     {
         FatalIOErrorIn
         (
@@ -1116,6 +1122,60 @@ Foam::mappedPatchBase::mappedPatchBase
 }
 
 
+Foam::mappedPatchBase::mappedPatchBase
+(
+    const polyPatch& pp,
+    const sampleMode mode,
+    const dictionary& dict
+)
+:
+    patch_(pp),
+    sampleRegion_(dict.lookupOrDefault<word>("sampleRegion", "")),
+    mode_(mode),
+    samplePatch_(dict.lookupOrDefault<word>("samplePatch", "")),
+    coupleGroup_(dict), //dict.lookupOrDefault<word>("coupleGroup", "")),
+    offsetMode_(UNIFORM),
+    offset_(vector::zero),
+    offsets_(0),
+    distance_(0.0),
+    sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
+    mapPtr_(NULL),
+    AMIPtr_(NULL),
+    AMIReverse_(dict.lookupOrDefault<bool>("flipNormals", false)),
+    surfPtr_(NULL),
+    surfDict_(dict.subOrEmptyDict("surface"))
+{
+    if (mode != NEARESTPATCHFACE && mode != NEARESTPATCHFACEAMI)
+    {
+        FatalIOErrorIn
+        (
+            "mappedPatchBase::mappedPatchBase\n"
+            "(\n"
+            "    const polyPatch&,\n"
+            "    const sampleMode,\n"
+            "    const dictionary&\n"
+            ")\n",
+            dict
+        )   << "Construct from sampleMode and dictionary only applicable for "
+            << " collocated patches in modes "
+            << sampleModeNames_[NEARESTPATCHFACE] << ','
+            << sampleModeNames_[NEARESTPATCHFACEAMI]
+            << exit(FatalIOError);
+    }
+
+
+    if (!coupleGroup_.valid())
+    {
+        if (sampleRegion_.empty())
+        {
+            // If no coupleGroup and no sampleRegion assume local region
+            sampleRegion_ = patch_.boundaryMesh().mesh().name();
+            sameRegion_ = true;
+        }
+    }
+}
+
+
 Foam::mappedPatchBase::mappedPatchBase
 (
     const polyPatch& pp,
@@ -1126,6 +1186,7 @@ Foam::mappedPatchBase::mappedPatchBase
     sampleRegion_(mpb.sampleRegion_),
     mode_(mpb.mode_),
     samplePatch_(mpb.samplePatch_),
+    coupleGroup_(mpb.coupleGroup_),
     offsetMode_(mpb.offsetMode_),
     offset_(mpb.offset_),
     offsets_(mpb.offsets_),
@@ -1150,6 +1211,7 @@ Foam::mappedPatchBase::mappedPatchBase
     sampleRegion_(mpb.sampleRegion_),
     mode_(mpb.mode_),
     samplePatch_(mpb.samplePatch_),
+    coupleGroup_(mpb.coupleGroup_),
     offsetMode_(mpb.offsetMode_),
     offset_(mpb.offset_),
     offsets_
@@ -1190,7 +1252,7 @@ const Foam::polyMesh& Foam::mappedPatchBase::sampleMesh() const
 {
     return patch_.boundaryMesh().mesh().time().lookupObject<polyMesh>
     (
-        sampleRegion_
+        sampleRegion()
     );
 }
 
@@ -1199,12 +1261,12 @@ const Foam::polyPatch& Foam::mappedPatchBase::samplePolyPatch() const
 {
     const polyMesh& nbrMesh = sampleMesh();
 
-    const label patchI = nbrMesh.boundaryMesh().findPatchID(samplePatch_);
+    const label patchI = nbrMesh.boundaryMesh().findPatchID(samplePatch());
 
     if (patchI == -1)
     {
         FatalErrorIn("mappedPatchBase::samplePolyPatch()")
-            << "Cannot find patch " << samplePatch_
+            << "Cannot find patch " << samplePatch()
             << " in region " << sampleRegion_ << endl
             << "Valid patches are " << nbrMesh.boundaryMesh().names()
             << exit(FatalError);
@@ -1340,46 +1402,60 @@ void Foam::mappedPatchBase::write(Ostream& os) const
 {
     os.writeKeyword("sampleMode") << sampleModeNames_[mode_]
         << token::END_STATEMENT << nl;
-    os.writeKeyword("sampleRegion") << sampleRegion_
+    os.writeKeyword("sampleRegion") << sampleRegion()
         << token::END_STATEMENT << nl;
-    os.writeKeyword("samplePatch") << samplePatch_
-        << token::END_STATEMENT << nl;
-
-    os.writeKeyword("offsetMode") << offsetModeNames_[offsetMode_]
+    os.writeKeyword("samplePatch") << samplePatch()
         << token::END_STATEMENT << nl;
+    coupleGroup_.write(os);
 
-    switch (offsetMode_)
+    if
+    (
+        offsetMode_ == UNIFORM
+     && offset_ == vector::zero
+     && (mode_ == NEARESTPATCHFACE || mode_ == NEARESTPATCHFACEAMI)
+    )
     {
-        case UNIFORM:
-        {
-            os.writeKeyword("offset") << offset_ << token::END_STATEMENT << nl;
-            break;
-        }
-        case NONUNIFORM:
-        {
-            offsets_.writeEntry("offsets", os);
-            break;
-        }
-        case NORMAL:
-        {
-            os.writeKeyword("distance") << distance_ << token::END_STATEMENT
-                << nl;
-            break;
-        }
+        // Collocated mode. No need to write offset data
     }
-
-    if (mode_ == NEARESTPATCHFACEAMI)
+    else
     {
-        if (AMIReverse_)
+        os.writeKeyword("offsetMode") << offsetModeNames_[offsetMode_]
+            << token::END_STATEMENT << nl;
+
+        switch (offsetMode_)
         {
-            os.writeKeyword("flipNormals") << AMIReverse_
-                << token::END_STATEMENT << nl;
+            case UNIFORM:
+            {
+                os.writeKeyword("offset") << offset_ << token::END_STATEMENT
+                    << nl;
+                break;
+            }
+            case NONUNIFORM:
+            {
+                offsets_.writeEntry("offsets", os);
+                break;
+            }
+            case NORMAL:
+            {
+                os.writeKeyword("distance") << distance_ << token::END_STATEMENT
+                    << nl;
+                break;
+            }
         }
 
-        if (!surfDict_.empty())
+        if (mode_ == NEARESTPATCHFACEAMI)
         {
-            os.writeKeyword(surfDict_.dictName());
-            os  << surfDict_;
+            if (AMIReverse_)
+            {
+                os.writeKeyword("flipNormals") << AMIReverse_
+                    << token::END_STATEMENT << nl;
+            }
+
+            if (!surfDict_.empty())
+            {
+                os.writeKeyword(surfDict_.dictName());
+                os  << surfDict_;
+            }
         }
     }
 }
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
index f72f407d8102a991e070229c3b1d34cc6c097a31..928a1d5b85fea42c6574ae9c5235986226d46337 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.H
@@ -45,9 +45,13 @@ Description
         //                         beforehand)
         sampleMode nearestCell;
 
-        // If sampleMod is nearestPatchFace : patch to find faces of
+        // If sampleMode is nearestPatchFace : patch to find faces of
         samplePatch movingWall;
 
+        // If sampleMode is nearestPatchFace : specify patchgroup to find
+        // samplePatch and sampleRegion (if not provided)
+        coupleGroup baffleGroup;
+
         // How to supply offset (w.r.t. my patch face centres):
         // - uniform : single offset vector
         // - nonuniform : per-face offset vector
@@ -78,6 +82,7 @@ SourceFiles
 #include "Tuple2.H"
 #include "pointIndexHit.H"
 #include "AMIPatchToPatchInterpolation.H"
+#include "coupleGroupIdentifier.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -180,13 +185,16 @@ protected:
         const polyPatch& patch_;
 
         //- Region to sample
-        const word sampleRegion_;
+        mutable word sampleRegion_;
 
         //- What to sample
         const sampleMode mode_;
 
         //- Patch (if in sampleMode NEARESTPATCH*)
-        const word samplePatch_;
+        mutable word samplePatch_;
+
+        //- PatchGroup (if in sampleMode NEARESTPATCH*)
+        const coupleGroupIdentifier coupleGroup_;
 
         //- How to obtain samples
         offsetMode offsetMode_;
@@ -201,7 +209,7 @@ protected:
         scalar distance_;
 
         //- Same region
-        const bool sameRegion_;
+        mutable bool sameRegion_;
 
 
         // Derived information
@@ -315,6 +323,11 @@ public:
         //- Construct from dictionary
         mappedPatchBase(const polyPatch&, const dictionary&);
 
+        //- Construct from dictionary and (collocated) sample mode
+        //  (only for nearestPatchFace, nearestPatchFaceAMI, nearestPatchPoint)
+        //  Assumes zero offset.
+        mappedPatchBase(const polyPatch&, const sampleMode, const dictionary&);
+
         //- Construct as copy, resetting patch
         mappedPatchBase(const polyPatch&, const mappedPatchBase&);
 
@@ -346,6 +359,9 @@ public:
             //- Patch (only if NEARESTPATCHFACE)
             inline const word& samplePatch() const;
 
+            //- PatchGroup (only if NEARESTPATCHFACE)
+            inline const word& coupleGroup() const;
+
             //- Return size of mapped mesh/patch/boundary
             inline label sampleSize() const;
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
index 927534210f1079d551dde3be37d7ff2bcd99a9a8..8133a9d69234ae1d7c9c2e1ccb140740ed4b1bf3 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBaseI.H
@@ -32,16 +32,62 @@ Foam::mappedPatchBase::mode() const
 
 inline const Foam::word& Foam::mappedPatchBase::sampleRegion() const
 {
+    if (sampleRegion_.empty())
+    {
+        if (!coupleGroup_.valid())
+        {
+            FatalErrorIn("mappedPatchBase::sampleRegion()")
+                << "Supply either a regionName or a coupleGroup"
+                << " for patch " << patch_.name()
+                << " in region " << patch_.boundaryMesh().mesh().name()
+                << exit(FatalError);
+        }
+
+        // Try and use patchGroup to find samplePatch and sampleRegion
+        label samplePatchID = coupleGroup_.findOtherPatchID
+        (
+            patch_,
+            sampleRegion_
+        );
+
+        samplePatch_ = sampleMesh().boundaryMesh()[samplePatchID].name();
+    }
     return sampleRegion_;
 }
 
 
 inline const Foam::word& Foam::mappedPatchBase::samplePatch() const
 {
+    if (samplePatch_.empty())
+    {
+        if (!coupleGroup_.valid())
+        {
+            FatalErrorIn("mappedPatchBase::samplePolyPatch()")
+                << "Supply either a patchName or a coupleGroup"
+                << " for patch " << patch_.name()
+                << " in region " << patch_.boundaryMesh().mesh().name()
+                << exit(FatalError);
+        }
+
+        // Try and use patchGroup to find samplePatch and sampleRegion
+        label samplePatchID = coupleGroup_.findOtherPatchID
+        (
+            patch_,
+            sampleRegion_
+        );
+
+        samplePatch_ = sampleMesh().boundaryMesh()[samplePatchID].name();
+    }
     return samplePatch_;
 }
 
 
+inline const Foam::word& Foam::mappedPatchBase::coupleGroup() const
+{
+    return coupleGroup_.name();
+}
+
+
 inline Foam::label Foam::mappedPatchBase::sampleSize() const
 {
     switch (mode_)
diff --git a/src/meshTools/tetOverlapVolume/tetOverlapVolume.C b/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
index e73df2f6952c2137a280a649bb2470b2edf7eb25..c44384084e81843db4c404aa34fbd010a1762fdf 100644
--- a/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
+++ b/src/meshTools/tetOverlapVolume/tetOverlapVolume.C
@@ -396,10 +396,7 @@ Foam::labelList Foam::tetOverlapVolume::overlappingCells
 {
     const indexedOctree<treeDataCell>& treeA = fromMesh.cellTree();
 
-    treeBoundBox bbB
-    (
-        pointField(toMesh.points(), toMesh.cellPoints()[iTo])
-    );
+    treeBoundBox bbB(toMesh.points(), toMesh.cellPoints()[iTo]);
 
     return treeA.findBox(bbB);
 }
diff --git a/src/sampling/meshToMeshInterpolation/meshToMeshNew/calcMethod/meshToMeshMethod/meshToMeshMethod.C b/src/sampling/meshToMeshInterpolation/meshToMeshNew/calcMethod/meshToMeshMethod/meshToMeshMethod.C
index 29e066dd542937b69a6872ca663bd0c9e1622c18..9bfb15adc06132042dc160efc0d9e70a923aa239 100644
--- a/src/sampling/meshToMeshInterpolation/meshToMeshNew/calcMethod/meshToMeshMethod/meshToMeshMethod.C
+++ b/src/sampling/meshToMeshInterpolation/meshToMeshNew/calcMethod/meshToMeshMethod/meshToMeshMethod.C
@@ -84,14 +84,7 @@ bool Foam::meshToMeshMethod::intersect
 
     tetOverlapVolume overlapEngine;
 
-    treeBoundBox bbTgtCell
-    (
-        pointField
-        (
-            tgt_.points(),
-            tgt_.cellPoints()[tgtCellI]
-        )
-    );
+    treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCellI]);
 
     return overlapEngine.cellCellOverlapMinDecomp
     (
@@ -113,14 +106,7 @@ Foam::scalar Foam::meshToMeshMethod::interVol
 {
     tetOverlapVolume overlapEngine;
 
-    treeBoundBox bbTgtCell
-    (
-        pointField
-        (
-            tgt_.points(),
-            tgt_.cellPoints()[tgtCellI]
-        )
-    );
+    treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCellI]);
 
     scalar vol = overlapEngine.cellCellOverlapVolumeMinDecomp
     (
diff --git a/src/triSurface/triSurface/interfaces/OBJ/readOBJ.C b/src/triSurface/triSurface/interfaces/OBJ/readOBJ.C
index 14a7f697f53a2694bd9a7d48af938a04c4879033..004661ab3cf36831c3bed3a63ee79d5797fe8e48 100644
--- a/src/triSurface/triSurface/interfaces/OBJ/readOBJ.C
+++ b/src/triSurface/triSurface/interfaces/OBJ/readOBJ.C
@@ -104,7 +104,7 @@ bool Foam::triSurface::readOBJ(const fileName& OBJfileName)
             while (true)
             {
                 string::size_type startNum =
-                    line.find_first_not_of(' ', endNum);
+                    line.find_first_not_of(" \r", endNum);
 
                 if (startNum == string::npos)
                 {
diff --git a/src/turbulenceModels/compressible/turbulenceModel/derivedFvPatchFields/thermalBaffle1D/thermalBaffle1DFvPatchScalarField.C b/src/turbulenceModels/compressible/turbulenceModel/derivedFvPatchFields/thermalBaffle1D/thermalBaffle1DFvPatchScalarField.C
index adc14b03ebc8e421d4966c8301bdc9b1c68dfb2b..b429c624475413598fac06e78600a6407b8b9f2f 100644
--- a/src/turbulenceModels/compressible/turbulenceModel/derivedFvPatchFields/thermalBaffle1D/thermalBaffle1DFvPatchScalarField.C
+++ b/src/turbulenceModels/compressible/turbulenceModel/derivedFvPatchFields/thermalBaffle1D/thermalBaffle1DFvPatchScalarField.C
@@ -93,14 +93,7 @@ thermalBaffle1DFvPatchScalarField
     const dictionary& dict
 )
 :
-    mappedPatchBase
-    (
-        p.patch(),
-        p.boundaryMesh().mesh().name(),
-        NEARESTPATCHFACE,
-        dict.lookup("samplePatch"),
-        0.0
-    ),
+    mappedPatchBase(p.patch(), NEARESTPATCHFACE, dict),
     mixedFvPatchScalarField(p, iF),
     TName_("T"),
     baffleActivated_(dict.lookupOrDefault<bool>("baffleActivated", true)),
diff --git a/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/0.org/include/1DBaffle/1DTemperatureMasterBafflePatches b/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/0.org/include/1DBaffle/1DTemperatureMasterBafflePatches
index 5e2ff882e363d59c2536f1ca83bd32a73b663932..1c2a91f28cbe1f0eb8a84bfd761318ad9c609660 100644
--- a/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/0.org/include/1DBaffle/1DTemperatureMasterBafflePatches
+++ b/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/0.org/include/1DBaffle/1DTemperatureMasterBafflePatches
@@ -9,7 +9,6 @@
 T
 {
     type   compressible::thermalBaffle1D<hConstSolidThermoPhysics>;
-    samplePatch     baffle1DWall_slave;
 
     thickness       uniform 0.005;  // thickness [m]
     Qs              uniform 100;    // heat flux [W/m2]
diff --git a/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/system/createBafflesDict b/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/system/createBafflesDict
index feb96c24ba198d3e8cf9d907f98bfd9c13a45a1a..fe16ef97a259ec6b8886c14ee462a6c0bdc0c79e 100644
--- a/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/system/createBafflesDict
+++ b/tutorials/heatTransfer/buoyantSimpleFoam/circuitBoardCooling/system/createBafflesDict
@@ -29,35 +29,13 @@ baffles
         surface     triSurfaceMesh;
         name        baffle1D.stl;
 
-        patches
+        patchPairs
         {
-            master
-            {
-                //- Master side patch
-                name            baffle1DWall_master;
-
-                type            wall;
-                inGroups        (baffleWallGroup);
-
-                patchFields
-                {
-                    #include "./0/include/wallBafflePatches"
-                    #include "./0/include/1DBaffle/1DTemperatureMasterBafflePatches"
-                }
-            }
-            slave
+            type            wall;
+            patchFields
             {
-                //- Slave side patch
-                name            baffle1DWall_slave;
-
-                type            wall;
-                inGroups        (baffleWallGroup);
-
-                patchFields
-                {
-                    #include "./0/include/wallBafflePatches"
-                    #include "./0/include/1DBaffle/1DTemperatureSlaveBafflePatches"
-                }
+                #include "./0/include/wallBafflePatches"
+                #include "./0/include/1DBaffle/1DTemperatureMasterBafflePatches"
             }
         }
     }
@@ -78,10 +56,7 @@ baffles
                 name            ${masterPatchName};
 
                 type            mappedWall;
-
-
-                type            interRegionMappedWallGenerator;
-                                inGroups        (baffleWallGroup);
+                inGroups        (baffleWallGroup);
 
                 sampleMode      nearestPatchFace;
                 sampleRegion    ${baffleRegionName};