diff --git a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
index 1d021b5280d82e72cdf4593bb6c458e924919ac7..194b08242c70a2379cc0f673d5690f041504c192 100644
--- a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
+++ b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,6 +46,7 @@ void mapConsistentMesh
     const fvMesh& meshTarget,
     const word& mapMethod,
     const word& AMIMapMethod,
+    const word& procMapMethod,
     const bool subtract,
     const wordHashSet& selectedFields,
     const bool noLagrangian
@@ -54,7 +55,14 @@ void mapConsistentMesh
     Info<< nl << "Consistently creating and mapping fields for time "
         << meshSource.time().timeName() << nl << endl;
 
-    meshToMesh interp(meshSource, meshTarget, mapMethod, AMIMapMethod);
+    meshToMesh interp
+    (
+        meshSource,
+        meshTarget,
+        mapMethod,
+        AMIMapMethod,
+        meshToMesh::procMapMethodNames_[procMapMethod]
+    );
 
     if (subtract)
     {
@@ -85,6 +93,7 @@ void mapSubMesh
     const wordList& cuttingPatches,
     const word& mapMethod,
     const word& AMIMapMethod,
+    const word& procMapMethod,
     const bool subtract,
     const wordHashSet& selectedFields,
     const bool noLagrangian
@@ -100,7 +109,8 @@ void mapSubMesh
         mapMethod,
         AMIMapMethod,
         patchMap,
-        cuttingPatches
+        cuttingPatches,
+        meshToMesh::procMapMethodNames_[procMapMethod]
     );
 
     if (subtract)
@@ -171,6 +181,12 @@ int main(int argc, char *argv[])
         "word",
         "specify the patch mapping method (direct|mapNearest|faceAreaWeight)"
     );
+    argList::addOption
+    (
+        "procMapMethod",
+        "word",
+        "specify the processor distribution map method (AABB|LOD)"
+    );
     argList::addBoolOption
     (
         "subtract",
@@ -217,7 +233,7 @@ int main(int argc, char *argv[])
 
     word mapMethod = meshToMesh::interpolationMethodNames_
     [
-        meshToMesh::imCellVolumeWeight
+        meshToMesh::interpolationMethod::imCellVolumeWeight
     ];
 
     if  (args.readIfPresent("mapMethod", mapMethod))
@@ -225,7 +241,6 @@ int main(int argc, char *argv[])
         Info<< "Mapping method: " << mapMethod << endl;
     }
 
-
     word patchMapMethod;
     if (meshToMesh::interpolationMethodNames_.found(mapMethod))
     {
@@ -233,12 +248,25 @@ int main(int argc, char *argv[])
         meshToMesh::interpolationMethod method =
             meshToMesh::interpolationMethodNames_[mapMethod];
 
-        patchMapMethod = AMIPatchToPatchInterpolation::interpolationMethodNames_
+        patchMapMethod =
+            AMIPatchToPatchInterpolation::interpolationMethodNames_
+            [
+                meshToMesh::interpolationMethodAMI(method)
+            ];
+    }
+
+    word procMapMethod =
+        meshToMesh::procMapMethodNames_
         [
-            meshToMesh::interpolationMethodAMI(method)
+            meshToMesh::procMapMethod::pmAABB
         ];
+
+    if (args.readIfPresent("procMapMethod", procMapMethod))
+    {
+        Info<< "Processor map method: " << procMapMethod << endl;
     }
 
+
     // Optionally override
     if (args.readIfPresent("patchMapMethod", patchMapMethod))
     {
@@ -325,6 +353,7 @@ int main(int argc, char *argv[])
             meshTarget,
             mapMethod,
             patchMapMethod,
+            procMapMethod,
             subtract,
             selectedFields,
             noLagrangian
@@ -340,12 +369,15 @@ int main(int argc, char *argv[])
             cuttingPatches,
             mapMethod,
             patchMapMethod,
+            procMapMethod,
             subtract,
             selectedFields,
             noLagrangian
         );
     }
 
+    runTimeSource.printExecutionTime(Info);
+
     Info<< "\nEnd\n" << endl;
 
     return 0;
diff --git a/src/fvOptions/interRegionOption/interRegionOption.C b/src/fvOptions/interRegionOption/interRegionOption.C
index 32a3f88564cd6f5de3c481231a123569dd51bdd6..bf81ccf38354254e39ce124783e7929be9906bbf 100644
--- a/src/fvOptions/interRegionOption/interRegionOption.C
+++ b/src/fvOptions/interRegionOption/interRegionOption.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -65,10 +65,17 @@ void Foam::fv::interRegionOption::setMapper()
                 (
                     mesh_,
                     nbrMesh,
-                    meshToMesh::interpolationMethodNames_.lookup
+                    meshToMesh::interpolationMethodNames_.lookupOrDefault
                     (
                         "interpolationMethod",
-                        coeffs_
+                        coeffs_,
+                        meshToMesh::interpolationMethod::imCellVolumeWeight
+                    ),
+                    meshToMesh::procMapMethodNames_.lookupOrDefault
+                    (
+                        "procMapMethod",
+                        coeffs_,
+                        meshToMesh::procMapMethod::pmAABB
                     ),
                     false // not interpolating patches
                 )
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 45f839d1a0defcd51550b297c566c4d808a09b1f..56dc286f3a9cb384b481beb518e59699a10a006c 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -231,6 +231,11 @@ triSurface/triSurfaceTools/pointToPointPlanarInterpolation.C
 
 twoDPointCorrector/twoDPointCorrector.C
 
+processorLOD/processorLOD/processorLOD.C
+processorLOD/box/box.C
+processorLOD/cellBox/cellBox.C
+processorLOD/faceBox/faceBox.C
+
 AMI=AMIInterpolation
 $(AMI)/AMIInterpolation/AMIInterpolationName.C
 $(AMI)/AMIInterpolation/AMIPatchToPatchInterpolation.C
diff --git a/src/meshTools/processorLOD/box/box.C b/src/meshTools/processorLOD/box/box.C
new file mode 100644
index 0000000000000000000000000000000000000000..f176ab0fd068adb71241bdd0e64c2201c9138ee1
--- /dev/null
+++ b/src/meshTools/processorLOD/box/box.C
@@ -0,0 +1,635 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "box.H"
+#include "mapDistribute.H"
+#include "OFstream.H"
+#include "meshTools.H"
+
+const Foam::label Foam::processorLODs::box::DROP = 0;
+const Foam::label Foam::processorLODs::box::REFINE = 1;
+const Foam::label Foam::processorLODs::box::FIXED = 2;
+const Foam::label Foam::processorLODs::box::nStartUpIter = 2;
+
+namespace Foam
+{
+namespace processorLODs
+{
+    defineTypeNameAndDebug(box, 0);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::processorLODs::box::writeBoxes
+(
+    const List<DynamicList<treeBoundBox>>& fixedBoxes,
+    const label iter
+) const
+{
+    static label time = 0;
+
+    OFstream os
+    (
+        "processor" + Foam::name(Pstream::myProcNo())
+        + "_time" + Foam::name(time)
+        + "_iter" + Foam::name(iter) + ".obj"
+    );
+
+    label verti = 0;
+    for (label proci = 0; proci < Pstream::nProcs(); ++proci)
+    {
+        if (proci == Pstream::myProcNo())
+        {
+            continue;
+        }
+
+        const DynamicList<treeBoundBox>& procBoxes = fixedBoxes[proci];
+        forAll(procBoxes, boxi)
+        {
+            const treeBoundBox& bb = procBoxes[boxi];
+
+            // Write the points
+            const pointField pts(bb.points());
+            for (const point& p : pts)
+            {
+                meshTools::writeOBJ(os, p);
+            }
+
+            // Write the box faces
+            for (const face& f : bb.faces)
+            {
+                os  << 'f';
+                for (const label fpi : f)
+                {
+                    os  << ' ' << fpi + verti + 1;
+                }
+                os  << nl;
+            }
+            verti += pts.size();
+        }
+    }
+
+    ++time;
+}
+
+
+void Foam::processorLODs::box::setRefineFlags
+(
+    const label refineIter,
+    const label nTgtObjects,
+    List<labelHashSet>& fixedSendElems,
+    List<List<labelList>>& localTgtElems,
+    List<labelList>& refineFlags,
+    labelList& nElems
+) const
+{
+    PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
+
+    // Identify src boxes that can be refined and send to all remote procs
+    for (label proci = 0; proci < Pstream::nProcs(); ++proci)
+    {
+        if (proci != Pstream::myProcNo())
+        {
+            UOPstream toProc(proci, pBufs);
+            toProc << nObjectsOfType_ << boxes_[proci] << newToOld_[proci];
+        }
+    }
+
+    pBufs.finishedSends();
+
+    // Test each remote src bb with local tgt objects to identify which remote
+    // src boxes can/should be refined
+    for (label proci = 0; proci < Pstream::nProcs(); ++proci)
+    {
+        if (proci == Pstream::myProcNo())
+        {
+            // Not refining boxes I send to myself - will be sending all local
+            // elements
+            continue;
+        }
+
+        // Receive the subset of changing src bound boxes for proci
+        UIPstream fromProc(proci, pBufs);
+        const label nObjects = readLabel(fromProc);
+        List<treeBoundBox> remoteSrcBoxes(fromProc);
+        const List<label> newToOld(fromProc);
+
+        if (remoteSrcBoxes.empty())
+        {
+            continue;
+        }
+
+
+        labelList& procRefineFlags = refineFlags[proci];
+        procRefineFlags.setSize(remoteSrcBoxes.size(), FIXED);
+
+        if (scalar(nTgtObjects)/scalar(nObjects) < 0.1)
+        {
+            // Sending less than 10% of objects of this type
+            // - shortcut by sending all
+            fixedSendElems[proci].insert(identity(nTgtObjects));
+            nElems[proci] = nTgtObjects;
+
+            continue;
+        }
+
+        label nElem = 0;
+        List<labelList>& localProcTgtElems = localTgtElems[proci];
+        List<labelList> newLocalProcTgtElems(remoteSrcBoxes.size());
+
+        forAll(remoteSrcBoxes, srcBoxi)
+        {
+            const treeBoundBox& remSrcBb = remoteSrcBoxes[srcBoxi];
+            DynamicList<label> selectedElems(maxObjectsPerLeaf_);
+
+            if (refineIter > nStartUpIter)
+            {
+                // Iterate over cached subset of tgt elements
+                const label oldBoxi = newToOld[srcBoxi];
+                const labelList& tgtBoxElems = localProcTgtElems[oldBoxi];
+
+                for (const label tgtObji : tgtBoxElems)
+                {
+                    if (calcTgtBox(tgtObji).overlaps(remSrcBb))
+                    {
+                        selectedElems.append(tgtObji);
+                    }
+                }
+            }
+            else
+            {
+                // Iterating over all target elements
+                for (label tgtObji = 0; tgtObji < nTgtObjects; ++tgtObji)
+                {
+                    if (calcTgtBox(tgtObji).overlaps(remSrcBb))
+                    {
+                        selectedElems.append(tgtObji);
+                    }
+                }
+            }
+
+            nElem += selectedElems.size();
+
+            if
+            (
+                proci == Pstream::myProcNo()
+             || selectedElems.size() < maxObjectsPerLeaf_
+            )
+            {
+                procRefineFlags[srcBoxi] = FIXED;
+                fixedSendElems[proci].insert(selectedElems);
+            }
+            else
+            {
+                procRefineFlags[srcBoxi] = REFINE;
+                if (refineIter >= nStartUpIter)
+                {
+                    newLocalProcTgtElems[srcBoxi].transfer(selectedElems);
+                }
+            }
+        }
+
+        localProcTgtElems.transfer(newLocalProcTgtElems);
+        nElems[proci] = nElem;
+    }
+}
+
+
+void Foam::processorLODs::box::refineBox
+(
+    const label boxi,
+    const label refineIter,
+    const label nSrcElem,
+    const treeBoundBox& origBox,
+    DynamicList<treeBoundBox>& procBoxes,
+    DynamicList<labelList>& procBoxElems,
+    DynamicList<label>& procNewToOld
+) const
+{
+    // Create the new boxes
+
+    if (refineIter == nStartUpIter)
+    {
+        // Start caching the addressing
+        for (direction octant = 0; octant < 8; ++octant)
+        {
+            const treeBoundBox subBb(origBox.subBbox(octant));
+
+            // Identify the src elements for each box
+            DynamicList<label> newElems(nSrcElem/2);
+
+            for (label srcElemi = 0; srcElemi < nSrcElem; ++srcElemi)
+            {
+                if (subBb.overlaps(calcSrcBox(srcElemi)))
+                {
+                    newElems.append(srcElemi);
+                }
+            }
+
+            if (newElems.size())
+            {
+                procBoxes.append(subBb);
+                procBoxElems.append(newElems);
+                procNewToOld.append(boxi);
+            }
+        }
+    }
+    else
+    {
+        for (direction octant = 0; octant < 8; ++octant)
+        {
+            const treeBoundBox subBb(origBox.subBbox(octant));
+
+            for (label srcElemi = 0; srcElemi < nSrcElem; ++srcElemi)
+            {
+                if (subBb.overlaps(calcSrcBox(srcElemi)))
+                {
+                    procBoxes.append(subBb);
+                    break;
+                }
+            }
+        }
+    }
+}
+
+
+void Foam::processorLODs::box::refineBox
+(
+    const label boxi,
+    const labelList& srcAddr,
+    const treeBoundBox& origBox,
+    DynamicList<treeBoundBox>& procBoxes,
+    DynamicList<labelList>& procBoxElems,
+    DynamicList<label>& procNewToOld
+) const
+{
+    // Create the new boxes
+    for (direction octant = 0; octant < 8; ++octant)
+    {
+        const treeBoundBox subBb(origBox.subBbox(octant));
+
+        // Identify the src elements for each box
+        DynamicList<label> newElems(srcAddr.size()/2);
+
+        for (const label srcElemi : srcAddr)
+        {
+            if (subBb.overlaps(calcSrcBox(srcElemi)))
+            {
+                newElems.append(srcElemi);
+            }
+        }
+
+        // Only keeping new box if it overlaps src objects
+        if (newElems.size())
+        {
+            procBoxes.append(subBb);
+            procBoxElems.append(newElems);
+            procNewToOld.append(boxi);
+        }
+    }
+}
+
+
+bool Foam::processorLODs::box::doRefineBoxes
+(
+    const label refineIter,
+    const label nSrcFaces,
+    const List<labelList>& refineFlags,
+    const labelList& nElems,
+    List<DynamicList<treeBoundBox>>& fixedBoxes
+)
+{
+    PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
+
+    // Send refine info back for list of evolving src boxes
+    forAll(refineFlags, proci)
+    {
+        if (proci != Pstream::myProcNo())
+        {
+            UOPstream toProc(proci, pBufs);
+            toProc << nElems[proci] << refineFlags[proci];
+        }
+    }
+
+    pBufs.finishedSends();
+
+    // Receive refine refinement actions from remote procs and use to refine
+    // local src boxes
+    bool refineBoxes = false;
+    label nElem = 0;
+    List<DynamicList<label>> newToOld(Pstream::nProcs());
+
+
+    for (label proci = 0; proci < Pstream::nProcs(); ++proci)
+    {
+        if (proci == Pstream::myProcNo())
+        {
+            continue;
+        }
+
+        UIPstream fromProc(proci, pBufs);
+        nElem += readLabel(fromProc);
+        const labelList refineFlags(fromProc);
+
+        const List<treeBoundBox>& procBoxes = boxes_[proci];
+        DynamicList<treeBoundBox> newProcBoxes(procBoxes.size());
+        DynamicList<labelList> newProcBoxElems(procBoxes.size());
+        newToOld[proci].setCapacity(boxes_[proci].size());
+        DynamicList<label>& newProcNewToOld = newToOld[proci];
+
+
+        forAll(procBoxes, boxi)
+        {
+            if (refineFlags[boxi] == DROP)
+            {
+                // Skip box
+            }
+            else if (refineFlags[boxi] == REFINE)
+            {
+                if (refineIter > nStartUpIter)
+                {
+                    // Can use locally cached info to speed up intersection
+                    // tests
+                    refineBox
+                    (
+                        boxi,
+                        boxSrcElems_[proci][boxi],
+                        procBoxes[boxi],
+                        newProcBoxes,
+                        newProcBoxElems,
+                        newProcNewToOld
+                    );
+                }
+                else
+                {
+                    refineBox
+                    (
+                        boxi,
+                        refineIter,
+                        nSrcFaces,
+                        procBoxes[boxi],
+                        newProcBoxes,
+                        newProcBoxElems,
+                        newProcNewToOld
+                    );
+                }
+
+                refineBoxes = true;
+            }
+            else if (refineFlags[boxi] == FIXED)
+            {
+                fixedBoxes[proci].append(procBoxes[boxi]);
+            }
+            else
+            {
+                FatalErrorInFunction
+                    << "Unhandled refine action " << refineFlags[boxi]
+                    << abort(FatalError);
+            }
+        }
+
+        // Only keeping boxes that are to be refined
+        boxes_[proci].transfer(newProcBoxes);
+        boxSrcElems_[proci].transfer(newProcBoxElems);
+        newToOld_[proci].transfer(newProcNewToOld);
+    }
+
+    return returnReduce(refineBoxes, orOp<bool>());
+}
+
+
+Foam::autoPtr<Foam::mapDistribute> Foam::processorLODs::box::createMap
+(
+    const label nSrcElems,
+    const label nTgtElems
+)
+{
+    // Store elements to send - will be used to build the mapDistribute
+    List<labelHashSet> fixedSendElems(Pstream::nProcs());
+
+    // List of local tgt elems to optimise searching fot tgt elements inside
+    // remote src boxes
+    List<List<labelList>> localTgtElems(Pstream::nProcs());
+
+    // Storage of boxes per processor - only useful for debugging
+    List<DynamicList<treeBoundBox>> fixedBoxes(Pstream::nProcs());
+
+    // Iterate to subdivide src boxes
+    label refinementIter = 1;
+    bool refineSrcBoxes = true;
+    while (refineSrcBoxes && (refinementIter <= nRefineIterMax_))
+    {
+        // Per processor refinement info
+        List<labelList> refineFlags(Pstream::nProcs());
+        labelList nElems(Pstream::nProcs(), 0);
+
+        // Assess how many target elements intersect the source bounding boxes
+        // and use the info to flag how the source boxes should be refined
+        setRefineFlags
+        (
+            refinementIter,
+            nTgtElems,
+            fixedSendElems,
+            localTgtElems,
+            refineFlags,
+            nElems
+        );
+
+        // Refine the source bounding boxes
+        refineSrcBoxes =
+            doRefineBoxes
+            (
+                refinementIter,
+                nSrcElems,
+                refineFlags,
+                nElems,
+                fixedBoxes
+            );
+
+        ++refinementIter;
+
+        if (debug > 1)
+        {
+            // Include any boxes that are still evolving
+            List<DynamicList<treeBoundBox>> allBoxes(fixedBoxes);
+            forAll(allBoxes, proci)
+            {
+                allBoxes[proci].append(boxes_[proci]);
+            }
+            writeBoxes(allBoxes, refinementIter);
+        }
+    }
+
+    if (debug)
+    {
+        Pout<< "Local src boxes after " << refinementIter-1 << " iterations:"
+            << nl;
+
+        forAll(fixedBoxes, proci)
+        {
+            // Include any boxes that are still evolving in box count
+            label nBox = fixedBoxes[proci].size() + boxes_[proci].size();
+            Pout<< "    proc:" << proci << " nBoxes:" << nBox << nl;
+        }
+        Pout<< endl;
+    }
+
+    // Convert send elems into a List<labelList>
+    List<labelList> sendElems(Pstream::nProcs());
+    forAll(localTgtElems, proci)
+    {
+        if (proci == Pstream::myProcNo() && nSrcElems)
+        {
+            sendElems[proci] = identity(nTgtElems);
+        }
+        else
+        {
+            labelHashSet& allIDs = fixedSendElems[proci];
+
+            const List<labelList>& procBoxElems = localTgtElems[proci];
+
+            for (const labelList& elems: procBoxElems)
+            {
+                allIDs.insert(elems);
+            }
+
+            sendElems[proci] = allIDs.toc();
+        }
+    }
+
+    fixedSendElems.clear();
+    localTgtElems.clear();
+
+    if (debug)
+    {
+        Pout<< "Local objects: " << nTgtElems << " Send map:" << nl
+            << tab << "proc" << tab << "objects" << endl;
+
+        forAll(sendElems, proci)
+        {
+            Pout<< tab << proci << tab << sendElems[proci].size()
+                << endl;
+        }
+    }
+
+    return createLODMap(sendElems);
+}
+
+
+Foam::autoPtr<Foam::mapDistribute> Foam::processorLODs::box::createLODMap
+(
+    List<labelList>& sendElems
+) const
+{
+    // Send over how many objects I need to receive
+    const label localProci = Pstream::myProcNo();
+    labelListList sendSizes(Pstream::nProcs());
+    sendSizes[localProci].setSize(Pstream::nProcs());
+    forAll(sendElems, proci)
+    {
+        sendSizes[localProci][proci] = sendElems[proci].size();
+    }
+    Pstream::gatherList(sendSizes);
+    Pstream::scatterList(sendSizes);
+
+
+    // Determine order of receiving
+    labelListList constructMap(Pstream::nProcs());
+
+    // My local segment first
+    constructMap[localProci] = identity(sendElems[localProci].size());
+
+    label segmenti = constructMap[localProci].size();
+    forAll(constructMap, proci)
+    {
+        if (proci != localProci)
+        {
+            // What I need to receive is what other processor is sending to me
+            label nRecv = sendSizes[proci][localProci];
+            constructMap[proci].setSize(nRecv);
+
+            for (label& addr : constructMap[proci])
+            {
+                addr = segmenti++;
+            }
+        }
+    }
+
+    autoPtr<mapDistribute> mapPtr
+    (
+        new mapDistribute
+        (
+            segmenti,                   // size after construction
+            std::move(sendElems),
+            std::move(constructMap)
+        )
+    );
+
+    return mapPtr;
+}
+
+
+// * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * //
+
+Foam::processorLODs::box::box
+(
+    const UList<point>& srcPoints,
+    const UList<point>& tgtPoints,
+    const label maxObjectsPerLeaf,
+    const label nObjectsOfType,
+    const label nRefineIterMax
+)
+:
+    processorLOD(maxObjectsPerLeaf, nObjectsOfType),
+    srcPoints_(srcPoints),
+    tgtPoints_(tgtPoints),
+    boxes_(Pstream::nProcs()),
+    nRefineIterMax_(nRefineIterMax),
+    newToOld_(Pstream::nProcs()),
+    boxSrcElems_(Pstream::nProcs())
+{
+    // Initialise each processor with a single box large enough to include all
+    // of its local src points
+    if (srcPoints_.size())
+    {
+        forAll(boxes_, proci)
+        {
+            List<treeBoundBox>& procBoxes = boxes_[proci];
+
+            // Note: inflate to ease overlap operations and to handle 2-D
+            // axis-aligned objects
+            treeBoundBox srcBb(srcPoints_);
+            srcBb.inflate(0.01);
+
+            DynamicList<treeBoundBox> newProcBoxes(1);
+            newProcBoxes.append(srcBb);
+            procBoxes.transfer(newProcBoxes);
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/box/box.H b/src/meshTools/processorLOD/box/box.H
new file mode 100644
index 0000000000000000000000000000000000000000..89214a22ee00001cc4eca05d317fbe340ed10c93
--- /dev/null
+++ b/src/meshTools/processorLOD/box/box.H
@@ -0,0 +1,206 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpeCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::processorLODs::box
+
+Description
+    Creates the parallel distribution map by describing the source and target
+    objects using box shapes.
+
+    A single box is created for the source object, which is then split using
+    2x2x2 refinement based on the number of remote target objects that overlap.
+    The refinement is local between communicating processor pairs, where the
+    refinement continues until a threshold number of remote target objects per
+    source box is achieved.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef processorLODs_box
+#define processorLODs_box
+
+#include "processorLOD.H"
+#include "treeBoundBox.H"
+#include "HashSet.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace processorLODs
+{
+
+/*---------------------------------------------------------------------------*\
+                             Class Box Declaration
+\*---------------------------------------------------------------------------*/
+
+class box
+:
+    public processorLOD
+{
+protected:
+
+    // Protected data
+
+        // Flags to indicate what to do with a box
+
+            //- Drop/discard
+            static const label DROP;
+
+            //- Refine
+            static const label REFINE;
+
+            //- Fixed - do not touch
+            static const label FIXED;
+
+
+    //- Reference to the source points
+    const UList<point>& srcPoints_;
+
+    //- Reference to the target points
+    const UList<point>& tgtPoints_;
+
+    //- Per processor, the list of src bound boxes
+    //  Note: using treeBoundBox to take advantage of subBbox() method
+    List<List<treeBoundBox>> boxes_;
+
+    //- Maximum number of refinement iterations
+    label nRefineIterMax_;
+
+
+    // Caching controls
+
+        //- Number of iterations before element indices are cached
+        static const label nStartUpIter;
+
+        //- Addressing per proc of new to old bound boxes
+        List<labelList> newToOld_;
+
+        //- Indices of elements in the src boxes
+        List<List<labelList>> boxSrcElems_;
+
+
+    // Private Member Functions
+
+        //- Helper function to write the boxes in OBJ format
+        void writeBoxes
+        (
+            const List<DynamicList<treeBoundBox>>& fixedBoxes,
+            const label iter
+        ) const;
+
+
+        virtual boundBox calcSrcBox(const label srcObji) const = 0;
+        virtual boundBox calcTgtBox(const label tgtObji) const = 0;
+
+        //- Set the box refinement flags
+        void setRefineFlags
+        (
+            const label refineIter,
+            const label nTgtObjects,
+            List<labelHashSet>& fixedSendElems,
+            List<List<labelList>>& localTgtElems,
+            List<labelList>& refineFlags,
+            labelList& nElems
+        ) const;
+
+        void refineBox
+        (
+            const label boxi,
+            const label refineIter,
+            const label nSrcElem,
+            const treeBoundBox& origBox,
+            DynamicList<treeBoundBox>& procBoxes,
+            DynamicList<labelList>& procBoxElems,
+            DynamicList<label>& procNewToOld
+        ) const;
+
+        void refineBox
+        (
+            const label boxi,
+            const labelList& srcAddr,
+            const treeBoundBox& origBox,
+            DynamicList<treeBoundBox>& procBoxes,
+            DynamicList<labelList>& procBoxElems,
+            DynamicList<label>& procNewToOld
+        ) const;
+
+
+        //- Apply the box refinements
+        //  \return true if still refining
+        bool doRefineBoxes
+        (
+            const label refineIter,
+            const label nSrcFaces,
+            const List<labelList>& refineFlags,
+            const labelList& nElems,
+            List<DynamicList<treeBoundBox>>& fixedBoxes
+        );
+
+        autoPtr<mapDistribute> createMap
+        (
+            const label nSrcElems,
+            const label nTgtElems
+        );
+
+        //- Use the current list of send elements to create the mapDistribute
+        autoPtr<mapDistribute> createLODMap
+        (
+            List<labelList>& sendElems
+        ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("box");
+
+    //- Construct from list of points
+    box
+    (
+        const UList<point>& srcPoints,
+        const UList<point>& tgtPoints,
+        const label maxObjectsPerLeaf,
+        const label nObjectsOfType,
+        const label nRefineIterMax = 100
+    );
+
+    //- Destructor
+    virtual ~box() = default;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace processorLODs
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/cellBox/cellBox.C b/src/meshTools/processorLOD/cellBox/cellBox.C
new file mode 100644
index 0000000000000000000000000000000000000000..db505dd661cc32d157ad8c8fe3ac1d05914ed089
--- /dev/null
+++ b/src/meshTools/processorLOD/cellBox/cellBox.C
@@ -0,0 +1,110 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "cellBox.H"
+#include "mapDistribute.H"
+
+namespace Foam
+{
+namespace processorLODs
+{
+    defineTypeNameAndDebug(cellBox, 0);
+}
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+Foam::boundBox Foam::processorLODs::cellBox::calcSrcBox
+(
+    const label srcObji
+) const
+{
+    const UList<label>& cellFaces = srcCells_[srcObji];
+
+    boundBox bb(srcPoints_, srcFaces_[cellFaces[0]], false);
+    for (label i = 1; i < cellFaces.size(); ++i)
+    {
+        bb.add(srcPoints_, srcFaces_[cellFaces[i]]);
+    }
+
+    return bb;
+}
+
+
+Foam::boundBox Foam::processorLODs::cellBox::calcTgtBox
+(
+    const label tgtObji
+) const
+{
+    const UList<label>& cellFaces = tgtCells_[tgtObji];
+
+    boundBox bb(tgtPoints_, tgtFaces_[cellFaces[0]], false);
+    for (label i = 1; i < cellFaces.size(); ++i)
+    {
+        bb.add(tgtPoints_, tgtFaces_[cellFaces[i]]);
+    }
+
+    return bb;
+}
+
+
+// * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * //
+
+Foam::processorLODs::cellBox::cellBox
+(
+    const cellList& srcCells,
+    const faceList& srcFaces,
+    const UList<point>& srcPoints,
+    const cellList& tgtCells,
+    const faceList& tgtFaces,
+    const UList<point>& tgtPoints,
+    const label maxObjectsPerLeaf,
+    const label nObjectsOfType,
+    const label nRefineIterMax
+)
+:
+    faceBox
+    (
+        srcFaces,
+        srcPoints,
+        tgtFaces,
+        tgtPoints,
+        maxObjectsPerLeaf,
+        nObjectsOfType,
+        nRefineIterMax
+    ),
+    srcCells_(srcCells),
+    tgtCells_(tgtCells)
+{}
+
+
+Foam::autoPtr<Foam::mapDistribute> Foam::processorLODs::cellBox::map()
+{
+    return createMap(srcCells_.size(), tgtCells_.size());
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/cellBox/cellBox.H b/src/meshTools/processorLOD/cellBox/cellBox.H
new file mode 100644
index 0000000000000000000000000000000000000000..69bcf3032e3639454df1ae3a42e67ee93b5c571f
--- /dev/null
+++ b/src/meshTools/processorLOD/cellBox/cellBox.H
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpeCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::processorLODs::cellBox
+
+Description
+    Creates the parallel distribution map by describing the source and target
+    objects using box shapes.
+
+    A single box is created for the source object, which is then split using
+    2x2x2 refinement based on the number of remote target objects that overlap.
+    The refinement is local between communicating processor pairs, where the
+    refinement continues until a threshold number of remote target objects per
+    source box is achieved.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef processorLODs_cellBox
+#define processorLODs_cellBox
+
+#include "faceBox.H"
+#include "cellList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace processorLODs
+{
+
+/*---------------------------------------------------------------------------*\
+                           Class cellBox Declaration
+\*---------------------------------------------------------------------------*/
+
+class cellBox
+:
+    public faceBox
+{
+private:
+
+    // Private data
+
+        //- Reference to the source face list
+        const cellList& srcCells_;
+
+        //- Reference to the target face list
+        const cellList& tgtCells_;
+
+
+    // Private Member Functions
+
+        virtual boundBox calcSrcBox(const label srcObji) const;
+        virtual boundBox calcTgtBox(const label tgtObji) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("box");
+
+    //- Construct from list of points
+    cellBox
+    (
+        const cellList& srcCells,
+        const faceList& srcFaces,
+        const UList<point>& srcPoints,
+        const cellList& tgtCells,
+        const faceList& tgtFaces,
+        const UList<point>& tgtPoints,
+        const label maxObjectsPerLeaf,
+        const label nObjectsOfType,
+        const label nRefineIterMax = 100
+    );
+
+    //- Destructor
+    virtual ~cellBox() = default;
+
+
+    // Member Functions
+
+        //- Return the parallel distribution map
+        virtual autoPtr<mapDistribute> map();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace processorLODs
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/faceBox/faceBox.C b/src/meshTools/processorLOD/faceBox/faceBox.C
new file mode 100644
index 0000000000000000000000000000000000000000..43e72a365ee7dd9ab683254c9b452603ed40ee6e
--- /dev/null
+++ b/src/meshTools/processorLOD/faceBox/faceBox.C
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "faceBox.H"
+#include "mapDistribute.H"
+
+namespace Foam
+{
+namespace processorLODs
+{
+    defineTypeNameAndDebug(faceBox, 0);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+Foam::boundBox Foam::processorLODs::faceBox::calcSrcBox
+(
+    const label srcObji
+) const
+{
+    return boundBox(srcPoints_, srcFaces_[srcObji], false);
+}
+
+
+Foam::boundBox Foam::processorLODs::faceBox::calcTgtBox
+(
+    const label tgtObji
+) const
+{
+    return boundBox(tgtPoints_, tgtFaces_[tgtObji], false);
+}
+
+
+// * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * //
+
+Foam::processorLODs::faceBox::faceBox
+(
+    const faceList& srcFaces,
+    const UList<point>& srcPoints,
+    const faceList& tgtFaces,
+    const UList<point>& tgtPoints,
+    const label maxObjectsPerLeaf,
+    const label nObjectsOfType,
+    const label nRefineIterMax
+)
+:
+    box(srcPoints, tgtPoints, maxObjectsPerLeaf, nObjectsOfType),
+    srcFaces_(srcFaces),
+    tgtFaces_(tgtFaces)
+{}
+
+
+Foam::autoPtr<Foam::mapDistribute> Foam::processorLODs::faceBox::map()
+{
+    return createMap(srcFaces_.size(), tgtFaces_.size());
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/faceBox/faceBox.H b/src/meshTools/processorLOD/faceBox/faceBox.H
new file mode 100644
index 0000000000000000000000000000000000000000..c504dde4af48ad074a591c1d2e36f94345a8889c
--- /dev/null
+++ b/src/meshTools/processorLOD/faceBox/faceBox.H
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpeCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::processorLODs::faceBox
+
+Description
+    Creates the parallel distribution map by describing the source and target
+    objects using box shapes.
+
+    A single box is created for the source object, which is then split using
+    2x2x2 refinement based on the number of remote target objects that overlap.
+    The refinement is local between communicating processor pairs, where the
+    refinement continues until a threshold number of remote target objects per
+    source box is achieved.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef processorLODs_faceBox
+#define processorLODs_faceBox
+
+#include "box.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace processorLODs
+{
+
+/*---------------------------------------------------------------------------*\
+                           Class faceBox Declaration
+\*---------------------------------------------------------------------------*/
+
+class faceBox
+:
+    public box
+{
+protected:
+
+    // Protected data
+
+        //- Reference to the source face list
+        const faceList& srcFaces_;
+
+        //- Reference to the target face list
+        const faceList& tgtFaces_;
+
+
+    // Protected Member Functions
+
+        virtual boundBox calcSrcBox(const label srcObji) const;
+        virtual boundBox calcTgtBox(const label tgtObji) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("box");
+
+    //- Construct from list of points
+    faceBox
+    (
+        const faceList& srcFaces,
+        const UList<point>& srcPoints,
+        const faceList& tgtFaces,
+        const UList<point>& tgtPoints,
+        const label maxObjectsPerLeaf,
+        const label nObjectsOfType,
+        const label nRefineIterMax = 100
+    );
+
+    //- Destructor
+    virtual ~faceBox() = default;
+
+
+    // Member Functions
+
+        //- Return the parallel distribution map
+        virtual autoPtr<mapDistribute> map();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace processorLODs
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/processorLOD/processorLOD.C b/src/meshTools/processorLOD/processorLOD/processorLOD.C
new file mode 100644
index 0000000000000000000000000000000000000000..2039c87692284d39c186fd732fddf0dc3c0334b3
--- /dev/null
+++ b/src/meshTools/processorLOD/processorLOD/processorLOD.C
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "processorLOD.H"
+
+namespace Foam
+{
+    defineTypeNameAndDebug(processorLOD, 0);
+}
+
+// * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * //
+
+Foam::processorLOD::processorLOD
+(
+    const label maxObjectsPerLeaf,
+    const label nObjectsOfType
+)
+:
+    maxObjectsPerLeaf_(maxObjectsPerLeaf),
+    nObjectsOfType_(nObjectsOfType)
+{}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/processorLOD/processorLOD/processorLOD.H b/src/meshTools/processorLOD/processorLOD/processorLOD.H
new file mode 100644
index 0000000000000000000000000000000000000000..94c20c63b070e320863a0abd5ddf8f5e1d85c113
--- /dev/null
+++ b/src/meshTools/processorLOD/processorLOD/processorLOD.H
@@ -0,0 +1,97 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::processorLOD
+
+Description
+    Base class to generate a parallel distribution map for sending sufficient
+    target objects to cover a description of the source object, based on
+    processor Level Of Detail (LOD) shapes
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef processorLOD_H
+#define processorLOD_H
+
+#include "autoPtr.H"
+#include "typeInfo.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class mapDistribute;
+
+/*---------------------------------------------------------------------------*\
+                        Class processorLOD Declaration
+\*---------------------------------------------------------------------------*/
+
+class processorLOD
+{
+
+protected:
+
+    // Protected data
+
+        //- Maximum number of objects per leaf
+        label maxObjectsPerLeaf_;
+
+        //- Number of objects of this type, e.g. number of faces/cells on this
+        //- processor
+        label nObjectsOfType_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("processorLOD");
+
+    //- Construct from components
+    processorLOD
+    (
+        const label maxObjectsPerLeaf,
+        const label nObjectsOfType
+    );
+
+    //- Destructor
+    virtual ~processorLOD() = default;
+
+
+    // Member Functions
+
+        //- Return the parallel distribution map
+        virtual autoPtr<mapDistribute> map() = 0;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/overset/cellCellStencil/cellVolumeWeight/cellVolumeWeightCellCellStencil.C b/src/overset/cellCellStencil/cellVolumeWeight/cellVolumeWeightCellCellStencil.C
index 8ffc7f6c6e8fcb3e15cd77930023b98a31d13ace..1c9a5e72c4022f530b4044627603a994b810ae69 100644
--- a/src/overset/cellCellStencil/cellVolumeWeight/cellVolumeWeightCellCellStencil.C
+++ b/src/overset/cellCellStencil/cellVolumeWeight/cellVolumeWeightCellCellStencil.C
@@ -784,9 +784,10 @@ bool Foam::cellCellStencils::cellVolumeWeight::update()
             (
                 srcMesh,
                 tgtMesh,
-                meshToMesh::imCellVolumeWeight,
+                meshToMesh::interpolationMethod::imCellVolumeWeight,
                 HashTable<word>(0),     // patchMap,
                 wordList(0),            // cuttingPatches
+                meshToMesh::procMapMethod::pmAABB,
                 false                   // do not normalise
             );
 
diff --git a/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C b/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
index afb6b727dc9d2cc526b468ae17c6be84c1aa5e5d..ef21a395f115da91e79bc8a21e52145cead9242e 100644
--- a/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
+++ b/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2013-2014 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -84,7 +84,14 @@ bool Foam::meshToMeshMethod::intersect
 
     tetOverlapVolume overlapEngine;
 
-    treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCelli]);
+    // Note: avoid demand-driven construction of cellPoints
+    // treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCelli]);
+    const UList<label>& cellFaces = tgt_.cells()[tgtCelli];
+    treeBoundBox bbTgtCell(tgt_.points(), tgt_.faces()[cellFaces[0]]);
+    for (label i = 1; i < cellFaces.size(); ++i)
+    {
+        bbTgtCell.add(tgt_.points(), tgt_.faces()[cellFaces[i]]);
+    }
 
     return overlapEngine.cellCellOverlapMinDecomp
     (
@@ -106,7 +113,14 @@ Foam::scalar Foam::meshToMeshMethod::interVol
 {
     tetOverlapVolume overlapEngine;
 
-    treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCelli]);
+    // Note: avoid demand-driven construction of cellPoints
+    // treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCelli]);
+    const UList<label>& cellFaces = tgt_.cells()[tgtCelli];
+    treeBoundBox bbTgtCell(tgt_.points(), tgt_.faces()[cellFaces[0]]);
+    for (label i = 1; i < cellFaces.size(); ++i)
+    {
+        bbTgtCell.add(tgt_.points(), tgt_.faces()[cellFaces[i]]);
+    }
 
     scalar vol = overlapEngine.cellCellOverlapVolumeMinDecomp
     (
@@ -124,21 +138,28 @@ Foam::scalar Foam::meshToMeshMethod::interVol
 Foam::Tuple2<Foam::scalar, Foam::point>
 Foam::meshToMeshMethod::interVolAndCentroid
 (
-    const label srcCellI,
-    const label tgtCellI
+    const label srcCelli,
+    const label tgtCelli
 )
 {
     tetOverlapVolume overlapEngine;
 
-    treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCellI]);
+    // Note: avoid demand-driven construction of cellPoints
+    // treeBoundBox bbTgtCell(tgt_.points(), tgt_.cellPoints()[tgtCelli]);
+    const UList<label>& cellFaces = tgt_.cells()[tgtCelli];
+    treeBoundBox bbTgtCell(tgt_.points(), tgt_.faces()[cellFaces[0]]);
+    for (label i = 1; i < cellFaces.size(); ++i)
+    {
+        bbTgtCell.add(tgt_.points(), tgt_.faces()[cellFaces[i]]);
+    }
 
     Tuple2<scalar, point> volAndInertia =
     overlapEngine.cellCellOverlapMomentMinDecomp
     (
         src_,
-        srcCellI,
+        srcCelli,
         tgt_,
-        tgtCellI,
+        tgtCelli,
         bbTgtCell
     );
 
diff --git a/src/sampling/meshToMesh/meshToMesh.C b/src/sampling/meshToMesh/meshToMesh.C
index d4ab2b7449d5b16f31633643758e8582f1bdc10e..2c6a5e8ab65fbefc82bf39f99078194ee66bbfe3 100644
--- a/src/sampling/meshToMesh/meshToMesh.C
+++ b/src/sampling/meshToMesh/meshToMesh.C
@@ -52,6 +52,17 @@ Foam::meshToMesh::interpolationMethodNames_
 };
 
 
+const Foam::Enum
+<
+    Foam::meshToMesh::procMapMethod
+>
+Foam::meshToMesh::procMapMethodNames_
+{
+    { procMapMethod::pmAABB, "AABB" },
+    { procMapMethod::pmLOD, "LOD" },
+};
+
+
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<>
@@ -628,18 +639,18 @@ Foam::meshToMesh::interpolationMethodAMI(const interpolationMethod method)
 {
     switch (method)
     {
-        case imDirect:
+        case interpolationMethod::imDirect:
         {
             return AMIPatchToPatchInterpolation::imDirect;
             break;
         }
-        case imMapNearest:
+        case interpolationMethod::imMapNearest:
         {
             return AMIPatchToPatchInterpolation::imMapNearest;
             break;
         }
-        case imCellVolumeWeight:
-        case imCorrectedCellVolumeWeight:
+        case interpolationMethod::imCellVolumeWeight:
+        case interpolationMethod::imCorrectedCellVolumeWeight:
         {
             return AMIPatchToPatchInterpolation::imFaceAreaWeight;
             break;
@@ -647,7 +658,7 @@ Foam::meshToMesh::interpolationMethodAMI(const interpolationMethod method)
         default:
         {
             FatalErrorInFunction
-                << "Unhandled enumeration " << method
+                << "Unhandled enumeration " << interpolationMethodNames_[method]
                 << abort(FatalError);
         }
     }
@@ -827,11 +838,13 @@ Foam::meshToMesh::meshToMesh
     const polyMesh& src,
     const polyMesh& tgt,
     const interpolationMethod& method,
+    const procMapMethod& mapMethod,
     bool interpAllPatches
 )
 :
     srcRegion_(src),
     tgtRegion_(tgt),
+    procMapMethod_(mapMethod),
     srcPatchID_(),
     tgtPatchID_(),
     patchAMIs_(),
@@ -865,11 +878,13 @@ Foam::meshToMesh::meshToMesh
     const polyMesh& tgt,
     const word& methodName,
     const word& AMIMethodName,
+    const procMapMethod& mapMethod,
     bool interpAllPatches
 )
 :
     srcRegion_(src),
     tgtRegion_(tgt),
+    procMapMethod_(mapMethod),
     srcPatchID_(),
     tgtPatchID_(),
     patchAMIs_(),
@@ -896,11 +911,13 @@ Foam::meshToMesh::meshToMesh
     const interpolationMethod& method,
     const HashTable<word>& patchMap,
     const wordList& cuttingPatches,
+    const procMapMethod& mapMethod,
     const bool normalise
 )
 :
     srcRegion_(src),
     tgtRegion_(tgt),
+    procMapMethod_(mapMethod),
     srcPatchID_(),
     tgtPatchID_(),
     patchAMIs_(),
@@ -936,11 +953,13 @@ Foam::meshToMesh::meshToMesh
     const word& AMIMethodName,  // boundary mapping
     const HashTable<word>& patchMap,
     const wordList& cuttingPatches,
+    const procMapMethod& mapMethod,
     const bool normalise
 )
 :
     srcRegion_(src),
     tgtRegion_(tgt),
+    procMapMethod_(mapMethod),
     srcPatchID_(),
     tgtPatchID_(),
     patchAMIs_(),
diff --git a/src/sampling/meshToMesh/meshToMesh.H b/src/sampling/meshToMesh/meshToMesh.H
index e1567c18a4bf029691473d285c497a4c891b8b7b..40aaaf329a09ffbeda4288b1cb989c4b647a1091 100644
--- a/src/sampling/meshToMesh/meshToMesh.H
+++ b/src/sampling/meshToMesh/meshToMesh.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2012-2016 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -66,7 +66,7 @@ public:
     // Public data types
 
         //- Enumeration specifying interpolation method
-        enum interpolationMethod
+        enum class interpolationMethod
         {
             imDirect,
             imMapNearest,
@@ -76,6 +76,15 @@ public:
 
         static const Enum<interpolationMethod> interpolationMethodNames_;
 
+        //- Enumeration specifying processor parallel map construction method
+        enum class procMapMethod
+        {
+            pmAABB,
+            pmLOD
+        };
+
+        static const Enum<procMapMethod> procMapMethodNames_;
+
 private:
 
     // Private data
@@ -86,6 +95,9 @@ private:
         //- Reference to the target mesh
         const polyMesh& tgtRegion_;
 
+        //- Processor parallel map construction method
+        procMapMethod procMapMethod_;
+
         //- List of target patch IDs per source patch (local index)
         List<label> srcPatchID_;
 
@@ -301,6 +313,7 @@ public:
         const polyMesh& src,
         const polyMesh& tgt,
         const interpolationMethod& method,
+        const procMapMethod& mapMethod = procMapMethod::pmAABB,
         const bool interpAllPatches = true
     );
 
@@ -311,6 +324,7 @@ public:
         const polyMesh& tgt,
         const word& methodName,     // internal mapping
         const word& AMIMethodName,  // boundary mapping
+        const procMapMethod& mapMethod = procMapMethod::pmAABB,
         const bool interpAllPatches = true
     );
 
@@ -322,6 +336,7 @@ public:
         const interpolationMethod& method,
         const HashTable<word>& patchMap,
         const wordList& cuttingPatches,
+        const procMapMethod& mapMethod = procMapMethod::pmAABB,
         const bool normalise = true
     );
 
@@ -335,6 +350,7 @@ public:
         const word& AMIMethodName,  // boundary mapping
         const HashTable<word>& patchMap,
         const wordList& cuttingPatches,
+        const procMapMethod& mapMethod = procMapMethod::pmAABB,
         const bool normalise = true
     );
 
diff --git a/src/sampling/meshToMesh/meshToMeshParallelOps.C b/src/sampling/meshToMesh/meshToMeshParallelOps.C
index bae3aa442a220a2b72e1fc55934826aa571c93de..b391a6132f1222198cfb46c8675a1b653727ca5b 100644
--- a/src/sampling/meshToMesh/meshToMeshParallelOps.C
+++ b/src/sampling/meshToMesh/meshToMeshParallelOps.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2012-2017 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2015-2017 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2015-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -31,6 +31,7 @@ License
 #include "processorPolyPatch.H"
 #include "SubField.H"
 #include "AABBTree.H"
+#include "cellBox.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
@@ -98,12 +99,12 @@ Foam::label Foam::meshToMesh::calcOverlappingProcs
     {
         const treeBoundBoxList& bbp = procBb[proci];
 
-        forAll(bbp, bbi)
+        for (const treeBoundBox& b : bbp)
         {
-            if (bbp[bbi].overlaps(bb))
+            if (b.overlaps(bb))
             {
                 overlaps[proci] = true;
-                nOverlaps++;
+                ++nOverlaps;
                 break;
             }
         }
@@ -119,139 +120,179 @@ Foam::autoPtr<Foam::mapDistribute> Foam::meshToMesh::calcProcMap
     const polyMesh& tgt
 ) const
 {
-    // get decomposition of cells on src mesh
-    List<treeBoundBoxList> procBb(Pstream::nProcs());
-
-    if (src.nCells() > 0)
+    switch (procMapMethod_)
     {
-        procBb[Pstream::myProcNo()] = AABBTree<labelList>
-        (
-            src.cellPoints(),
-            src.points(),
-            false
-        ).boundBoxes();
-    }
-    else
-    {
-        procBb[Pstream::myProcNo()] = treeBoundBoxList();
-    }
-
+        case procMapMethod::pmLOD:
+        {
+            Info<< "meshToMesh: Using processorLOD method" << endl;
+
+            // Create processor map of overlapping faces. This map gets
+            // (possibly remote) cells from the tgt mesh such that they
+            // (together) cover all of the src mesh
+            label nGlobalSrcCells =
+                returnReduce(src.cells().size(), sumOp<label>());
+            label cellsPerBox = max(1, 0.001*nGlobalSrcCells);
+            typename processorLODs::cellBox boxLOD
+            (
+                src.cells(),
+                src.faces(),
+                src.points(),
+                tgt.cells(),
+                tgt.faces(),
+                tgt.points(),
+                cellsPerBox,
+                src.nCells()
+            );
+
+            return boxLOD.map();
+            break;
+        }
+        default:
+        {
+            Info<< "meshToMesh: Using AABBTree method" << endl;
 
-    Pstream::gatherList(procBb);
-    Pstream::scatterList(procBb);
+            // get decomposition of cells on src mesh
+            List<treeBoundBoxList> procBb(Pstream::nProcs());
 
+            if (src.nCells() > 0)
+            {
+                procBb[Pstream::myProcNo()] = AABBTree<labelList>
+                (
+                    src.cellPoints(),
+                    src.points(),
+                    false
+                ).boundBoxes();
+            }
+            else
+            {
+                procBb[Pstream::myProcNo()] = treeBoundBoxList();
+            }
 
-    if (debug)
-    {
-        InfoInFunction
-            << "Determining extent of src mesh per processor:" << nl
-            << "\tproc\tbb" << endl;
-        forAll(procBb, proci)
-        {
-            Info<< '\t' << proci << '\t' << procBb[proci] << endl;
-        }
-    }
 
+            Pstream::gatherList(procBb);
+            Pstream::scatterList(procBb);
 
-    // determine which cells of tgt mesh overlaps src mesh per proc
-    const cellList& cells = tgt.cells();
-    const faceList& faces = tgt.faces();
-    const pointField& points = tgt.points();
 
-    labelListList sendMap;
+            if (debug)
+            {
+                InfoInFunction
+                    << "Determining extent of src mesh per processor:" << nl
+                    << "\tproc\tbb" << endl;
+                forAll(procBb, proci)
+                {
+                    Info<< '\t' << proci << '\t' << procBb[proci] << endl;
+                }
+            }
 
-    {
-        // per processor indices into all segments to send
-        List<DynamicList<label>> dynSendMap(Pstream::nProcs());
-        label iniSize = floor(tgt.nCells()/Pstream::nProcs());
 
-        forAll(dynSendMap, proci)
-        {
-            dynSendMap[proci].setCapacity(iniSize);
-        }
+            // determine which cells of tgt mesh overlaps src mesh per proc
+            const cellList& cells = tgt.cells();
+            const faceList& faces = tgt.faces();
+            const pointField& points = tgt.points();
 
-        // work array - whether src processor bb overlaps the tgt cell bounds
-        boolList procBbOverlaps(Pstream::nProcs());
-        forAll(cells, celli)
-        {
-            const cell& c = cells[celli];
+            labelListList sendMap;
 
-            // determine bounding box of tgt cell
-            boundBox cellBb(boundBox::invertedBox);
-            forAll(c, facei)
             {
-                const face& f = faces[c[facei]];
-                forAll(f, fp)
+                // per processor indices into all segments to send
+                List<DynamicList<label>> dynSendMap(Pstream::nProcs());
+                label iniSize = floor(tgt.nCells()/Pstream::nProcs());
+
+                forAll(dynSendMap, proci)
                 {
-                    cellBb.add(points, f);
+                    dynSendMap[proci].setCapacity(iniSize);
+                }
+
+                // work array - whether src processor bb overlaps the tgt cell
+                // bounds
+                boolList procBbOverlaps(Pstream::nProcs());
+                forAll(cells, celli)
+                {
+                    const cell& c = cells[celli];
+
+                    // determine bounding box of tgt cell
+                    boundBox cellBb(boundBox::invertedBox);
+                    forAll(c, facei)
+                    {
+                        const face& f = faces[c[facei]];
+                        forAll(f, fp)
+                        {
+                            cellBb.add(points, f);
+                        }
+                    }
+
+                    // find the overlapping tgt cells on each src processor
+                    (void)calcOverlappingProcs(procBb, cellBb, procBbOverlaps);
+
+                    forAll(procBbOverlaps, proci)
+                    {
+                        if (procBbOverlaps[proci])
+                        {
+                            dynSendMap[proci].append(celli);
+                        }
+                    }
                 }
-            }
 
-            // find the overlapping tgt cells on each src processor
-            (void)calcOverlappingProcs(procBb, cellBb, procBbOverlaps);
+                // convert dynamicList to labelList
+                sendMap.setSize(Pstream::nProcs());
+                forAll(sendMap, proci)
+                {
+                    sendMap[proci].transfer(dynSendMap[proci]);
+                }
+            }
 
-            forAll(procBbOverlaps, proci)
+            // debug printing
+            if (debug)
             {
-                if (procBbOverlaps[proci])
+                Pout<< "Of my " << cells.size()
+                    << " target cells I need to send to:" << nl
+                    << "\tproc\tcells" << endl;
+                forAll(sendMap, proci)
                 {
-                    dynSendMap[proci].append(celli);
+                    Pout<< '\t' << proci << '\t' << sendMap[proci].size()
+                        << endl;
                 }
             }
-        }
 
-        // convert dynamicList to labelList
-        sendMap.setSize(Pstream::nProcs());
-        forAll(sendMap, proci)
-        {
-            sendMap[proci].transfer(dynSendMap[proci]);
-        }
-    }
 
-    // debug printing
-    if (debug)
-    {
-        Pout<< "Of my " << cells.size() << " target cells I need to send to:"
-            << nl << "\tproc\tcells" << endl;
-        forAll(sendMap, proci)
-        {
-            Pout<< '\t' << proci << '\t' << sendMap[proci].size() << endl;
-        }
-    }
+            // send over how many tgt cells I need to receive from each
+            // processor
+            labelListList sendSizes(Pstream::nProcs());
+            sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
+            forAll(sendMap, proci)
+            {
+                sendSizes[Pstream::myProcNo()][proci] = sendMap[proci].size();
+            }
+            Pstream::gatherList(sendSizes);
+            Pstream::scatterList(sendSizes);
 
 
-    // send over how many tgt cells I need to receive from each processor
-    labelListList sendSizes(Pstream::nProcs());
-    sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
-    forAll(sendMap, proci)
-    {
-        sendSizes[Pstream::myProcNo()][proci] = sendMap[proci].size();
-    }
-    Pstream::gatherList(sendSizes);
-    Pstream::scatterList(sendSizes);
+            // determine order of receiving
+            labelListList constructMap(Pstream::nProcs());
 
+            label segmentI = 0;
+            forAll(constructMap, proci)
+            {
+                // what I need to receive is what other processor is sending
+                // to me
+                label nRecv = sendSizes[proci][Pstream::myProcNo()];
+                constructMap[proci].setSize(nRecv);
 
-    // determine order of receiving
-    labelListList constructMap(Pstream::nProcs());
+                for (label i = 0; i < nRecv; i++)
+                {
+                    constructMap[proci][i] = segmentI++;
+                }
+            }
 
-    label segmentI = 0;
-    forAll(constructMap, proci)
-    {
-        // what I need to receive is what other processor is sending to me
-        label nRecv = sendSizes[proci][Pstream::myProcNo()];
-        constructMap[proci].setSize(nRecv);
+            return autoPtr<mapDistribute>::New
+            (
+                segmentI,       // size after construction
+                std::move(sendMap),
+                std::move(constructMap)
+            );
 
-        for (label i = 0; i < nRecv; i++)
-        {
-            constructMap[proci][i] = segmentI++;
+            break;
         }
     }
-
-    return autoPtr<mapDistribute>::New
-    (
-        segmentI,       // size after construction
-        std::move(sendMap),
-        std::move(constructMap)
-    );
 }