diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
index bd655dc2512fb3529d73e1ad79bdbacc55b75657..15be3f9c1d0e5c195602f49549f69e4e1950729b 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 1991-2007 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -33,12 +33,86 @@ Description
 #include "argList.H"
 #include "Time.H"
 #include "fvMesh.H"
-#include "autoHexMeshDriver.H"
+#include "autoRefineDriver.H"
+#include "autoSnapDriver.H"
+#include "autoLayerDriver.H"
+#include "searchableSurfaces.H"
+#include "refinementSurfaces.H"
+#include "shellSurfaces.H"
+#include "decompositionMethod.H"
+#include "fvMeshDistribute.H"
+#include "wallPolyPatch.H"
+#include "refinementParameters.H"
+#include "snapParameters.H"
+#include "layerParameters.H"
+
 
 using namespace Foam;
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+// Check writing tolerance before doing any serious work
+scalar getMergeDistance(const polyMesh& mesh, const scalar mergeTol)
+{
+    const boundBox& meshBb = mesh.bounds();
+    scalar mergeDist = mergeTol*mag(meshBb.max() - meshBb.min());
+    scalar writeTol = std::pow
+    (
+        scalar(10.0),
+       -scalar(IOstream::defaultPrecision())
+    );
+
+    Info<< nl
+        << "Overall mesh bounding box  : " << meshBb << nl
+        << "Relative tolerance         : " << mergeTol << nl
+        << "Absolute matching distance : " << mergeDist << nl
+        << endl;
+
+    if (mesh.time().writeFormat() == IOstream::ASCII && mergeTol < writeTol)
+    {
+        FatalErrorIn("getMergeDistance(const polyMesh&, const scalar)")
+            << "Your current settings specify ASCII writing with "
+            << IOstream::defaultPrecision() << " digits precision." << endl
+            << "Your merging tolerance (" << mergeTol << ") is finer than this."
+            << endl
+            << "Please change your writeFormat to binary"
+            << " or increase the writePrecision" << endl
+            << "or adjust the merge tolerance (-mergeTol)."
+            << exit(FatalError);
+    }
+
+    return mergeDist;
+}
+
+
+// Write mesh and additional information
+void writeMesh
+(
+    const string& msg,
+    const meshRefinement& meshRefiner,
+    const label debug
+)
+{
+    const fvMesh& mesh = meshRefiner.mesh();
+
+    meshRefiner.printMeshInfo(debug, msg);
+    Info<< "Writing mesh to time " << mesh.time().timeName() << endl;
+
+    meshRefiner.write(meshRefinement::MESH|meshRefinement::SCALARLEVELS, "");
+    if (debug & meshRefinement::OBJINTERSECTIONS)
+    {
+        meshRefiner.write
+        (
+            meshRefinement::OBJINTERSECTIONS,
+            mesh.time().path()/mesh.time().timeName()
+        );
+    }
+    Info<< "Written mesh in = "
+        << mesh.time().cpuTimeIncrement() << " s." << endl;
+}
+
+
+
 int main(int argc, char *argv[])
 {
 #   include "setRootCase.H"
@@ -49,6 +123,11 @@ int main(int argc, char *argv[])
     Info<< "Read mesh in = "
         << runTime.cpuTimeIncrement() << " s" << endl;
 
+    // Check patches and faceZones are synchronised
+    mesh.boundaryMesh().checkParallelSync(true);
+    meshRefinement::checkCoupledFaceZones(mesh);
+
+
     // Read decomposePar dictionary
     IOdictionary decomposeDict
     (
@@ -75,47 +154,282 @@ int main(int argc, char *argv[])
        )
     );
 
-    // refinement parameters
-    const dictionary& refineDict = meshDict.subDict("refineDict");
+    // all surface geometry
+    const dictionary& geometryDict = meshDict.subDict("geometry");
 
-    // snap-to-surface parameters
-    const dictionary& snapDict = meshDict.subDict("snapDict");
+    // refinement parameters
+    const dictionary& refineDict = meshDict.subDict("castellatedMeshControls");
 
     // mesh motion and mesh quality parameters
-    const dictionary& motionDict = meshDict.subDict("motionDict");
+    const dictionary& motionDict = meshDict.subDict("meshQualityControls");
+
+    // snap-to-surface parameters
+    const dictionary& snapDict = meshDict.subDict("snapControls");
 
     // layer addition parameters
-    const dictionary& layerDict = meshDict.subDict("layerDict");
+    const dictionary& layerDict = meshDict.subDict("addLayersControls");
+
+
+
+    // Debug
+    // ~~~~~
+
+    const label debug(readLabel(meshDict.lookup("debug")));
+    if (debug > 0)
+    {
+        meshRefinement::debug = debug;
+        autoRefineDriver::debug = debug;
+        autoSnapDriver::debug = debug;
+        autoLayerDriver::debug = debug;
+    }        
+
+
+    // Read geometry
+    // ~~~~~~~~~~~~~
+
+    searchableSurfaces allGeometry
+    (
+        IOobject
+        (
+            "abc",                      // dummy name
+            mesh.time().constant(),     // directory
+            "triSurface",               // instance
+            mesh.time(),                // registry
+            IOobject::MUST_READ,
+            IOobject::NO_WRITE
+        ),
+        geometryDict
+    );
+
+
+    // Read refinement surfaces
+    // ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Info<< "Reading refinement surfaces." << endl;
+    refinementSurfaces surfaces
+    (
+        allGeometry,
+        refineDict.subDict("refinementSurfaces")
+    );
+    Info<< "Read refinement surfaces in = "
+        << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+
+
+    // Read refinement shells
+    // ~~~~~~~~~~~~~~~~~~~~~~
+
+    Info<< "Reading refinement shells." << endl;
+    shellSurfaces shells
+    (
+        allGeometry,
+        refineDict.subDict("refinementRegions")
+    );
+    Info<< "Read refinement shells in = "
+        << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+
+
+    Info<< "Setting refinement level of surface to be consistent"
+        << " with shells." << endl;
+    surfaces.setMinLevelFields(shells);
+    Info<< "Checked shell refinement in = "
+        << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+
+
 
 
-    // Main meshing driver. Read surfaces. Determine initial intersections.
-    autoHexMeshDriver meshEngine
+    // Add all the surface regions as patches
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    labelList globalToPatch;
+    {
+        Info<< nl
+            << "Adding patches for surface regions" << nl
+            << "----------------------------------" << nl
+            << endl;
+
+        // From global region number to mesh patch.
+        globalToPatch.setSize(surfaces.nRegions(), -1);
+
+        Info<< "Patch\tRegion" << nl
+            << "-----\t------"
+            << endl;
+
+        const labelList& surfaceGeometry = surfaces.surfaces();
+        forAll(surfaceGeometry, surfI)
+        {
+            label geomI = surfaceGeometry[surfI];
+
+            const wordList& regNames = allGeometry.regionNames()[geomI];
+
+            Info<< surfaces.names()[surfI] << ':' << nl << nl;
+
+            forAll(regNames, i)
+            {
+                label patchI = meshRefinement::addPatch
+                (
+                    mesh,
+                    regNames[i],
+                    wallPolyPatch::typeName
+                );
+
+                Info<< patchI << '\t' << regNames[i] << nl;
+
+                globalToPatch[surfaces.globalRegion(surfI, i)] = patchI;
+            }
+
+            Info<< nl;
+        }
+        Info<< "Added patches in = "
+            << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+    }
+
+
+    // Parallel
+    // ~~~~~~~~
+
+    // Decomposition
+    autoPtr<decompositionMethod> decomposerPtr
+    (
+        decompositionMethod::New
+        (
+            decomposeDict,
+            mesh
+        )
+    );
+    decompositionMethod& decomposer = decomposerPtr();
+
+    if (Pstream::parRun() && !decomposer.parallelAware())
+    {
+        FatalErrorIn(args.executable())
+            << "You have selected decomposition method "
+            << decomposer.typeName
+            << " which is not parallel aware." << endl
+            << "Please select one that is (hierarchical, parMetis)"
+            << exit(FatalError);
+    }
+
+    const scalar mergeDist = getMergeDistance
     (
         mesh,
-        meshDict,       // global control parameters
-        refineDict,     // refinement parameters
-        decomposeDict
+        readScalar(meshDict.lookup("mergeTolerance"))
     );
 
-    Switch wantRefine(meshDict.lookup("doRefine"));
-    Switch wantSnap(meshDict.lookup("doSnap"));
-    Switch wantLayers(meshDict.lookup("doLayers"));
+
+    // Mesh distribution engine (uses tolerance to reconstruct meshes)
+    fvMeshDistribute distributor(mesh, mergeDist);
+
+
+    // Refinement engine
+    // ~~~~~~~~~~~~~~~~~
+
+    Info<< nl
+        << "Determining initial surface intersections" << nl
+        << "-----------------------------------------" << nl
+        << endl;
+
+    // Main refinement engine
+    meshRefinement meshRefiner
+    (
+        mesh,
+        mergeDist,          // tolerance used in sorting coordinates
+        surfaces,           // for surface intersection refinement
+        shells              // for volume (inside/outside) refinement
+    );
+    Info<< "Calculated surface intersections in = "
+        << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+
+    // Some stats
+    meshRefiner.printMeshInfo(debug, "Initial mesh");
+
+    meshRefiner.write
+    (
+        debug&meshRefinement::OBJINTERSECTIONS,
+        mesh.time().path()/mesh.time().timeName()
+    );
+
+
+
+
+    // Now do the real work -refinement -snapping -layers
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Switch wantRefine(meshDict.lookup("castellatedMesh"));
+    Switch wantSnap(meshDict.lookup("snap"));
+    Switch wantLayers(meshDict.lookup("addLayers"));
 
     if (wantRefine)
     {
-        meshEngine.doRefine(refineDict, wantSnap);
+        autoRefineDriver refineDriver
+        (
+            meshRefiner,
+            decomposer,
+            distributor,
+            globalToPatch
+        );
+
+        // Refinement parameters
+        refinementParameters refineParams(refineDict);
+
+        refineDriver.doRefine(refineDict, refineParams, wantSnap);
+
+        writeMesh
+        (
+            "Refined mesh",
+            meshRefiner,
+            debug
+        );
     }
 
     if (wantSnap)
     {
-        meshEngine.doSnap(snapDict, motionDict);
+        autoSnapDriver snapDriver
+        (
+            meshRefiner,
+            globalToPatch
+        );
+
+        // Snap parameters
+        snapParameters snapParams(snapDict);
+
+        snapDriver.doSnap(snapDict, motionDict, snapParams);
+
+        writeMesh
+        (
+            "Snapped mesh",
+            meshRefiner,
+            debug
+        );
     }
 
     if (wantLayers)
     {
-        meshEngine.doLayers(layerDict, motionDict);
+        autoLayerDriver layerDriver
+        (
+            meshRefiner,
+            globalToPatch
+        );
+
+        // Layer addition parameters
+        layerParameters layerParams(layerDict, mesh.boundaryMesh());
+
+        layerDriver.doLayers
+        (
+            layerDict,
+            motionDict,
+            layerParams,
+            decomposer,
+            distributor
+        );
+
+        writeMesh
+        (
+            "Layer mesh",
+            meshRefiner,
+            debug
+        );
     }
 
+
     Info<< "Finished meshing in = "
         << runTime.elapsedCpuTime() << " s." << endl;
 
diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
index e9aa8f189d9d748994d1f564cabedb37c55a30ec..e97b1ee9a756bc9d0e42de449a28e596566f60a6 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
@@ -22,161 +22,171 @@ FoamFile
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Which phases to run.
-doRefine true;
-doSnap   true;
-doLayers true; // includes autoMergeFaces
+// Which of the steps to run
+castellatedMesh true;
+snap            true;
+addLayers       false;
+
+
+// Geometry. Definition of all surfaces. All surfaces are of class
+// searchableSurface.
+// Surfaces are used
+// - to specify refinement for any mesh cell intersecting it
+// - to specify refinement for any mesh cell inside/outside/near
+// - to 'snap' the mesh boundary to the surface
+geometry
+{
+    box1x1x1
+    {
+        type searchableBox;
+        min (1.5 1 -0.5);
+        max (3.5 2 0.5);
+    }
+
+    sphere.stl
+    {
+        type triSurfaceMesh;
+
+        // Per region the patchname. If not provided will be <name>_<region>.
+        regions
+        {
+            secondSolid
+            {
+                name mySecondPatch;
+            }
+        }
+    }
 
+    sphere2
+    {
+        type searchableSphere;
+        centre  (1.5 1.5 1.5);
+        radius  1.03;
+    }
+};
 
-// Whether to dump intermediate meshes and print lots
-// 1 : write mesh
-// 2 : write volScalarField with cellLevel for postprocessing
-// 4 : write current intersections as .obj files
-debug 0;
 
-refineDict
-{
-    // Which part to keep.
-    // NOTE: This point should never be on a face, always inside a cell, even
-    // after refinement.
-    keepPoints ((3 0.28 0.43));
 
-    // Whether to remove/split cells likely to give problems when snapping
-    handleSnapProblems on;
+// Settings for the castellatedMesh generation.
+castellatedMeshControls
+{
 
-    // Merge tolerance. Is fraction of overall bounding box of initial mesh
-    mergeTolerance 1E-6;
+    // Refinement parameters
+    // ~~~~~~~~~~~~~~~~~~~~~
 
     // While refining maximum number of cells per processor. This is basically
     // the number of cells that fit on a processor. If you choose this too small
     // it will do just more refinement iterations to obtain a similar mesh.
-    procCellLimit 1000000;
-
+    maxLocalCells 1000000;
 
     // Overall cell limit (approximately). Refinement will stop immediately
     // upon reaching this number so a refinement level might not complete.
     // Note that this is the number of cells before removing the part which
-    // is not 'visible' from the keepPoint. The final number of cells might actually
-    // be a lot less.
-    cellLimit 2000000;
-
+    // is not 'visible' from the keepPoint. The final number of cells might
+    // actually be a lot less.
+    maxGlobalCells 2000000;
 
     // The surface refinement loop might spend lots of iterations refining just a
     // few cells. This setting will cause refinement to stop if <= minimumRefine
     // are selected for refinement. Note: it will at least do one iteration
     // (unless the number of cells to refine is 0)
-    minimumRefine 0;
-
-
-
+    minRefinementCells 0;
 
     // Number of buffer layers between different levels.
     // 1 means normal 2:1 refinement restriction, larger means slower
     // refinement.
-    nBufferLayers 1;
+    nCellsBetweenLevels 1;
 
 
-    // Feature Edge Refinement
-    // ~~~~~~~~~~~~~~~~~~~~~~~
 
-    // External feature file. Read from constant/triSurface for now.
-    // Limitations:
-    // - either start or edge of any feature line has to be inside domain
-    //   and be visible from keepPoint on starting mesh.
-    // - refining cells containing features is separate phase before refining
-    //   based on surface.
-    // - no load balancing, no check for cellLimit is done while doing this.
+    // Explicit feature edge refinement
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    // Specifies a level for any cell intersected by its edges.
+    // This is a featureEdgeMesh, read from constant/triSurface for now.
     features
     (
-    //    {
-    //        file "someLine.eMesh";
-    //        level 2;
-    //    }
+        //{
+        //    file "someLine.eMesh";
+        //    level 2;
+        //}
     );
 
 
-    // Internal Mesh Refinement
+
+    // Surface based refinement
     // ~~~~~~~~~~~~~~~~~~~~~~~~
 
-    // Specifies the areas where the refinement has to be a certain level.
-    // These surfaces have to be closed. Refinement is either inside
-    // (refineInside = true) or outside. (note:insideness or outsideness
-    // is with respect to far away)
-    // Note:that even using these the transition can never be faster than
-    // nBufferLayers!
-    // Note:the refinement level can never be higher than any of the surfaces
-    // they overlap with. See below for the surface refinement level specification.
-    refinementShells
-    (
+    // Specifies two levels for every surface. The first is the minimum level,
+    // every cell intersecting a surface gets refined up to the minimum level.
+    // The second level is the maximum level. Cells that 'see' multiple
+    // intersections where the intersections make an
+    // angle > resolveFeatureAngle get refined up to the maximum level.
+
+    refinementSurfaces
+    {
+        sphere.stl
         {
-            //type triSurfaceMesh;
-            //name cube1x1x1.stl;
-            type searchableBox;
-            name box1x1x1;
-            min (2.5 -0.5 -0.5);
-            max (3.5 0.5 0.5);
-
-            level 4;
-            refineInside true;
+            // Surface-wise min and max refinement level
+            level (2 2);
+        
+            // Optional region-wise level specification
+            regions
+            {
+                secondSolid
+                {
+                    level (3 3);
+                }
+            }
         }
-    );
+    }
 
+    resolveFeatureAngle 30;
 
-    // Surface based refinement
-    // ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    // Curvature. Cosine of angle between two neighbouring surface intersections.
-    // Only used if cell level > minLevel and < maxLevel.
-    curvature 0.5;
 
+    // Region-wise refinement
+    // ~~~~~~~~~~~~~~~~~~~~~~
 
-    // Overall the refinement is according to the following rules:
-    // - if the cell-cell vector intersects a surface any cell that
-    //   is less refined than the minRefinementLevel of the surface gets refined.
-    // - else if the refinement level of the cell is between the
-    //   minRefinementLevel and maxRefinementLevel the cell gets refined if
-    //      - the normal of neighbouring surface intersections differ by more
-    //        than above curvature
-    //      - or if neighbouring surface intersections are on different surfaces or
-    //        different surface regions.
+    // Specifies refinement level for cells in relation to a surface. One of
+    // three modes
+    // - distance. 'levels' specifies per distance to the surface the
+    //   wanted refinement level. The distances need to be specified in
+    //   descending order.
+    // - inside. 'levels' is only one entry and only the level is used. All
+    //   cells inside the surface get refined up to the level. The surface
+    //   needs to be closed for this to be possible.
+    // - outside. Same but cells outside.
 
-
-    // surfaces
-    surfaces
-    (
+    refinementRegions
+    {
+        box1x1x1
         {
-            name    sphere;
-            file    "sphere.stl";
+            mode inside;
+            levels ((1.0 4));
+        }
+        //sphere.stl
+        //{
+        //    mode distance;
+        //    levels ((1.0 5) (2.0 3));
+        //}
+    }
 
-            // Surface wide refinement level
-            minRefinementLevel 1;
-            maxRefinementLevel 1;
 
-            // Layers
-            surfaceLayers 1;
+    // Mesh selection
+    // ~~~~~~~~~~~~~~
 
-            // Region specific refinement level
-            regions
-            (
-                {
-                    name firstSolid;
-                    minRefinementLevel 3;
-                    maxRefinementLevel 3;
-                    surfaceLayers 2;
-                }
-                {
-                    name secondSolid;
-                    minRefinementLevel 1;
-                    maxRefinementLevel 1;
-                    surfaceLayers 1;
-                }
-            );
-        }
-    );
+    // After refinement patches get added for all refinementSurfaces and
+    // all cells intersecting the surfaces get put into these patches. The
+    // section reachable from the locationInMesh is kept.
+    // NOTE: This point should never be on a face, always inside a cell, even
+    // after refinement.
+    locationInMesh (5 0.28 0.43);
 }
 
-// For snapping
-snapDict
+
+
+// Settings for the snapping.
+snapControls
 {
     //- Number of patch smoothing iterations before finding correspondence
     //  to surface
@@ -185,47 +195,64 @@ snapDict
     //- Relative distance for points to be attracted by surface feature point
     //  or edge. True distance is this factor times local
     //  maximum edge length.
-    snapTol 4.0;
+    tolerance 4.0;
 
-    //- Whether to move internal mesh as well as boundary
-    smoothMesh true;
-
-    //- Number of mesh displacement smoothing iterations.
-    nSmoothDispl 30;
+    //- Number of mesh displacement relaxation iterations.
+    nSolveIter 30;
 
     //- Maximum number of snapping relaxation iterations. Should stop
     //  before upon reaching a correct mesh.
-    nSnap 5;
+    nRelaxIter 5;
 }
 
 
-// For cell layers
-layerDict
+
+// Settings for the layer addition.
+addLayersControls
 {
-    //- When not to extrude surface. 0 is flat surface, 90 is when two faces
-    //  make straight angle.
-    featureAngle 60;
+    // Per final patch (so not geometry!) the layer information
+    layers
+    {
+        sphere.stl_firstSolid
+        {
+            nSurfaceLayers 1;
 
-    //- Maximum number of snapping relaxation iterations. Should stop
-    //  before upon reaching a correct mesh.
-    nSnap 5;
+        }
+        maxY
+        {
+            nSurfaceLayers 1;
+        }
+    }
 
+    // Expansion factor for layer mesh
+    expansionRatio 1.0;
 
-    //- Minimum thickness of cell layer. If for any reason layer cannot be
-    //  above minThickness do not add layer if thickness below minThickNess.
-    //  Relative to undistorted cell size
+    //- Wanted thickness of final added cell layer. If multiple layers
+    //  is the
+    //  thickness of the layer furthest away from the wall.
+    //  Relative to undistorted size of cell outside layer.
+    finalLayerRatio 0.3; 
+
+    //- Minimum thickness of cell layer. If for any reason layer
+    //  cannot be above minThickness do not add layer.
+    //  Relative to undistorted size of cell outside layer.
     minThickness 0.25;
 
     //- If points get not extruded do nGrow layers of connected faces that are
-    //  not grown. Is used to not do layers at all close to features.
+    //  also not grown. This helps convergence of the layer addition process
+    //  close to features.
     nGrow 1;
 
-    // Expansion factor for layer mesh
-    expansionRatio 1.3;	
 
-    // Ratio of cell size in final added cell layer to cell size 
-    // outside layer 
-    finalLayerRatio 0.3;
+    // Advanced settings
+
+    //- When not to extrude surface. 0 is flat surface, 90 is when two faces
+    //  make straight angle.
+    featureAngle 60;
+
+    //- Maximum number of snapping relaxation iterations. Should stop
+    //  before upon reaching a correct mesh.
+    nRelaxIter 5;
 
     // Number of smoothing iterations of surface normals 
     nSmoothSurfaceNormals 1;
@@ -248,20 +275,14 @@ layerDict
 
     // Create buffer region for new layer terminations
     nBufferCellsNoExtrude 0;
-
-    thickness 0.5;
-    nSmoothDispl 4;
 }
 
 
-// For mesh motion
-motionDict
-{
-    //
-    // Mesh Quality Parameters. Decide when mesh is good enough to stop
-    // smoothing.
-    //
 
+// Generic mesh quality settings. At any undoable phase these determine
+// where to undo.
+meshQualityControls
+{
     //- Maximum non-orthogonality allowed. Set to 180 to disable.
     maxNonOrtho 65;
 
@@ -298,10 +319,10 @@ motionDict
     //- minVolRatio (0 -> 1)
     minVolRatio 0.01;
 
-
     //must be >0 for Fluent compatibility
     minTriangleTwist -1;
 
+
     // Advanced
 
     //- Number of error distribution iterations
@@ -311,4 +332,19 @@ motionDict
 }
 
 
+// Advanced
+
+// Flags for optional output
+// 0 : only write final meshes
+// 1 : write intermediate meshes
+// 2 : write volScalarField with cellLevel for postprocessing
+// 4 : write current intersections as .obj files
+debug 0;
+
+
+// Merge tolerance. Is fraction of overall bounding box of initial mesh.
+// Note: the write tolerance needs to be higher than this.
+mergeTolerance 1E-6;
+
+
 // ************************************************************************* //
diff --git a/bin/foamPackSource b/bin/foamPackSource
index 30e52c72daed9d7dc846f9ebed7b0844df028d41..519d18929dea979894c366a2e5c16c8d35635ce3 100755
--- a/bin/foamPackSource
+++ b/bin/foamPackSource
@@ -73,13 +73,16 @@ find -H $packDir               \
  -a ! -name "core"             \
  -a ! -name "core.[1-9]*"      \
  -a ! -name "log[0-9]*"        \
+ -a ! -name "libccmio*"        \
  -a ! -name "\.ebrowse"        \
 | sed                          \
  -e "\@$packDir/.git/@d"       \
  -e "\@$packDir/lib/@d"        \
- -e '\@applications/bin/@d'       \
- -e '\@/t/@d'                     \
- -e '\@Make[.A-Za-z]*/[^/]*/@d'   \
+ -e "\@libccmio.*/@d"          \
+ -e '\@applications/bin/@d'    \
+ -e '\@/t/@d'                  \
+ -e '\@Make[.A-Za-z]*/[^/]*/@d'\
+ -e '\@/platforms/@d'          \
  > $tmpFile
 
 tar czpf $packFile --files-from $tmpFile
diff --git a/bin/foamPackThirdPartyBin b/bin/foamPackThirdPartyBin
new file mode 100755
index 0000000000000000000000000000000000000000..484c0c1e4b9251b718ac04bfe93e8e84d21c1536
--- /dev/null
+++ b/bin/foamPackThirdPartyBin
@@ -0,0 +1,78 @@
+#!/bin/sh
+#------------------------------------------------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+#     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Script
+#     foamPackThirdPartyBin <archOptions> [outputDir]
+#
+# Description
+#     Packs and compresses binary version of OpenFOAM ThirdParty for release
+#
+#------------------------------------------------------------------------------
+
+if [ $# = 0 ]
+then
+   echo "Error: archOptionsitecture type expected, exiting"
+   echo
+   echo "Usage : ${0##*/} <archOptions> [outputDir]"
+   echo
+   exit 1
+fi
+archOptions=$1
+arch=${archOptions%%G*}
+arch3264=$(echo "$arch" | sed 's@64@-64@')
+
+timeStamp=$(date +%Y-%m-%d)
+packDir=ThirdParty
+packFile=${packDir}.${archOptions}_${timeStamp}.gtgz
+
+# add optional output directory
+if [ -d "$2" ]
+then
+   packFile="$2/$packFile"
+fi
+
+if [ -f $packFile ]
+then
+   echo "Error: $packFile already exists"
+   exit 1
+fi
+
+# get list of directories
+dirList=`find $packDir -type d -name $arch -o -type d -name $archOptions -o -type l -name $arch3264`
+echo
+echo "Packing $archOptions port of $packDir into $packFile"
+echo
+
+tar czpf $packFile $dirList
+
+if [ $? = 0 ]
+then
+   echo "Finished packing and compressing file $packFile"
+else
+   echo "Error: failure packing $packFile"
+   rm -f $packFile 2>/dev/null
+fi
+
+#------------------------------------------------------------------------------
diff --git a/bin/foamPackThirdPartyGeneral b/bin/foamPackThirdPartyGeneral
new file mode 100755
index 0000000000000000000000000000000000000000..b874e4a0bdb8b3207a191ce1303ebcdfc137bf63
--- /dev/null
+++ b/bin/foamPackThirdPartyGeneral
@@ -0,0 +1,70 @@
+#!/bin/sh
+#------------------------------------------------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+#     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Script
+#     foamPackThirdPartyGeneral [outputDir]
+#
+# Description
+#     Packs and compresses the OpenFOAM ThirdParty directory for release
+#
+#------------------------------------------------------------------------------
+
+timeStamp=$(date +%Y-%m-%d)
+packDir=ThirdParty
+packFile=${packDir}.General_${timeStamp}.gtgz
+
+if [ ! -d $packDir ]
+then
+   echo "Error: directory $packDir does not exist"
+   exit 1
+fi
+
+# add optional output directory
+if [ -d "$1" ]
+then
+   packFile="$1/$packFile"
+fi
+
+if [ -f $packFile ]
+then
+   echo "Error: $packFile already exists"
+   exit 1
+fi
+
+# Create time stamp file
+# ~~~~~~~~~~~~~~~~~~~~~~
+
+echo $timeStamp 2>/dev/null > $packDir/.timeStamp
+
+# Pack and compress the packFile
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+echo
+echo "Packing $packDir into $packFile"
+echo
+
+foamPackSource $packDir $packFile
+
+#------------------------------------------------------------------------------
diff --git a/src/autoMesh/Make/files b/src/autoMesh/Make/files
index c562ee5c6de5bbb273ba95decce1e88dc70eda2e..96141445bea1ba3e2928715d3a50880f693d29b6 100644
--- a/src/autoMesh/Make/files
+++ b/src/autoMesh/Make/files
@@ -1,15 +1,23 @@
 autoHexMesh             = autoHexMesh
+autoHexMeshDriver       = $(autoHexMesh)/autoHexMeshDriver
+
+$(autoHexMeshDriver)/autoLayerDriver.C
+$(autoHexMeshDriver)/autoLayerDriverShrink.C
+$(autoHexMeshDriver)/autoSnapDriver.C
+$(autoHexMeshDriver)/autoRefineDriver.C
+$(autoHexMeshDriver)/autoHexMeshDriver.C
+
+$(autoHexMeshDriver)/layerParameters/layerParameters.C
+$(autoHexMeshDriver)/refinementParameters/refinementParameters.C
+$(autoHexMeshDriver)/snapParameters/snapParameters.C
+$(autoHexMeshDriver)/pointData/pointData.C
 
-$(autoHexMesh)/autoHexMeshDriver/autoHexMeshDriver.C
-$(autoHexMesh)/autoHexMeshDriver/autoHexMeshDriverLayers.C
-$(autoHexMesh)/autoHexMeshDriver/autoHexMeshDriverShrink.C
-$(autoHexMesh)/autoHexMeshDriver/autoHexMeshDriverSnap.C
-$(autoHexMesh)/autoHexMeshDriver/pointData/pointData.C
 $(autoHexMesh)/meshRefinement/meshRefinementBaffles.C
 $(autoHexMesh)/meshRefinement/meshRefinement.C
 $(autoHexMesh)/meshRefinement/meshRefinementMerge.C
 $(autoHexMesh)/meshRefinement/meshRefinementRefine.C
 $(autoHexMesh)/refinementSurfaces/refinementSurfaces.C
+$(autoHexMesh)/shellSurfaces/shellSurfaces.C
 $(autoHexMesh)/trackedParticle/trackedParticle.C
 $(autoHexMesh)/trackedParticle/trackedParticleCloud.C
 
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.C
index a15b4220324da337c72f24b4003735a255f20ede..48f52d7880cebe3c905afc26e752479af6991fd9 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.C
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.C
@@ -35,6 +35,13 @@ License
 #include "syncTools.H"
 #include "motionSmoother.H"
 #include "pointMesh.H"
+#include "refinementParameters.H"
+#include "snapParameters.H"
+#include "layerParameters.H"
+#include "autoRefineDriver.H"
+#include "autoSnapDriver.H"
+#include "autoLayerDriver.H"
+#include "triSurfaceMesh.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -60,7 +67,8 @@ Foam::scalar Foam::autoHexMeshDriver::getMergeDistance(const scalar mergeTol)
        -scalar(IOstream::defaultPrecision())
     );
 
-    Info<< "Overall mesh bounding box  : " << meshBb << nl
+    Info<< nl
+        << "Overall mesh bounding box  : " << meshBb << nl
         << "Relative tolerance         : " << mergeTol << nl
         << "Absolute matching distance : " << mergeDist << nl
         << endl;
@@ -82,206 +90,63 @@ Foam::scalar Foam::autoHexMeshDriver::getMergeDistance(const scalar mergeTol)
 }
 
 
-// Return per keeppoint -1 or the local cell label the point is in. Guaranteed
-// to be only on one processor.
-Foam::labelList Foam::autoHexMeshDriver::findCells(const pointField& keepPoints)
- const
-{
-    // Global calculation engine
-    globalIndex globalCells(mesh_.nCells());
-
-    // Cell label per point
-    labelList cellLabels(keepPoints.size());
-
-    forAll(keepPoints, i)
-    {
-        const point& keepPoint = keepPoints[i];
-
-        label localCellI = mesh_.findCell(keepPoint);
-
-        label globalCellI = -1;
-
-        if (localCellI != -1)
-        {
-            Pout<< "Found point " << keepPoint << " in cell " << localCellI
-                << " on processor " << Pstream::myProcNo() << endl;
-            globalCellI = globalCells.toGlobal(localCellI);
-        }
-
-        reduce(globalCellI, maxOp<label>());
-
-        if (globalCellI == -1)
-        {
-            FatalErrorIn
-            (
-                "autoHexMeshDriver::findCells(const pointField&) const"
-            )   << "Point " << keepPoint << " is not inside the mesh." << nl
-                << "Bounding box of the mesh:" << mesh_.bounds()
-                << exit(FatalError);
-        }
-
-        if (globalCells.isLocal(globalCellI))
-        {
-            cellLabels[i] = localCellI;
-        }
-        else
-        {
-            cellLabels[i] = -1;
-        }
-    }
-    return cellLabels;
-}
-
-
-// Specifically orient using a calculated point outside
-void Foam::autoHexMeshDriver::orientOutside(PtrList<searchableSurface>& shells)
-{
-    // Determine outside point.
-    boundBox overallBb
-    (
-        point(GREAT, GREAT, GREAT),
-        point(-GREAT, -GREAT, -GREAT)
-    );
-
-    bool hasSurface = false;
-
-    forAll(shells, shellI)
-    {
-        if (isA<triSurfaceMesh>(shells[shellI]))
-        {
-            const triSurfaceMesh& shell =
-                refCast<const triSurfaceMesh>(shells[shellI]);
-
-            hasSurface = true;
-
-            boundBox shellBb(shell.localPoints(), false);
-
-            overallBb.min() = min(overallBb.min(), shellBb.min());
-            overallBb.max() = max(overallBb.max(), shellBb.max());
-        }
-    }
-
-    if (hasSurface)
-    {
-        const point outsidePt(2*overallBb.max() - overallBb.min());
-
-        //Info<< "Using point " << outsidePt << " to orient shells" << endl;
-
-        forAll(shells, shellI)
-        {
-            if (isA<triSurfaceMesh>(shells[shellI]))
-            {
-                triSurfaceMesh& shell = refCast<triSurfaceMesh>(shells[shellI]);
-
-                if (!refinementSurfaces::isSurfaceClosed(shell))
-                {
-                    FatalErrorIn("orientOutside(PtrList<searchableSurface>&)")
-                        << "Refinement shell "
-                        << shell.searchableSurface::name()
-                        << " is not closed." << exit(FatalError);
-                }
-
-                refinementSurfaces::orientSurface(outsidePt, shell);
-            }
-        }
-    }
-}
-
-
-// Check that face zones are synced
-void Foam::autoHexMeshDriver::checkCoupledFaceZones() const
-{
-    const faceZoneMesh& fZones = mesh_.faceZones();
-
-    // Check any zones are present anywhere and in same order
-
-    {
-        List<wordList> zoneNames(Pstream::nProcs());
-        zoneNames[Pstream::myProcNo()] = fZones.names();
-        Pstream::gatherList(zoneNames);
-        Pstream::scatterList(zoneNames);
-        // All have same data now. Check.
-        forAll(zoneNames, procI)
-        {
-            if (procI != Pstream::myProcNo())
-            {
-                if (zoneNames[procI] != zoneNames[Pstream::myProcNo()])
-                {
-                    FatalErrorIn
-                    (
-                        "autoHexMeshDriver::checkCoupledFaceZones() const"
-                    )   << "faceZones are not synchronised on processors." << nl
-                        << "Processor " << procI << " has faceZones "
-                        << zoneNames[procI] << nl
-                        << "Processor " << Pstream::myProcNo()
-                        << " has faceZones "
-                        << zoneNames[Pstream::myProcNo()] << nl
-                        << exit(FatalError);
-                }
-            }
-        }
-    }
-
-    // Check that coupled faces are present on both sides.
-
-    labelList faceToZone(mesh_.nFaces()-mesh_.nInternalFaces(), -1);
-
-    forAll(fZones, zoneI)
-    {
-        const faceZone& fZone = fZones[zoneI];
-
-        forAll(fZone, i)
-        {
-            label bFaceI = fZone[i]-mesh_.nInternalFaces();
-
-            if (bFaceI >= 0)
-            {
-                if (faceToZone[bFaceI] == -1)
-                {
-                    faceToZone[bFaceI] = zoneI;
-                }
-                else if (faceToZone[bFaceI] == zoneI)
-                {
-                    FatalErrorIn
-                    (
-                        "autoHexMeshDriver::checkCoupledFaceZones()"
-                    )   << "Face " << fZone[i] << " in zone "
-                        << fZone.name()
-                        << " is twice in zone!"
-                        << abort(FatalError);
-                }
-                else
-                {
-                    FatalErrorIn
-                    (
-                        "autoHexMeshDriver::checkCoupledFaceZones()"
-                    )   << "Face " << fZone[i] << " in zone "
-                        << fZone.name()
-                        << " is also in zone "
-                        << fZones[faceToZone[bFaceI]].name()
-                        << abort(FatalError);
-                }
-            }
-        }
-    }
-
-    labelList neiFaceToZone(faceToZone);
-    syncTools::swapBoundaryFaceList(mesh_, neiFaceToZone, false);
-
-    forAll(faceToZone, i)
-    {
-        if (faceToZone[i] != neiFaceToZone[i])
-        {
-            FatalErrorIn
-            (
-                "autoHexMeshDriver::checkCoupledFaceZones()"
-            )   << "Face " << mesh_.nInternalFaces()+i
-                << " is in zone " << faceToZone[i]
-                << ", its coupled face is in zone " << neiFaceToZone[i]
-                << abort(FatalError);
-        }
-    }
-}
+//// Specifically orient using a calculated point outside
+//void Foam::autoHexMeshDriver::orientOutside
+//(
+//    PtrList<searchableSurface>& shells
+//)
+//{
+//    // Determine outside point.
+//    boundBox overallBb
+//    (
+//        point(GREAT, GREAT, GREAT),
+//        point(-GREAT, -GREAT, -GREAT)
+//    );
+//
+//    bool hasSurface = false;
+//
+//    forAll(shells, shellI)
+//    {
+//        if (isA<triSurfaceMesh>(shells[shellI]))
+//        {
+//            const triSurfaceMesh& shell =
+//                refCast<const triSurfaceMesh>(shells[shellI]);
+//
+//            hasSurface = true;
+//
+//            boundBox shellBb(shell.localPoints(), false);
+//
+//            overallBb.min() = min(overallBb.min(), shellBb.min());
+//            overallBb.max() = max(overallBb.max(), shellBb.max());
+//        }
+//    }
+//
+//    if (hasSurface)
+//    {
+//        const point outsidePt(2*overallBb.max() - overallBb.min());
+//
+//        //Info<< "Using point " << outsidePt << " to orient shells" << endl;
+//
+//        forAll(shells, shellI)
+//        {
+//            if (isA<triSurfaceMesh>(shells[shellI]))
+//            {
+//                triSurfaceMesh& shell =
+//                  refCast<triSurfaceMesh>(shells[shellI]);
+//
+//                if (!refinementSurfaces::isSurfaceClosed(shell))
+//                {
+//                    FatalErrorIn("orientOutside(PtrList<searchableSurface>&)")
+//                        << "Refinement shell "
+//                        << shell.searchableSurface::name()
+//                        << " is not closed." << exit(FatalError);
+//                }
+//
+//                refinementSurfaces::orientSurface(outsidePt, shell);
+//            }
+//        }
+//    }
+//}
 
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
@@ -297,85 +162,86 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
     mesh_(mesh),
     dict_(dict),
     debug_(readLabel(dict_.lookup("debug"))),
-    maxGlobalCells_(readLabel(dict_.lookup("cellLimit"))),
-    maxLocalCells_(readLabel(dict_.lookup("procCellLimit"))),
-    minRefineCells_(readLabel(dict_.lookup("minimumRefine"))),
-    curvature_(readScalar(dict_.lookup("curvature"))),
-    nBufferLayers_(readLabel(dict_.lookup("nBufferLayers"))),
-    keepPoints_(dict_.lookup("keepPoints")),
     mergeDist_(getMergeDistance(readScalar(dict_.lookup("mergeTolerance"))))
 {
     if (debug_ > 0)
     {
         meshRefinement::debug = debug_;
         autoHexMeshDriver::debug = debug_;
+        autoRefineDriver::debug = debug;
+        autoSnapDriver::debug = debug;
+        autoLayerDriver::debug = debug;
     }
 
-    Info<< "Overall cell limit                         : " << maxGlobalCells_
-        << endl;
-    Info<< "Per processor cell limit                   : " << maxLocalCells_
-        << endl;
-    Info<< "Minimum number of cells to refine          : " << minRefineCells_
-        << endl;
-    Info<< "Curvature                                  : " << curvature_
-        << nl << endl;
-    Info<< "Layers between different refinement levels : " << nBufferLayers_
-        << endl;
+    refinementParameters refineParams(dict, 1);
 
-    // Check keepPoints are sensible
-    findCells(keepPoints_);
+    Info<< "Overall cell limit                         : "
+        << refineParams.maxGlobalCells() << endl;
+    Info<< "Per processor cell limit                   : "
+        << refineParams.maxLocalCells() << endl;
+    Info<< "Minimum number of cells to refine          : "
+        << refineParams.minRefineCells() << endl;
+    Info<< "Curvature                                  : "
+        << refineParams.curvature() << nl << endl;
+    Info<< "Layers between different refinement levels : "
+        << refineParams.nBufferLayers() << endl;
 
+    PtrList<dictionary> shellDicts(dict_.lookup("refinementShells"));
 
-    // Read refinement shells
-    // ~~~~~~~~~~~~~~~~~~~~~~
+    PtrList<dictionary> surfaceDicts(dict_.lookup("surfaces"));
 
-    {
-        Info<< "Reading refinement shells." << endl;
 
-        PtrList<dictionary> shellDicts(dict_.lookup("refinementShells"));
+    // Read geometry
+    // ~~~~~~~~~~~~~
+
+    {
+        Info<< "Reading all geometry." << endl;
 
-        shells_.setSize(shellDicts.size());
-        shellLevels_.setSize(shellDicts.size());
-        shellRefineInside_.setSize(shellDicts.size());
+        // Construct dictionary with all shells and all refinement surfaces
+        dictionary geometryDict;
 
-        forAll(shellDicts, i)
+        forAll(shellDicts, shellI)
         {
-            const dictionary& dict = shellDicts[i];
+            dictionary shellDict = shellDicts[shellI];
+            const word name(shellDict.lookup("name"));
+            shellDict.remove("name");
+            shellDict.remove("level");
+            shellDict.remove("refineInside");
+            geometryDict.add(name, shellDict);
+        }
 
-            shells_.set
-            (
-                i,
-                searchableSurface::New
-                (
-                    dict.lookup("type"),
-                    dict.lookup("name"),
-                    mesh_.time(),
-                    dict
-                )
-            );
-            shellLevels_[i] = readLabel(dict.lookup("level"));
-            shellRefineInside_[i] = Switch(dict.lookup("refineInside"));
-
-            if (shellRefineInside_[i])
-            {
-                Info<< "Refinement level " << shellLevels_[i]
-                    << " for all cells inside " << shells_[i].name() << endl;
-            }
-            else
+        forAll(surfaceDicts, surfI)
+        {
+            dictionary surfDict = surfaceDicts[surfI];
+            const word name(string::validate<word>(surfDict.lookup("file")));
+            surfDict.remove("file");
+            surfDict.remove("regions");
+            if (!surfDict.found("name"))
             {
-                Info<< "Refinement level " << shellLevels_[i]
-                    << " for all cells outside " << shells_[i].name() << endl;
+                surfDict.add("name", name);
             }
+            surfDict.add("type", triSurfaceMesh::typeName);
+            geometryDict.add(name, surfDict);
         }
 
-        Info<< "Read refinement shells in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
+        allGeometryPtr_.reset
+        (
+            new searchableSurfaces
+            (
+                IOobject
+                (
+                    "abc",                      // dummy name
+                    mesh_.time().constant(),    // directory
+                    "triSurface",               // instance
+                    mesh_.time(),               // registry
+                    IOobject::MUST_READ,
+                    IOobject::NO_WRITE
+                ),
+                geometryDict
+            )
+        );
 
-        // Orient shell surfaces before any searching is done.
-        Info<< "Orienting triSurface shells so point far away is outside."
-            << endl;
-        orientOutside(shells_);
-        Info<< "Oriented shells in = "
+        Info<< "Read geometry in = "
             << mesh_.time().cpuTimeIncrement() << " s" << endl;
     }
 
@@ -390,51 +256,47 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
         (
             new refinementSurfaces
             (
-                IOobject
-                (
-                    "",                                 // dummy name
-                    mesh_.time().constant(),            // directory
-                    "triSurface",                       // instance
-                    mesh_.time(),                       // registry
-                    IOobject::MUST_READ,
-                    IOobject::NO_WRITE
-                ),
-                dict_.lookup("surfaces")
+                allGeometryPtr_(),
+                surfaceDicts
             )
         );
         Info<< "Read surfaces in = "
             << mesh_.time().cpuTimeIncrement() << " s" << endl;
+    }
 
-        // Orient surfaces (if they're closed) before any searching is done.
-        Info<< "Orienting (closed) surfaces so keepPoint is outside." << endl;
-        forAll(surfaces(), i)
-        {
-            if (refinementSurfaces::isSurfaceClosed(surfaces()[i]))
-            {
-                refinementSurfaces::orientSurface
-                (
-                    keepPoints_[0],
-                    surfacesPtr_()[i]
-                );
-            }
-        }
-        Info<< "Oriented closed surfaces in = "
+    // Read refinement shells
+    // ~~~~~~~~~~~~~~~~~~~~~~
+
+    {
+        Info<< "Reading refinement shells." << endl;
+        shellsPtr_.reset
+        (
+            new shellSurfaces
+            (
+                allGeometryPtr_(),
+                shellDicts
+            )
+        );
+        Info<< "Read refinement shells in = "
             << mesh_.time().cpuTimeIncrement() << " s" << endl;
 
+        //// Orient shell surfaces before any searching is done.
+        //Info<< "Orienting triSurface shells so point far away is outside."
+        //    << endl;
+        //orientOutside(shells_);
+        //Info<< "Oriented shells in = "
+        //    << mesh_.time().cpuTimeIncrement() << " s" << endl;
+
         Info<< "Setting refinement level of surface to be consistent"
             << " with shells." << endl;
-        surfacesPtr_().setMinLevelFields
-        (
-            shells_,
-            shellLevels_,
-            shellRefineInside_
-        );
+        surfacesPtr_().setMinLevelFields(shells());
         Info<< "Checked shell refinement in = "
             << mesh_.time().cpuTimeIncrement() << " s" << endl;
     }
 
+
     // Check faceZones are synchronised
-    checkCoupledFaceZones();
+    meshRefinement::checkCoupledFaceZones(mesh_);
 
 
     // Add all the surface regions as patches
@@ -453,55 +315,40 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
             << "-----\t------"
             << endl;
 
-        forAll(surfaces(), surfI)
+        const labelList& surfaceGeometry = surfaces().surfaces();
+        forAll(surfaceGeometry, surfI)
         {
-            const triSurfaceMesh& s = surfaces()[surfI];
+            label geomI = surfaceGeometry[surfI];
 
-            Info<< surfaces().names()[surfI] << ':' << nl << nl;
+            const wordList& regNames = allGeometryPtr_().regionNames()[geomI];
 
-            const geometricSurfacePatchList& regions = s.patches();
+            Info<< surfaces().names()[surfI] << ':' << nl << nl;
 
-            labelList nTrisPerRegion(surfaces().countRegions(s));
+            //const triSurfaceMesh& s = surfaces()[surfI];
+            //const geometricSurfacePatchList& regions = s.patches();
+            //labelList nTrisPerRegion(surfaces().countRegions(s));
 
-            forAll(regions, i)
+            forAll(regNames, i)
             {
-                if (nTrisPerRegion[i] > 0)
-                {
-                    label globalRegionI = surfaces().globalRegion(surfI, i);
-
-                    // Use optionally specified patch type and name
-                    word patchType = surfaces().patchType()[globalRegionI];
-                    if (patchType == "")
-                    {
-                        patchType = wallPolyPatch::typeName;
-                    }
-
-                    word patchName = surfaces().patchName()[globalRegionI];
-                    if (patchName == "")
-                    {
-                        patchName =
-                            surfaces().names()[surfI]
-                          + '_'
-                          + regions[i].name();
-                    }
-
+                //if (nTrisPerRegion[i] > 0)
+                //{
                     label patchI = meshRefinement::addPatch
                     (
                         mesh,
-                        patchName,
-                        patchType
+                        regNames[i],
+                        wallPolyPatch::typeName
                     );
 
-                    Info<< patchI << '\t' << regions[i].name() << nl;
+                    Info<< patchI << '\t' << regNames[i] << nl;
 
-                    globalToPatch_[globalRegionI] = patchI;
-                }
+                    globalToPatch_[surfaces().globalRegion(surfI, i)] = patchI;
+                //}
             }
 
             Info<< nl;
         }
         Info<< "Added patches in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
+            << mesh_.time().cpuTimeIncrement() << " s" << nl << endl;
     }
 
 
@@ -559,7 +406,8 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
 
         if (Pstream::parRun() && !decomposer.parallelAware())
         {
-            FatalErrorIn("autoHexMeshDriver::autoHexMeshDriver(const IOobject&, fvMesh&)")
+            FatalErrorIn("autoHexMeshDriver::autoHexMeshDriver"
+                "(const IOobject&, fvMesh&)")
                 << "You have selected decomposition method "
                 << decomposer.typeName
                 << " which is not parallel aware." << endl
@@ -588,7 +436,8 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
             (
                 mesh,
                 mergeDist_,         // tolerance used in sorting coordinates
-                surfaces()
+                surfaces(),
+                shells()
             )
         );
         Info<< "Calculated surface intersections in = "
@@ -606,1284 +455,109 @@ Foam::autoHexMeshDriver::autoHexMeshDriver
 }
 
 
-// Construct from separate dictionaries.
-Foam::autoHexMeshDriver::autoHexMeshDriver
-(
-    fvMesh& mesh,
-    const dictionary& controlDict,
-    const dictionary& refineDict,
-    const dictionary& decomposeDict
-)
-:
-    mesh_(mesh),
-    dict_(controlDict),
-    debug_(readLabel(controlDict.lookup("debug"))),
-    maxGlobalCells_(readLabel(refineDict.lookup("cellLimit"))),
-    maxLocalCells_(readLabel(refineDict.lookup("procCellLimit"))),
-    minRefineCells_(readLabel(refineDict.lookup("minimumRefine"))),
-    curvature_(readScalar(refineDict.lookup("curvature"))),
-    nBufferLayers_(readLabel(refineDict.lookup("nBufferLayers"))),
-    keepPoints_(refineDict.lookup("keepPoints")),
-    mergeDist_
-    (
-        getMergeDistance(readScalar(refineDict.lookup("mergeTolerance")))
-    )
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::autoHexMeshDriver::writeMesh(const string& msg) const
 {
-    if (debug_ > 0)
+    const meshRefinement& meshRefiner = meshRefinerPtr_();
+
+    meshRefiner.printMeshInfo(debug_, msg);
+    Info<< "Writing mesh to time " << mesh_.time().timeName() << endl;
+
+    meshRefiner.write(meshRefinement::MESH|meshRefinement::SCALARLEVELS, "");
+    if (debug_ & meshRefinement::OBJINTERSECTIONS)
     {
-        meshRefinement::debug = debug_;
-        autoHexMeshDriver::debug = debug_;
+        meshRefiner.write
+        (
+            meshRefinement::OBJINTERSECTIONS,
+            mesh_.time().path()/mesh_.time().timeName()
+        );
     }
+    Info<< "Written mesh in = "
+        << mesh_.time().cpuTimeIncrement() << " s." << endl;
+}
 
-    Info<< "Overall cell limit                         : " << maxGlobalCells_
-        << endl;
-    Info<< "Per processor cell limit                   : " << maxLocalCells_
-        << endl;
-    Info<< "Minimum number of cells to refine          : " << minRefineCells_
-        << endl;
-    Info<< "Curvature                                  : " << curvature_
-        << nl << endl;
-    Info<< "Layers between different refinement levels : " << nBufferLayers_
-        << endl;
-
-    // Check keepPoints are sensible
-    findCells(keepPoints_);
 
+void Foam::autoHexMeshDriver::doMesh()
+{
+    Switch wantRefine(dict_.lookup("doRefine"));
+    Switch wantSnap(dict_.lookup("doSnap"));
+    Switch wantLayers(dict_.lookup("doLayers"));
 
-    // Read refinement shells
-    // ~~~~~~~~~~~~~~~~~~~~~~
+    Info<< "Do refinement : " << wantRefine << nl
+        << "Do snapping   : " << wantSnap << nl
+        << "Do layers     : " << wantLayers << nl
+        << endl;
 
+    if (wantRefine)
     {
-        Info<< "Reading refinement shells." << endl;
+        autoRefineDriver refineDriver
+        (
+            meshRefinerPtr_(),
+            decomposerPtr_(),
+            distributorPtr_(),
+            globalToPatch_
+        );
 
-        PtrList<dictionary> shellDicts(refineDict.lookup("refinementShells"));
+        // Get all the refinement specific params
+        refinementParameters refineParams(dict_, 1);
 
-        shells_.setSize(shellDicts.size());
-        shellLevels_.setSize(shellDicts.size());
-        shellRefineInside_.setSize(shellDicts.size());
+        refineDriver.doRefine(dict_, refineParams, wantSnap);
 
-        forAll(shellDicts, i)
-        {
-            const dictionary& dict = shellDicts[i];
+        // Write mesh
+        writeMesh("Refined mesh");
+    }
 
-            shells_.set
-            (
-                i,
-                searchableSurface::New
-                (
-                    dict.lookup("type"),
-                    dict.lookup("name"),
-                    mesh_.time(),
-                    dict
-                )
-            );
-            shellLevels_[i] = readLabel(dict.lookup("level"));
-            shellRefineInside_[i] = Switch(dict.lookup("refineInside"));
-
-            if (shellRefineInside_[i])
-            {
-                Info<< "Refinement level " << shellLevels_[i]
-                    << " for all cells inside " << shells_[i].name() << endl;
-            }
-            else
-            {
-                Info<< "Refinement level " << shellLevels_[i]
-                    << " for all cells outside " << shells_[i].name() << endl;
-            }
-        }
+    if (wantSnap)
+    {
+        const dictionary& snapDict = dict_.subDict("snapDict");
+        const dictionary& motionDict = dict_.subDict("motionDict");
 
-        Info<< "Read refinement shells in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
+        autoSnapDriver snapDriver
+        (
+            meshRefinerPtr_(),
+            globalToPatch_
+        );
 
-        // Orient shell surfaces before any searching is done.
-        Info<< "Orienting triSurface shells so point far away is outside."
-            << endl;
-        orientOutside(shells_);
-        Info<< "Oriented shells in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-    }
+        // Get all the snapping specific params
+        snapParameters snapParams(snapDict, 1);
 
+        snapDriver.doSnap(snapDict, motionDict, snapParams);
 
-    // Read refinement surfaces
-    // ~~~~~~~~~~~~~~~~~~~~~~~~
+        // Write mesh.
+        writeMesh("Snapped mesh");
+    }
 
+    if (wantLayers)
     {
-        Info<< "Reading surfaces and constructing search trees." << endl;
+        const dictionary& motionDict = dict_.subDict("motionDict");
+        const dictionary& shrinkDict = dict_.subDict("shrinkDict");
+        PtrList<dictionary> surfaceDicts(dict_.lookup("surfaces"));
 
-        surfacesPtr_.reset
+        autoLayerDriver layerDriver
         (
-            new refinementSurfaces
-            (
-                IOobject
-                (
-                    "",                                 // dummy name
-                    mesh_.time().constant(),            // directory
-                    "triSurface",                       // instance
-                    mesh_.time(),                       // registry
-                    IOobject::MUST_READ,
-                    IOobject::NO_WRITE
-                ),
-                refineDict.lookup("surfaces")
-            )
+            meshRefinerPtr_(),
+            globalToPatch_
         );
-        Info<< "Read surfaces in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
 
-        // Orient surfaces (if they're closed) before any searching is done.
-        Info<< "Orienting (closed) surfaces so keepPoint is outside." << endl;
-        forAll(surfaces(), i)
-        {
-            if (refinementSurfaces::isSurfaceClosed(surfaces()[i]))
-            {
-                refinementSurfaces::orientSurface
-                (
-                    keepPoints_[0],
-                    surfacesPtr_()[i]
-                );
-            }
-        }
-        Info<< "Oriented closed surfaces in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-        Info<< "Setting refinement level of surface to be consistent"
-            << " with shells." << endl;
-        surfacesPtr_().setMinLevelFields
+        // Get all the layer specific params
+        layerParameters layerParams
         (
-            shells_,
-            shellLevels_,
-            shellRefineInside_
+            surfaceDicts,
+            surfacesPtr_(),
+            globalToPatch_,
+            shrinkDict,
+            mesh_.boundaryMesh()
         );
-        Info<< "Checked shell refinement in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-    }
 
-    // Check faceZones are synchronised
-    checkCoupledFaceZones();
-
-
-    // Add all the surface regions as patches
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    {
-        Info<< nl
-            << "Adding patches for surface regions" << nl
-            << "----------------------------------" << nl
-            << endl;
-
-        // From global region number to mesh patch.
-        globalToPatch_.setSize(surfaces().nRegions(), -1);
-
-        Info<< "Patch\tRegion" << nl
-            << "-----\t------"
-            << endl;
-
-        forAll(surfaces(), surfI)
-        {
-            const triSurfaceMesh& s = surfaces()[surfI];
-
-            Info<< surfaces().names()[surfI] << ':' << nl << nl;
-
-            const geometricSurfacePatchList& regions = s.patches();
-
-            labelList nTrisPerRegion(surfaces().countRegions(s));
-
-            forAll(regions, i)
-            {
-                if (nTrisPerRegion[i] > 0)
-                {
-                    label patchI = meshRefinement::addPatch
-                    (
-                        mesh,
-                        //s.searchableSurface::name() + '_' + regions[i].name(),
-                        surfaces().names()[surfI] + '_' + regions[i].name(),
-                        wallPolyPatch::typeName
-                    );
-
-                    Info<< patchI << '\t' << regions[i].name() << nl;
-
-                    globalToPatch_[surfaces().globalRegion(surfI, i)] = patchI;
-                }
-            }
-
-            Info<< nl;
-        }
-        Info<< "Added patches in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-    }
-
-    // Parallel
-    // ~~~~~~~~
-
-    {
-        // Decomposition
-        decomposerPtr_ = decompositionMethod::New
-        (
-            decomposeDict,
-            mesh_
-        );
-        decompositionMethod& decomposer = decomposerPtr_();
-
-
-        if (Pstream::parRun() && !decomposer.parallelAware())
-        {
-            FatalErrorIn("autoHexMeshDriver::autoHexMeshDriver(const IOobject&, fvMesh&)")
-                << "You have selected decomposition method "
-                << decomposer.typeName
-                << " which is not parallel aware." << endl
-                << "Please select one that is (parMetis, hierarchical)"
-                << exit(FatalError);
-        }
-
-        // Mesh distribution engine (uses tolerance to reconstruct meshes)
-        distributorPtr_.reset(new fvMeshDistribute(mesh_, mergeDist_));
-    }
-
-
-    // Refinement engine
-    // ~~~~~~~~~~~~~~~~~
-
-    {
-        Info<< nl
-            << "Determining initial surface intersections" << nl
-            << "-----------------------------------------" << nl
-            << endl;
-
-        // Main refinement engine
-        meshRefinerPtr_.reset
-        (
-            new meshRefinement
-            (
-                mesh,
-                mergeDist_,         // tolerance used in sorting coordinates
-                surfaces()
-            )
-        );
-        Info<< "Calculated surface intersections in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-        // Some stats
-        meshRefinerPtr_().printMeshInfo(debug_, "Initial mesh");
-
-        meshRefinerPtr_().write
-        (
-            debug_&meshRefinement::OBJINTERSECTIONS,
-            mesh_.time().path()/mesh_.time().timeName()
-        );
-    }
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-// Read explicit feature edges
-Foam::label Foam::autoHexMeshDriver::readFeatureEdges
-(
-    const PtrList<dictionary>& featDicts
-)
-{
-    Info<< "Reading external feature lines." << endl;
-
-    featureMeshes_.setSize(featDicts.size());
-    featureLevels_.setSize(featDicts.size());
-
-    forAll(featDicts, i)
-    {
-        const dictionary& dict = featDicts[i];
-
-        fileName featFileName(dict.lookup("file"));
-
-        featureMeshes_.set
-        (
-            i,
-            new featureEdgeMesh
-            (
-                IOobject
-                (
-                    featFileName,           // name
-                    mesh_.time().constant(),// directory
-                    "triSurface",           // instance
-                    mesh_.db(),             // registry
-                    IOobject::MUST_READ,
-                    IOobject::NO_WRITE,
-                    false
-                )
-            )
-        );
-
-        featureMeshes_[i].mergePoints(mergeDist_);
-        featureLevels_[i] = readLabel(dict.lookup("level"));
-
-        Info<< "Refinement level " << featureLevels_[i]
-            << " for all cells crossed by feature " << featFileName
-            << " (" << featureMeshes_[i].points().size() << " points, "
-            << featureMeshes_[i].edges().size() << ")." << endl;
-    }
-
-    Info<< "Read feature lines in = "
-        << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-    return featureMeshes_.size();
-}
-
-
-Foam::label Foam::autoHexMeshDriver::featureEdgeRefine
-(
-    const PtrList<dictionary>& featDicts,
-    const label maxIter,
-    const label minRefine
-)
-{
-    // Read explicit feature edges
-    readFeatureEdges(featDicts);
-
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    label iter = 0;
-
-    if (featureMeshes_.size() > 0 && maxIter > 0)
-    {
-        for (; iter < maxIter; iter++)
-        {
-            Info<< nl
-                << "Feature refinement iteration " << iter << nl
-                << "------------------------------" << nl
-                << endl;
-
-            labelList candidateCells
-            (
-                meshRefiner.refineCandidates
-                (
-                    keepPoints_[0],     // For now only use one.
-                    globalToPatch_,
-                    curvature_,
-
-                    featureMeshes_,
-                    featureLevels_,
-
-                    shells_,
-                    shellLevels_,
-                    shellRefineInside_,
-
-                    true,               // featureRefinement
-                    false,              // internalRefinement
-                    false,              // surfaceRefinement
-                    false,              // curvatureRefinement
-                    maxGlobalCells_,
-                    maxLocalCells_
-                )
-            );
-            labelList cellsToRefine
-            (
-                meshRefiner.meshCutter().consistentRefinement
-                (
-                    candidateCells,
-                    true
-                )
-            );
-            Info<< "Determined cells to refine in = "
-                << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-
-
-            label nCellsToRefine = cellsToRefine.size();
-            reduce(nCellsToRefine, sumOp<label>());
-
-            Info<< "Selected for feature refinement : " << nCellsToRefine
-                << " cells (out of " << mesh_.globalData().nTotalCells()
-                << ')' << endl;
-
-            if (nCellsToRefine <= minRefine)
-            {
-                Info<< "Stopping refining since too few cells selected."
-                    << nl << endl;
-                break;
-            }
-
-
-            if (debug_ > 0)
-            {
-                const_cast<Time&>(mesh_.time())++;
-            }
-
-            meshRefiner.refineAndBalance
-            (
-                "feature refinement iteration " + name(iter),
-                decomposerPtr_(),
-                distributorPtr_(),
-                cellsToRefine
-            );
-        }
-    }
-    return iter;
-}
-
-
-Foam::label Foam::autoHexMeshDriver::surfaceOnlyRefine(const label maxIter)
-{
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    // Determine the maximum refinement level over all surfaces. This
-    // determines the minumum number of surface refinement iterations.
-    label overallMaxLevel = max(surfaces().maxLevel());
-
-    label iter;
-    for (iter = 0; iter < maxIter; iter++)
-    {
-        Info<< nl
-            << "Surface refinement iteration " << iter << nl
-            << "------------------------------" << nl
-            << endl;
-
-
-        // Determine cells to refine
-        // ~~~~~~~~~~~~~~~~~~~~~~~~~
-        // Only look at surface intersections (minLevel and surface curvature),
-        // do not do internal refinement (refinementShells)
-
-        labelList candidateCells
-        (
-            meshRefiner.refineCandidates
-            (
-                keepPoints_[0],
-                globalToPatch_,
-                curvature_,
-
-                featureMeshes_,
-                featureLevels_,
-
-                shells_,
-                shellLevels_,
-                shellRefineInside_,
-
-                false,              // featureRefinement
-                false,              // internalRefinement
-                true,               // surfaceRefinement
-                true,               // curvatureRefinement
-                maxGlobalCells_,
-                maxLocalCells_
-            )
-        );
-        labelList cellsToRefine
-        (
-            meshRefiner.meshCutter().consistentRefinement
-            (
-                candidateCells,
-                true
-            )
-        );
-        Info<< "Determined cells to refine in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-
-        label nCellsToRefine = cellsToRefine.size();
-        reduce(nCellsToRefine, sumOp<label>());
-
-        Info<< "Selected for refinement : " << nCellsToRefine
-            << " cells (out of " << mesh_.globalData().nTotalCells()
-            << ')' << endl;
-
-        // Stop when no cells to refine or have done minimum nessecary
-        // iterations and not enough cells to refine.
-        if
-        (
-            nCellsToRefine == 0
-         || (
-                iter >= overallMaxLevel
-             && nCellsToRefine <= minRefineCells_
-            )
-        )
-        {
-            Info<< "Stopping refining since too few cells selected."
-                << nl << endl;
-            break;
-        }
-
-
-        if (debug_)
-        {
-            const_cast<Time&>(mesh_.time())++;
-        }
-
-        meshRefiner.refineAndBalance
-        (
-            "surface refinement iteration " + name(iter),
-            decomposerPtr_(),
-            distributorPtr_(),
-            cellsToRefine
-        );
-    }
-    return iter;
-}
-
-
-void Foam::autoHexMeshDriver::removeInsideCells(const label nBufferLayers)
-{
-
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    Info<< nl
-        << "Removing mesh beyond surface intersections" << nl
-        << "------------------------------------------" << nl
-        << endl;
-
-    if (debug_)
-    {
-       const_cast<Time&>(mesh_.time())++;
-    }
-
-    meshRefiner.splitMesh
-    (
-        nBufferLayers,                  // nBufferLayers
-        globalToPatch_,
-        keepPoints_[0]
-    );
-
-    if (debug_)
-    {
-        Pout<< "Writing subsetted mesh to time "
-            << mesh_.time().timeName() << '.' << endl;
-        meshRefiner.write(debug_, mesh_.time().path()/mesh_.time().timeName());
-        Pout<< "Dumped mesh in = "
-            << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
-    }
-}
-
-
-Foam::label Foam::autoHexMeshDriver::shellRefine(const label maxIter)
-{
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-
-    // Mark current boundary faces with 0. Have meshRefiner maintain them.
-    meshRefiner.userFaceData().setSize(1);
-
-    // mark list to remove any refined faces
-    meshRefiner.userFaceData()[0].first() = meshRefinement::REMOVE;
-    meshRefiner.userFaceData()[0].second() = createWithValues<labelList>
-    (
-        mesh_.nFaces(),
-        -1,
-        meshRefiner.intersectedFaces(),
-        0
-    );
-
-    // Determine the maximum refinement level over all volume refinement
-    // regions. This determines the minumum number of shell refinement
-    // iterations.
-    label overallMaxShellLevel;
-    if (shellLevels_.size() == 0)
-    {
-        overallMaxShellLevel = 0;
-    }
-    else
-    {
-        overallMaxShellLevel = max(shellLevels_);
-    }
-
-    label iter;
-    for (iter = 0; iter < maxIter; iter++)
-    {
-        Info<< nl
-            << "Shell refinement iteration " << iter << nl
-            << "----------------------------" << nl
-            << endl;
-
-        labelList candidateCells
-        (
-            meshRefiner.refineCandidates
-            (
-                keepPoints_[0],
-                globalToPatch_,
-                curvature_,
-
-                featureMeshes_,
-                featureLevels_,
-
-                shells_,
-                shellLevels_,
-                shellRefineInside_,
-
-                false,              // featureRefinement
-                true,               // internalRefinement
-                false,              // surfaceRefinement
-                false,              // curvatureRefinement
-                maxGlobalCells_,
-                maxLocalCells_
-            )
-        );
-
-        if (debug_)
-        {
-            Pout<< "Dumping " << candidateCells.size()
-                << " cells to cellSet candidateCellsFromShells." << endl;
-
-            cellSet(mesh_, "candidateCellsFromShells", candidateCells).write();
-        }
-
-        // Problem choosing starting faces for bufferlayers (bFaces)
-        //  - we can't use the current intersected boundary faces
-        //    (intersectedFaces) since this grows indefinitely
-        //  - if we use 0 faces we don't satisfy bufferLayers from the
-        //    surface.
-        //  - possibly we want to have bFaces only the initial set of faces
-        //    and maintain the list while doing the refinement.
-        labelList bFaces
-        (
-            findIndices(meshRefiner.userFaceData()[0].second(), 0)
-        );
-
-        Info<< "Collected boundary faces : "
-            << returnReduce(bFaces.size(), sumOp<label>()) << endl;
-
-        labelList cellsToRefine;
-
-        if (nBufferLayers_ <= 2)
-        {
-            cellsToRefine = meshRefiner.meshCutter().consistentSlowRefinement
-            (
-                nBufferLayers_,
-                candidateCells,                 // cells to refine
-                bFaces,                         // faces for nBufferLayers
-                1,                              // point difference
-                meshRefiner.intersectedPoints() // points to check
-            );
-        }
-        else
-        {
-            cellsToRefine = meshRefiner.meshCutter().consistentSlowRefinement2
-            (
-                nBufferLayers_,
-                candidateCells,                 // cells to refine
-                bFaces                          // faces for nBufferLayers
-            );
-        }
-
-        Info<< "Determined cells to refine in = "
-            << mesh_.time().cpuTimeIncrement() << " s" << endl;
-
-
-        label nCellsToRefine = cellsToRefine.size();
-        reduce(nCellsToRefine, sumOp<label>());
-
-        Info<< "Selected for internal refinement : " << nCellsToRefine
-            << " cells (out of " << mesh_.globalData().nTotalCells()
-            << ')' << endl;
-
-        // Stop when no cells to refine or have done minimum nessecary
-        // iterations and not enough cells to refine.
-        if
-        (
-            nCellsToRefine == 0
-         || (
-                iter >= overallMaxShellLevel
-             && nCellsToRefine <= minRefineCells_
-            )
-        )
-        {
-            Info<< "Stopping refining since too few cells selected."
-                << nl << endl;
-            break;
-        }
-
-
-        if (debug_)
-        {
-            const_cast<Time&>(mesh_.time())++;
-        }
-
-        meshRefiner.refineAndBalance
-        (
-            "shell refinement iteration " + name(iter),
-            decomposerPtr_(),
-            distributorPtr_(),
-            cellsToRefine
-        );
-    }
-    meshRefiner.userFaceData().clear();
-
-    return iter;
-}
-
-
-void Foam::autoHexMeshDriver::baffleAndSplitMesh(const bool handleSnapProblems)
-{
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    Info<< nl
-        << "Splitting mesh at surface intersections" << nl
-        << "---------------------------------------" << nl
-        << endl;
-
-    // Introduce baffles at surface intersections. Note:
-    // meshRefiment::surfaceIndex() will
-    // be like boundary face from now on so not coupled anymore.
-    meshRefiner.baffleAndSplitMesh
-    (
-        handleSnapProblems,
-        !handleSnapProblems,            // merge free standing baffles?
-        const_cast<Time&>(mesh_.time()),
-        globalToPatch_,
-        keepPoints_[0]
-    );
-}
-
-
-void Foam::autoHexMeshDriver::zonify()
-{
-    // Mesh is at its finest. Do zoning
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    // This puts all faces with intersection across a zoneable surface
-    // into that surface's faceZone. All cells inside faceZone get given the
-    // same cellZone.
-
-
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    if (surfaces().getNamedSurfaces().size() > 0)
-    {
-        Info<< nl
-            << "Introducing zones for interfaces" << nl
-            << "--------------------------------" << nl
-            << endl;
-
-        if (debug_)
-        {
-            const_cast<Time&>(mesh_.time())++;
-        }
-
-        meshRefiner.zonify(keepPoints_[0]);
-
-        if (debug_)
-        {
-            Pout<< "Writing zoned mesh to time "
-                << mesh_.time().timeName() << '.' << endl;
-            meshRefiner.write
-            (
-                debug_,
-                mesh_.time().path()/mesh_.time().timeName()
-            );
-        }
-
-        // Check that all faces are synced
-        checkCoupledFaceZones();
-    }
-}
-
-
-void Foam::autoHexMeshDriver::splitAndMergeBaffles
-(
-    const bool handleSnapProblems
-)
-{
-    Info<< nl
-        << "Handling cells with snap problems" << nl
-        << "---------------------------------" << nl
-        << endl;
-
-    // Introduce baffles and split mesh
-    if (debug_)
-    {
-        const_cast<Time&>(mesh_.time())++;
-    }
-
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    meshRefiner.baffleAndSplitMesh
-    (
-        handleSnapProblems,
-        false,                  // merge free standing baffles?
-        const_cast<Time&>(mesh_.time()),
-        globalToPatch_,
-        keepPoints_[0]
-    );
-
-    if (debug_)
-    {
-        const_cast<Time&>(mesh_.time())++;
-    }
-
-    // Duplicate points on baffles that are on more than one cell
-    // region. This will help snapping pull them to separate surfaces.
-    meshRefiner.dupNonManifoldPoints();
-
-
-    // Merge all baffles that are still remaining after duplicating points.
-    List<labelPair> couples
-    (
-        meshRefiner.getDuplicateFaces   // get all baffles
-        (
-            identity(mesh_.nFaces()-mesh_.nInternalFaces())
-           +mesh_.nInternalFaces()
-        )
-    );
-
-    label nCouples = returnReduce(couples.size(), sumOp<label>());
-
-    Info<< "Detected unsplittable baffles : "
-        << nCouples << endl;
-
-    if (nCouples > 0)
-    {
-        // Actually merge baffles. Note: not exactly parallellized. Should
-        // convert baffle faces into processor faces if they resulted
-        // from them.
-        meshRefiner.mergeBaffles(couples);
-
-        if (debug_)
-        {
-            // Debug:test all is still synced across proc patches
-            meshRefiner.checkData();
-        }
-        Info<< "Merged free-standing baffles in = "
-            << mesh_.time().cpuTimeIncrement() << " s." << endl;
-    }
-
-    if (debug_)
-    {
-        Pout<< "Writing handleProblemCells mesh to time "
-            << mesh_.time().timeName() << '.' << endl;
-        meshRefiner.write(debug_, mesh_.time().path()/mesh_.time().timeName());
-    }
-}
-
-
-void Foam::autoHexMeshDriver::mergePatchFaces()
-{
-    Info<< nl
-        << "Merge refined boundary faces" << nl
-        << "----------------------------" << nl
-        << endl;
-
-    if (debug_)
-    {
-        const_cast<Time&>(mesh_.time())++;
-    }
-
-    meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    meshRefiner.mergePatchFaces
-    (
-        Foam::cos(45*mathematicalConstant::pi/180.0),
-        Foam::cos(45*mathematicalConstant::pi/180.0),
-        meshRefinement::addedPatches(globalToPatch_)
-    );
-
-    if (debug_)
-    {
-        meshRefiner.checkData();
-    }
-
-    meshRefiner.mergeEdges(Foam::cos(45*mathematicalConstant::pi/180.0));
-
-    if (debug_)
-    {
-        meshRefiner.checkData();
-    }
-}
-
-
-Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::autoHexMeshDriver::balance
-(
-    const bool keepZoneFaces,
-    const bool keepBaffles
-)
-{
-    autoPtr<mapDistributePolyMesh> map;
-
-    if (Pstream::parRun())
-    {
-        Info<< nl
-            << "Doing final balancing" << nl
-            << "---------------------" << nl
-            << endl;
-
-        if (debug_)
-        {
-            const_cast<Time&>(mesh_.time())++;
-        }
-
-        meshRefinement& meshRefiner = meshRefinerPtr_();
-        decompositionMethod& decomposer = decomposerPtr_();
-        fvMeshDistribute& distributor = distributorPtr_();
-
-        // Wanted distribution
-        labelList distribution;
-
-        if (keepZoneFaces || keepBaffles)
-        {
-            // Faces where owner and neighbour are not 'connected' so can
-            // go to different processors.
-            boolList blockedFace(mesh_.nFaces(), true);
-            // Pairs of baffles
-            List<labelPair> couples;
-
-            if (keepZoneFaces)
-            {
-                label nNamed = surfaces().getNamedSurfaces().size();
-
-                Info<< "Found " << nNamed << " surfaces with faceZones."
-                    << " Applying special decomposition to keep those together."
-                    << endl;
-
-                // Determine decomposition to keep/move surface zones
-                // on one processor. The reason is that snapping will make these
-                // into baffles, move and convert them back so if they were
-                // proc boundaries after baffling&moving the points might be no
-                // longer snychronised so recoupling will fail. To prevent this
-                // keep owner&neighbour of such a surface zone on the same
-                // processor.
-
-                const wordList& fzNames = surfaces().faceZoneNames();
-                const faceZoneMesh& fZones = mesh_.faceZones();
-
-                // Get faces whose owner and neighbour should stay together, i.e.
-                // they are not 'blocked'.
-
-                label nZoned = 0;
-
-                forAll(fzNames, surfI)
-                {
-                    if (fzNames[surfI].size() > 0)
-                    {
-                        // Get zone
-                        label zoneI = fZones.findZoneID(fzNames[surfI]);
-
-                        const faceZone& fZone = fZones[zoneI];
-
-                        forAll(fZone, i)
-                        {
-                            if (blockedFace[fZone[i]])
-                            {
-                                blockedFace[fZone[i]] = false;
-                                nZoned++;
-                            }
-                        }
-                    }
-                }
-                Info<< "Found " << returnReduce(nZoned, sumOp<label>())
-                    << " zoned faces to keep together."
-                    << endl;
-            }
-
-            if (keepBaffles)
-            {
-                // Get boundary baffles that need to stay together.
-                couples =  meshRefiner.getDuplicateFaces   // all baffles
-                (
-                    identity(mesh_.nFaces()-mesh_.nInternalFaces())
-                   +mesh_.nInternalFaces()
-                );
-
-                Info<< "Found " << returnReduce(couples.size(), sumOp<label>())
-                    << " baffles to keep together."
-                    << endl;
-            }
-
-            distribution = meshRefiner.decomposeCombineRegions
-            (
-                blockedFace,
-                couples,
-                decomposer
-            );
-
-            labelList nProcCells(distributor.countCells(distribution));
-            Pstream::listCombineGather(nProcCells, plusEqOp<label>());
-            Pstream::listCombineScatter(nProcCells);
-
-            Info<< "Calculated decomposition:" << endl;
-            forAll(nProcCells, procI)
-            {
-                Info<< "    " << procI << '\t' << nProcCells[procI] << endl;
-            }
-            Info<< endl;
-        }
-        else
-        {
-            // Normal decomposition
-            distribution = decomposer.decompose(mesh_.cellCentres());
-        }
-
-        if (debug_)
-        {
-            Pout<< "Wanted distribution:"
-                << distributor.countCells(distribution)
-                << endl;
-        }
-        // Do actual sending/receiving of mesh
-        map = distributor.distribute(distribution);
-
-        // Update numbering of meshRefiner
-        meshRefiner.distribute(map);
-    }
-    return map;
-}
-
-
-void Foam::autoHexMeshDriver::writeMesh(const string& msg) const
-{
-    const meshRefinement& meshRefiner = meshRefinerPtr_();
-
-    meshRefiner.printMeshInfo(debug_, msg);
-    Info<< "Writing mesh to time " << mesh_.time().timeName() << endl;
-
-    meshRefiner.write(meshRefinement::MESH|meshRefinement::SCALARLEVELS, "");
-    if (debug_ & meshRefinement::OBJINTERSECTIONS)
-    {
-        meshRefiner.write
-        (
-            meshRefinement::OBJINTERSECTIONS,
-            mesh_.time().path()/mesh_.time().timeName()
-        );
-    }
-    Info<< "Written mesh in = "
-        << mesh_.time().cpuTimeIncrement() << " s." << endl;
-}
-
-
-
-
-void Foam::autoHexMeshDriver::doRefine
-(
-    const dictionary& refineDict,
-    const bool prepareForSnapping
-)
-{
-    Info<< nl
-        << "Refinement phase" << nl
-        << "----------------" << nl
-        << endl;
-
-    const_cast<Time&>(mesh_.time())++;
-
-    PtrList<dictionary> featDicts(refineDict.lookup("features"));
-
-    // Refine around feature edges
-    featureEdgeRefine
-    (
-        featDicts,
-        100,    // maxIter
-        0       // min cells to refine
-    );
-
-    // Refine based on surface
-    surfaceOnlyRefine
-    (
-        100     // maxIter
-    );
-
-    // Remove cells (a certain distance) beyond surface intersections
-    removeInsideCells
-    (
-        1       // nBufferLayers
-    );
-
-    // Internal mesh refinement
-    shellRefine
-    (
-        100    // maxIter
-    );
-
-    // Introduce baffles at surface intersections
-    baffleAndSplitMesh(prepareForSnapping);
-
-    // Mesh is at its finest. Do optional zoning.
-    zonify();
-
-    // Pull baffles apart
-    splitAndMergeBaffles(prepareForSnapping);
-
-    // Do something about cells with refined faces on the boundary
-    if (prepareForSnapping)
-    {
-        mergePatchFaces();
-    }
-
-    // Do final balancing. Keep zoned faces on one processor.
-    balance(true, false);
-
-    // Write mesh
-    writeMesh("Refined mesh");
-}
-
-
-void Foam::autoHexMeshDriver::doSnap
-(
-    const dictionary& snapDict,
-    const dictionary& motionDict
-)
-{
-    Info<< nl
-        << "Morphing phase" << nl
-        << "--------------" << nl
-        << endl;
-
-    const_cast<Time&>(mesh_.time())++;
-
-    // Get the labels of added patches.
-    labelList adaptPatchIDs(getSurfacePatches());
-
-    // Create baffles (pairs of faces that share the same points)
-    // Baffles stored as owner and neighbour face that have been created.
-    List<labelPair> baffles;
-    createZoneBaffles(baffles);
-
-    {
-        autoPtr<indirectPrimitivePatch> ppPtr
-        (
-            meshRefinement::makePatch
-            (
-                mesh_,
-                adaptPatchIDs
-            )
-        );
-        indirectPrimitivePatch& pp = ppPtr();
-
-        // Distance to attact to nearest feature on surface
-        const scalarField snapDist(calcSnapDistance(snapDict, pp));
-
-
-        // Construct iterative mesh mover.
-        Info<< "Constructing mesh displacer ..." << endl;
-        Info<< "Using mesh parameters " << motionDict << nl << endl;
-
-        pointMesh pMesh(mesh_);
-
-        motionSmoother meshMover
-        (
-            mesh_,
-            pp,
-            adaptPatchIDs,
-            meshRefinement::makeDisplacementField(pMesh, adaptPatchIDs),
-            motionDict
-        );
-
-
-        // Check initial mesh
-        Info<< "Checking initial mesh ..." << endl;
-        labelHashSet wrongFaces(mesh_.nFaces()/100);
-        motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
-        const label nInitErrors = returnReduce
-        (
-            wrongFaces.size(),
-            sumOp<label>()
-        );
-
-        Info<< "Detected " << nInitErrors << " illegal faces"
-            << " (concave, zero area or negative cell pyramid volume)"
-            << endl;
-
-
-        Info<< "Checked initial mesh in = "
-            << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
-
-        // Pre-smooth patch vertices (so before determining nearest)
-        preSmoothPatch(snapDict, nInitErrors, baffles, meshMover);
-
-        // Calculate displacement at every patch point. Insert into
-        // meshMover.
-        calcNearestSurface(snapDist, meshMover);
-
-        // Get smoothly varying internal displacement field.
-        smoothDisplacement(snapDict, meshMover);
-
-        // Apply internal displacement to mesh.
-        scaleMesh(snapDict, nInitErrors, baffles, meshMover);
-    }
-
-    // Merge any introduced baffles.
-    mergeZoneBaffles(baffles);
-
-    // Write mesh.
-    writeMesh("Snapped mesh");
-}
-
-
-void Foam::autoHexMeshDriver::doLayers
-(
-    const dictionary& shrinkDict,
-    const dictionary& motionDict
-)
-{
-    Info<< nl
-        << "Shrinking and layer addition phase" << nl
-        << "----------------------------------" << nl
-        << endl;
-
-    const_cast<Time&>(mesh_.time())++;
-
-    Info<< "Using mesh parameters " << motionDict << nl << endl;
-
-    // Merge coplanar boundary faces
-    mergePatchFacesUndo(shrinkDict, motionDict);
-
-    // Per global region the number of layers (0 if no layer)
-    const labelList& numLayers = surfaces().numLayers();
-
-    // Patches that need to get a layer
-    DynamicList<label> patchIDs(numLayers.size());
-    label nFacesWithLayers = 0;
-    forAll(numLayers, region)
-    {
-        if (numLayers[region] > 0 && globalToPatch()[region] != -1)
-        {
-            label patchI = globalToPatch()[region];
-            patchIDs.append(patchI);
-            nFacesWithLayers += mesh_.boundaryMesh()[patchI].size();
-        }
-    }
-    patchIDs.shrink();
-
-    if (returnReduce(nFacesWithLayers, sumOp<label>()) == 0)
-    {
-        Info<< nl << "No layers to generate ..." << endl;
-    }
-    else
-    {
-        autoPtr<indirectPrimitivePatch> ppPtr
-        (
-            meshRefinement::makePatch
-            (
-                mesh_,
-                patchIDs
-            )
-        );
-        indirectPrimitivePatch& pp = ppPtr();
-
-        // Construct iterative mesh mover.
-        Info<< "Constructing mesh displacer ..." << endl;
-
-        {
-            pointMesh pMesh(mesh_);
-
-            motionSmoother meshMover
-            (
-                mesh_,
-                pp,
-                patchIDs,
-                meshRefinement::makeDisplacementField(pMesh, patchIDs),
-                motionDict
-            );
-
-            // Check that outside of mesh is not multiply connected.
-            checkMeshManifold();
-
-            // Check initial mesh
-            Info<< "Checking initial mesh ..." << endl;
-            labelHashSet wrongFaces(mesh_.nFaces()/100);
-            motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
-            const label nInitErrors = returnReduce
-            (
-                wrongFaces.size(),
-                sumOp<label>()
-            );
-
-            Info<< "Detected " << nInitErrors << " illegal faces"
-                << " (concave, zero area or negative cell pyramid volume)"
-                << endl;
-
-            // Do all topo changes
-            addLayers(shrinkDict, motionDict, nInitErrors, meshMover);
-        }
+        layerDriver.doLayers
+        (
+            shrinkDict,
+            motionDict,
+            layerParams,
+            decomposerPtr_(),
+            distributorPtr_()
+        );
 
         // Write mesh.
         writeMesh("Layer mesh");
@@ -1891,38 +565,4 @@ void Foam::autoHexMeshDriver::doLayers
 }
 
 
-void Foam::autoHexMeshDriver::doMesh()
-{
-    Switch wantRefine(dict_.lookup("doRefine"));
-    Switch wantSnap(dict_.lookup("doSnap"));
-    Switch wantLayers(dict_.lookup("doLayers"));
-
-    Info<< "Do refinement : " << wantRefine << nl
-        << "Do snapping   : " << wantSnap << nl
-        << "Do layers     : " << wantLayers << nl
-        << endl;
-
-    if (wantRefine)
-    {
-        doRefine(dict_, wantSnap);
-    }
-
-    if (wantSnap)
-    {
-        const dictionary& snapDict = dict_.subDict("snapDict");
-        const dictionary& motionDict = dict_.subDict("motionDict");
-
-        doSnap(snapDict, motionDict);
-    }
-
-    if (wantLayers)
-    {
-        const dictionary& motionDict = dict_.subDict("motionDict");
-        const dictionary& shrinkDict = dict_.subDict("shrinkDict");
-
-        doLayers(shrinkDict, motionDict);
-    }
-}
-
-
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.H
index 28a4845b9a81090d778dd8df172844fc53a7aaf5..553f1690b8e9837e3ee5264d8bb5ef433b3d0629 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.H
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriver.H
@@ -30,10 +30,6 @@ Description
 
 SourceFiles
     autoHexMeshDriver.C
-    autoHexMeshDriverSnap.C
-    autoHexMeshDriverLayers.C
-    autoHexMeshDriverShrink.C
-    autoHexMeshDriverTemplates.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -44,13 +40,10 @@ SourceFiles
 #include "dictionary.H"
 #include "pointField.H"
 #include "boolList.H"
-#include "Switch.H"
-#include "PackedList.H"
 #include "wallPoint.H"
-#include "indirectPrimitivePatch.H"
-#include "featureEdgeMesh.H"
-#include "searchableSurface.H"
+#include "searchableSurfaces.H"
 #include "refinementSurfaces.H"
+#include "shellSurfaces.H"
 #include "meshRefinement.H"
 #include "decompositionMethod.H"
 #include "fvMeshDistribute.H"
@@ -62,14 +55,6 @@ namespace Foam
 
 // Class forward declarations
 class fvMesh;
-class pointMesh;
-class motionSmoother;
-class removePoints;
-class pointSet;
-class pointData;
-class faceSet;
-class addPatchCellLayer;
-class mapDistributePolyMesh;
 
 /*---------------------------------------------------------------------------*\
                            Class autoHexMeshDriver Declaration
@@ -79,15 +64,13 @@ class autoHexMeshDriver
 {
     // Static data members
 
-        //- Default angle for faces to be convcave
-        static const scalar defaultConcaveAngle;
-
         //- Extrusion controls
         enum extrudeMode
         {
             NOEXTRUDE,      /*!< Do not extrude. No layers added. */
             EXTRUDE,        /*!< Extrude */
-            EXTRUDEREMOVE   /*!< Extrude but afterwards remove added faces locally */
+            EXTRUDEREMOVE   /*!< Extrude but afterwards remove added */
+                            /*!< faces locally */
         };
 
 
@@ -141,42 +124,17 @@ class autoHexMeshDriver
         //- Debug level
         const label debug_;
 
-        //- Total number of cells
-        const label maxGlobalCells_;
-
-        //- Per processor max number of cells
-        const label maxLocalCells_;
-
-        //- When to stop refining
-        const label minRefineCells_;
-
-        //- Curvature
-        const scalar curvature_;
-
-        //- Number of layers between different refinement levels
-        const label nBufferLayers_;
-
-        //- Areas to keep
-        const pointField keepPoints_;
-
         //- Merge distance
         const scalar mergeDist_;
 
 
+        //- All surface based geometry
+        autoPtr<searchableSurfaces> allGeometryPtr_;
 
-        //- Explicit features
-        PtrList<featureEdgeMesh> featureMeshes_;
-        //- Per feature the refinement level
-        labelList featureLevels_;
-
-        //- Shells
-        PtrList<searchableSurface> shells_;
-        //- Per shell the refinement level
-        labelList shellLevels_;
-        //- Per shell whether to refine inside or outside
-        boolList shellRefineInside_;
+        //- Shells (geometry for inside/outside refinement)
+        autoPtr<shellSurfaces> shellsPtr_;
 
-        //- Surfaces with refinement levels built-in
+        //- Surfaces (geometry for intersection based refinement)
         autoPtr<refinementSurfaces> surfacesPtr_;
 
         //- Per refinement surface region the patch
@@ -195,434 +153,10 @@ class autoHexMeshDriver
 
     // Private Member Functions
 
-        // Refinement
-
-            //- Calculate merge distance. Check against writing tolerance.
-            scalar getMergeDistance(const scalar mergeTol) const;
-
-            // Return per keeppoint -1 or the local cell label the point is in.
-            // Guaranteed to be only on one processor.
-            labelList findCells(const pointField&) const;
-
-            static void orientOutside(PtrList<searchableSurface>&);
-
-            //- Read feature edges
-            label readFeatureEdges(const PtrList<dictionary>&);
-
-        // Snapping
-
-             //- Split surfaces into non-zoned and zones ones
-            void getZonedSurfaces(labelList&, labelList&) const;
-
-            //- Get faces to repatch. Returns map from face to patch.
-            Map<label> getZoneBafflePatches(const bool allowBoundary) const;
-
-            //- Calculates (geometric) shared points
-            static label getCollocatedPoints
-            (
-                const scalar tol,
-                const pointField&,
-                PackedList<1>&
-            );
-
-            //- Calculate displacement per patch point to smooth out patch.
-            //  Quite complicated in determining which points to move where.
-            pointField smoothPatchDisplacement(const motionSmoother&) const;
-
-            //- Check that face zones are synced
-            void checkCoupledFaceZones() const;
-
-            //- Per edge distance to patch
-            static tmp<scalarField> edgePatchDist
-            (
-                const pointMesh&,
-                const indirectPrimitivePatch&
-            );
-
-            //- Write displacement as .obj file.
-            static void dumpMove
-            (
-                const fileName&,
-                const pointField&,
-                const pointField&
-            );
-
-            //- Check displacement is outwards pointing
-            static bool outwardsDisplacement
-            (
-                const indirectPrimitivePatch&,
-                const vectorField&
-            );
-
-        // Face merging
-
-            //- Merge patch faces. Undo until no checkMesh errors.
-            label mergePatchFacesUndo
-            (
-                const scalar minCos,
-                const scalar concaveCos,
-                const dictionary&
-            );
-
-            //- Remove points.
-            autoPtr<mapPolyMesh> doRemovePoints
-            (
-                removePoints& pointRemover,
-                const boolList& pointCanBeDeleted
-            );
-
-            //- Restore faces (which contain removed points)
-            autoPtr<mapPolyMesh> doRestorePoints
-            (
-                removePoints& pointRemover,
-                const labelList& facesToRestore
-            );
-
-            //- Return candidateFaces that are also in set.
-            labelList collectFaces
-            (
-                const labelList& candidateFaces,
-                const labelHashSet& set
-            ) const;
-
-            //- Pick up faces of cells of faces in set.
-            labelList growFaceCellFace(const labelHashSet&) const;
-
-            //- Remove points not used by any face or points used by only
-            //  two faces where the edges are in line
-            label mergeEdgesUndo(const scalar minCos, const dictionary&);
-
-
-        // Layers
-
-            //- For debugging: Dump displacement to .obj files
-            static void dumpDisplacement
-            (
-                const fileName&,
-                const indirectPrimitivePatch&,
-                const vectorField&,
-                const List<extrudeMode>&
-            );
-
-            //- Check that primitivePatch is not multiply connected.
-            //  Collect non-manifold points in pointSet.
-            static void checkManifold
-            (
-                const indirectPrimitivePatch&,
-                pointSet& nonManifoldPoints
-            );
-
-
-            // Static extrusion setup
-
-                //- Unset extrusion on point. Returns true if anything unset.
-                static bool unmarkExtrusion
-                (
-                    const label patchPointI,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                );
-
-                //- Unset extrusion on face. Returns true if anything unset.
-                static bool unmarkExtrusion
-                (
-                    const face& localFace,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                );
-
-                //- No extrusion at non-manifold points.
-                void handleNonManifolds
-                (
-                    const indirectPrimitivePatch& pp,
-                    const labelList& meshEdges,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- No extrusion on feature edges. Assumes non-manifold
-                //  edges already handled.
-                void handleFeatureAngle
-                (
-                    const indirectPrimitivePatch& pp,
-                    const labelList& meshEdges,
-                    const scalar minCos,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- No extrusion on warped faces
-                void handleWarpedFaces
-                (
-                    const indirectPrimitivePatch& pp,
-                    const scalar faceRatio,
-                    const scalar edge0Len,
-                    const labelList& cellLevel,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- Determine the number of layers per point from the number of
-                //  layers per surface.
-                void setNumLayers
-                (
-                    const labelList& patchIDs,
-                    const indirectPrimitivePatch& pp,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- Grow no-extrusion layer.
-                static void growNoExtrusion
-                (
-                    const indirectPrimitivePatch& pp,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                );
-
-                //- Calculate pointwise wanted and minimum thickness.
-                //  thickness: wanted thickness
-                //  minthickness: when to give up and not extrude
-                void calculateLayerThickness
-                (
-                    const scalar expansionRatio,
-                    const scalar finalLayerRatio,
-                    const scalar relMinThickness,
-                    const indirectPrimitivePatch& pp,
-                    const labelList& cellLevel,
-                    const labelList& patchNLayers,
-                    const scalar edge0Len,
-                    scalarField& thickness,
-                    scalarField& minThickness
-                ) const;
-
-
-            // Extrusion execution
-
-                //- Synchronize displacement among coupled patches.
-                void syncPatchDisplacement
-                (
-                    const motionSmoother& meshMover,
-                    const scalarField& minThickness,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- Get nearest point on surface to snap to
-                void getPatchDisplacement
-                (
-                    const motionSmoother& meshMover,
-                    const scalarField& thickness,
-                    const scalarField& minThickness,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- Truncates displacement
-                // - for all patchFaces in the faceset displacement gets set
-                //   to zero
-                // - all displacement < minThickness gets set to zero
-                label truncateDisplacement
-                (
-                    const motionSmoother& meshMover,
-                    const scalarField& minThickness,
-                    const faceSet& illegalPatchFaces,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                ) const;
-
-                //- Setup layer information (at points and faces) to
-                //  modify mesh topology in
-                //  regions where layer mesh terminates. Guarantees an
-                //  optional slow decreasing of the number of layers.
-                //  Returns the number of layers per face and per point
-                //  to go into the actual layer addition engine.
-                void setupLayerInfoTruncation
-                (
-                    const motionSmoother& meshMover,
-                    const labelList& patchNLayers,
-                    const List<extrudeMode>& extrudeStatus,
-                    const label nBufferCellsNoExtrude,
-                    labelList& nPatchPointLayers,
-                    labelList& nPatchFaceLayers
-                ) const;
-
-                //- Does any of the cells use a face from faces?
-                static bool cellsUseFace
-                (
-                    const polyMesh& mesh,
-                    const labelList& cellLabels,
-                    const labelHashSet& faces
-                );
-
-                //- Checks the newly added cells and locally unmarks points
-                //  so they will not get extruded next time round. Returns
-                //  global number of unmarked points (0 if all was fine)
-                static label checkAndUnmark
-                (
-                    const addPatchCellLayer& addLayer,
-                    const dictionary& motionDict,
-                    const indirectPrimitivePatch& pp,
-                    const fvMesh&,
-
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    List<extrudeMode>& extrudeStatus
-                );
-
-                //- Count global number of extruded faces
-                static label countExtrusion
-                (
-                    const indirectPrimitivePatch& pp,
-                    const List<extrudeMode>& extrudeStatus
-                );
-
-                //- Collect layer faces and layer cells into bools
-                //  for ease of handling
-                static void getLayerCellsFaces
-                (
-                    const polyMesh&,
-                    const addPatchCellLayer&,
-                    boolList&,
-                    boolList&
-                );
-
-            // Mesh shrinking (to create space for layers)
-
-                //- Average field (over all subset of mesh points) by
-                //  summing contribution from edges. Global parallel since only
-                //  does master edges for coupled edges.
-                template<class Type>
-                void averageNeighbours
-                (
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& meshEdges,
-                    const labelList& meshPoints,
-                    const edgeList& edges,
-                    const scalarField& invSumWeight,
-                    const Field<Type>& data,
-                    Field<Type>& average
-                ) const;
-
-                //- Calculate inverse sum of edge weights (currently always 1.0)
-                void sumWeights
-                (
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& meshEdges,
-                    const labelList& meshPoints,
-                    const edgeList& edges,
-                    scalarField& invSumWeight
-                ) const;
-
-                //- Smooth scalar field on patch
-                void smoothField
-                (
-                    const motionSmoother& meshMover,
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& meshEdges,
-                    const scalarField& fieldMin,
-                    const label& nSmoothDisp,
-                    scalarField& field
-                ) const;
-
-                //- Smooth normals on patch.
-                void smoothPatchNormals
-                (
-                    const motionSmoother& meshMover,
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& meshEdges,
-                    const label nSmoothDisp,
-                    pointField& normals
-                ) const;
-
-                //- Smooth normals in interior.
-                void smoothNormals
-                (
-                    const label nSmoothDisp,
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& fixedPoints,
-                    pointVectorField& normals
-                ) const;
-
-                bool isMaxEdge
-                (
-                    const List<pointData>&,
-                    const label edgeI,
-                    const scalar minCos
-                ) const;
-
-                //- Stop layer growth where mesh wraps around edge with a
-                //  large feature angle
-                void handleFeatureAngleLayerTerminations
-                (
-                    const indirectPrimitivePatch& pp,
-                    const scalar minCos,
-                    List<extrudeMode>& extrudeStatus,
-                    pointField& patchDisp,
-                    labelList& patchNLayers,
-                    label& nPointCounter
-                ) const;
-
-                //- Find isolated islands (points, edges and faces and
-                // layer terminations)
-                // in the layer mesh and stop any layer growth at these points.
-                void findIsolatedRegions
-                (
-                    const indirectPrimitivePatch& pp,
-                    const PackedList<1>& isMasterEdge,
-                    const labelList& meshEdges,
-                    const scalar minCosLayerTermination,
-                    scalarField& field,
-                    List<extrudeMode>& extrudeStatus,
-                    pointField& patchDisp,
-                    labelList& patchNLayers
-                ) const;
-
-                // Calculate medial axis fields
-                void medialAxisSmoothingInfo
-                (
-                    const motionSmoother& meshMover,
-                    const label nSmoothNormals,
-                    const label nSmoothSurfaceNormals,
-                    const scalar minMedianAxisAngleCos,
-
-                    pointVectorField& dispVec,
-                    pointScalarField& medialRatio,
-                    pointScalarField& medialDist
-                ) const;
-
-                //- Main routine to shrink mesh
-                void shrinkMeshMedialDistance
-                (
-                    motionSmoother& meshMover,
-                    const label nSmoothThickness,
-                    const scalar maxThicknessToMedialRatio,
-                    const label nAllowableErrors,
-                    const label nSnap,
-                    const scalar minCosLayerTermination,
-
-                    const scalarField& layerThickness,
-                    const scalarField& minThickness,
-
-                    const pointVectorField& dispVec,
-                    const pointScalarField& medialRatio,
-                    const pointScalarField& medialDist,
-
-                    List<extrudeMode>& extrudeStatus,
-                    pointField& patchDisp,
-                    labelList& patchNLayers
-                ) const;
+        //- Calculate merge distance. Check against writing tolerance.
+        scalar getMergeDistance(const scalar mergeTol) const;
 
+        //static void orientOutside(PtrList<searchableSurface>&);
 
         //- Disallow default bitwise copy construct
         autoHexMeshDriver(const autoHexMeshDriver&);
@@ -646,16 +180,6 @@ public:
             const dictionary& decomposeDict
         );
 
-        //- Alternative constructor from top-level controldictionary and
-        //  refinement specific dictionary
-        autoHexMeshDriver
-        (
-            fvMesh& mesh,
-            const dictionary& controlDict,
-            const dictionary& refineDict,
-            const dictionary& decomposeDict
-        );
-
 
     // Member Functions
 
@@ -677,6 +201,12 @@ public:
                 return surfacesPtr_();
             }
 
+            //- Surfaces to volume refinement on
+            const shellSurfaces& shells() const
+            {
+                return shellsPtr_();
+            }
+
             //- Per refinementsurface, per region the patch
             const labelList& globalToPatch() const
             {
@@ -684,153 +214,11 @@ public:
             }
 
 
-        // Refinement
-
-            //- Refine around explicit feature edges
-            label featureEdgeRefine
-            (
-                const PtrList<dictionary>& featDicts,
-                const label maxIter,
-                const label minRefine
-            );
-
-            //- Refine at surface intersections
-            label surfaceOnlyRefine(const label maxIter);
-
-            //- Remove cells not reachable from keep points
-            void removeInsideCells(const label nBufferLayers);
-
-            //- Refine volume regions (interior of shells)
-            label shellRefine(const label maxIter);
-
-            //- Introduce baffles; remove unreachable mesh parts
-            //  handleSnapProblems : whether to remove free floating cells
-            void baffleAndSplitMesh(const bool handleSnapProblems);
-
-            //- Move cells to zones
-            void zonify();
-
-            //- Split and recombine baffles to get rid of single face baffles.
-            void splitAndMergeBaffles(const bool handleSnapProblems);
-
-            //- Merge multiple boundary faces on single cell
-            void mergePatchFaces();
-
-            //- Redecompose according to cell count
-            //  keepZoneFaces : find all faceZones from zoned surfaces and keep
-            //                  owner and neighbour together
-            //  keepBaffles   : find all baffles and keep them together
-            autoPtr<mapDistributePolyMesh> balance
-            (
-                const bool keepZoneFaces,
-                const bool keepBaffles
-            );
+        // Meshing
 
             //- Write mesh
             void writeMesh(const string&) const;
 
-
-        // Snapping
-
-            //- Create baffles for faces straddling zoned surfaces. Return
-            //  baffles.
-            autoPtr<mapPolyMesh> createZoneBaffles(List<labelPair>&);
-
-            //- Merge baffles.
-            autoPtr<mapPolyMesh> mergeZoneBaffles(const List<labelPair>&);
-
-            //- Calculate edge length per patch point.
-            scalarField calcSnapDistance
-            (
-                const dictionary& snapDict,
-                const indirectPrimitivePatch&
-            ) const;
-
-            //- Get patches generated for surfaces.
-            labelList getSurfacePatches() const;
-
-            //- Smooth the mesh (patch and internal) to increase visibility
-            //  of surface points (on castellated mesh) w.r.t. surface.
-            void preSmoothPatch
-            (
-                const dictionary& snapDict,
-                const label nInitErrors,
-                const List<labelPair>& baffles,
-                motionSmoother&
-            ) const;
-
-            //- Per patch point calculate point on nearest surface. Set as
-            //  boundary conditions of motionSmoother displacement field. Return
-            //  displacement of patch points.
-            vectorField calcNearestSurface
-            (
-                const scalarField& snapDist,
-                motionSmoother& meshMover
-            ) const;
-
-            //- Smooth the displacement field to the internal.
-            void smoothDisplacement
-            (
-                const dictionary& snapDict,
-                motionSmoother&
-            ) const;
-
-            //- Do the hard work: move the mesh according to displacement,
-            //  locally relax the displacement.
-            void scaleMesh
-            (
-                const dictionary& snapDict,
-                const label nInitErrors,
-                const List<labelPair>& baffles,
-                motionSmoother&
-            );
-
-
-        // Layers
-
-            //- Merge patch faces on same cell.
-            void mergePatchFacesUndo
-            (
-                const dictionary& shrinkDict,
-                const dictionary& motionDict
-            );
-
-            //- Check that mesh outside is not multiply connected.
-            void checkMeshManifold() const;
-
-            //- Add cell layers
-            void addLayers
-            (
-                const dictionary& shrinkDict,
-                const dictionary& motionDict,
-                const label nAllowableErrors,
-                motionSmoother&
-            );
-
-
-        // Other
-
-            //- Do all refinement.
-            void doRefine
-            (
-                const dictionary& refineDict,
-                const bool prepareForSnapping
-            );
-
-            //- Do all snapping.
-            void doSnap
-            (
-                const dictionary& snapDict,
-                const dictionary& motionDict
-            );
-
-            //- Do alllayer addition.
-            void doLayers
-            (
-                const dictionary& shrinkDict,
-                const dictionary& motionDict
-            );
-
             //- Do all : refine, snap, layers
             void doMesh();
 };
@@ -842,12 +230,6 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-#ifdef NoRepository
-#   include "autoHexMeshDriverTemplates.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
 #endif
 
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverLayers.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C
similarity index 80%
rename from src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverLayers.C
rename to src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C
index 0615d504765172caa43557661955fb7b88cd09f7..7caa1af11e1fa895bfb54af6796cbdef72a51840 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverLayers.C
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C
@@ -27,11 +27,10 @@ Description
 
 \*----------------------------------------------------------------------------*/
 
-#include "ListOps.H"
-#include "autoHexMeshDriver.H"
+#include "autoLayerDriver.H"
 #include "fvMesh.H"
 #include "Time.H"
-#include "combineFaces.H"
+#include "meshRefinement.H"
 #include "removePoints.H"
 #include "pointFields.H"
 #include "motionSmoother.H"
@@ -43,31 +42,41 @@ Description
 #include "mapPolyMesh.H"
 #include "addPatchCellLayer.H"
 #include "mapDistributePolyMesh.H"
+#include "OFstream.H"
+#include "layerParameters.H"
+#include "combineFaces.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-const Foam::scalar Foam::autoHexMeshDriver::defaultConcaveAngle = 90;
+namespace Foam
+{
+
+defineTypeNameAndDebug(autoLayerDriver, 0);
+
+} // End namespace Foam
 
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
+Foam::label Foam::autoLayerDriver::mergePatchFacesUndo
 (
     const scalar minCos,
     const scalar concaveCos,
     const dictionary& motionDict
 )
 {
+    fvMesh& mesh = meshRefiner_.mesh();
+
     // Patch face merging engine
-    combineFaces faceCombiner(mesh_, true);
+    combineFaces faceCombiner(mesh, true);
 
     // Pick up all candidate cells on boundary
-    labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces());
+    labelHashSet boundaryCells(mesh.nFaces()-mesh.nInternalFaces());
 
     {
         labelList patchIDs(meshRefinement::addedPatches(globalToPatch_));
 
-        const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+        const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
         forAll(patchIDs, i)
         {
@@ -79,7 +88,7 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             {
                 forAll(patch, i)
                 {
-                    boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]);
+                    boundaryCells.insert(mesh.faceOwner()[patch.start()+i]);
                 }
             }
         }
@@ -102,9 +111,9 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
 
     if (nFaceSets > 0)
     {
-        if (debug_)
+        if (debug)
         {
-            faceSet allSets(mesh_, "allFaceSets", allFaceSets.size());
+            faceSet allSets(mesh, "allFaceSets", allFaceSets.size());
             forAll(allFaceSets, setI)
             {
                 forAll(allFaceSets[setI], i)
@@ -119,13 +128,13 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
 
 
         // Topology changes container
-        polyTopoChange meshMod(mesh_);
+        polyTopoChange meshMod(mesh);
 
         // Merge all faces of a set into the first face of the set.
         faceCombiner.setRefinement(allFaceSets, meshMod);
 
         // Experimental: store data for all the points that have been deleted
-        meshRefinerPtr_().storeData
+        meshRefiner_.storeData
         (
             faceCombiner.savedPointLabels(),    // points to store
             labelList(0),                       // faces to store
@@ -133,20 +142,20 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
         );
 
         // Change the mesh (no inflation)
-        autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
+        autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
 
         // Update fields
-        mesh_.updateMesh(map);
+        mesh.updateMesh(map);
 
         // Move mesh (since morphing does not do this)
         if (map().hasMotionPoints())
         {
-            mesh_.movePoints(map().preMotionPoints());
+            mesh.movePoints(map().preMotionPoints());
         }
 
         faceCombiner.updateMesh(map);
 
-        meshRefinerPtr_().updateMesh(map, labelList(0));
+        meshRefiner_.updateMesh(map, labelList(0));
 
 
 
@@ -162,14 +171,14 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
 
             faceSet errorFaces
             (
-                mesh_,
+                mesh,
                 "errorFaces",
-                mesh_.nFaces()-mesh_.nInternalFaces()
+                mesh.nFaces()-mesh.nInternalFaces()
             );
             bool hasErrors = motionSmoother::checkMesh
             (
                 false,  // report
-                mesh_,
+                mesh,
                 motionDict,
                 errorFaces
             );
@@ -181,7 +190,7 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             //    label nOldSize = errorFaces.size();
             //
             //    hasErrors =
-            //        mesh_.checkFaceFaces
+            //        mesh.checkFaceFaces
             //        (
             //            false,
             //            &errorFaces
@@ -200,7 +209,7 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             }
 
 
-            if (debug_)
+            if (debug)
             {
                 Pout<< "Writing all faces in error to faceSet "
                     << errorFaces.objectPath() << nl << endl;
@@ -219,9 +228,9 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
 
                 if (masterFaceI != -1)
                 {
-                    label masterCellII = mesh_.faceOwner()[masterFaceI];
+                    label masterCellII = mesh.faceOwner()[masterFaceI];
 
-                    const cell& cFaces = mesh_.cells()[masterCellII];
+                    const cell& cFaces = mesh.cells()[masterCellII];
 
                     forAll(cFaces, i)
                     {
@@ -244,9 +253,9 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             Info<< "Masters that need to be restored:"
                 << nRestore << endl;
 
-            if (debug_)
+            if (debug)
             {
-                faceSet restoreSet(mesh_, "mastersToRestore", mastersToRestore);
+                faceSet restoreSet(mesh, "mastersToRestore", mastersToRestore);
                 Pout<< "Writing all " << mastersToRestore.size()
                     << " masterfaces to be restored to set "
                     << restoreSet.objectPath() << endl;
@@ -264,7 +273,7 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             // ~~~~
 
             // Topology changes container
-            polyTopoChange meshMod(mesh_);
+            polyTopoChange meshMod(mesh);
 
             // Merge all faces of a set into the first face of the set.
             // Experimental:mark all points/faces/cells that have been restored.
@@ -282,15 +291,15 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             );
 
             // Change the mesh (no inflation)
-            autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
+            autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
 
             // Update fields
-            mesh_.updateMesh(map);
+            mesh.updateMesh(map);
 
             // Move mesh (since morphing does not do this)
             if (map().hasMotionPoints())
             {
-                mesh_.movePoints(map().preMotionPoints());
+                mesh.movePoints(map().preMotionPoints());
             }
 
             faceCombiner.updateMesh(map);
@@ -301,7 +310,7 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             inplaceMapKey(map().reverseCellMap(), restoredCells);
 
             // Experimental:restore all points/face/cells in maps
-            meshRefinerPtr_().updateMesh
+            meshRefiner_.updateMesh
             (
                 map,
                 labelList(0),       // changedFaces
@@ -313,11 +322,11 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
             Info<< endl;
         }
 
-        if (debug_)
+        if (debug)
         {
             Pout<< "Writing merged-faces mesh to time "
-                << mesh_.time().timeName() << nl << endl;
-            mesh_.write();
+                << mesh.time().timeName() << nl << endl;
+            mesh.write();
         }
     }
     else
@@ -330,45 +339,49 @@ Foam::label Foam::autoHexMeshDriver::mergePatchFacesUndo
 
 
 // Remove points. pointCanBeDeleted is parallel synchronised.
-Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::doRemovePoints
+Foam::autoPtr<Foam::mapPolyMesh> Foam::autoLayerDriver::doRemovePoints
 (
     removePoints& pointRemover,
     const boolList& pointCanBeDeleted
 )
 {
+    fvMesh& mesh = meshRefiner_.mesh();
+
     // Topology changes container
-    polyTopoChange meshMod(mesh_);
+    polyTopoChange meshMod(mesh);
 
     pointRemover.setRefinement(pointCanBeDeleted, meshMod);
 
     // Change the mesh (no inflation)
-    autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
+    autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
 
     // Update fields
-    mesh_.updateMesh(map);
+    mesh.updateMesh(map);
 
     // Move mesh (since morphing does not do this)
     if (map().hasMotionPoints())
     {
-        mesh_.movePoints(map().preMotionPoints());
+        mesh.movePoints(map().preMotionPoints());
     }
 
     pointRemover.updateMesh(map);
-    meshRefinerPtr_().updateMesh(map, labelList(0));
+    meshRefiner_.updateMesh(map, labelList(0));
 
     return map;
 }
 
 
 // Restore faces (which contain removed points)
-Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::doRestorePoints
+Foam::autoPtr<Foam::mapPolyMesh> Foam::autoLayerDriver::doRestorePoints
 (
     removePoints& pointRemover,
     const labelList& facesToRestore
 )
 {
+    fvMesh& mesh = meshRefiner_.mesh();
+
     // Topology changes container
-    polyTopoChange meshMod(mesh_);
+    polyTopoChange meshMod(mesh);
 
     // Determine sets of points and faces to restore
     labelList localFaces, localPoints;
@@ -388,19 +401,19 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::doRestorePoints
     );
 
     // Change the mesh (no inflation)
-    autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true);
+    autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
 
     // Update fields
-    mesh_.updateMesh(map);
+    mesh.updateMesh(map);
 
     // Move mesh (since morphing does not do this)
     if (map().hasMotionPoints())
     {
-        mesh_.movePoints(map().preMotionPoints());
+        mesh.movePoints(map().preMotionPoints());
     }
 
     pointRemover.updateMesh(map);
-    meshRefinerPtr_().updateMesh(map, labelList(0));
+    meshRefiner_.updateMesh(map, labelList(0));
 
     return map;
 }
@@ -408,14 +421,16 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::doRestorePoints
 
 // Collect all faces that are both in candidateFaces and in the set.
 // If coupled face also collects the coupled face.
-Foam::labelList Foam::autoHexMeshDriver::collectFaces
+Foam::labelList Foam::autoLayerDriver::collectFaces
 (
     const labelList& candidateFaces,
     const labelHashSet& set
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     // Has face been selected?
-    boolList selected(mesh_.nFaces(), false);
+    boolList selected(mesh.nFaces(), false);
 
     forAll(candidateFaces, i)
     {
@@ -428,7 +443,7 @@ Foam::labelList Foam::autoHexMeshDriver::collectFaces
     }
     syncTools::syncFaceList
     (
-        mesh_,
+        mesh,
         selected,
         orEqOp<bool>(),     // combine operator
         false               // separation
@@ -441,30 +456,32 @@ Foam::labelList Foam::autoHexMeshDriver::collectFaces
 
 
 // Pick up faces of cells of faces in set.
-Foam::labelList Foam::autoHexMeshDriver::growFaceCellFace
+Foam::labelList Foam::autoLayerDriver::growFaceCellFace
 (
     const labelHashSet& set
 ) const
 {
-    boolList selected(mesh_.nFaces(), false);
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    boolList selected(mesh.nFaces(), false);
 
     forAllConstIter(faceSet, set, iter)
     {
         label faceI = iter.key();
 
-        label own = mesh_.faceOwner()[faceI];
+        label own = mesh.faceOwner()[faceI];
 
-        const cell& ownFaces = mesh_.cells()[own];
+        const cell& ownFaces = mesh.cells()[own];
         forAll(ownFaces, ownFaceI)
         {
             selected[ownFaces[ownFaceI]] = true;
         }
 
-        if (mesh_.isInternalFace(faceI))
+        if (mesh.isInternalFace(faceI))
         {
-            label nbr = mesh_.faceNeighbour()[faceI];
+            label nbr = mesh.faceNeighbour()[faceI];
 
-            const cell& nbrFaces = mesh_.cells()[nbr];
+            const cell& nbrFaces = mesh.cells()[nbr];
             forAll(nbrFaces, nbrFaceI)
             {
                 selected[nbrFaces[nbrFaceI]] = true;
@@ -473,7 +490,7 @@ Foam::labelList Foam::autoHexMeshDriver::growFaceCellFace
     }
     syncTools::syncFaceList
     (
-        mesh_,
+        mesh,
         selected,
         orEqOp<bool>(),     // combine operator
         false               // separation
@@ -484,12 +501,14 @@ Foam::labelList Foam::autoHexMeshDriver::growFaceCellFace
 
 // Remove points not used by any face or points used by only two faces where
 // the edges are in line
-Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
+Foam::label Foam::autoLayerDriver::mergeEdgesUndo
 (
     const scalar minCos,
     const dictionary& motionDict
 )
 {
+    fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl
         << "Merging all points on surface that" << nl
         << "- are used by only two boundary faces and" << nl
@@ -497,7 +516,7 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
         << "." << nl << endl;
 
     // Point removal analysis engine with undo
-    removePoints pointRemover(mesh_, true);
+    removePoints pointRemover(mesh, true);
 
     // Count usage of points
     boolList pointCanBeDeleted;
@@ -526,14 +545,14 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
 
             faceSet errorFaces
             (
-                mesh_,
+                mesh,
                 "errorFaces",
-                mesh_.nFaces()-mesh_.nInternalFaces()
+                mesh.nFaces()-mesh.nInternalFaces()
             );
             bool hasErrors = motionSmoother::checkMesh
             (
                 false,  // report
-                mesh_,
+                mesh,
                 motionDict,
                 errorFaces
             );
@@ -545,7 +564,7 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
             //    label nOldSize = errorFaces.size();
             //
             //    hasErrors =
-            //        mesh_.checkFaceFaces
+            //        mesh.checkFaceFaces
             //        (
             //            false,
             //            &errorFaces
@@ -553,7 +572,7 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
             //     || hasErrors;
             //
             //    Info<< "Detected additional "
-            //        << returnReduce(errorFaces.size()-nOldSize, sumOp<label>())
+            //        << returnReduce(errorFaces.size()-nOldSize,sumOp<label>())
             //        << " faces with illegal face-face connectivity"
             //        << endl;
             //}
@@ -563,7 +582,7 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
                 break;
             }
 
-            if (debug_)
+            if (debug)
             {
                 Pout<< "**Writing all faces in error to faceSet "
                     << errorFaces.objectPath() << nl << endl;
@@ -613,11 +632,11 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
             doRestorePoints(pointRemover, masterErrorFaces);
         }
 
-        if (debug_)
+        if (debug)
         {
             Pout<< "Writing merged-edges mesh to time "
-                << mesh_.time().timeName() << nl << endl;
-            mesh_.write();
+                << mesh.time().timeName() << nl << endl;
+            mesh.write();
         }
     }
     else
@@ -630,7 +649,7 @@ Foam::label Foam::autoHexMeshDriver::mergeEdgesUndo
 
 
 // For debugging: Dump displacement to .obj files
-void Foam::autoHexMeshDriver::dumpDisplacement
+void Foam::autoLayerDriver::dumpDisplacement
 (
     const fileName& prefix,
     const indirectPrimitivePatch& pp,
@@ -676,7 +695,7 @@ void Foam::autoHexMeshDriver::dumpDisplacement
 
 // Check that primitivePatch is not multiply connected. Collect non-manifold
 // points in pointSet.
-void Foam::autoHexMeshDriver::checkManifold
+void Foam::autoLayerDriver::checkManifold
 (
     const indirectPrimitivePatch& fp,
     pointSet& nonManifoldPoints
@@ -703,23 +722,25 @@ void Foam::autoHexMeshDriver::checkManifold
 }
 
 
-void Foam::autoHexMeshDriver::checkMeshManifold() const
+void Foam::autoLayerDriver::checkMeshManifold() const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl << "Checking mesh manifoldness ..." << endl;
 
     // Get all outside faces
-    labelList outsideFaces(mesh_.nFaces() - mesh_.nInternalFaces());
+    labelList outsideFaces(mesh.nFaces() - mesh.nInternalFaces());
 
-    for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
+    for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
     {
-         outsideFaces[faceI - mesh_.nInternalFaces()] = faceI;
+         outsideFaces[faceI - mesh.nInternalFaces()] = faceI;
     }
 
     pointSet nonManifoldPoints
     (
-        mesh_,
+        mesh,
         "nonManifoldPoints",
-        mesh_.nPoints() / 100
+        mesh.nPoints() / 100
     );
 
     // Build primitivePatch out of faces and check it for problems.
@@ -727,8 +748,8 @@ void Foam::autoHexMeshDriver::checkMeshManifold() const
     (
         indirectPrimitivePatch
         (
-            IndirectList<face>(mesh_.faces(), outsideFaces),
-            mesh_.points()
+            IndirectList<face>(mesh.faces(), outsideFaces),
+            mesh.points()
         ),
         nonManifoldPoints
     );
@@ -753,7 +774,7 @@ void Foam::autoHexMeshDriver::checkMeshManifold() const
 
 
 // Unset extrusion on point. Returns true if anything unset.
-bool Foam::autoHexMeshDriver::unmarkExtrusion
+bool Foam::autoLayerDriver::unmarkExtrusion
 (
     const label patchPointI,
     pointField& patchDisp,
@@ -783,7 +804,7 @@ bool Foam::autoHexMeshDriver::unmarkExtrusion
 
 
 // Unset extrusion on face. Returns true if anything unset.
-bool Foam::autoHexMeshDriver::unmarkExtrusion
+bool Foam::autoLayerDriver::unmarkExtrusion
 (
     const face& localFace,
     pointField& patchDisp,
@@ -814,7 +835,7 @@ bool Foam::autoHexMeshDriver::unmarkExtrusion
 
 
 // No extrusion at non-manifold points.
-void Foam::autoHexMeshDriver::handleNonManifolds
+void Foam::autoLayerDriver::handleNonManifolds
 (
     const indirectPrimitivePatch& pp,
     const labelList& meshEdges,
@@ -823,12 +844,14 @@ void Foam::autoHexMeshDriver::handleNonManifolds
     List<extrudeMode>& extrudeStatus
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl << "Handling non-manifold points ..." << endl;
 
     // Detect non-manifold points
     Info<< nl << "Checking patch manifoldness ..." << endl;
 
-    pointSet nonManifoldPoints(mesh_, "nonManifoldPoints", pp.nPoints());
+    pointSet nonManifoldPoints(mesh, "nonManifoldPoints", pp.nPoints());
 
     // 1. Local check
     checkManifold(pp, nonManifoldPoints);
@@ -844,16 +867,16 @@ void Foam::autoHexMeshDriver::handleNonManifolds
 
     // 2. Parallel check
     // For now disable extrusion at any shared edges.
-    const labelHashSet sharedEdgeSet(mesh_.globalData().sharedEdgeLabels());
+    const labelHashSet sharedEdgeSet(mesh.globalData().sharedEdgeLabels());
 
     forAll(pp.edges(), edgeI)
     {
         if (sharedEdgeSet.found(meshEdges[edgeI]))
         {
-            const edge& e = mesh_.edges()[meshEdges[edgeI]];
+            const edge& e = mesh.edges()[meshEdges[edgeI]];
 
             Pout<< "Disabling extrusion at edge "
-                << mesh_.points()[e[0]] << mesh_.points()[e[1]]
+                << mesh.points()[e[0]] << mesh.points()[e[1]]
                 << " since it is non-manifold across coupled patches."
                 << endl;
 
@@ -905,7 +928,7 @@ void Foam::autoHexMeshDriver::handleNonManifolds
 
 
 // Parallel feature edge detection. Assumes non-manifold edges already handled.
-void Foam::autoHexMeshDriver::handleFeatureAngle
+void Foam::autoLayerDriver::handleFeatureAngle
 (
     const indirectPrimitivePatch& pp,
     const labelList& meshEdges,
@@ -915,12 +938,14 @@ void Foam::autoHexMeshDriver::handleFeatureAngle
     List<extrudeMode>& extrudeStatus
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl << "Handling feature edges ..." << endl;
 
     if (minCos < 1-SMALL)
     {
         // Normal component of normals of connected faces.
-        vectorField edgeNormal(mesh_.nEdges(), wallPoint::greatPoint);
+        vectorField edgeNormal(mesh.nEdges(), wallPoint::greatPoint);
 
         const labelListList& edgeFaces = pp.edgeFaces();
 
@@ -942,7 +967,7 @@ void Foam::autoHexMeshDriver::handleFeatureAngle
 
         syncTools::syncEdgeList
         (
-            mesh_,
+            mesh,
             edgeNormal,
             nomalsCombine(),
             wallPoint::greatPoint,  // null value
@@ -951,9 +976,9 @@ void Foam::autoHexMeshDriver::handleFeatureAngle
 
         label vertI = 0;
         autoPtr<OFstream> str;
-        if (debug_)
+        if (debug)
         {
-            str.reset(new OFstream(mesh_.time().path()/"featureEdges.obj"));
+            str.reset(new OFstream(mesh.time().path()/"featureEdges.obj"));
             Info<< "Writing feature edges to " << str().name() << endl;
         }
 
@@ -1017,7 +1042,7 @@ void Foam::autoHexMeshDriver::handleFeatureAngle
 // layer and compares it to the space the warped face takes up. Disables
 // extrusion if layer thickness is more than faceRatio of the thickness of
 // the face.
-void Foam::autoHexMeshDriver::handleWarpedFaces
+void Foam::autoLayerDriver::handleWarpedFaces
 (
     const indirectPrimitivePatch& pp,
     const scalar faceRatio,
@@ -1028,9 +1053,11 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
     List<extrudeMode>& extrudeStatus
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl << "Handling cells with warped patch faces ..." << nl;
 
-    const pointField& points = mesh_.points();
+    const pointField& points = mesh.points();
 
     label nWarpedFaces = 0;
 
@@ -1042,11 +1069,11 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
         {
             label faceI = pp.addressing()[i];
 
-            label ownLevel = cellLevel[mesh_.faceOwner()[faceI]];
+            label ownLevel = cellLevel[mesh.faceOwner()[faceI]];
             scalar edgeLen = edge0Len/(1<<ownLevel);
 
             // Normal distance to face centre plane
-            const point& fc = mesh_.faceCentres()[faceI];
+            const point& fc = mesh.faceCentres()[faceI];
             const vector& fn = pp.faceNormals()[i];
 
             scalarField vProj(f.size());
@@ -1090,7 +1117,7 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
 
 //// No extrusion on cells with multiple patch faces. There ususally is a reason
 //// why combinePatchFaces hasn't succeeded.
-//void Foam::autoHexMeshDriver::handleMultiplePatchFaces
+//void Foam::autoLayerDriver::handleMultiplePatchFaces
 //(
 //    const indirectPrimitivePatch& pp,
 //    pointField& patchDisp,
@@ -1098,12 +1125,14 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
 //    List<extrudeMode>& extrudeStatus
 //) const
 //{
+//    const fvMesh& mesh = meshRefiner_.mesh();
+//
 //    Info<< nl << "Handling cells with multiple patch faces ..." << nl;
 //
 //    const labelListList& pointFaces = pp.pointFaces();
 //
 //    // Cells that should not get an extrusion layer
-//    cellSet multiPatchCells(mesh_, "multiPatchCells", pp.size());
+//    cellSet multiPatchCells(mesh, "multiPatchCells", pp.size());
 //
 //    // Detect points that use multiple faces on same cell.
 //    forAll(pointFaces, patchPointI)
@@ -1114,7 +1143,7 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
 //
 //        forAll(pFaces, i)
 //        {
-//            label cellI = mesh_.faceOwner()[pp.addressing()[pFaces[i]]];
+//            label cellI = mesh.faceOwner()[pp.addressing()[pFaces[i]]];
 //
 //            if (!pointCells.insert(cellI))
 //            {
@@ -1158,7 +1187,7 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
 //                forAll(pFaces, i)
 //                {
 //                    label cellI =
-//                        mesh_.faceOwner()[pp.addressing()[pFaces[i]]];
+//                        mesh.faceOwner()[pp.addressing()[pFaces[i]]];
 //
 //                    if (multiPatchCells.found(cellI))
 //                    {
@@ -1189,8 +1218,9 @@ void Foam::autoHexMeshDriver::handleWarpedFaces
 
 
 // No extrusion on faces with differing number of layers for points
-void Foam::autoHexMeshDriver::setNumLayers
+void Foam::autoLayerDriver::setNumLayers
 (
+    const labelList& patchToNLayers,
     const labelList& patchIDs,
     const indirectPrimitivePatch& pp,
     pointField& patchDisp,
@@ -1198,21 +1228,11 @@ void Foam::autoHexMeshDriver::setNumLayers
     List<extrudeMode>& extrudeStatus
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< nl << "Handling points with inconsistent layer specification ..."
         << endl;
 
-    const labelList& nSurfLayers = surfaces().numLayers();
-
-    // Build map from patch to layers
-    Map<label> patchToNLayers(nSurfLayers.size());
-    forAll(nSurfLayers, region)
-    {
-        if (globalToPatch_[region] != -1)
-        {
-            patchToNLayers.insert(globalToPatch_[region], nSurfLayers[region]);
-        }
-    }
-
     // Get for every point (really only nessecary on patch external points)
     // the max and min of any patch faces using it.
     labelList maxLayers(patchNLayers.size(), labelMin);
@@ -1222,7 +1242,7 @@ void Foam::autoHexMeshDriver::setNumLayers
     {
         label patchI = patchIDs[i];
 
-        const labelList& meshPoints = mesh_.boundaryMesh()[patchI].meshPoints();
+        const labelList& meshPoints = mesh.boundaryMesh()[patchI].meshPoints();
 
         label wantedLayers = patchToNLayers[patchI];
 
@@ -1237,7 +1257,7 @@ void Foam::autoHexMeshDriver::setNumLayers
 
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         pp.meshPoints(),
         maxLayers,
         maxEqOp<label>(),
@@ -1246,7 +1266,7 @@ void Foam::autoHexMeshDriver::setNumLayers
     );
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         pp.meshPoints(),
         minLayers,
         minEqOp<label>(),
@@ -1257,7 +1277,7 @@ void Foam::autoHexMeshDriver::setNumLayers
     // Unmark any point with different min and max
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    label nConflicts = 0;
+    //label nConflicts = 0;
 
     forAll(maxLayers, i)
     {
@@ -1294,16 +1314,16 @@ void Foam::autoHexMeshDriver::setNumLayers
         }
     }
 
-    reduce(nConflicts, sumOp<label>());
-
-    Info<< "Set displacement to zero for " << nConflicts
-        << " points due to points being on multiple regions"
-        << " with inconsistent nLayers specification." << endl;
+    //reduce(nConflicts, sumOp<label>());
+    //
+    //Info<< "Set displacement to zero for " << nConflicts
+    //    << " points due to points being on multiple regions"
+    //    << " with inconsistent nLayers specification." << endl;
 }
 
 
 // Grow no-extrusion layer
-void Foam::autoHexMeshDriver::growNoExtrusion
+void Foam::autoLayerDriver::growNoExtrusion
 (
     const indirectPrimitivePatch& pp,
     pointField& patchDisp,
@@ -1369,60 +1389,139 @@ void Foam::autoHexMeshDriver::growNoExtrusion
 }
 
 
-
-// Calculate pointwise wanted and minimum thickness.
-// thickness: wanted thickness per point
-void Foam::autoHexMeshDriver::calculateLayerThickness
+void Foam::autoLayerDriver::calculateLayerThickness
 (
-    const scalar expansionRatio,
-    const scalar finalLayerRatio,
-    const scalar relMinThickness,
     const indirectPrimitivePatch& pp,
+    const labelList& patchIDs,
+    const scalarField& patchExpansionRatio,
+    const scalarField& patchFinalLayerRatio,
+    const scalarField& patchRelMinThickness,
     const labelList& cellLevel,
     const labelList& patchNLayers,
     const scalar edge0Len,
+
     scalarField& thickness,
-    scalarField& minThickness
+    scalarField& minThickness,
+    scalarField& expansionRatio
 ) const
 {
-    if (relMinThickness < 0 || relMinThickness > 2)
+    const fvMesh& mesh = meshRefiner_.mesh();
+    const polyBoundaryMesh& patches = mesh.boundaryMesh();
+
+    if (min(patchRelMinThickness) < 0 || max(patchRelMinThickness) > 2)
     {
         FatalErrorIn("calculateLayerThickness(..)")
             << "Thickness should be factor of local undistorted cell size."
             << " Valid values are [0..2]." << nl
-            << " minThickness:" << relMinThickness
+            << " minThickness:" << patchRelMinThickness
             << exit(FatalError);
     }
 
-    thickness.setSize(pp.nPoints());
-    minThickness.setSize(pp.nPoints());
 
     // Per point the max cell level of connected cells
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
     labelList maxPointLevel(pp.nPoints(), labelMin);
 
-    forAll(pp, i)
     {
-        label ownLevel = cellLevel[mesh_.faceOwner()[pp.addressing()[i]]];
+        forAll(pp, i)
+        {
+            label ownLevel = cellLevel[mesh.faceOwner()[pp.addressing()[i]]];
 
-        const face& f = pp.localFaces()[i];
+            const face& f = pp.localFaces()[i];
 
-        forAll(f, fp)
+            forAll(f, fp)
+            {
+                maxPointLevel[f[fp]] = max(maxPointLevel[f[fp]], ownLevel);
+            }
+        }
+
+        syncTools::syncPointList
+        (
+            mesh,
+            pp.meshPoints(),
+            maxPointLevel,
+            maxEqOp<label>(),
+            labelMin,           // null value
+            false               // no separation
+        );
+    }
+
+
+    // Rework patch-wise layer parameters into minimum per point
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    expansionRatio.setSize(pp.nPoints());
+    expansionRatio = GREAT;
+    scalarField finalLayerRatio(pp.nPoints(), GREAT);
+    scalarField relMinThickness(pp.nPoints(), GREAT);
+
+    {
+        forAll(patchIDs, i)
         {
-            maxPointLevel[f[fp]] = max(maxPointLevel[f[fp]], ownLevel);
+            label patchI = patchIDs[i];
+
+            const labelList& meshPoints = patches[patchI].meshPoints();
+
+            forAll(meshPoints, patchPointI)
+            {
+                label ppPointI = pp.meshPointMap()[meshPoints[patchPointI]];
+
+                expansionRatio[ppPointI] = min
+                (
+                    expansionRatio[ppPointI],
+                    patchExpansionRatio[patchI]
+                );
+                finalLayerRatio[ppPointI] = min
+                (
+                    finalLayerRatio[ppPointI],
+                    patchFinalLayerRatio[patchI]
+                );
+                relMinThickness[ppPointI] = min
+                (
+                    relMinThickness[ppPointI],
+                    patchRelMinThickness[patchI]
+                );
+            }
         }
+
+        syncTools::syncPointList
+        (
+            mesh,
+            pp.meshPoints(),
+            expansionRatio,
+            minEqOp<scalar>(),
+            GREAT,              // null value
+            false               // no separation
+        );
+        syncTools::syncPointList
+        (
+            mesh,
+            pp.meshPoints(),
+            finalLayerRatio,
+            minEqOp<scalar>(),
+            GREAT,              // null value
+            false               // no separation
+        );
+        syncTools::syncPointList
+        (
+            mesh,
+            pp.meshPoints(),
+            relMinThickness,
+            minEqOp<scalar>(),
+            GREAT,              // null value
+            false               // no separation
+        );
     }
 
-    syncTools::syncPointList
-    (
-        mesh_,
-        pp.meshPoints(),
-        maxPointLevel,
-        maxEqOp<label>(),
-        labelMin,           // null value
-        false               // no separation
-    );
 
 
+    // Per mesh point the expansion parameters
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    thickness.setSize(pp.nPoints());
+    minThickness.setSize(pp.nPoints());
+
     forAll(maxPointLevel, pointI)
     {
         // Find undistorted edge size for this level.
@@ -1430,19 +1529,24 @@ void Foam::autoHexMeshDriver::calculateLayerThickness
 
         // Calculate layer thickness based on expansion ratio
         // and final layer height
-        if (expansionRatio == 1)
+        if (expansionRatio[pointI] == 1)
         {
-            thickness[pointI] = finalLayerRatio*patchNLayers[pointI]*edgeLen;
-            minThickness[pointI] = relMinThickness*edgeLen;
+            thickness[pointI] =
+                finalLayerRatio[pointI]
+              * patchNLayers[pointI]
+              * edgeLen;
+            minThickness[pointI] = relMinThickness[pointI]*edgeLen;
         }
         else
         {
-            scalar invExpansion = 1.0 / expansionRatio;
+            scalar invExpansion = 1.0 / expansionRatio[pointI];
             label nLay = patchNLayers[pointI];
-            thickness[pointI] = finalLayerRatio*edgeLen
-                * (1.0 - pow(invExpansion, nLay))
-                / (1.0 - invExpansion);
-            minThickness[pointI] = relMinThickness*edgeLen;
+            thickness[pointI] =
+                finalLayerRatio[pointI]
+              * edgeLen
+              * (1.0 - pow(invExpansion, nLay))
+              / (1.0 - invExpansion);
+            minThickness[pointI] = relMinThickness[pointI]*edgeLen;
         }
     }
 
@@ -1452,7 +1556,7 @@ void Foam::autoHexMeshDriver::calculateLayerThickness
 
 
 // Synchronize displacement among coupled patches.
-void Foam::autoHexMeshDriver::syncPatchDisplacement
+void Foam::autoLayerDriver::syncPatchDisplacement
 (
     const motionSmoother& meshMover,
     const scalarField& minThickness,
@@ -1461,6 +1565,7 @@ void Foam::autoHexMeshDriver::syncPatchDisplacement
     List<extrudeMode>& extrudeStatus
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
     const labelList& meshPoints = meshMover.patch().meshPoints();
 
     label nChangedTotal = 0;
@@ -1472,7 +1577,7 @@ void Foam::autoHexMeshDriver::syncPatchDisplacement
         // Sync displacement (by taking min)
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             patchDisp,
             minEqOp<vector>(),
@@ -1505,7 +1610,7 @@ void Foam::autoHexMeshDriver::syncPatchDisplacement
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             syncPatchNLayers,
             minEqOp<label>(),
@@ -1536,7 +1641,7 @@ void Foam::autoHexMeshDriver::syncPatchDisplacement
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             syncPatchNLayers,
             maxEqOp<label>(),
@@ -1583,7 +1688,7 @@ void Foam::autoHexMeshDriver::syncPatchDisplacement
 // of the faces using it.
 // extrudeStatus is both input and output and gives the status of each
 // patch point.
-void Foam::autoHexMeshDriver::getPatchDisplacement
+void Foam::autoLayerDriver::getPatchDisplacement
 (
     const motionSmoother& meshMover,
     const scalarField& thickness,
@@ -1596,6 +1701,7 @@ void Foam::autoHexMeshDriver::getPatchDisplacement
     Info<< nl << "Determining displacement for added points"
         << " according to pointNormal ..." << endl;
 
+    const fvMesh& mesh = meshRefiner_.mesh();
     const indirectPrimitivePatch& pp = meshMover.patch();
     const vectorField& faceNormals = pp.faceNormals();
     const labelListList& pointFaces = pp.pointFaces();
@@ -1622,7 +1728,7 @@ void Foam::autoHexMeshDriver::getPatchDisplacement
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             pointNormals,
             plusEqOp<vector>(),
@@ -1632,7 +1738,7 @@ void Foam::autoHexMeshDriver::getPatchDisplacement
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             nPointFaces,
             plusEqOp<label>(),
@@ -1735,7 +1841,7 @@ void Foam::autoHexMeshDriver::getPatchDisplacement
 // Truncates displacement
 // - for all patchFaces in the faceset displacement gets set to zero
 // - all displacement < minThickness gets set to zero
-Foam::label Foam::autoHexMeshDriver::truncateDisplacement
+Foam::label Foam::autoLayerDriver::truncateDisplacement
 (
     const motionSmoother& meshMover,
     const scalarField& minThickness,
@@ -1939,7 +2045,7 @@ Foam::label Foam::autoHexMeshDriver::truncateDisplacement
 
 // Setup layer information (at points and faces) to modify mesh topology in
 // regions where layer mesh terminates.
-void Foam::autoHexMeshDriver::setupLayerInfoTruncation
+void Foam::autoLayerDriver::setupLayerInfoTruncation
 (
     const motionSmoother& meshMover,
     const labelList& patchNLayers,
@@ -2137,7 +2243,7 @@ void Foam::autoHexMeshDriver::setupLayerInfoTruncation
 
 
 // Does any of the cells use a face from faces?
-bool Foam::autoHexMeshDriver::cellsUseFace
+bool Foam::autoLayerDriver::cellsUseFace
 (
     const polyMesh& mesh,
     const labelList& cellLabels,
@@ -2163,7 +2269,7 @@ bool Foam::autoHexMeshDriver::cellsUseFace
 // Checks the newly added cells and locally unmarks points so they
 // will not get extruded next time round. Returns global number of unmarked
 // points (0 if all was fine)
-Foam::label Foam::autoHexMeshDriver::checkAndUnmark
+Foam::label Foam::autoLayerDriver::checkAndUnmark
 (
     const addPatchCellLayer& addLayer,
     const dictionary& motionDict,
@@ -2230,7 +2336,7 @@ Foam::label Foam::autoHexMeshDriver::checkAndUnmark
 
 
 //- Count global number of extruded faces
-Foam::label Foam::autoHexMeshDriver::countExtrusion
+Foam::label Foam::autoLayerDriver::countExtrusion
 (
     const indirectPrimitivePatch& pp,
     const List<extrudeMode>& extrudeStatus
@@ -2261,7 +2367,7 @@ Foam::label Foam::autoHexMeshDriver::countExtrusion
 
 
 // Collect layer faces and layer cells into bools for ease of handling
-void Foam::autoHexMeshDriver::getLayerCellsFaces
+void Foam::autoLayerDriver::getLayerCellsFaces
 (
     const polyMesh& mesh,
     const addPatchCellLayer& addLayer,
@@ -2305,35 +2411,51 @@ void Foam::autoHexMeshDriver::getLayerCellsFaces
 }
 
 
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::autoLayerDriver::autoLayerDriver
+(
+    meshRefinement& meshRefiner,
+    const labelList& globalToPatch
+)
+:
+    meshRefiner_(meshRefiner),
+    globalToPatch_(globalToPatch)
+{}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-void Foam::autoHexMeshDriver::mergePatchFacesUndo
+void Foam::autoLayerDriver::mergePatchFacesUndo
 (
-    const dictionary& shrinkDict,
+    const layerParameters& layerParams,
     const dictionary& motionDict
 )
 {
-    const scalar featureAngle(readScalar(shrinkDict.lookup("featureAngle")));
-    scalar minCos = Foam::cos(featureAngle*mathematicalConstant::pi/180.0);
-
-    scalar concaveAngle = defaultConcaveAngle;
+    scalar minCos = Foam::cos
+    (
+        layerParams.featureAngle()
+      * mathematicalConstant::pi/180.0
+    );
 
-    if (shrinkDict.found("concaveAngle"))
-    {
-        concaveAngle = readScalar(shrinkDict.lookup("concaveAngle"));
-    }
-    scalar concaveCos = Foam::cos(concaveAngle*mathematicalConstant::pi/180.0);
+    scalar concaveCos = Foam::cos
+    (
+        layerParams.concaveAngle()
+      * mathematicalConstant::pi/180.0
+    );
 
     Info<< nl
         << "Merging all faces of a cell" << nl
         << "---------------------------" << nl
         << "    - which are on the same patch" << nl
-        << "    - which make an angle < " << featureAngle << " degrees"
+        << "    - which make an angle < " << layerParams.featureAngle()
+        << " degrees"
         << nl
         << "      (cos:" << minCos << ')' << nl
         << "    - as long as the resulting face doesn't become concave"
         << " by more than "
-        << concaveAngle << " degrees (0=straight, 180=fully concave)" << nl
+        << layerParams.concaveAngle()
+        << " degrees (0=straight, 180=fully concave)" << nl
         << endl;
 
     label nChanged = mergePatchFacesUndo(minCos, concaveCos, motionDict);
@@ -2342,82 +2464,23 @@ void Foam::autoHexMeshDriver::mergePatchFacesUndo
 }
 
 
-void Foam::autoHexMeshDriver::addLayers
+void Foam::autoLayerDriver::addLayers
 (
-    const dictionary& shrinkDict,
+    const layerParameters& layerParams,
     const dictionary& motionDict,
     const label nAllowableErrors,
-    motionSmoother& meshMover
+    motionSmoother& meshMover,
+    decompositionMethod& decomposer,
+    fvMeshDistribute& distributor
 )
 {
-    // Read some more dictionary settings
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    // Min thickness per cell
-    const scalar relMinThickness(readScalar(shrinkDict.lookup("minThickness")));
-
-    // Warped faces recoginition
-    const scalar maxFaceThicknessRatio
-    (
-        readScalar(shrinkDict.lookup("maxFaceThicknessRatio"))
-    );
-    const scalar featureAngle(readScalar(shrinkDict.lookup("featureAngle")));
-
-    // Feature angle
-    const scalar minCos = Foam::cos(featureAngle*mathematicalConstant::pi/180.);
-
-    // Number of layers for to grow non-extrusion region by
-    const label nGrow(readLabel(shrinkDict.lookup("nGrow")));
-
-    //const label nSmoothDisp(readLabel(shrinkDict.lookup("nSmoothDispl")));
-    // Snapping iterations
-    const label nSnap(readLabel(shrinkDict.lookup("nSnap")));
-
-    // Mesh termination and medial axis smoothing quantities
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    const scalar minCosLayerTermination =
-        Foam::cos(0.5*featureAngle*mathematicalConstant::pi/180.);
-    const scalar expansionRatio
-    (
-        readScalar(shrinkDict.lookup("expansionRatio"))
-    );
-    const scalar finalLayerRatio
-    (
-        readScalar(shrinkDict.lookup("finalLayerRatio"))
-    );
-    const label nBufferCellsNoExtrude
-    (
-        readLabel(shrinkDict.lookup("nBufferCellsNoExtrude"))
-    );
-    const label nSmoothSurfaceNormals
-    (
-        readLabel(shrinkDict.lookup("nSmoothSurfaceNormals"))
-    );
-    const label nSmoothNormals(readLabel(shrinkDict.lookup("nSmoothNormals")));
-    const label nSmoothThickness
-    (
-        readLabel(shrinkDict.lookup("nSmoothThickness"))
-    );
-    const scalar maxThicknessToMedialRatio
-    (
-        readScalar(shrinkDict.lookup("maxThicknessToMedialRatio"))
-    );
-
-    // Medial axis setup
-
-    const scalar minMedianAxisAngle
-    (
-        readScalar(shrinkDict.lookup("minMedianAxisAngle"))
-    );
-    const scalar minMedianAxisAngleCos =
-        Foam::cos(minMedianAxisAngle*mathematicalConstant::pi/180.);
-
-
-    // Precalculate mesh edge labels for patch edges
+    fvMesh& mesh = meshRefiner_.mesh();
     const indirectPrimitivePatch& pp = meshMover.patch();
     const labelList& meshPoints = pp.meshPoints();
 
+    // Precalculate mesh edge labels for patch edges
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
     labelList meshEdges(pp.nEdges());
     forAll(pp.edges(), edgeI)
     {
@@ -2426,8 +2489,8 @@ void Foam::autoHexMeshDriver::addLayers
         label v1 = meshPoints[ppEdge[1]];
         meshEdges[edgeI] = meshTools::findEdge
         (
-            mesh_.edges(),
-            mesh_.pointEdges()[v0],
+            mesh.edges(),
+            mesh.pointEdges()[v0],
             v0,
             v1
         );
@@ -2449,8 +2512,9 @@ void Foam::autoHexMeshDriver::addLayers
 
     setNumLayers
     (
-        meshMover.adaptPatchIDs(),
-        pp,
+        layerParams.numLayers(),    // per patch the num layers
+        meshMover.adaptPatchIDs(),  // patches that are being moved
+        pp,                         // indirectpatch for all faces moving
 
         patchDisp,
         patchNLayers,
@@ -2477,7 +2541,7 @@ void Foam::autoHexMeshDriver::addLayers
     (
         pp,
         meshEdges,
-        minCos,
+        layerParams.featureAngle()*mathematicalConstant::pi/180.0,
 
         patchDisp,
         patchNLayers,
@@ -2488,13 +2552,13 @@ void Foam::autoHexMeshDriver::addLayers
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     // Undistorted edge length
-    const scalar edge0Len = meshRefinerPtr_().meshCutter().level0EdgeLength();
-    const labelList& cellLevel = meshRefinerPtr_().meshCutter().cellLevel();
+    const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength();
+    const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel();
 
     handleWarpedFaces
     (
         pp,
-        maxFaceThicknessRatio,
+        layerParams.maxFaceThicknessRatio(),
         edge0Len,
         cellLevel,
 
@@ -2517,7 +2581,7 @@ void Foam::autoHexMeshDriver::addLayers
 
 
     // Grow out region of non-extrusion
-    for (label i = 0; i < nGrow; i++)
+    for (label i = 0; i < layerParams.nGrow(); i++)
     {
         growNoExtrusion
         (
@@ -2528,21 +2592,24 @@ void Foam::autoHexMeshDriver::addLayers
         );
     }
 
-    // Determine point-wise layer thickness
+    // Determine (wanted) point-wise layer thickness and expansion ratio
     scalarField thickness(pp.nPoints());
     scalarField minThickness(pp.nPoints());
+    scalarField expansionRatio(pp.nPoints());
     calculateLayerThickness
     (
-        expansionRatio,
-        finalLayerRatio,
-        relMinThickness,
         pp,
+        meshMover.adaptPatchIDs(),
+        layerParams.expansionRatio(),
+        layerParams.finalLayerRatio(),
+        layerParams.minThickness(),
         cellLevel,
         patchNLayers,
         edge0Len,
 
         thickness,
-        minThickness
+        minThickness,
+        expansionRatio
     );
 
     // Calculate wall to medial axis distance for smoothing displacement
@@ -2553,8 +2620,8 @@ void Foam::autoHexMeshDriver::addLayers
         IOobject
         (
             "pointMedialDist",
-            mesh_.time().timeName(),
-            mesh_,
+            mesh.time().timeName(),
+            mesh,
             IOobject::NO_READ,
             IOobject::NO_WRITE,
             false
@@ -2568,8 +2635,8 @@ void Foam::autoHexMeshDriver::addLayers
         IOobject
         (
             "dispVec",
-            mesh_.time().timeName(),
-            mesh_,
+            mesh.time().timeName(),
+            mesh,
             IOobject::NO_READ,
             IOobject::NO_WRITE,
             false
@@ -2583,8 +2650,8 @@ void Foam::autoHexMeshDriver::addLayers
         IOobject
         (
             "medialRatio",
-            mesh_.time().timeName(),
-            mesh_,
+            mesh.time().timeName(),
+            mesh,
             IOobject::NO_READ,
             IOobject::NO_WRITE,
             false
@@ -2602,9 +2669,9 @@ void Foam::autoHexMeshDriver::addLayers
     medialAxisSmoothingInfo
     (
         meshMover,
-        nSmoothNormals,
-        nSmoothSurfaceNormals,
-        minMedianAxisAngleCos,
+        layerParams.nSmoothNormals(),
+        layerParams.nSmoothSurfaceNormals(),
+        layerParams.minMedianAxisAngleCos(),
 
         dispVec,
         medialRatio,
@@ -2614,10 +2681,10 @@ void Foam::autoHexMeshDriver::addLayers
 
 
     // Saved old points
-    pointField oldPoints(mesh_.points());
+    pointField oldPoints(mesh.points());
 
     // Last set of topology changes. (changing mesh clears out polyTopoChange)
-    polyTopoChange savedMeshMod(mesh_.boundaryMesh().size());
+    polyTopoChange savedMeshMod(mesh.boundaryMesh().size());
 
     boolList flaggedCells;
     boolList flaggedFaces;
@@ -2657,8 +2724,8 @@ void Foam::autoHexMeshDriver::addLayers
             //    debug,
             //    meshMover,
             //    -patchDisp,     // Shrink in opposite direction of addedPoints
-            //    nSmoothDisp,
-            //    nSnap
+            //    layerParams.nSmoothDisp(),
+            //    layerParams.nSnap()
             //);
 
             // Medial axis based shrinking
@@ -2666,11 +2733,11 @@ void Foam::autoHexMeshDriver::addLayers
             (
                 meshMover,
 
-                nSmoothThickness,
-                maxThicknessToMedialRatio,
+                layerParams.nSmoothThickness(),
+                layerParams.maxThicknessToMedialRatio(),
                 nAllowableErrors,
-                nSnap,
-                minCosLayerTermination,
+                layerParams.nSnap(),
+                layerParams.layerTerminationCos(),
 
                 thickness,
                 minThickness,
@@ -2690,7 +2757,7 @@ void Foam::autoHexMeshDriver::addLayers
 
         // Truncate displacements that are too small (this will do internal
         // ones, coupled ones have already been truncated by syncPatch)
-        faceSet dummySet(mesh_, "wrongPatchFaces", 0);
+        faceSet dummySet(mesh, "wrongPatchFaces", 0);
         truncateDisplacement
         (
             meshMover,
@@ -2703,27 +2770,27 @@ void Foam::autoHexMeshDriver::addLayers
 
 
         // Dump to .obj file for debugging.
-        if (debug_)
+        if (debug)
         {
             dumpDisplacement
             (
-                mesh_.time().path()/"layer",
+                mesh.time().path()/"layer",
                 pp,
                 patchDisp,
                 extrudeStatus
             );
 
-            const_cast<Time&>(mesh_.time())++;
-            Info<< "Writing shrunk mesh to " << mesh_.time().timeName() << endl;
-            mesh_.write();
+            const_cast<Time&>(mesh.time())++;
+            Info<< "Writing shrunk mesh to " << mesh.time().timeName() << endl;
+            mesh.write();
         }
 
 
         // Mesh topo change engine
-        polyTopoChange meshMod(mesh_);
+        polyTopoChange meshMod(mesh);
 
         // Grow layer of cells on to patch. Handles zero sized displacement.
-        addPatchCellLayer addLayer(mesh_);
+        addPatchCellLayer addLayer(mesh);
 
         // Determine per point/per face number of layers to extrude. Also
         // handles the slow termination of layers when going switching layers
@@ -2735,7 +2802,7 @@ void Foam::autoHexMeshDriver::addLayers
             meshMover,
             patchNLayers,
             extrudeStatus,
-            nBufferCellsNoExtrude,
+            layerParams.nBufferCellsNoExtrude(),
             nPatchPointLayers,
             nPatchFaceLayers
         );
@@ -2747,7 +2814,7 @@ void Foam::autoHexMeshDriver::addLayers
         {
             if (patchNLayers[i] > 0)
             {
-                if (expansionRatio == 1.0)
+                if (expansionRatio[i] == 1.0)
                 {
                     firstDisp[i] = patchDisp[i]/nPatchPointLayers[i];
                 }
@@ -2755,15 +2822,15 @@ void Foam::autoHexMeshDriver::addLayers
                 {
                     label nLay = nPatchPointLayers[i];
                     scalar h =
-                        pow(expansionRatio,nLay - 1)
-                      * (mag(patchDisp[i])*(1.0 - expansionRatio))
-                      / (1.0 - pow(expansionRatio, nLay));
+                        pow(expansionRatio[i], nLay - 1)
+                      * (mag(patchDisp[i])*(1.0 - expansionRatio[i]))
+                      / (1.0 - pow(expansionRatio[i], nLay));
                     firstDisp[i] = h/mag(patchDisp[i])*patchDisp[i];
                 }
             }
         }
 
-        scalar invExpansionRatio = 1.0 / expansionRatio;
+        scalarField invExpansionRatio = 1.0 / expansionRatio;
 
         // Add topo regardless of whether extrudeStatus is extruderemove.
         // Not add layer if patchDisp is zero.
@@ -2777,9 +2844,9 @@ void Foam::autoHexMeshDriver::addLayers
             meshMod
         );
 
-        if (debug_)
+        if (debug)
         {
-            const_cast<Time&>(mesh_.time())++;
+            const_cast<Time&>(mesh.time())++;
         }
 
         // Store mesh changes for if mesh is correct.
@@ -2796,13 +2863,13 @@ void Foam::autoHexMeshDriver::addLayers
             IOobject
             (
                 //mesh.name()+"_layer",
-                mesh_.name(),
-                static_cast<polyMesh&>(mesh_).instance(),
-                mesh_.db(),
-                static_cast<polyMesh&>(mesh_).readOpt(),
-                static_cast<polyMesh&>(mesh_).writeOpt()
+                mesh.name(),
+                static_cast<polyMesh&>(mesh).instance(),
+                mesh.db(),
+                static_cast<polyMesh&>(mesh).readOpt(),
+                static_cast<polyMesh&>(mesh).writeOpt()
             ),          // io params from original mesh but new name
-            mesh_,      // original mesh
+            mesh,      // original mesh
             true        // parallel sync
         );
         fvMesh& newMesh = newMeshPtr();
@@ -2830,9 +2897,9 @@ void Foam::autoHexMeshDriver::addLayers
         );
 
 
-        if (debug_)
+        if (debug)
         {
-            Info<< "Writing layer mesh to " << mesh_.time().timeName() << endl;
+            Info<< "Writing layer mesh to " << mesh.time().timeName() << endl;
             newMesh.write();
             cellSet addedCellSet
             (
@@ -2843,8 +2910,7 @@ void Foam::autoHexMeshDriver::addLayers
             Info<< "Writing "
                 << returnReduce(addedCellSet.size(), sumOp<label>())
                 << " added cells to cellSet "
-                << addedCellSet.objectPath()
-                << endl;
+                << addedCellSet.name() << endl;
             addedCellSet.write();
 
             faceSet layerFacesSet
@@ -2856,8 +2922,7 @@ void Foam::autoHexMeshDriver::addLayers
             Info<< "Writing "
                 << returnReduce(layerFacesSet.size(), sumOp<label>())
                 << " faces inside added layer to faceSet "
-                << layerFacesSet.objectPath()
-                << endl;
+                << layerFacesSet.name() << endl;
             layerFacesSet.write();
         }
 
@@ -2895,18 +2960,18 @@ void Foam::autoHexMeshDriver::addLayers
     // current mesh.
 
     // Apply the stored topo changes to the current mesh.
-    autoPtr<mapPolyMesh> map = savedMeshMod.changeMesh(mesh_, false);
+    autoPtr<mapPolyMesh> map = savedMeshMod.changeMesh(mesh, false);
 
     // Update fields
-    mesh_.updateMesh(map);
+    mesh.updateMesh(map);
 
     // Move mesh (since morphing does not do this)
     if (map().hasMotionPoints())
     {
-        mesh_.movePoints(map().preMotionPoints());
+        mesh.movePoints(map().preMotionPoints());
     }
 
-    meshRefinerPtr_().updateMesh(map, labelList(0));
+    meshRefiner_.updateMesh(map, labelList(0));
 
 
     // Do final balancing
@@ -2914,8 +2979,24 @@ void Foam::autoHexMeshDriver::addLayers
 
     if (Pstream::parRun())
     {
+        Info<< nl
+            << "Doing final balancing" << nl
+            << "---------------------" << nl
+            << endl;
+
+        if (debug)
+        {
+            const_cast<Time&>(mesh.time())++;
+        }
+
         // Balance. No restriction on face zones and baffles.
-        autoPtr<mapDistributePolyMesh> map = balance(false, false);
+        autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance
+        (
+            false,
+            false,
+            decomposer,
+            distributor
+        );
 
         // Re-distribute flag of layer faces and cells
         map().distributeCellData(flaggedCells);
@@ -2926,23 +3007,122 @@ void Foam::autoHexMeshDriver::addLayers
     // Write mesh
     // ~~~~~~~~~~
 
-    //writeMesh("Layer mesh");
-    cellSet addedCellSet(mesh_, "addedCells", findIndices(flaggedCells, true));
+    cellSet addedCellSet(mesh, "addedCells", findIndices(flaggedCells, true));
     Info<< "Writing "
         << returnReduce(addedCellSet.size(), sumOp<label>())
         << " added cells to cellSet "
-        << addedCellSet.objectPath()
-        << endl;
+        << addedCellSet.name() << endl;
     addedCellSet.write();
 
-    faceSet layerFacesSet(mesh_, "layerFaces", findIndices(flaggedFaces, true));
+    faceSet layerFacesSet(mesh, "layerFaces", findIndices(flaggedFaces, true));
     Info<< "Writing "
         << returnReduce(layerFacesSet.size(), sumOp<label>())
         << " faces inside added layer to faceSet "
-        << layerFacesSet.objectPath()
-        << endl;
+        << layerFacesSet.name() << endl;
     layerFacesSet.write();
 }
 
 
+void Foam::autoLayerDriver::doLayers
+(
+    const dictionary& shrinkDict,
+    const dictionary& motionDict,
+    const layerParameters& layerParams,
+    decompositionMethod& decomposer,
+    fvMeshDistribute& distributor
+)
+{
+    fvMesh& mesh = meshRefiner_.mesh();
+
+    Info<< nl
+        << "Shrinking and layer addition phase" << nl
+        << "----------------------------------" << nl
+        << endl;
+
+    const_cast<Time&>(mesh.time())++;
+
+    Info<< "Using mesh parameters " << motionDict << nl << endl;
+
+    // Merge coplanar boundary faces
+    mergePatchFacesUndo(layerParams, motionDict);
+
+    // Per patch the number of layers (0 if no layer)
+    const labelList& numLayers = layerParams.numLayers();
+
+    // Patches that need to get a layer
+    DynamicList<label> patchIDs(numLayers.size());
+    label nFacesWithLayers = 0;
+    forAll(numLayers, patchI)
+    {
+        if (numLayers[patchI] > 0)
+        {
+            patchIDs.append(patchI);
+            nFacesWithLayers += mesh.boundaryMesh()[patchI].size();
+        }
+    }
+    patchIDs.shrink();
+
+    if (returnReduce(nFacesWithLayers, sumOp<label>()) == 0)
+    {
+        Info<< nl << "No layers to generate ..." << endl;
+    }
+    else
+    {
+        autoPtr<indirectPrimitivePatch> ppPtr
+        (
+            meshRefinement::makePatch
+            (
+                mesh,
+                patchIDs
+            )
+        );
+        indirectPrimitivePatch& pp = ppPtr();
+
+        // Construct iterative mesh mover.
+        Info<< "Constructing mesh displacer ..." << endl;
+
+        {
+            pointMesh pMesh(mesh);
+
+            motionSmoother meshMover
+            (
+                mesh,
+                pp,
+                patchIDs,
+                meshRefinement::makeDisplacementField(pMesh, patchIDs),
+                motionDict
+            );
+
+            // Check that outside of mesh is not multiply connected.
+            checkMeshManifold();
+
+            // Check initial mesh
+            Info<< "Checking initial mesh ..." << endl;
+            labelHashSet wrongFaces(mesh.nFaces()/100);
+            motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces);
+            const label nInitErrors = returnReduce
+            (
+                wrongFaces.size(),
+                sumOp<label>()
+            );
+
+            Info<< "Detected " << nInitErrors << " illegal faces"
+                << " (concave, zero area or negative cell pyramid volume)"
+                << endl;
+
+            // Do all topo changes
+            addLayers
+            (
+                layerParams,
+                motionDict,
+                nInitErrors,
+                meshMover,
+                decomposer,
+                distributor
+            );
+        }
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..af768a64aa42ad05d67d63a46c12a8605594282f
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H
@@ -0,0 +1,562 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::autoLayerDriver
+
+Description
+    All to do with adding layers
+
+SourceFiles
+    autoLayerDriver.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef autoLayerDriver_H
+#define autoLayerDriver_H
+
+#include "meshRefinement.H"
+#include "wallPoint.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class removePoints;
+class pointSet;
+class motionSmoother;
+class addPatchCellLayer;
+class pointData;
+class wallPoint;
+class faceSet;
+class layerParameters;
+
+/*---------------------------------------------------------------------------*\
+                           Class autoLayerDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class autoLayerDriver
+{
+    // Static data members
+
+        //- Extrusion controls
+        enum extrudeMode
+        {
+            NOEXTRUDE,      /*!< Do not extrude. No layers added. */
+            EXTRUDE,        /*!< Extrude */
+            EXTRUDEREMOVE   /*!< Extrude but afterwards remove added */
+                            /*!< faces locally */
+        };
+
+
+    // Private classes
+
+        //- Combine operator class to combine normal with other normal.
+        class nomalsCombine
+        {
+        public:
+
+            void operator()(vector& x, const vector& y) const
+            {
+                if (y != wallPoint::greatPoint)
+                {
+                    if (x == wallPoint::greatPoint)
+                    {
+                        x = y;
+                    }
+                    else
+                    {
+                        x *= (x&y);
+                    }
+                }
+            }
+        };
+
+
+    // Private data
+
+        //- Mesh+surface
+        meshRefinement& meshRefiner_;
+
+        //- From surface region to patch
+        const labelList globalToPatch_;
+
+
+
+    // Private Member Functions
+
+
+        // Face merging
+
+            //- Merge patch faces. Undo until no checkMesh errors.
+            label mergePatchFacesUndo
+            (
+                const scalar minCos,
+                const scalar concaveCos,
+                const dictionary&
+            );
+
+            //- Remove points.
+            autoPtr<mapPolyMesh> doRemovePoints
+            (
+                removePoints& pointRemover,
+                const boolList& pointCanBeDeleted
+            );
+
+            //- Restore faces (which contain removed points)
+            autoPtr<mapPolyMesh> doRestorePoints
+            (
+                removePoints& pointRemover,
+                const labelList& facesToRestore
+            );
+
+            //- Return candidateFaces that are also in set.
+            labelList collectFaces
+            (
+                const labelList& candidateFaces,
+                const labelHashSet& set
+            ) const;
+
+            //- Pick up faces of cells of faces in set.
+            labelList growFaceCellFace(const labelHashSet&) const;
+
+            //- Remove points not used by any face or points used by only
+            //  two faces where the edges are in line
+            label mergeEdgesUndo(const scalar minCos, const dictionary&);
+
+
+        // Layers
+
+            //- For debugging: Dump displacement to .obj files
+            static void dumpDisplacement
+            (
+                const fileName&,
+                const indirectPrimitivePatch&,
+                const vectorField&,
+                const List<extrudeMode>&
+            );
+
+            //- Check that primitivePatch is not multiply connected.
+            //  Collect non-manifold points in pointSet.
+            static void checkManifold
+            (
+                const indirectPrimitivePatch&,
+                pointSet& nonManifoldPoints
+            );
+
+            //- Check that mesh outside is not multiply connected.
+            void checkMeshManifold() const;
+
+
+            // Static extrusion setup
+
+                //- Unset extrusion on point. Returns true if anything unset.
+                static bool unmarkExtrusion
+                (
+                    const label patchPointI,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                );
+
+                //- Unset extrusion on face. Returns true if anything unset.
+                static bool unmarkExtrusion
+                (
+                    const face& localFace,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                );
+
+                //- No extrusion at non-manifold points.
+                void handleNonManifolds
+                (
+                    const indirectPrimitivePatch& pp,
+                    const labelList& meshEdges,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- No extrusion on feature edges. Assumes non-manifold
+                //  edges already handled.
+                void handleFeatureAngle
+                (
+                    const indirectPrimitivePatch& pp,
+                    const labelList& meshEdges,
+                    const scalar minCos,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- No extrusion on warped faces
+                void handleWarpedFaces
+                (
+                    const indirectPrimitivePatch& pp,
+                    const scalar faceRatio,
+                    const scalar edge0Len,
+                    const labelList& cellLevel,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- Determine the number of layers per point from the number of
+                //  layers per surface.
+                void setNumLayers
+                (
+                    const labelList& patchToNLayers,
+                    const labelList& patchIDs,
+                    const indirectPrimitivePatch& pp,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- Grow no-extrusion layer.
+                static void growNoExtrusion
+                (
+                    const indirectPrimitivePatch& pp,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                );
+
+                //- Calculate pointwise wanted and minimum thickness.
+                //  thickness: wanted thickness
+                //  minthickness: when to give up and not extrude
+                //  Gets per patch parameters and determine pp pointwise
+                //  parameters.
+                void calculateLayerThickness
+                (
+                    const indirectPrimitivePatch& pp,
+                    const labelList& patchIDs,
+                    const scalarField& patchExpansionRatio,
+                    const scalarField& patchFinalLayerRatio,
+                    const scalarField& patchRelMinThickness,
+                    const labelList& cellLevel,
+                    const labelList& patchNLayers,
+                    const scalar edge0Len,
+
+                    scalarField& thickness,
+                    scalarField& minThickness,
+                    scalarField& expansionRatio
+                ) const;
+
+
+            // Extrusion execution
+
+                //- Synchronize displacement among coupled patches.
+                void syncPatchDisplacement
+                (
+                    const motionSmoother& meshMover,
+                    const scalarField& minThickness,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- Get nearest point on surface to snap to
+                void getPatchDisplacement
+                (
+                    const motionSmoother& meshMover,
+                    const scalarField& thickness,
+                    const scalarField& minThickness,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- Truncates displacement
+                // - for all patchFaces in the faceset displacement gets set
+                //   to zero
+                // - all displacement < minThickness gets set to zero
+                label truncateDisplacement
+                (
+                    const motionSmoother& meshMover,
+                    const scalarField& minThickness,
+                    const faceSet& illegalPatchFaces,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                ) const;
+
+                //- Setup layer information (at points and faces) to
+                //  modify mesh topology in
+                //  regions where layer mesh terminates. Guarantees an
+                //  optional slow decreasing of the number of layers.
+                //  Returns the number of layers per face and per point
+                //  to go into the actual layer addition engine.
+                void setupLayerInfoTruncation
+                (
+                    const motionSmoother& meshMover,
+                    const labelList& patchNLayers,
+                    const List<extrudeMode>& extrudeStatus,
+                    const label nBufferCellsNoExtrude,
+                    labelList& nPatchPointLayers,
+                    labelList& nPatchFaceLayers
+                ) const;
+
+                //- Does any of the cells use a face from faces?
+                static bool cellsUseFace
+                (
+                    const polyMesh& mesh,
+                    const labelList& cellLabels,
+                    const labelHashSet& faces
+                );
+
+                //- Checks the newly added cells and locally unmarks points
+                //  so they will not get extruded next time round. Returns
+                //  global number of unmarked points (0 if all was fine)
+                static label checkAndUnmark
+                (
+                    const addPatchCellLayer& addLayer,
+                    const dictionary& motionDict,
+                    const indirectPrimitivePatch& pp,
+                    const fvMesh&,
+
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    List<extrudeMode>& extrudeStatus
+                );
+
+                //- Count global number of extruded faces
+                static label countExtrusion
+                (
+                    const indirectPrimitivePatch& pp,
+                    const List<extrudeMode>& extrudeStatus
+                );
+
+                //- Collect layer faces and layer cells into bools
+                //  for ease of handling
+                static void getLayerCellsFaces
+                (
+                    const polyMesh&,
+                    const addPatchCellLayer&,
+                    boolList&,
+                    boolList&
+                );
+
+            // Mesh shrinking (to create space for layers)
+
+                //- Average field (over all subset of mesh points) by
+                //  summing contribution from edges. Global parallel since only
+                //  does master edges for coupled edges.
+                template<class Type>
+                static void averageNeighbours
+                (
+                    const polyMesh& mesh,
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& meshEdges,
+                    const labelList& meshPoints,
+                    const edgeList& edges,
+                    const scalarField& invSumWeight,
+                    const Field<Type>& data,
+                    Field<Type>& average
+                );
+
+                //- Calculate inverse sum of edge weights (currently always 1.0)
+                void sumWeights
+                (
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& meshEdges,
+                    const labelList& meshPoints,
+                    const edgeList& edges,
+                    scalarField& invSumWeight
+                ) const;
+
+                //- Smooth scalar field on patch
+                void smoothField
+                (
+                    const motionSmoother& meshMover,
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& meshEdges,
+                    const scalarField& fieldMin,
+                    const label& nSmoothDisp,
+                    scalarField& field
+                ) const;
+
+                //- Smooth normals on patch.
+                void smoothPatchNormals
+                (
+                    const motionSmoother& meshMover,
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& meshEdges,
+                    const label nSmoothDisp,
+                    pointField& normals
+                ) const;
+
+                //- Smooth normals in interior.
+                void smoothNormals
+                (
+                    const label nSmoothDisp,
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& fixedPoints,
+                    pointVectorField& normals
+                ) const;
+
+                bool isMaxEdge
+                (
+                    const List<pointData>&,
+                    const label edgeI,
+                    const scalar minCos
+                ) const;
+
+                //- Stop layer growth where mesh wraps around edge with a
+                //  large feature angle
+                void handleFeatureAngleLayerTerminations
+                (
+                    const indirectPrimitivePatch& pp,
+                    const scalar minCos,
+                    List<extrudeMode>& extrudeStatus,
+                    pointField& patchDisp,
+                    labelList& patchNLayers,
+                    label& nPointCounter
+                ) const;
+
+                //- Find isolated islands (points, edges and faces and
+                // layer terminations)
+                // in the layer mesh and stop any layer growth at these points.
+                void findIsolatedRegions
+                (
+                    const indirectPrimitivePatch& pp,
+                    const PackedList<1>& isMasterEdge,
+                    const labelList& meshEdges,
+                    const scalar minCosLayerTermination,
+                    scalarField& field,
+                    List<extrudeMode>& extrudeStatus,
+                    pointField& patchDisp,
+                    labelList& patchNLayers
+                ) const;
+
+                // Calculate medial axis fields
+                void medialAxisSmoothingInfo
+                (
+                    const motionSmoother& meshMover,
+                    const label nSmoothNormals,
+                    const label nSmoothSurfaceNormals,
+                    const scalar minMedianAxisAngleCos,
+
+                    pointVectorField& dispVec,
+                    pointScalarField& medialRatio,
+                    pointScalarField& medialDist
+                ) const;
+
+                //- Main routine to shrink mesh
+                void shrinkMeshMedialDistance
+                (
+                    motionSmoother& meshMover,
+                    const label nSmoothThickness,
+                    const scalar maxThicknessToMedialRatio,
+                    const label nAllowableErrors,
+                    const label nSnap,
+                    const scalar minCosLayerTermination,
+
+                    const scalarField& layerThickness,
+                    const scalarField& minThickness,
+
+                    const pointVectorField& dispVec,
+                    const pointScalarField& medialRatio,
+                    const pointScalarField& medialDist,
+
+                    List<extrudeMode>& extrudeStatus,
+                    pointField& patchDisp,
+                    labelList& patchNLayers
+                ) const;
+
+
+
+        //- Disallow default bitwise copy construct
+        autoLayerDriver(const autoLayerDriver&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const autoLayerDriver&);
+
+
+public:
+
+    //- Runtime type information
+    ClassName("autoLayerDriver");
+
+    // Constructors
+
+        //- Construct from components
+        autoLayerDriver
+        (
+            meshRefinement& meshRefiner,
+            const labelList& globalToPatch
+        );
+
+
+    // Member Functions
+
+            //- Merge patch faces on same cell.
+            void mergePatchFacesUndo
+            (
+                const layerParameters& layerParams,
+                const dictionary& motionDict
+            );
+
+            //- Add cell layers
+            void addLayers
+            (
+                const layerParameters& layerParams,
+                const dictionary& motionDict,
+                const label nAllowableErrors,
+                motionSmoother& meshMover,
+                decompositionMethod& decomposer,
+                fvMeshDistribute& distributor
+            );
+
+            //- Add layers according to the dictionary settings
+            void doLayers
+            (
+                const dictionary& shrinkDict,
+                const dictionary& motionDict,
+                const layerParameters& layerParams,
+                decompositionMethod& decomposer,
+                fvMeshDistribute& distributor
+            );
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+#   include "autoLayerDriverTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverShrink.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverShrink.C
similarity index 93%
rename from src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverShrink.C
rename to src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverShrink.C
index 0c66073e84c422135bcdb5aa82a2b6314641d61b..8c41371ddc2ef07f5a9613baebf765d91632cf8e 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverShrink.C
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverShrink.C
@@ -27,7 +27,7 @@ Description
 
 \*----------------------------------------------------------------------------*/
 
-#include "autoHexMeshDriver.H"
+#include "autoLayerDriver.H"
 #include "fvMesh.H"
 #include "Time.H"
 #include "pointFields.H"
@@ -41,7 +41,7 @@ Description
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 // Calculate inverse sum of edge weights (currently always 1.0)
-void Foam::autoHexMeshDriver::sumWeights
+void Foam::autoLayerDriver::sumWeights
 (
     const PackedList<1>& isMasterEdge,
     const labelList& meshEdges,
@@ -67,7 +67,7 @@ void Foam::autoHexMeshDriver::sumWeights
 
     syncTools::syncPointList
     (
-        mesh_,
+        meshRefiner_.mesh(),
         meshPoints,
         invSumWeight,
         plusEqOp<scalar>(),
@@ -88,7 +88,7 @@ void Foam::autoHexMeshDriver::sumWeights
 
 
 // Smooth field on moving patch
-void Foam::autoHexMeshDriver::smoothField
+void Foam::autoLayerDriver::smoothField
 (
     const motionSmoother& meshMover,
     const PackedList<1>& isMasterEdge,
@@ -120,6 +120,7 @@ void Foam::autoHexMeshDriver::smoothField
         scalarField average(pp.nPoints());
         averageNeighbours
         (
+            meshMover.mesh(),
             isMasterEdge,
             meshEdges,
             meshPoints,
@@ -159,7 +160,7 @@ void Foam::autoHexMeshDriver::smoothField
 
 
 // Smooth normals on moving patch.
-void Foam::autoHexMeshDriver::smoothPatchNormals
+void Foam::autoLayerDriver::smoothPatchNormals
 (
     const motionSmoother& meshMover,
     const PackedList<1>& isMasterEdge,
@@ -192,6 +193,7 @@ void Foam::autoHexMeshDriver::smoothPatchNormals
         vectorField average(pp.nPoints());
         averageNeighbours
         (
+            meshMover.mesh(),
             isMasterEdge,
             meshEdges,
             meshPoints,
@@ -223,7 +225,7 @@ void Foam::autoHexMeshDriver::smoothPatchNormals
 
 
 // Smooth normals in interior.
-void Foam::autoHexMeshDriver::smoothNormals
+void Foam::autoLayerDriver::smoothNormals
 (
     const label nSmoothDisp,
     const PackedList<1>& isMasterEdge,
@@ -234,10 +236,11 @@ void Foam::autoHexMeshDriver::smoothNormals
     // Get smoothly varying internal normals field.
     Info<< "shrinkMeshDistance : Smoothing normals ..." << endl;
 
-    const edgeList& edges = mesh_.edges();
+    const fvMesh& mesh = meshRefiner_.mesh();
+    const edgeList& edges = mesh.edges();
 
     // Points that do not change.
-    PackedList<1> isFixedPoint(mesh_.nPoints(), 0);
+    PackedList<1> isFixedPoint(mesh.nPoints(), 0);
 
     // Internal points that are fixed
     forAll(fixedPoints, i)
@@ -247,12 +250,12 @@ void Foam::autoHexMeshDriver::smoothNormals
     }
 
     // Correspondence between local edges/points and mesh edges/points
-    const labelList meshEdges(identity(mesh_.nEdges()));
-    const labelList meshPoints(identity(mesh_.nPoints()));
+    const labelList meshEdges(identity(mesh.nEdges()));
+    const labelList meshPoints(identity(mesh.nPoints()));
 
     // Calculate inverse sum of weights
 
-    scalarField invSumWeight(mesh_.nPoints(), 0);
+    scalarField invSumWeight(mesh.nPoints(), 0);
     sumWeights
     (
         isMasterEdge,
@@ -266,9 +269,10 @@ void Foam::autoHexMeshDriver::smoothNormals
 
     for (label iter = 0; iter < nSmoothDisp; iter++)
     {
-        vectorField average(mesh_.nPoints());
+        vectorField average(mesh.nPoints());
         averageNeighbours
         (
+            mesh,
             isMasterEdge,
             meshEdges,
             meshPoints,
@@ -305,18 +309,19 @@ void Foam::autoHexMeshDriver::smoothNormals
 
 // Tries and find a medial axis point. Done by comparing vectors to nearest
 // wall point for both vertices of edge.
-bool Foam::autoHexMeshDriver::isMaxEdge
+bool Foam::autoLayerDriver::isMaxEdge
 (
     const List<pointData>& pointWallDist,
     const label edgeI,
     const scalar minCos
 ) const
 {
-    const pointField& points = mesh_.points();
+    const fvMesh& mesh = meshRefiner_.mesh();
+    const pointField& points = mesh.points();
 
     // Do not mark edges with one side on moving wall.
 
-    const edge& e = mesh_.edges()[edgeI];
+    const edge& e = mesh.edges()[edgeI];
 
     vector v0(points[e[0]] - pointWallDist[e[0]].origin());
     scalar magV0(mag(v0));
@@ -351,7 +356,7 @@ bool Foam::autoHexMeshDriver::isMaxEdge
 
 // Stop layer growth where mesh wraps around edge with a
 // large feature angle
-void Foam::autoHexMeshDriver::handleFeatureAngleLayerTerminations
+void Foam::autoLayerDriver::handleFeatureAngleLayerTerminations
 (
     const indirectPrimitivePatch& pp,
     const scalar minCos,
@@ -444,7 +449,7 @@ void Foam::autoHexMeshDriver::handleFeatureAngleLayerTerminations
 
 // Find isolated islands (points, edges and faces and layer terminations)
 // in the layer mesh and stop any layer growth at these points.
-void Foam::autoHexMeshDriver::findIsolatedRegions
+void Foam::autoLayerDriver::findIsolatedRegions
 (
     const indirectPrimitivePatch& pp,
     const PackedList<1>& isMasterEdge,
@@ -456,6 +461,8 @@ void Foam::autoHexMeshDriver::findIsolatedRegions
     labelList& patchNLayers
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
+
     Info<< "shrinkMeshDistance : Removing isolated regions ..." << endl;
 
     // Keep count of number of points unextruded
@@ -514,7 +521,7 @@ void Foam::autoHexMeshDriver::findIsolatedRegions
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             pp.meshPoints(),
             keptPoints,
             orEqOp<bool>(),
@@ -582,7 +589,7 @@ void Foam::autoHexMeshDriver::findIsolatedRegions
 
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         pp.meshPoints(),
         isolatedPoint,
         plusEqOp<label>(),
@@ -650,7 +657,7 @@ void Foam::autoHexMeshDriver::findIsolatedRegions
 // medialDist  : distance to medial axis
 // medialRatio : ratio of medial distance to wall distance.
 //               (1 at wall, 0 at medial axis)
-void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
+void Foam::autoLayerDriver::medialAxisSmoothingInfo
 (
     const motionSmoother& meshMover,
     const label nSmoothNormals,
@@ -666,7 +673,8 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
     Info<< "medialAxisSmoothingInfo :"
         << " Calculate distance to Medial Axis ..." << endl;
 
-    const pointField& points = mesh_.points();
+    const polyMesh& mesh = meshMover.mesh();
+    const pointField& points = mesh.points();
     const pointMesh& pMesh = meshMover.pMesh();
 
     const indirectPrimitivePatch& pp = meshMover.patch();
@@ -677,7 +685,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
     // ~~~~~~~~~~~~~~~~~~~~~~~
 
     // Precalulate master edge (only relevant for shared edges)
-    PackedList<1> isMasterEdge(syncTools::getMasterEdges(mesh_));
+    PackedList<1> isMasterEdge(syncTools::getMasterEdges(mesh));
     // Precalculate meshEdge per pp edge
     labelList meshEdges(pp.nEdges());
 
@@ -689,8 +697,8 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
         label v1 = pp.meshPoints()[e[1]];
         meshEdges[patchEdgeI] = meshTools::findEdge
         (
-            mesh_.edges(),
-            mesh_.pointEdges()[v0],
+            mesh.edges(),
+            mesh.pointEdges()[v0],
             v0,
             v1
         );
@@ -717,7 +725,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             pointNormals,
             plusEqOp<vector>(),
@@ -727,7 +735,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 
         syncTools::syncPointList
         (
-            mesh_,
+            mesh,
             meshPoints,
             nPointFaces,
             plusEqOp<label>(),
@@ -756,7 +764,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     // Distance to wall
-    List<pointData> pointWallDist(mesh_.nPoints());
+    List<pointData> pointWallDist(mesh.nPoints());
 
 
     // 1. Calculate distance to points where displacement is specified.
@@ -777,7 +785,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
         }
 
         // Do all calculations
-        List<pointData> edgeWallDist(mesh_.nEdges());
+        List<pointData> edgeWallDist(mesh.nEdges());
         PointEdgeWave<pointData> wallDistCalc
         (
             pMesh,
@@ -785,15 +793,15 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
             wallInfo,
             pointWallDist,
             edgeWallDist,
-            mesh_.nPoints()  // max iterations
+            mesh.nPoints()  // max iterations
         );
     }
 
     // 2. Find points with max distance and transport information back to
     //    wall.
     {
-        List<pointData> pointMedialDist(mesh_.nPoints());
-        List<pointData> edgeMedialDist(mesh_.nEdges());
+        List<pointData> pointMedialDist(mesh.nPoints());
+        List<pointData> edgeMedialDist(mesh.nEdges());
 
         // Seed point data.
         DynamicList<pointData> maxInfo(meshPoints.size());
@@ -801,7 +809,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 
         // 1. Medial axis points
 
-        const edgeList& edges = mesh_.edges();
+        const edgeList& edges = mesh.edges();
 
         forAll(edges, edgeI)
         {
@@ -836,7 +844,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 
 
         // 2. Seed non-adapt patches
-        const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+        const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
         labelHashSet adaptPatches(meshMover.adaptPatchIDs());
 
@@ -890,7 +898,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 
             pointMedialDist,
             edgeMedialDist,
-            mesh_.nPoints()  // max iterations
+            mesh.nPoints()  // max iterations
         );
 
         // Extract medial axis distance as pointScalarField
@@ -925,7 +933,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
         }
     }
 
-    if (debug_)
+    if (debug)
     {
         Info<< "medialAxisSmoothingInfo :"
             << " Writing:" << nl
@@ -943,7 +951,7 @@ void Foam::autoHexMeshDriver::medialAxisSmoothingInfo
 }
 
 
-void Foam::autoHexMeshDriver::shrinkMeshMedialDistance
+void Foam::autoLayerDriver::shrinkMeshMedialDistance
 (
     motionSmoother& meshMover,
     const label nSmoothThickness,
@@ -973,7 +981,7 @@ void Foam::autoHexMeshDriver::shrinkMeshMedialDistance
     const labelList& meshPoints = pp.meshPoints();
 
     // Precalulate master edge (only relevant for shared edges)
-    PackedList<1> isMasterEdge(syncTools::getMasterEdges(mesh_));
+    PackedList<1> isMasterEdge(syncTools::getMasterEdges(mesh));
     // Precalculate meshEdge per pp edge
     labelList meshEdges(pp.nEdges());
 
@@ -985,8 +993,8 @@ void Foam::autoHexMeshDriver::shrinkMeshMedialDistance
         label v1 = pp.meshPoints()[e[1]];
         meshEdges[patchEdgeI] = meshTools::findEdge
         (
-            mesh_.edges(),
-            mesh_.pointEdges()[v0],
+            mesh.edges(),
+            mesh.pointEdges()[v0],
             v0,
             v1
         );
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverTemplates.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverTemplates.C
similarity index 95%
rename from src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverTemplates.C
rename to src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverTemplates.C
index 31807f0dee77b8e43cd5d9e70684391adcf566b3..7284b4a355bb7f2cf17a53f25594504aa5f2d1c4 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverTemplates.C
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriverTemplates.C
@@ -24,14 +24,15 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "autoHexMeshDriver.H"
+#include "autoLayerDriver.H"
 #include "syncTools.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-void Foam::autoHexMeshDriver::averageNeighbours
+void Foam::autoLayerDriver::averageNeighbours
 (
+    const polyMesh& mesh,
     const PackedList<1>& isMasterEdge,
     const labelList& meshEdges,
     const labelList& meshPoints,
@@ -39,7 +40,7 @@ void Foam::autoHexMeshDriver::averageNeighbours
     const scalarField& invSumWeight,
     const Field<Type>& data,
     Field<Type>& average
-) const
+)
 {
     average = pTraits<Type>::zero;
 
@@ -60,7 +61,7 @@ void Foam::autoHexMeshDriver::averageNeighbours
 
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         meshPoints,
         average,
         plusEqOp<Type>(),
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C
new file mode 100644
index 0000000000000000000000000000000000000000..218087f7748435f5638052e36addf1914d20cd8d
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C
@@ -0,0 +1,776 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*----------------------------------------------------------------------------*/
+
+#include "autoRefineDriver.H"
+#include "fvMesh.H"
+#include "Time.H"
+#include "boundBox.H"
+#include "mapDistributePolyMesh.H"
+#include "cellSet.H"
+#include "syncTools.H"
+#include "refinementParameters.H"
+#include "featureEdgeMesh.H"
+#include "refinementSurfaces.H"
+#include "shellSurfaces.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(autoRefineDriver, 0);
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// Read explicit feature edges
+Foam::label Foam::autoRefineDriver::readFeatureEdges
+(
+    const PtrList<dictionary>& featDicts,
+    PtrList<featureEdgeMesh>& featureMeshes,
+    labelList& featureLevels
+) const
+{
+    Info<< "Reading external feature lines." << endl;
+
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    featureMeshes.setSize(featDicts.size());
+    featureLevels.setSize(featDicts.size());
+
+    forAll(featDicts, i)
+    {
+        const dictionary& dict = featDicts[i];
+
+        fileName featFileName(dict.lookup("file"));
+
+        featureMeshes.set
+        (
+            i,
+            new featureEdgeMesh
+            (
+                IOobject
+                (
+                    featFileName,           // name
+                    mesh.time().constant(), // directory
+                    "triSurface",           // instance
+                    mesh.db(),              // registry
+                    IOobject::MUST_READ,
+                    IOobject::NO_WRITE,
+                    false
+                )
+            )
+        );
+
+        featureMeshes[i].mergePoints(meshRefiner_.mergeDistance());
+        featureLevels[i] = readLabel(dict.lookup("level"));
+
+        Info<< "Refinement level " << featureLevels[i]
+            << " for all cells crossed by feature " << featFileName
+            << " (" << featureMeshes[i].points().size() << " points, "
+            << featureMeshes[i].edges().size() << ")." << endl;
+    }
+
+    Info<< "Read feature lines in = "
+        << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+
+    return featureMeshes.size();
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct from components
+Foam::autoRefineDriver::autoRefineDriver
+(
+    meshRefinement& meshRefiner,
+    decompositionMethod& decomposer,
+    fvMeshDistribute& distributor,
+    const labelList& globalToPatch
+)
+:
+    meshRefiner_(meshRefiner),
+    decomposer_(decomposer),
+    distributor_(distributor),
+    globalToPatch_(globalToPatch)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::label Foam::autoRefineDriver::featureEdgeRefine
+(
+    const refinementParameters& refineParams,
+    const PtrList<dictionary>& featDicts,
+    const label maxIter,
+    const label minRefine
+)
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    // Read explicit feature edges
+    PtrList<featureEdgeMesh> featureMeshes;
+    // Per feature the refinement level
+    labelList featureLevels;
+    readFeatureEdges(featDicts, featureMeshes, featureLevels);
+
+
+    label iter = 0;
+
+    if (featureMeshes.size() > 0 && maxIter > 0)
+    {
+        for (; iter < maxIter; iter++)
+        {
+            Info<< nl
+                << "Feature refinement iteration " << iter << nl
+                << "------------------------------" << nl
+                << endl;
+
+            labelList candidateCells
+            (
+                meshRefiner_.refineCandidates
+                (
+                    refineParams.keepPoints()[0],    // For now only use one.
+                    refineParams.curvature(),
+
+                    featureMeshes,
+                    featureLevels,
+
+                    true,               // featureRefinement
+                    false,              // internalRefinement
+                    false,              // surfaceRefinement
+                    false,              // curvatureRefinement
+                    refineParams.maxGlobalCells(),
+                    refineParams.maxLocalCells()
+                )
+            );
+            labelList cellsToRefine
+            (
+                meshRefiner_.meshCutter().consistentRefinement
+                (
+                    candidateCells,
+                    true
+                )
+            );
+            Info<< "Determined cells to refine in = "
+                << mesh.time().cpuTimeIncrement() << " s" << endl;
+
+
+
+            label nCellsToRefine = cellsToRefine.size();
+            reduce(nCellsToRefine, sumOp<label>());
+
+            Info<< "Selected for feature refinement : " << nCellsToRefine
+                << " cells (out of " << mesh.globalData().nTotalCells()
+                << ')' << endl;
+
+            if (nCellsToRefine <= minRefine)
+            {
+                Info<< "Stopping refining since too few cells selected."
+                    << nl << endl;
+                break;
+            }
+
+
+            if (debug > 0)
+            {
+                const_cast<Time&>(mesh.time())++;
+            }
+
+            meshRefiner_.refineAndBalance
+            (
+                "feature refinement iteration " + name(iter),
+                decomposer_,
+                distributor_,
+                cellsToRefine
+            );
+        }
+    }
+    return iter;
+}
+
+
+Foam::label Foam::autoRefineDriver::surfaceOnlyRefine
+(
+    const refinementParameters& refineParams,
+    const label maxIter
+)
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    // Determine the maximum refinement level over all surfaces. This
+    // determines the minumum number of surface refinement iterations.
+    label overallMaxLevel = max(meshRefiner_.surfaces().maxLevel());
+
+    label iter;
+    for (iter = 0; iter < maxIter; iter++)
+    {
+        Info<< nl
+            << "Surface refinement iteration " << iter << nl
+            << "------------------------------" << nl
+            << endl;
+
+
+        // Determine cells to refine
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~
+        // Only look at surface intersections (minLevel and surface curvature),
+        // do not do internal refinement (refinementShells)
+
+        labelList candidateCells
+        (
+            meshRefiner_.refineCandidates
+            (
+                refineParams.keepPoints()[0],
+                refineParams.curvature(),
+
+                PtrList<featureEdgeMesh>(0),    // dummy featureMeshes;
+                labelList(0),                   // dummy featureLevels;
+
+                false,              // featureRefinement
+                false,              // internalRefinement
+                true,               // surfaceRefinement
+                true,               // curvatureRefinement
+                refineParams.maxGlobalCells(),
+                refineParams.maxLocalCells()
+            )
+        );
+        labelList cellsToRefine
+        (
+            meshRefiner_.meshCutter().consistentRefinement
+            (
+                candidateCells,
+                true
+            )
+        );
+        Info<< "Determined cells to refine in = "
+            << mesh.time().cpuTimeIncrement() << " s" << endl;
+
+
+        label nCellsToRefine = cellsToRefine.size();
+        reduce(nCellsToRefine, sumOp<label>());
+
+        Info<< "Selected for refinement : " << nCellsToRefine
+            << " cells (out of " << mesh.globalData().nTotalCells()
+            << ')' << endl;
+
+        // Stop when no cells to refine or have done minimum nessecary
+        // iterations and not enough cells to refine.
+        if
+        (
+            nCellsToRefine == 0
+         || (
+                iter >= overallMaxLevel
+             && nCellsToRefine <= refineParams.minRefineCells()
+            )
+        )
+        {
+            Info<< "Stopping refining since too few cells selected."
+                << nl << endl;
+            break;
+        }
+
+
+        if (debug)
+        {
+            const_cast<Time&>(mesh.time())++;
+        }
+
+        meshRefiner_.refineAndBalance
+        (
+            "surface refinement iteration " + name(iter),
+            decomposer_,
+            distributor_,
+            cellsToRefine
+        );
+    }
+    return iter;
+}
+
+
+void Foam::autoRefineDriver::removeInsideCells
+(
+    const refinementParameters& refineParams,
+    const label nBufferLayers
+)
+{
+    Info<< nl
+        << "Removing mesh beyond surface intersections" << nl
+        << "------------------------------------------" << nl
+        << endl;
+
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    if (debug)
+    {
+       const_cast<Time&>(mesh.time())++;
+    }
+
+    meshRefiner_.splitMesh
+    (
+        nBufferLayers,                  // nBufferLayers
+        globalToPatch_,
+        refineParams.keepPoints()[0]
+    );
+
+    if (debug)
+    {
+        Pout<< "Writing subsetted mesh to time "
+            << mesh.time().timeName() << '.' << endl;
+        meshRefiner_.write(debug, mesh.time().path()/mesh.time().timeName());
+        Pout<< "Dumped mesh in = "
+            << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
+    }
+}
+
+
+Foam::label Foam::autoRefineDriver::shellRefine
+(
+    const refinementParameters& refineParams,
+    const label maxIter
+)
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    // Mark current boundary faces with 0. Have meshRefiner maintain them.
+    meshRefiner_.userFaceData().setSize(1);
+
+    // mark list to remove any refined faces
+    meshRefiner_.userFaceData()[0].first() = meshRefinement::REMOVE;
+    meshRefiner_.userFaceData()[0].second() = createWithValues<labelList>
+    (
+        mesh.nFaces(),
+        -1,
+        meshRefiner_.intersectedFaces(),
+        0
+    );
+
+    // Determine the maximum refinement level over all volume refinement
+    // regions. This determines the minumum number of shell refinement
+    // iterations.
+    label overallMaxShellLevel = meshRefiner_.shells().maxLevel();
+
+    label iter;
+    for (iter = 0; iter < maxIter; iter++)
+    {
+        Info<< nl
+            << "Shell refinement iteration " << iter << nl
+            << "----------------------------" << nl
+            << endl;
+
+        labelList candidateCells
+        (
+            meshRefiner_.refineCandidates
+            (
+                refineParams.keepPoints()[0],
+                refineParams.curvature(),
+
+                PtrList<featureEdgeMesh>(0),    // dummy featureMeshes;
+                labelList(0),                   // dummy featureLevels;
+
+                false,              // featureRefinement
+                true,               // internalRefinement
+                false,              // surfaceRefinement
+                false,              // curvatureRefinement
+                refineParams.maxGlobalCells(),
+                refineParams.maxLocalCells()
+            )
+        );
+
+        if (debug)
+        {
+            Pout<< "Dumping " << candidateCells.size()
+                << " cells to cellSet candidateCellsFromShells." << endl;
+
+            cellSet(mesh, "candidateCellsFromShells", candidateCells).write();
+        }
+
+        // Problem choosing starting faces for bufferlayers (bFaces)
+        //  - we can't use the current intersected boundary faces
+        //    (intersectedFaces) since this grows indefinitely
+        //  - if we use 0 faces we don't satisfy bufferLayers from the
+        //    surface.
+        //  - possibly we want to have bFaces only the initial set of faces
+        //    and maintain the list while doing the refinement.
+        labelList bFaces
+        (
+            findIndices(meshRefiner_.userFaceData()[0].second(), 0)
+        );
+
+        //Info<< "Collected boundary faces : "
+        //    << returnReduce(bFaces.size(), sumOp<label>()) << endl;
+
+        labelList cellsToRefine;
+
+        if (refineParams.nBufferLayers() <= 2)
+        {
+            cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement
+            (
+                refineParams.nBufferLayers(),
+                candidateCells,                     // cells to refine
+                bFaces,                             // faces for nBufferLayers
+                1,                                  // point difference
+                meshRefiner_.intersectedPoints()    // points to check
+            );
+        }
+        else
+        {
+            cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement2
+            (
+                refineParams.nBufferLayers(),
+                candidateCells,                 // cells to refine
+                bFaces                          // faces for nBufferLayers
+            );
+        }
+
+        Info<< "Determined cells to refine in = "
+            << mesh.time().cpuTimeIncrement() << " s" << endl;
+
+
+        label nCellsToRefine = cellsToRefine.size();
+        reduce(nCellsToRefine, sumOp<label>());
+
+        Info<< "Selected for internal refinement : " << nCellsToRefine
+            << " cells (out of " << mesh.globalData().nTotalCells()
+            << ')' << endl;
+
+        // Stop when no cells to refine or have done minimum nessecary
+        // iterations and not enough cells to refine.
+        if
+        (
+            nCellsToRefine == 0
+         || (
+                iter >= overallMaxShellLevel
+             && nCellsToRefine <= refineParams.minRefineCells()
+            )
+        )
+        {
+            Info<< "Stopping refining since too few cells selected."
+                << nl << endl;
+            break;
+        }
+
+
+        if (debug)
+        {
+            const_cast<Time&>(mesh.time())++;
+        }
+
+        meshRefiner_.refineAndBalance
+        (
+            "shell refinement iteration " + name(iter),
+            decomposer_,
+            distributor_,
+            cellsToRefine
+        );
+    }
+    meshRefiner_.userFaceData().clear();
+
+    return iter;
+}
+
+
+void Foam::autoRefineDriver::baffleAndSplitMesh
+(
+    const refinementParameters& refineParams,
+    const bool handleSnapProblems
+)
+{
+    Info<< nl
+        << "Splitting mesh at surface intersections" << nl
+        << "---------------------------------------" << nl
+        << endl;
+
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    // Introduce baffles at surface intersections. Note:
+    // meshRefiment::surfaceIndex() will
+    // be like boundary face from now on so not coupled anymore.
+    meshRefiner_.baffleAndSplitMesh
+    (
+        handleSnapProblems,
+        !handleSnapProblems,            // merge free standing baffles?
+        const_cast<Time&>(mesh.time()),
+        globalToPatch_,
+        refineParams.keepPoints()[0]
+    );
+}
+
+
+void Foam::autoRefineDriver::zonify
+(
+    const refinementParameters& refineParams
+)
+{
+    // Mesh is at its finest. Do zoning
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    // This puts all faces with intersection across a zoneable surface
+    // into that surface's faceZone. All cells inside faceZone get given the
+    // same cellZone.
+
+    if (meshRefiner_.surfaces().getNamedSurfaces().size() > 0)
+    {
+        Info<< nl
+            << "Introducing zones for interfaces" << nl
+            << "--------------------------------" << nl
+            << endl;
+
+        const fvMesh& mesh = meshRefiner_.mesh();
+
+        if (debug)
+        {
+            const_cast<Time&>(mesh.time())++;
+        }
+
+        meshRefiner_.zonify(refineParams.keepPoints()[0]);
+
+        if (debug)
+        {
+            Pout<< "Writing zoned mesh to time "
+                << mesh.time().timeName() << '.' << endl;
+            meshRefiner_.write
+            (
+                debug,
+                mesh.time().path()/mesh.time().timeName()
+            );
+        }
+
+        // Check that all faces are synced
+        meshRefinement::checkCoupledFaceZones(mesh);
+    }
+}
+
+
+void Foam::autoRefineDriver::splitAndMergeBaffles
+(
+    const refinementParameters& refineParams,
+    const bool handleSnapProblems
+)
+{
+    Info<< nl
+        << "Handling cells with snap problems" << nl
+        << "---------------------------------" << nl
+        << endl;
+
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    // Introduce baffles and split mesh
+    if (debug)
+    {
+        const_cast<Time&>(mesh.time())++;
+    }
+
+    meshRefiner_.baffleAndSplitMesh
+    (
+        handleSnapProblems,
+        false,                  // merge free standing baffles?
+        const_cast<Time&>(mesh.time()),
+        globalToPatch_,
+        refineParams.keepPoints()[0]
+    );
+
+    if (debug)
+    {
+        const_cast<Time&>(mesh.time())++;
+    }
+
+    // Duplicate points on baffles that are on more than one cell
+    // region. This will help snapping pull them to separate surfaces.
+    meshRefiner_.dupNonManifoldPoints();
+
+
+    // Merge all baffles that are still remaining after duplicating points.
+    List<labelPair> couples
+    (
+        meshRefiner_.getDuplicateFaces   // get all baffles
+        (
+            identity(mesh.nFaces()-mesh.nInternalFaces())
+          + mesh.nInternalFaces()
+        )
+    );
+
+    label nCouples = returnReduce(couples.size(), sumOp<label>());
+
+    Info<< "Detected unsplittable baffles : "
+        << nCouples << endl;
+
+    if (nCouples > 0)
+    {
+        // Actually merge baffles. Note: not exactly parallellized. Should
+        // convert baffle faces into processor faces if they resulted
+        // from them.
+        meshRefiner_.mergeBaffles(couples);
+
+        if (debug)
+        {
+            // Debug:test all is still synced across proc patches
+            meshRefiner_.checkData();
+        }
+        Info<< "Merged free-standing baffles in = "
+            << mesh.time().cpuTimeIncrement() << " s." << endl;
+    }
+
+    if (debug)
+    {
+        Pout<< "Writing handleProblemCells mesh to time "
+            << mesh.time().timeName() << '.' << endl;
+        meshRefiner_.write(debug, mesh.time().path()/mesh.time().timeName());
+    }
+}
+
+
+void Foam::autoRefineDriver::mergePatchFaces
+(
+    const refinementParameters& refineParams
+)
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    Info<< nl
+        << "Merge refined boundary faces" << nl
+        << "----------------------------" << nl
+        << endl;
+
+    if (debug)
+    {
+        const_cast<Time&>(mesh.time())++;
+    }
+
+    meshRefiner_.mergePatchFaces
+    (
+        Foam::cos(45*mathematicalConstant::pi/180.0),
+        Foam::cos(45*mathematicalConstant::pi/180.0),
+        meshRefinement::addedPatches(globalToPatch_)
+    );
+
+    if (debug)
+    {
+        meshRefiner_.checkData();
+    }
+
+    meshRefiner_.mergeEdges(Foam::cos(45*mathematicalConstant::pi/180.0));
+
+    if (debug)
+    {
+        meshRefiner_.checkData();
+    }
+}
+
+
+void Foam::autoRefineDriver::doRefine
+(
+    const dictionary& refineDict,
+    const refinementParameters& refineParams,
+    const bool prepareForSnapping
+)
+{
+    Info<< nl
+        << "Refinement phase" << nl
+        << "----------------" << nl
+        << endl;
+
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    const_cast<Time&>(mesh.time())++;
+
+
+    // Check that all the keep points are inside the mesh.
+    refineParams.findCells(mesh);
+
+    PtrList<dictionary> featDicts(refineDict.lookup("features"));
+
+    // Refine around feature edges
+    featureEdgeRefine
+    (
+        refineParams,
+        featDicts,
+        100,    // maxIter
+        0       // min cells to refine
+    );
+
+    // Refine based on surface
+    surfaceOnlyRefine
+    (
+        refineParams,
+        100     // maxIter
+    );
+
+    // Remove cells (a certain distance) beyond surface intersections
+    removeInsideCells
+    (
+        refineParams,
+        1       // nBufferLayers
+    );
+
+    // Internal mesh refinement
+    shellRefine
+    (
+        refineParams,
+        100    // maxIter
+    );
+
+    // Introduce baffles at surface intersections
+    baffleAndSplitMesh(refineParams, prepareForSnapping);
+
+    // Mesh is at its finest. Do optional zoning.
+    zonify(refineParams);
+
+    // Pull baffles apart
+    splitAndMergeBaffles(refineParams, prepareForSnapping);
+
+    // Do something about cells with refined faces on the boundary
+    if (prepareForSnapping)
+    {
+        mergePatchFaces(refineParams);
+    }
+
+
+    if (Pstream::parRun())
+    {
+        Info<< nl
+            << "Doing final balancing" << nl
+            << "---------------------" << nl
+            << endl;
+
+        if (debug)
+        {
+            const_cast<Time&>(mesh.time())++;
+        }
+
+        // Do final balancing. Keep zoned faces on one processor.
+        meshRefiner_.balance
+        (
+            true,
+            false,
+            decomposer_,
+            distributor_
+        );
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..8aa33855def83c7c8cade6f4fa9abbaf202b0ba5
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H
@@ -0,0 +1,179 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::autoRefineDriver
+
+Description
+
+SourceFiles
+    autoRefineDriver.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef autoRefineDriver_H
+#define autoRefineDriver_H
+
+#include "meshRefinement.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class featureEdgeMesh;
+class refinementParameters;
+
+
+/*---------------------------------------------------------------------------*\
+                           Class autoRefineDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class autoRefineDriver
+{
+    // Private data
+
+        //- Mesh+surface
+        meshRefinement& meshRefiner_;
+
+        //- Reference to decomposition method
+        decompositionMethod& decomposer_;
+
+        //- Reference to mesh distribution engine
+        fvMeshDistribute& distributor_;
+
+        //- From surface region to patch
+        const labelList globalToPatch_;
+
+
+    // Private Member Functions
+
+        //- Read explicit feature edges
+        label readFeatureEdges
+        (
+            const PtrList<dictionary>& featDicts,
+            PtrList<featureEdgeMesh>& featureMeshes,
+            labelList& featureLevel
+        ) const;
+
+        //- Refine all cells pierced by explicit feature edges
+        label featureEdgeRefine
+        (
+            const refinementParameters& refineParams,
+            const PtrList<dictionary>& featDicts,
+            const label maxIter,
+            const label minRefine
+        );
+
+        //- Refine all cells interacting with the surface
+        label surfaceOnlyRefine
+        (
+            const refinementParameters& refineParams,
+            const label maxIter
+        );
+
+        //- Remove all cells within intersected region
+        void removeInsideCells
+        (
+            const refinementParameters& refineParams,
+            const label nBufferLayers
+        );
+
+        //- Remove all cells inside/outside shell
+        label shellRefine
+        (
+            const refinementParameters& refineParams,
+            const label maxIter
+        );
+
+        //- Add baffles and remove unreachable cells
+        void baffleAndSplitMesh
+        (
+            const refinementParameters& refineParams,
+            const bool handleSnapProblems
+        );
+
+        //- Add zones
+        void zonify(const refinementParameters& refineParams);
+
+        void splitAndMergeBaffles
+        (
+            const refinementParameters& refineParams,
+            const bool handleSnapProblems
+        );
+
+        //- Merge refined boundary faces (from exposing coarser cell)
+        void mergePatchFaces
+        (
+            const refinementParameters& refineParams
+        );
+
+
+        //- Disallow default bitwise copy construct
+        autoRefineDriver(const autoRefineDriver&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const autoRefineDriver&);
+
+
+public:
+
+    //- Runtime type information
+    ClassName("autoRefineDriver");
+
+
+    // Constructors
+
+        //- Construct from components
+        autoRefineDriver
+        (
+            meshRefinement& meshRefiner,
+            decompositionMethod& decomposer,
+            fvMeshDistribute& distributor,
+            const labelList& globalToPatch
+        );
+
+
+    // Member Functions
+
+        //- Do all the refinement
+        void doRefine
+        (
+            const dictionary& refineDict,
+            const refinementParameters& refineParams,
+            const bool prepareForSnapping
+        );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverSnap.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C
similarity index 70%
rename from src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverSnap.C
rename to src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C
index d4c72ea4dadfef698cf8ab40debe7e6b2dbed864..85b4f9d5d7adf8826d627ac54690747b16aec5c6 100644
--- a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoHexMeshDriverSnap.C
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C
@@ -27,7 +27,12 @@ Description
 
 \*----------------------------------------------------------------------------*/
 
-#include "autoHexMeshDriver.H"
+#include "autoSnapDriver.H"
+#include "Time.H"
+#include "pointFields.H"
+#include "motionSmoother.H"
+#include "polyTopoChange.H"
+#include "OFstream.H"
 #include "syncTools.H"
 #include "fvMesh.H"
 #include "Time.H"
@@ -37,16 +42,29 @@ Description
 #include "pointEdgePoint.H"
 #include "PointEdgeWave.H"
 #include "mergePoints.H"
+#include "snapParameters.H"
+#include "refinementSurfaces.H"
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(autoSnapDriver, 0);
+
+} // End namespace Foam
+
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-void Foam::autoHexMeshDriver::getZonedSurfaces
+void Foam::autoSnapDriver::getZonedSurfaces
 (
     labelList& zonedSurfaces,
     labelList& unzonedSurfaces
 ) const
 {    // Surfaces with zone information
-    const wordList& faceZoneNames = surfaces().faceZoneNames();
+    const wordList& faceZoneNames = meshRefiner_.surfaces().faceZoneNames();
 
     zonedSurfaces.setSize(faceZoneNames.size());
     label zonedI = 0;
@@ -70,15 +88,18 @@ void Foam::autoHexMeshDriver::getZonedSurfaces
 
 
 // Get faces to repatch. Returns map from face to patch.
-Foam::Map<Foam::label> Foam::autoHexMeshDriver::getZoneBafflePatches
+Foam::Map<Foam::label> Foam::autoSnapDriver::getZoneBafflePatches
 (
     const bool allowBoundary
 ) const
 {
-    Map<label> bafflePatch(mesh_.nFaces()/1000);
+    const fvMesh& mesh = meshRefiner_.mesh();
+    const refinementSurfaces& surfaces = meshRefiner_.surfaces();
+
+    Map<label> bafflePatch(mesh.nFaces()/1000);
 
-    const wordList& faceZoneNames = surfaces().faceZoneNames();
-    const faceZoneMesh& fZones = mesh_.faceZones();
+    const wordList& faceZoneNames = surfaces.faceZoneNames();
+    const faceZoneMesh& fZones = mesh.faceZones();
 
     forAll(faceZoneNames, surfI)
     {
@@ -92,13 +113,12 @@ Foam::Map<Foam::label> Foam::autoHexMeshDriver::getZoneBafflePatches
             //// Get patch allocated for zone
             //label patchI = surfaceToCyclicPatch_[surfI];
             // Get patch of (first region) of surface
-            label patchI = globalToPatch_[surfaces().globalRegion(surfI, 0)];
+            label patchI = globalToPatch_[surfaces.globalRegion(surfI, 0)];
 
             Info<< "For surface "
-                << surfaces()[surfI].IOobject::name()
-                //<< surfaces().names()[surfI]
+                << surfaces.names()[surfI]
                 << " found faceZone " << fZone.name()
-                << " and patch " << mesh_.boundaryMesh()[patchI].name()
+                << " and patch " << mesh.boundaryMesh()[patchI].name()
                 << endl;
 
 
@@ -106,7 +126,7 @@ Foam::Map<Foam::label> Foam::autoHexMeshDriver::getZoneBafflePatches
             {
                 label faceI = fZone[i];
 
-                if (allowBoundary || mesh_.isInternalFace(faceI))
+                if (allowBoundary || mesh.isInternalFace(faceI))
                 {
                     if (!bafflePatch.insert(faceI, patchI))
                     {
@@ -116,11 +136,11 @@ Foam::Map<Foam::label> Foam::autoHexMeshDriver::getZoneBafflePatches
                         {
                             FatalErrorIn("getZoneBafflePatches(const bool)")
                                 << "Face " << faceI
-                                << " fc:" << mesh_.faceCentres()[faceI]
+                                << " fc:" << mesh.faceCentres()[faceI]
                                 << " is in faceZone "
-                                << mesh_.boundaryMesh()[oldPatchI].name()
+                                << mesh.boundaryMesh()[oldPatchI].name()
                                 << " and in faceZone "
-                                << mesh_.boundaryMesh()[patchI].name()
+                                << mesh.boundaryMesh()[patchI].name()
                                 << abort(FatalError);
                         }
                     }
@@ -134,7 +154,7 @@ Foam::Map<Foam::label> Foam::autoHexMeshDriver::getZoneBafflePatches
 
 // Calculate geometrically collocated points, Requires PackedList to be
 // sizes and initalised!
-Foam::label Foam::autoHexMeshDriver::getCollocatedPoints
+Foam::label Foam::autoSnapDriver::getCollocatedPoints
 (
     const scalar tol,
     const pointField& points,
@@ -196,7 +216,7 @@ Foam::label Foam::autoHexMeshDriver::getCollocatedPoints
 
 
 // Calculate displacement as average of patch points.
-Foam::pointField Foam::autoHexMeshDriver::smoothPatchDisplacement
+Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement
 (
     const motionSmoother& meshMover
 ) const
@@ -483,7 +503,7 @@ Foam::pointField Foam::autoHexMeshDriver::smoothPatchDisplacement
 }
 
 
-Foam::tmp<Foam::scalarField> Foam::autoHexMeshDriver::edgePatchDist
+Foam::tmp<Foam::scalarField> Foam::autoSnapDriver::edgePatchDist
 (
     const pointMesh& pMesh,
     const indirectPrimitivePatch& pp
@@ -565,7 +585,7 @@ Foam::tmp<Foam::scalarField> Foam::autoHexMeshDriver::edgePatchDist
 }
 
 
-void Foam::autoHexMeshDriver::dumpMove
+void Foam::autoSnapDriver::dumpMove
 (
     const fileName& fName,
     const pointField& meshPts,
@@ -596,7 +616,7 @@ void Foam::autoHexMeshDriver::dumpMove
 
 // Check whether all displacement vectors point outwards of patch. Return true
 // if so.
-bool Foam::autoHexMeshDriver::outwardsDisplacement
+bool Foam::autoSnapDriver::outwardsDisplacement
 (
     const indirectPrimitivePatch& pp,
     const vectorField& patchDisp
@@ -637,9 +657,22 @@ bool Foam::autoHexMeshDriver::outwardsDisplacement
 }
 
 
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::autoSnapDriver::autoSnapDriver
+(
+    meshRefinement& meshRefiner,
+    const labelList& globalToPatch
+)
+:
+    meshRefiner_(meshRefiner),
+    globalToPatch_(globalToPatch)
+{}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::createZoneBaffles
+Foam::autoPtr<Foam::mapPolyMesh> Foam::autoSnapDriver::createZoneBaffles
 (
     List<labelPair>& baffles
 )
@@ -653,6 +686,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::createZoneBaffles
     // No need to sync; all processors will have all same zonedSurfaces.
     if (zonedSurfaces.size() > 0)
     {
+        fvMesh& mesh = meshRefiner_.mesh();
+
         // Split internal faces on interface surfaces
         Info<< "Converting zoned faces into baffles ..." << endl;
 
@@ -664,14 +699,14 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::createZoneBaffles
         if (nZoneFaces > 0)
         {
             // Convert into labelLists
-            labelList ownPatch(mesh_.nFaces(), -1);
+            labelList ownPatch(mesh.nFaces(), -1);
             forAllConstIter(Map<label>, faceToPatch, iter)
             {
                 ownPatch[iter.key()] = iter();
             }
 
             // Create baffles. both sides same patch.
-            map = meshRefinerPtr_().createBaffles(ownPatch, ownPatch);
+            map = meshRefiner_.createBaffles(ownPatch, ownPatch);
 
             // Get pairs of faces created.
             // Just loop over faceMap and store baffle if we encounter a slave
@@ -702,29 +737,29 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::createZoneBaffles
 
             if (baffleI != faceToPatch.size())
             {
-                FatalErrorIn("autoHexMeshDriver::createZoneBaffles(..)")
+                FatalErrorIn("autoSnapDriver::createZoneBaffles(..)")
                     << "Had " << faceToPatch.size() << " patches to create "
                     << " but encountered " << baffleI
                     << " slave faces originating from patcheable faces."
                     << abort(FatalError);
             }
 
-            if (debug_)
+            if (debug)
             {
-                const_cast<Time&>(mesh_.time())++;
-                Pout<< "Writing baffled mesh to time " << mesh_.time().timeName()
-                    << endl;
-                mesh_.write();
+                const_cast<Time&>(mesh.time())++;
+                Pout<< "Writing baffled mesh to time "
+                    << mesh.time().timeName() << endl;
+                mesh.write();
             }
         }
         Info<< "Created " << nZoneFaces << " baffles in = "
-            << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+            << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
     }
     return map;
 }
 
 
-Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::mergeZoneBaffles
+Foam::autoPtr<Foam::mapPolyMesh> Foam::autoSnapDriver::mergeZoneBaffles
 (
     const List<labelPair>& baffles
 )
@@ -743,29 +778,27 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::autoHexMeshDriver::mergeZoneBaffles
         Info<< "Converting " << nBaffles << " baffles back into zoned faces ..."
             << endl;
 
-        map = meshRefinerPtr_().mergeBaffles(baffles);
+        map = meshRefiner_.mergeBaffles(baffles);
 
         Info<< "Converted baffles in = "
-            << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+            << meshRefiner_.mesh().time().cpuTimeIncrement()
+            << " s\n" << nl << endl;
     }
 
     return map;
 }
 
 
-Foam::scalarField Foam::autoHexMeshDriver::calcSnapDistance
+Foam::scalarField Foam::autoSnapDriver::calcSnapDistance
 (
-    const dictionary& snapDict,
+    const snapParameters& snapParams,
     const indirectPrimitivePatch& pp
 ) const
 {
-    // When to snap
-    scalar snapTol(readScalar(snapDict.lookup("snapTol")));
-
-
     const edgeList& edges = pp.edges();
     const labelListList& pointEdges = pp.pointEdges();
     const pointField& localPoints = pp.localPoints();
+    const fvMesh& mesh = meshRefiner_.mesh();
 
     scalarField maxEdgeLen(localPoints.size(), -GREAT);
 
@@ -785,7 +818,7 @@ Foam::scalarField Foam::autoHexMeshDriver::calcSnapDistance
 
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         pp.meshPoints(),
         maxEdgeLen,
         maxEqOp<scalar>(),  // combine op
@@ -793,57 +826,61 @@ Foam::scalarField Foam::autoHexMeshDriver::calcSnapDistance
         false               // no separation
     );
 
-    return snapTol*maxEdgeLen;
+    return snapParams.snapTol()*maxEdgeLen;
 }
 
 
-// Invert globalToPatch_ to get the patches related to surfaces.
-Foam::labelList Foam::autoHexMeshDriver::getSurfacePatches() const
-{
-    // Set of patches originating from surface
-    labelHashSet surfacePatchSet(surfaces().size());
-
-    forAll(globalToPatch_, i)
-    {
-        if (globalToPatch_[i] != -1)
-        {
-            surfacePatchSet.insert(globalToPatch_[i]);
-        }
-    }
-
-    DynamicList<label> surfacePatches(surfacePatchSet.size());
-
-    for (label patchI = 0; patchI < mesh_.boundaryMesh().size(); patchI++)
-    {
-        if (surfacePatchSet.found(patchI))
-        {
-            surfacePatches.append(patchI);
-        }
-    }
-    return surfacePatches.shrink();
-}
-
-
-void Foam::autoHexMeshDriver::preSmoothPatch
+//// Invert globalToPatch_ to get the patches related to surfaces.
+//Foam::labelList Foam::autoSnapDriver::getSurfacePatches() const
+//{
+//    // Set of patches originating from surface
+//    labelHashSet surfacePatchSet(globalToPatch_.size());
+//
+//    forAll(globalToPatch_, i)
+//    {
+//        if (globalToPatch_[i] != -1)
+//        {
+//            surfacePatchSet.insert(globalToPatch_[i]);
+//        }
+//    }
+//
+//    const fvMesh& mesh = meshRefiner_.mesh();
+//
+//    DynamicList<label> surfacePatches(surfacePatchSet.size());
+//
+//    for (label patchI = 0; patchI < mesh.boundaryMesh().size(); patchI++)
+//    {
+//        if (surfacePatchSet.found(patchI))
+//        {
+//            surfacePatches.append(patchI);
+//        }
+//    }
+//    return surfacePatches.shrink();
+//}
+
+
+void Foam::autoSnapDriver::preSmoothPatch
 (
-    const dictionary& snapDict,
+    const snapParameters& snapParams,
     const label nInitErrors,
     const List<labelPair>& baffles,
     motionSmoother& meshMover
 ) const
 {
-    // Smoothing iterations
-    label nSmoothPatch(readLabel(snapDict.lookup("nSmoothPatch")));
-    // Snapping iterations
-    label nSnap(readLabel(snapDict.lookup("nSnap")));
+    const fvMesh& mesh = meshRefiner_.mesh();
 
     labelList checkFaces;
 
     Info<< "Smoothing patch points ..." << endl;
-    for (label smoothIter = 0; smoothIter < nSmoothPatch; smoothIter++)
+    for
+    (
+        label smoothIter = 0;
+        smoothIter < snapParams.nSmoothPatch();
+        smoothIter++
+    )
     {
         Info<< "Smoothing iteration " << smoothIter << endl;
-        checkFaces.setSize(mesh_.nFaces());
+        checkFaces.setSize(mesh.nFaces());
         forAll(checkFaces, faceI)
         {
             checkFaces[faceI] = faceI;
@@ -857,11 +894,11 @@ void Foam::autoHexMeshDriver::preSmoothPatch
 
         scalar oldErrorReduction = -1;
 
-        for (label snapIter = 0; snapIter < 2*nSnap; snapIter++)
+        for (label snapIter = 0; snapIter < 2*snapParams.nSnap(); snapIter++)
         {
             Info<< nl << "Scaling iteration " << snapIter << endl;
 
-            if (snapIter == nSnap)
+            if (snapIter == snapParams.nSnap())
             {
                 Info<< "Displacement scaling for error reduction set to 0."
                     << endl;
@@ -888,20 +925,72 @@ void Foam::autoHexMeshDriver::preSmoothPatch
     // The current mesh is the starting mesh to smooth from.
     meshMover.correct();
 
-    if (debug_)
+    if (debug)
     {
-        const_cast<Time&>(mesh_.time())++;
-        Pout<< "Writing patch smoothed mesh to time " << mesh_.time().timeName()
+        const_cast<Time&>(mesh.time())++;
+        Pout<< "Writing patch smoothed mesh to time " << mesh.time().timeName()
             << endl;
-        mesh_.write();
+        mesh.write();
     }
 
     Info<< "Patch points smoothed in = "
-        << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+        << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
+}
+
+
+// Get (pp-local) indices of points that are both on zone and on patched surface
+Foam::labelList Foam::autoSnapDriver::getZoneSurfacePoints
+(
+    const indirectPrimitivePatch& pp,
+    const word& zoneName
+) const
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    label zoneI = mesh.faceZones().findZoneID(zoneName);
+
+    if (zoneI == -1)
+    {
+        FatalErrorIn
+        (
+            "autoSnapDriver::getZoneSurfacePoints"
+            "(const indirectPrimitivePatch&, const word&)"
+        )   << "Cannot find zone " << zoneName
+            << exit(FatalError);
+    }
+
+    const faceZone& fZone = mesh.faceZones()[zoneI];
+
+
+    // Could use PrimitivePatch & localFaces to extract points but might just
+    // as well do it ourselves.
+
+    boolList pointOnZone(pp.nPoints(), false);
+
+    forAll(fZone, i)
+    {
+        const face& f = mesh.faces()[fZone[i]];
+
+        forAll(f, fp)
+        {
+            label meshPointI = f[fp];
+
+            Map<label>::const_iterator iter =
+                pp.meshPointMap().find(meshPointI);
+
+            if (iter != pp.meshPointMap().end())
+            {
+                label pointI = iter();
+                pointOnZone[pointI] = true;
+            }
+        }
+    }
+
+    return findIndices(pointOnZone, true);
 }
 
 
-Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
+Foam::vectorField Foam::autoSnapDriver::calcNearestSurface
 (
     const scalarField& snapDist,
     motionSmoother& meshMover
@@ -912,6 +1001,8 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
 
     const indirectPrimitivePatch& pp = meshMover.patch();
     const pointField& localPoints = pp.localPoints();
+    const refinementSurfaces& surfaces = meshRefiner_.surfaces();
+    const fvMesh& mesh = meshRefiner_.mesh();
 
     // Divide surfaces into zoned and unzoned
     labelList zonedSurfaces;
@@ -925,38 +1016,45 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
     // 1. All points to non-interface surfaces
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    forAll(localPoints, pointI)
     {
-        pointIndexHit pHit;
-
-        label surfI = surfaces().findNearest
+        List<pointIndexHit> hitInfo;
+        labelList hitSurface;
+        surfaces.findNearest
         (
             unzonedSurfaces,
-            localPoints[pointI],
-            sqr(4*snapDist[pointI]),            // sqr of attract distance
-            pHit
+            localPoints,
+            sqr(4*snapDist),        // sqr of attract distance
+            hitSurface,
+            hitInfo
         );
 
-        if (surfI != -1)
+        forAll(hitInfo, pointI)
         {
-            patchDisp[pointI] = pHit.hitPoint() - localPoints[pointI];
+            if (hitInfo[pointI].hit())
+            {
+                patchDisp[pointI] =
+                    hitInfo[pointI].hitPoint()
+                  - localPoints[pointI];
+            }
+            //else
+            //{
+            //   WarningIn("autoSnapDriver::calcNearestSurface(..)")
+            //        << "For point:" << pointI
+            //        << " coordinate:" << localPoints[pointI]
+            //        << " did not find any surface within:"
+            //        << 4*snapDist[pointI]
+            //        << " meter." << endl;
+            //}
         }
-        //else
-        //{
-        //   WarningIn("autoHexMeshDriver::calcNearestSurface(..)")
-        //        << "For point:" << pointI
-        //        << " coordinate:" << localPoints[pointI]
-        //        << " did not find any surface within:" << 4*snapDist[pointI]
-        //        << " meter." << endl;
-        //}
     }
 
 
+
     // 2. All points on zones to their respective surface
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
     // Surfaces with zone information
-    const wordList& faceZoneNames = surfaces().faceZoneNames();
+    const wordList& faceZoneNames = surfaces.faceZoneNames();
 
     forAll(zonedSurfaces, i)
     {
@@ -964,50 +1062,50 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
 
         const labelList surfacesToTest(1, zoneSurfI);
 
-        label zoneI = mesh_.faceZones().findZoneID(faceZoneNames[zoneSurfI]);
-
-        const faceZone& fZone = mesh_.faceZones()[zoneI];
+        // Get indices of points both on faceZone and on pp.
+        labelList zonePointIndices
+        (
+            getZoneSurfacePoints
+            (
+                pp,
+                faceZoneNames[zoneSurfI]
+            )
+        );
 
-        forAll(fZone, i)
+        pointField zonePoints(zonePointIndices.size());
+        forAll(zonePointIndices, i)
         {
-            const face& f = mesh_.faces()[fZone[i]];
-
-            forAll(f, fp)
-            {
-                label meshPointI = f[fp];
-
-                Map<label>::const_iterator iter =
-                    pp.meshPointMap().find(meshPointI);
-
-                if (iter != pp.meshPointMap().end())
-                {
-                    label pointI = iter();
-
-                    pointIndexHit pHit;
+            zonePoints[i] = localPoints[zonePointIndices[i]];
+        }
 
-                    label surfI = surfaces().findNearest
-                    (
-                        surfacesToTest,
-                        localPoints[pointI],
-                        sqr(4*snapDist[pointI]),    // sqr of attract distance
-                        pHit
-                    );
+        // Find nearest for points both on faceZone and pp.
+        List<pointIndexHit> hitInfo;
+        labelList hitSurface;
+        surfaces.findNearest
+        (
+            labelList(1, zoneSurfI),
+            zonePoints,
+            sqr(4*snapDist),
+            hitSurface,
+            hitInfo
+        );
 
-                    if (surfI != -1)
-                    {
-                        patchDisp[pointI] =
-                            pHit.hitPoint() - localPoints[pointI];
-                    }
-                    else
-                    {
-                        WarningIn("autoHexMeshDriver::calcNearestSurface(..)")
-                            << "For point:" << pointI
-                            << " coordinate:" << localPoints[pointI]
-                            << " did not find any surface within:"
-                            << 4*snapDist[pointI]
-                            << " meter." << endl;
-                    }
-                }
+        forAll(hitInfo, pointI)
+        {
+            if (hitInfo[pointI].hit())
+            {
+                patchDisp[pointI] =
+                    hitInfo[pointI].hitPoint()
+                  - localPoints[pointI];
+            }
+            else
+            {
+                WarningIn("autoSnapDriver::calcNearestSurface(..)")
+                    << "For point:" << pointI
+                    << " coordinate:" << localPoints[pointI]
+                    << " did not find any surface within:"
+                    << 4*snapDist[pointI]
+                    << " meter." << endl;
             }
         }
     }
@@ -1023,7 +1121,7 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
     }
 
     Info<< "Calculated surface displacement in = "
-        << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+        << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
 
 
     // Limit amount of movement.
@@ -1045,7 +1143,7 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
     // will not do condition 2 on all. Sync explicitly.
     syncTools::syncPointList
     (
-        mesh_,
+        mesh,
         pp.meshPoints(),
         patchDisp,
         minMagEqOp(),                   // combine op
@@ -1062,11 +1160,11 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
     // pointVectorField.
     meshMover.setDisplacement(patchDisp);
 
-    if (debug_)
+    if (debug)
     {
         dumpMove
         (
-            mesh_.time().path()/"patchDisplacement.obj",
+            mesh.time().path()/"patchDisplacement.obj",
             pp.localPoints(),
             pp.localPoints() + patchDisp
         );
@@ -1076,29 +1174,27 @@ Foam::vectorField Foam::autoHexMeshDriver::calcNearestSurface
 }
 
 
-void Foam::autoHexMeshDriver::smoothDisplacement
+void Foam::autoSnapDriver::smoothDisplacement
 (
-    const dictionary& snapDict,
+    const snapParameters& snapParams,
     motionSmoother& meshMover
 ) const
 {
+    const fvMesh& mesh = meshRefiner_.mesh();
     const pointMesh& pMesh = meshMover.pMesh();
     const indirectPrimitivePatch& pp = meshMover.patch();
 
     Info<< "Smoothing displacement ..." << endl;
 
-    // Smoothing iterations
-    label nSmoothDisp(readLabel(snapDict.lookup("nSmoothDispl")));
-
     // Set edge diffusivity as inverse of distance to patch
     scalarField edgeGamma(1.0/(edgePatchDist(pMesh, pp) + SMALL));
-    //scalarField edgeGamma(mesh_.nEdges(), 1.0);
+    //scalarField edgeGamma(mesh.nEdges(), 1.0);
     //scalarField edgeGamma(wallGamma(mesh, pp, 10, 1));
 
     // Get displacement field
     pointVectorField& disp = meshMover.displacement();
 
-    for (label iter = 0; iter < nSmoothDisp; iter++)
+    for (label iter = 0; iter < snapParams.nSmoothDispl(); iter++)
     {
         if ((iter % 10) == 0)
         {
@@ -1109,14 +1205,14 @@ void Foam::autoHexMeshDriver::smoothDisplacement
         meshMover.smooth(oldDisp, edgeGamma, false, disp);
     }
     Info<< "Displacement smoothed in = "
-        << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+        << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
 
-    if (debug_)
+    if (debug)
     {
-        const_cast<Time&>(mesh_.time())++;
-        Pout<< "Writing smoothed mesh to time " << mesh_.time().timeName()
+        const_cast<Time&>(mesh.time())++;
+        Pout<< "Writing smoothed mesh to time " << mesh.time().timeName()
             << endl;
-        mesh_.write();
+        mesh.write();
 
         Pout<< "Writing displacement field ..." << endl;
         disp.write();
@@ -1130,7 +1226,7 @@ void Foam::autoHexMeshDriver::smoothDisplacement
         );
         dumpMove
         (
-            mesh_.time().path()/"actualPatchDisplacement.obj",
+            mesh.time().path()/"actualPatchDisplacement.obj",
             pp.localPoints(),
             pp.localPoints() + actualPatchDisp
         );
@@ -1138,30 +1234,28 @@ void Foam::autoHexMeshDriver::smoothDisplacement
 }
 
 
-void Foam::autoHexMeshDriver::scaleMesh
+void Foam::autoSnapDriver::scaleMesh
 (
-    const dictionary& snapDict,
+    const snapParameters& snapParams,
     const label nInitErrors,
     const List<labelPair>& baffles,
     motionSmoother& meshMover
 )
 {
-    // Snapping iterations
-    label nSnap(readLabel(snapDict.lookup("nSnap")));
-
+    const fvMesh& mesh = meshRefiner_.mesh();
 
     // Relax displacement until correct mesh
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    labelList checkFaces(identity(mesh_.nFaces()));
+    labelList checkFaces(identity(mesh.nFaces()));
 
     scalar oldErrorReduction = -1;
 
     Info<< "Moving mesh ..." << endl;
-    for (label iter = 0; iter < 2*nSnap; iter++)
+    for (label iter = 0; iter < 2*snapParams.nSnap(); iter++)
     {
         Info<< nl << "Iteration " << iter << endl;
 
-        if (iter == nSnap)
+        if (iter == snapParams.nSnap())
         {
             Info<< "Displacement scaling for error reduction set to 0." << endl;
             oldErrorReduction = meshMover.setErrorReduction(0.0);
@@ -1173,12 +1267,12 @@ void Foam::autoHexMeshDriver::scaleMesh
 
             break;
         }
-        if (debug_)
+        if (debug)
         {
-            const_cast<Time&>(mesh_.time())++;
-            Pout<< "Writing scaled mesh to time " << mesh_.time().timeName()
+            const_cast<Time&>(mesh.time())++;
+            Pout<< "Writing scaled mesh to time " << mesh.time().timeName()
                 << endl;
-            mesh_.write();
+            mesh.write();
 
             Pout<< "Writing displacement field ..." << endl;
             meshMover.displacement().write();
@@ -1192,7 +1286,99 @@ void Foam::autoHexMeshDriver::scaleMesh
         meshMover.setErrorReduction(oldErrorReduction);
     }
     Info<< "Moved mesh in = "
-        << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl;
+        << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
+}
+
+
+void Foam::autoSnapDriver::doSnap
+(
+    const dictionary& snapDict,
+    const dictionary& motionDict,
+    const snapParameters& snapParams
+)
+{
+    fvMesh& mesh = meshRefiner_.mesh();
+
+    Info<< nl
+        << "Morphing phase" << nl
+        << "--------------" << nl
+        << endl;
+
+    const_cast<Time&>(mesh.time())++;
+
+    // Get the labels of added patches.
+    labelList adaptPatchIDs(meshRefinement::addedPatches(globalToPatch_));
+
+    // Create baffles (pairs of faces that share the same points)
+    // Baffles stored as owner and neighbour face that have been created.
+    List<labelPair> baffles;
+    createZoneBaffles(baffles);
+
+    {
+        autoPtr<indirectPrimitivePatch> ppPtr
+        (
+            meshRefinement::makePatch
+            (
+                mesh,
+                adaptPatchIDs
+            )
+        );
+        indirectPrimitivePatch& pp = ppPtr();
+
+        // Distance to attact to nearest feature on surface
+        const scalarField snapDist(calcSnapDistance(snapParams, pp));
+
+
+        // Construct iterative mesh mover.
+        Info<< "Constructing mesh displacer ..." << endl;
+        Info<< "Using mesh parameters " << motionDict << nl << endl;
+
+        pointMesh pMesh(mesh);
+
+        motionSmoother meshMover
+        (
+            mesh,
+            pp,
+            adaptPatchIDs,
+            meshRefinement::makeDisplacementField(pMesh, adaptPatchIDs),
+            motionDict
+        );
+
+
+        // Check initial mesh
+        Info<< "Checking initial mesh ..." << endl;
+        labelHashSet wrongFaces(mesh.nFaces()/100);
+        motionSmoother::checkMesh(false, mesh, motionDict, wrongFaces);
+        const label nInitErrors = returnReduce
+        (
+            wrongFaces.size(),
+            sumOp<label>()
+        );
+
+        Info<< "Detected " << nInitErrors << " illegal faces"
+            << " (concave, zero area or negative cell pyramid volume)"
+            << endl;
+
+
+        Info<< "Checked initial mesh in = "
+            << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
+
+        // Pre-smooth patch vertices (so before determining nearest)
+        preSmoothPatch(snapParams, nInitErrors, baffles, meshMover);
+
+        // Calculate displacement at every patch point. Insert into
+        // meshMover.
+        calcNearestSurface(snapDist, meshMover);
+
+        // Get smoothly varying internal displacement field.
+        smoothDisplacement(snapParams, meshMover);
+
+        // Apply internal displacement to mesh.
+        scaleMesh(snapParams, nInitErrors, baffles, meshMover);
+    }
+
+    // Merge any introduced baffles.
+    mergeZoneBaffles(baffles);
 }
 
 
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H
new file mode 100644
index 0000000000000000000000000000000000000000..82cf448b98ae6b506fea1bf81bc16f2bd17e5dd7
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H
@@ -0,0 +1,236 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::autoSnapDriver
+
+Description
+    All to do with snapping to surface
+
+SourceFiles
+    autoSnapDriver.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef autoSnapDriver_H
+#define autoSnapDriver_H
+
+#include "meshRefinement.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class motionSmoother;
+class snapParameters;
+
+/*---------------------------------------------------------------------------*\
+                           Class autoSnapDriver Declaration
+\*---------------------------------------------------------------------------*/
+
+class autoSnapDriver
+{
+    // Private classes
+
+        //- Combine operator class for equalizing displacements.
+        class minMagEqOp
+        {
+        public:
+
+            void operator()(vector& x, const vector& y) const
+            {
+                if (magSqr(y) < magSqr(x))
+                {
+                    x = y;
+                }
+            }
+        };
+
+
+    // Private data
+
+        //- Mesh+surface
+        meshRefinement& meshRefiner_;
+
+        //- From surface region to patch
+        const labelList globalToPatch_;
+
+
+    // Private Member Functions
+
+
+        // Snapping
+
+             //- Split surfaces into non-zoned and zones ones
+            void getZonedSurfaces(labelList&, labelList&) const;
+
+            //- Get faces to repatch. Returns map from face to patch.
+            Map<label> getZoneBafflePatches(const bool allowBoundary) const;
+
+            //- Calculates (geometric) shared points
+            static label getCollocatedPoints
+            (
+                const scalar tol,
+                const pointField&,
+                PackedList<1>&
+            );
+
+            //- Calculate displacement per patch point to smooth out patch.
+            //  Quite complicated in determining which points to move where.
+            pointField smoothPatchDisplacement(const motionSmoother&) const;
+
+            //- Check that face zones are synced
+            void checkCoupledFaceZones() const;
+
+            //- Per edge distance to patch
+            static tmp<scalarField> edgePatchDist
+            (
+                const pointMesh&,
+                const indirectPrimitivePatch&
+            );
+
+            //- Write displacement as .obj file.
+            static void dumpMove
+            (
+                const fileName&,
+                const pointField&,
+                const pointField&
+            );
+
+            //- Check displacement is outwards pointing
+            static bool outwardsDisplacement
+            (
+                const indirectPrimitivePatch&,
+                const vectorField&
+            );
+
+
+        //- Disallow default bitwise copy construct
+        autoSnapDriver(const autoSnapDriver&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const autoSnapDriver&);
+
+
+public:
+
+    //- Runtime type information
+    ClassName("autoSnapDriver");
+
+
+    // Constructors
+
+        //- Construct from components
+        autoSnapDriver
+        (
+            meshRefinement& meshRefiner,
+            const labelList& globalToPatch
+        );
+
+
+    // Member Functions
+
+        // Snapping
+
+            //- Create baffles for faces straddling zoned surfaces. Return
+            //  baffles.
+            autoPtr<mapPolyMesh> createZoneBaffles(List<labelPair>&);
+
+            //- Merge baffles.
+            autoPtr<mapPolyMesh> mergeZoneBaffles(const List<labelPair>&);
+
+            //- Calculate edge length per patch point.
+            scalarField calcSnapDistance
+            (
+                const snapParameters& snapParams,
+                const indirectPrimitivePatch&
+            ) const;
+
+            ////- Get patches generated for surfaces.
+            //labelList getSurfacePatches() const;
+
+            //- Smooth the mesh (patch and internal) to increase visibility
+            //  of surface points (on castellated mesh) w.r.t. surface.
+            void preSmoothPatch
+            (
+                const snapParameters& snapParams,
+                const label nInitErrors,
+                const List<labelPair>& baffles,
+                motionSmoother&
+            ) const;
+
+            //- Get points both on patch and facezone.
+            labelList getZoneSurfacePoints
+            (
+                const indirectPrimitivePatch&,
+                const word& zoneName
+            ) const;
+
+            //- Per patch point calculate point on nearest surface. Set as
+            //  boundary conditions of motionSmoother displacement field. Return
+            //  displacement of patch points.
+            vectorField calcNearestSurface
+            (
+                const scalarField& snapDist,
+                motionSmoother& meshMover
+            ) const;
+
+            //- Smooth the displacement field to the internal.
+            void smoothDisplacement
+            (
+                const snapParameters& snapParams,
+                motionSmoother&
+            ) const;
+
+            //- Do the hard work: move the mesh according to displacement,
+            //  locally relax the displacement.
+            void scaleMesh
+            (
+                const snapParameters& snapParams,
+                const label nInitErrors,
+                const List<labelPair>& baffles,
+                motionSmoother&
+            );
+
+            void doSnap
+            (
+                const dictionary& snapDict,
+                const dictionary& motionDict,
+                const snapParameters& snapParams
+            );
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C
new file mode 100644
index 0000000000000000000000000000000000000000..a2fd5ca976afe7c1891bdaf20e8b5b5dfdaed26d
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C
@@ -0,0 +1,327 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "layerParameters.H"
+#include "polyBoundaryMesh.H"
+#include "mathematicalConstants.H"
+#include "refinementSurfaces.H"
+#include "searchableSurfaces.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const Foam::scalar Foam::layerParameters::defaultConcaveAngle = 90;
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// Read the number of layers from dictionary. Per patch 0 or the number
+// of layers.
+Foam::labelList Foam::layerParameters::readNumLayers
+(
+    const PtrList<dictionary>& surfaceDicts,
+    const refinementSurfaces& refineSurfaces,
+    const labelList& globalToPatch,
+    const polyBoundaryMesh& boundaryMesh
+)
+{
+    // Per surface the number of layers
+    labelList globalSurfLayers(surfaceDicts.size());
+    // Per surface, per region the number of layers
+    List<Map<label> > regionSurfLayers(surfaceDicts.size());
+
+    const labelList& surfaceIndices = refineSurfaces.surfaces();
+
+    forAll(surfaceDicts, surfI)
+    {
+        const dictionary& dict = surfaceDicts[surfI];
+
+        globalSurfLayers[surfI] = readLabel(dict.lookup("surfaceLayers"));
+
+        if (dict.found("regions"))
+        {
+            // Per-region layer information
+
+            PtrList<dictionary> regionDicts(dict.lookup("regions"));
+
+            const wordList& regionNames =
+                refineSurfaces.geometry()[surfaceIndices[surfI]].regions();
+
+            forAll(regionDicts, dictI)
+            {
+                const dictionary& regionDict = regionDicts[dictI];
+
+                const word regionName(regionDict.lookup("name"));
+
+                label regionI = findIndex(regionNames, regionName);
+
+                label nLayers = readLabel(regionDict.lookup("surfaceLayers"));
+
+                Info<< "    region " << regionName << ':'<< nl
+                    << "        surface layers:" << nLayers << nl;
+
+                regionSurfLayers[surfI].insert(regionI, nLayers);
+            }
+        }
+    }
+
+
+    // Transfer per surface/region information into patchwise region info
+
+    labelList nLayers(boundaryMesh.size(), 0);
+
+    forAll(surfaceIndices, surfI)
+    {
+        const wordList& regionNames =
+            refineSurfaces.geometry()[surfaceIndices[surfI]].regions();
+
+        forAll(regionNames, regionI)
+        {
+            const word& regionName = regionNames[regionI];
+
+            label global = refineSurfaces.globalRegion(surfI, regionI);
+
+            label patchI = globalToPatch[global];
+
+            // Initialise to surface-wise layers
+            nLayers[patchI] = globalSurfLayers[surfI];
+
+            // Override with region specific data if available
+            Map<label>::const_iterator iter =
+                regionSurfLayers[surfI].find(regionI);
+
+            if (iter != regionSurfLayers[surfI].end())
+            {
+                nLayers[patchI] = iter();
+            }
+
+            // Check
+            if (nLayers[patchI] < 0)
+            {
+                FatalErrorIn
+                (
+                    "layerParameters::readNumLayers(..)"
+                )   << "Illegal number of layers " << nLayers[patchI]
+                    << " for surface "
+                    << refineSurfaces.names()[surfI]
+                    << " region " << regionName << endl
+                    << exit(FatalError);
+            }
+        }
+    }
+    return nLayers;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct from dictionary
+Foam::layerParameters::layerParameters
+(
+    const PtrList<dictionary>& surfaceDicts,
+    const refinementSurfaces& refineSurfaces,
+    const labelList& globalToPatch,
+    const dictionary& dict,
+    const polyBoundaryMesh& boundaryMesh
+)
+:
+    numLayers_
+    (
+        readNumLayers
+        (
+            surfaceDicts,
+            refineSurfaces,
+            globalToPatch,
+            boundaryMesh
+        )
+    ),
+    expansionRatio_
+    (
+        numLayers_.size(),
+        readScalar(dict.lookup("expansionRatio"))
+    ),
+    finalLayerRatio_
+    (
+        numLayers_.size(),
+        readScalar(dict.lookup("finalLayerRatio"))
+    ),
+    minThickness_
+    (
+        numLayers_.size(),
+        readScalar(dict.lookup("minThickness"))
+    ),
+    featureAngle_(readScalar(dict.lookup("featureAngle"))),
+    concaveAngle_
+    (
+        dict.found("concaveAngle")
+      ? readScalar(dict.lookup("concaveAngle"))
+      : defaultConcaveAngle
+    ),
+    nGrow_(readLabel(dict.lookup("nGrow"))),
+    nSmoothSurfaceNormals_
+    (
+        readLabel(dict.lookup("nSmoothSurfaceNormals"))
+    ),
+    nSmoothNormals_(readLabel(dict.lookup("nSmoothNormals"))),
+    nSmoothThickness_(readLabel(dict.lookup("nSmoothThickness"))),
+    maxFaceThicknessRatio_
+    (
+        readScalar(dict.lookup("maxFaceThicknessRatio"))
+    ),
+    layerTerminationCos_
+    (
+        Foam::cos
+        (
+            0.5
+          * featureAngle_
+          * mathematicalConstant::pi/180.
+        )
+    ),
+    maxThicknessToMedialRatio_
+    (
+        readScalar(dict.lookup("maxThicknessToMedialRatio"))
+    ),
+    minMedianAxisAngleCos_
+    (
+        Foam::cos(readScalar(dict.lookup("minMedianAxisAngle")))
+      * mathematicalConstant::pi/180.
+    ),
+    nBufferCellsNoExtrude_
+    (
+        readLabel(dict.lookup("nBufferCellsNoExtrude"))
+    ),
+    nSnap_(readLabel(dict.lookup("nSnap")))
+{}
+
+
+// Construct from dictionary
+Foam::layerParameters::layerParameters
+(
+    const dictionary& dict,
+    const polyBoundaryMesh& boundaryMesh
+)
+:
+    numLayers_(boundaryMesh.size(), 0),
+    expansionRatio_
+    (
+        boundaryMesh.size(),
+        readScalar(dict.lookup("expansionRatio"))
+    ),
+    finalLayerRatio_
+    (
+        boundaryMesh.size(),
+        readScalar(dict.lookup("finalLayerRatio"))
+    ),
+    minThickness_
+    (
+        boundaryMesh.size(),
+        readScalar(dict.lookup("minThickness"))
+    ),
+    featureAngle_(readScalar(dict.lookup("featureAngle"))),
+    concaveAngle_
+    (
+        dict.found("concaveAngle")
+      ? readScalar(dict.lookup("concaveAngle"))
+      : defaultConcaveAngle
+    ),
+    nGrow_(readLabel(dict.lookup("nGrow"))),
+    nSmoothSurfaceNormals_
+    (
+        readLabel(dict.lookup("nSmoothSurfaceNormals"))
+    ),
+    nSmoothNormals_(readLabel(dict.lookup("nSmoothNormals"))),
+    nSmoothThickness_(readLabel(dict.lookup("nSmoothThickness"))),
+    maxFaceThicknessRatio_
+    (
+        readScalar(dict.lookup("maxFaceThicknessRatio"))
+    ),
+    layerTerminationCos_
+    (
+        Foam::cos
+        (
+            0.5
+          * featureAngle_
+          * mathematicalConstant::pi/180.
+        )
+    ),
+    maxThicknessToMedialRatio_
+    (
+        readScalar(dict.lookup("maxThicknessToMedialRatio"))
+    ),
+    minMedianAxisAngleCos_
+    (
+        Foam::cos(readScalar(dict.lookup("minMedianAxisAngle")))
+      * mathematicalConstant::pi/180.
+    ),
+    nBufferCellsNoExtrude_
+    (
+        readLabel(dict.lookup("nBufferCellsNoExtrude"))
+    ),
+    nSnap_(readLabel(dict.lookup("nRelaxIter")))
+{
+    const dictionary& layersDict = dict.subDict("layers");
+
+    forAllConstIter(dictionary, layersDict, iter)
+    {
+        const word& key = iter().keyword();
+
+        if (layersDict.isDict(key))
+        {
+            label patchI = boundaryMesh.findPatchID(key);
+
+            if (patchI == -1)
+            {
+                FatalErrorIn
+                (
+                    "layerParameters::layerParameters"
+                    "(const dictionary&, const polyBoundaryMesh&)"
+                )   << "Specified illegal patch " << key
+                    << " in layer dictionary." << endl
+                    << "Valid patch names are " << boundaryMesh.names()
+                    << exit(FatalError);
+            }
+
+            const dictionary& layerDict = layersDict.subDict(key);
+
+            numLayers_[patchI] =
+                readLabel(layerDict.lookup("nSurfaceLayers"));
+
+            //- Patch-wise layer parameters disabled for now. Just remove
+            //  settings in initialiser list and uncomment below.
+            //expansionRatio_[patchI] =
+            //    readScalar(layerDict.lookup("expansionRatio"));
+            //finalLayerRatio_[patchI] =
+            //    readScalar(layerDict.lookup("finalLayerRatio"));
+            //minThickness_[patchI] =
+            //    readScalar(layerDict.lookup("minThickness"));
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H
new file mode 100644
index 0000000000000000000000000000000000000000..2e391328e16bc8a59774c9e16f1fdc4881b93bb7
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H
@@ -0,0 +1,263 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::layerParameters
+
+Description
+    Simple container to keep together layer specific information.
+
+SourceFiles
+    layerParameters.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef layerParameters_H
+#define layerParameters_H
+
+#include "dictionary.H"
+#include "scalarField.H"
+#include "labelList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Class forward declarations
+class polyBoundaryMesh;
+class refinementSurfaces;
+
+/*---------------------------------------------------------------------------*\
+                           Class layerParameters Declaration
+\*---------------------------------------------------------------------------*/
+
+class layerParameters
+{
+    // Static data members
+
+        //- Default angle for faces to be convcave
+        static const scalar defaultConcaveAngle;
+
+
+    // Private data
+
+        // Per patch (not region!) information
+
+            //- How many layers to add.
+            labelList numLayers_;
+
+            scalarField expansionRatio_;
+
+            //- Wanted thickness of final added cell layer. If multiple layers
+            //  is the thickness of the layer furthest away from the wall.
+            //  Relative to undistorted size of cell outside layer.
+            scalarField finalLayerRatio_;
+
+            //- Minimum thickness of cell layer. If for any reason layer
+            //  cannot be above minThickness do not add layer.
+            //  Relative to undistorted size of cell outside layer.
+            scalarField minThickness_;
+
+
+        scalar featureAngle_;
+
+        scalar concaveAngle_;
+
+        label nGrow_;
+
+        label nSmoothSurfaceNormals_;
+
+        label nSmoothNormals_;
+
+        label nSmoothThickness_;
+
+        scalar maxFaceThicknessRatio_;
+
+        scalar layerTerminationCos_;
+
+        scalar maxThicknessToMedialRatio_;
+
+        scalar minMedianAxisAngleCos_;
+
+        label nBufferCellsNoExtrude_;
+
+        label nSnap_;
+
+
+    // Private Member Functions
+
+        //- Extract patch-wise number of layers
+        static labelList readNumLayers
+        (
+            const PtrList<dictionary>& surfaceDicts,
+            const refinementSurfaces& refineSurfaces,
+            const labelList& globalToPatch,
+            const polyBoundaryMesh& boundaryMesh
+        );
+
+        //- Disallow default bitwise copy construct
+        layerParameters(const layerParameters&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const layerParameters&);
+
+
+public:
+
+    // Constructors
+
+        //- Construct from dictionary - old syntax
+        layerParameters
+        (
+            const PtrList<dictionary>& surfaceDicts,
+            const refinementSurfaces& refineSurfaces,
+            const labelList& globalToPatch,
+            const dictionary& dict,
+            const polyBoundaryMesh& boundaryMesh
+        );
+
+        //- Construct from dictionary - new syntax
+        layerParameters(const dictionary& dict, const polyBoundaryMesh&);
+
+
+    // Member Functions
+
+        // Access
+
+            // Per patch information
+
+                //- How many layers to add.
+                const labelList& numLayers() const
+                {
+                    return numLayers_;
+                }
+
+                // Expansion factor for layer mesh
+                const scalarField& expansionRatio() const
+                {
+                    return expansionRatio_;
+                }
+
+                //- Wanted thickness of final added cell layer. If multiple
+                //  layers
+                //  is the thickness of the layer furthest away from the wall.
+                //  Relative to undistorted size of cell outside layer.
+                const scalarField& finalLayerRatio() const
+                {
+                    return finalLayerRatio_;
+                }
+
+                //- Minimum thickness of cell layer. If for any reason layer
+                //  cannot be above minThickness do not add layer.
+                //  Relative to undistorted size of cell outside layer.
+                const scalarField& minThickness() const
+                {
+                    return minThickness_;
+                }
+
+
+            scalar featureAngle() const
+            {
+                return featureAngle_;
+            }
+
+            scalar concaveAngle() const
+            {
+                return concaveAngle_;
+            }
+
+            //- If points get not extruded do nGrow layers of connected faces
+            //  that are not grown. Is used to not do layers at all close to
+            //  features.
+            label nGrow() const
+            {
+                return nGrow_;
+            }
+
+            // Number of smoothing iterations of surface normals 
+            label nSmoothSurfaceNormals() const
+            {
+                return nSmoothSurfaceNormals_;
+            }
+
+            // Number of smoothing iterations of interior mesh movement
+            // direction  
+            label nSmoothNormals() const
+            {
+                return nSmoothNormals_;
+            }
+
+            // Smooth layer thickness over surface patches
+            label nSmoothThickness() const
+            {
+                return nSmoothThickness_;
+            }
+
+            // Stop layer growth on highly warped cells 
+            scalar maxFaceThicknessRatio() const
+            {
+                return maxFaceThicknessRatio_;
+            }
+
+            scalar layerTerminationCos() const
+            {
+                return layerTerminationCos_;
+            }
+
+            // Reduce layer growth where ratio thickness to medial 
+            // distance is large 
+            scalar maxThicknessToMedialRatio() const
+            {
+                return maxThicknessToMedialRatio_;
+            }
+
+            // Angle used to pick up medial axis points
+            scalar minMedianAxisAngleCos() const
+            {
+                return minMedianAxisAngleCos_;
+            }
+
+            // Create buffer region for new layer terminations
+            label nBufferCellsNoExtrude() const
+            {
+                return nBufferCellsNoExtrude_;
+            }
+
+            label nSnap() const
+            {
+                return nSnap_;
+            }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C
new file mode 100644
index 0000000000000000000000000000000000000000..7912279e017eefb0d30f3a56af5a7526d112abf3
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C
@@ -0,0 +1,123 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "refinementParameters.H"
+#include "mathematicalConstants.H"
+#include "polyMesh.H"
+#include "globalIndex.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct from dictionary
+Foam::refinementParameters::refinementParameters
+(
+    const dictionary& dict,
+    const label dummy
+)
+:
+    maxGlobalCells_(readLabel(dict.lookup("cellLimit"))),
+    maxLocalCells_(readLabel(dict.lookup("procCellLimit"))),
+    minRefineCells_(readLabel(dict.lookup("minimumRefine"))),
+    curvature_(readScalar(dict.lookup("curvature"))),
+    nBufferLayers_(readLabel(dict.lookup("nBufferLayers"))),
+    keepPoints_(dict.lookup("keepPoints"))
+{}
+
+
+Foam::refinementParameters::refinementParameters(const dictionary& dict)
+:
+    maxGlobalCells_(readLabel(dict.lookup("maxGlobalCells"))),
+    maxLocalCells_(readLabel(dict.lookup("maxLocalCells"))),
+    minRefineCells_(readLabel(dict.lookup("minRefinementCells"))),
+    nBufferLayers_(readLabel(dict.lookup("nCellsBetweenLevels"))),
+    keepPoints_(pointField(1, dict.lookup("locationInMesh")))
+{
+    scalar featAngle(readScalar(dict.lookup("resolveFeatureAngle")));
+
+    if (featAngle < 0 || featAngle > 180)
+    {
+        curvature_ = -GREAT;
+    }
+    else
+    {
+        curvature_ = Foam::cos(featAngle*mathematicalConstant::pi/180.0);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::labelList Foam::refinementParameters::findCells(const polyMesh& mesh)
+ const
+{
+    // Global calculation engine
+    globalIndex globalCells(mesh.nCells());
+
+    // Cell label per point
+    labelList cellLabels(keepPoints_.size());
+
+    forAll(keepPoints_, i)
+    {
+        const point& keepPoint = keepPoints_[i];
+
+        label localCellI = mesh.findCell(keepPoint);
+
+        label globalCellI = -1;
+
+        if (localCellI != -1)
+        {
+            Pout<< "Found point " << keepPoint << " in cell " << localCellI
+                << " on processor " << Pstream::myProcNo() << endl;
+            globalCellI = globalCells.toGlobal(localCellI);
+        }
+
+        reduce(globalCellI, maxOp<label>());
+
+        if (globalCellI == -1)
+        {
+            FatalErrorIn
+            (
+                "refinementParameters::findCells(const polyMesh&) const"
+            )   << "Point " << keepPoint
+                << " is not inside the mesh or on a face or edge." << nl
+                << "Bounding box of the mesh:" << mesh.bounds()
+                << exit(FatalError);
+        }
+
+        if (globalCells.isLocal(globalCellI))
+        {
+            cellLabels[i] = localCellI;
+        }
+        else
+        {
+            cellLabels[i] = -1;
+        }
+    }
+    return cellLabels;
+}
+
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H
new file mode 100644
index 0000000000000000000000000000000000000000..25b97af0346fa6afb41d8e90297502a69493232d
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H
@@ -0,0 +1,154 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::refinementParameters
+
+Description
+    Simple container to keep together refinement specific information.
+
+SourceFiles
+    refinementParameters.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef refinementParameters_H
+#define refinementParameters_H
+
+#include "dictionary.H"
+#include "pointField.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Class forward declarations
+class polyMesh;
+
+/*---------------------------------------------------------------------------*\
+                           Class refinementParameters Declaration
+\*---------------------------------------------------------------------------*/
+
+class refinementParameters
+{
+    // Private data
+
+        //- Total number of cells
+        const label maxGlobalCells_;
+
+        //- Per processor max number of cells
+        const label maxLocalCells_;
+
+        //- When to stop refining
+        const label minRefineCells_;
+
+        //- Curvature
+        scalar curvature_;
+
+        //- Number of layers between different refinement levels
+        const label nBufferLayers_;
+
+        //- Areas to keep
+        const pointField keepPoints_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        refinementParameters(const refinementParameters&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const refinementParameters&);
+
+
+public:
+
+    // Constructors
+
+        //- Construct from dictionary - old syntax
+        refinementParameters(const dictionary& dict, const label dummy);
+
+        //- Construct from dictionary - new syntax
+        refinementParameters(const dictionary& dict);
+
+
+    // Member Functions
+
+        // Access
+
+            //- Total number of cells
+            label maxGlobalCells() const
+            {
+                return maxGlobalCells_;
+            }
+
+            //- Per processor max number of cells
+            label maxLocalCells() const
+            {
+                return maxLocalCells_;
+            }
+
+            //- When to stop refining
+            label minRefineCells() const
+            {
+                return minRefineCells_;
+            }
+
+            //- Curvature
+            scalar curvature() const
+            {
+                return curvature_;
+            }
+
+            //- Number of layers between different refinement levels
+            label nBufferLayers() const
+            {
+                return nBufferLayers_;
+            }
+
+            //- Areas to keep
+            const pointField& keepPoints() const
+            {
+                return keepPoints_;
+            }
+
+
+        // Other
+
+            //- Checks that cells are in mesh. Returns cells they are in.
+            labelList findCells(const polyMesh&) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C b/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C
new file mode 100644
index 0000000000000000000000000000000000000000..253826c38fddfdb52c106951353469ee46f21c3c
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "snapParameters.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct from dictionary
+Foam::snapParameters::snapParameters(const dictionary& dict, const label dummy)
+:
+    nSmoothPatch_(readLabel(dict.lookup("nSmoothPatch"))),
+    snapTol_(readScalar(dict.lookup("snapTol"))),
+    nSmoothDispl_(readLabel(dict.lookup("nSmoothDispl"))),
+    nSnap_(readLabel(dict.lookup("nSnap")))
+{}
+
+
+// Construct from dictionary
+Foam::snapParameters::snapParameters(const dictionary& dict)
+:
+    nSmoothPatch_(readLabel(dict.lookup("nSmoothPatch"))),
+    snapTol_(readScalar(dict.lookup("tolerance"))),
+    nSmoothDispl_(readLabel(dict.lookup("nSolveIter"))),
+    nSnap_(readLabel(dict.lookup("nRelaxIter")))
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H b/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H
new file mode 100644
index 0000000000000000000000000000000000000000..2a173578b0c5e336e8fe888d2b63c35b8346287c
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::snapParameters
+
+Description
+    Simple container to keep together snap specific information.
+
+SourceFiles
+    snapParameters.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef snapParameters_H
+#define snapParameters_H
+
+#include "dictionary.H"
+#include "scalar.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Class forward declarations
+
+/*---------------------------------------------------------------------------*\
+                           Class snapParameters Declaration
+\*---------------------------------------------------------------------------*/
+
+class snapParameters
+{
+    // Private data
+
+        const label nSmoothPatch_;
+
+        const scalar snapTol_;
+
+        const label nSmoothDispl_;
+
+        const label nSnap_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        snapParameters(const snapParameters&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const snapParameters&);
+
+
+public:
+
+    // Constructors
+
+        //- Construct from dictionary - old syntax
+        snapParameters(const dictionary& dict, const label dummy);
+
+        //- Construct from dictionary - new syntax
+        snapParameters(const dictionary& dict);
+
+
+    // Member Functions
+
+        // Access
+
+            //- Number of patch smoothing iterations before finding
+            //  correspondence to surface
+            label nSmoothPatch() const
+            {
+                return nSmoothPatch_;
+            }
+
+            //- Relative distance for points to be attracted by surface
+            //  feature point
+            //  or edge. True distance is this factor times local
+            //  maximum edge length.
+            scalar snapTol() const
+            {
+                return snapTol_;
+            }
+
+            //- Number of mesh displacement smoothing iterations.
+            label nSmoothDispl() const
+            {
+                return nSmoothDispl_;
+            }
+
+            //- Maximum number of snapping relaxation iterations. Should stop
+            //  before upon reaching a correct mesh.
+            label nSnap() const
+            {
+                return nSnap_;
+            }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C
index 8b9869ad57f0b28b9cf0a8a7df5ce2940ead8d73..4fe3005766f4f224c2fd219d7f27451d6a4fa290 100644
--- a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C
+++ b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C
@@ -24,9 +24,7 @@ License
 
 \*----------------------------------------------------------------------------*/
 
-#include "Pstream.H"
 #include "meshRefinement.H"
-
 #include "volMesh.H"
 #include "volFields.H"
 #include "surfaceMesh.H"
@@ -34,7 +32,6 @@ License
 #include "Time.H"
 #include "refinementHistory.H"
 #include "refinementSurfaces.H"
-#include "cellSet.H"
 #include "decompositionMethod.H"
 #include "regionSplit.H"
 #include "fvMeshDistribute.H"
@@ -50,8 +47,9 @@ License
 #include "globalPointPatchFields.H"
 #include "calculatedPointPatchFields.H"
 #include "processorPointPatch.H"
-#include "featureEdgeMesh.H"
 #include "globalIndex.H"
+#include "meshTools.H"
+#include "OFstream.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -117,90 +115,6 @@ void Foam::meshRefinement::calcNeighbourData
 }
 
 
-//void Foam::meshRefinement::calcCanonicalBoundaryData
-//(
-//    labelList& leftLevel,
-//    pointField& leftCc,
-//    labelList& rightLevel,
-//    pointField& rightCc
-//)  const
-//{
-//    const labelList& cellLevel = meshCutter_.cellLevel();
-//    const pointField& cellCentres = mesh_.cellCentres();
-//    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
-//
-//    // Get boundary face centre and level. Coupled aware.
-//    calcNeighbourData(rightLevel, rightCc);
-//
-//    // Get owner face centre and level. Canonical sort for coupled faces.
-//    forAll(patches, patchI)
-//    {
-//        const polyPatch& pp = patches[patchI];
-//
-//        const unallocLabelList& faceCells = pp.faceCells();
-//        const vectorField::subField faceCentres = pp.faceCentres();
-//
-//        if (isA<processorPolyPatch>(pp))
-//        {
-//            const processorPolyPatch& procPp =
-//                refCast<const processorPolyPatch>(patches[patchI]);
-//
-//            label bFaceI = pp.start()-mesh_.nInternalFaces();
-//            forAll(faceCells, i)
-//            {
-//                leftLevel[bFaceI] = cellLevel[faceCells[i]];
-//                leftCc[bFaceI] = cellCentres[faceCells[i]];
-//                bFaceI++;
-//            }
-//
-//            // Swap if on neighbour so both sides have same values
-//            if (!procPp.owner())
-//            {
-//                forAll(leftLevel, i)
-//                {
-//                    Swap(leftLevel[i], rightLevel[i]);
-//                    Swap(leftCc[i], rightCc[i]);
-//                }
-//            }
-//        }
-//        else if (isA<cyclicPolyPatch>(pp))
-//        {
-//            const processorPolyPatch& cycPp =
-//                refCast<const processorPolyPatch>(patches[patchI]);
-//
-//            label bFaceI = pp.start()-mesh_.nInternalFaces();
-//            forAll(faceCells, i)
-//            {
-//                leftLevel[bFaceI] = cellLevel[faceCells[i]];
-//                leftCc[bFaceI] = cellCentres[faceCells[i]];
-//                bFaceI++;
-//            }
-//
-//            // First half has data in normal order: owner in leftLevel,leftCc
-//            // and coupled data in rightLevel, rightCc. Second half has it
-//            // swapped:
-//            bFaceI = pp.start()+cycPp.size()/2-mesh_.nInternalFaces();
-//            for (label i = 0; i < cycPp.size()/2; i++)
-//            {
-//                Swap(leftLevel[bFaceI], rightLevel[bFaceI]);
-//                Swap(leftCc[bFaceI], rightCc[bFaceI]);
-//                bFaceI++;
-//            }
-//        }
-//        else
-//        {
-//            label bFaceI = pp.start()-mesh_.nInternalFaces();
-//            forAll(faceCells, i)
-//            {
-//                leftLevel[bFaceI] = cellLevel[faceCells[i]];
-//                leftCc[bFaceI] = cellCentres[faceCells[i]];
-//                bFaceI++;
-//            }
-//        }
-//    }
-//}
-
-
 // Find intersections of edges (given by their two endpoints) with surfaces.
 // Returns first intersection if there are more than one.
 void Foam::meshRefinement::updateIntersections(const labelList& changedFaces)
@@ -220,33 +134,46 @@ void Foam::meshRefinement::updateIntersections(const labelList& changedFaces)
     pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
     calcNeighbourData(neiLevel, neiCc);
 
+    // Collect segments we want to test for
+    pointField start(changedFaces.size());
+    pointField end(changedFaces.size());
+
     forAll(changedFaces, i)
     {
         label faceI = changedFaces[i];
         label own = mesh_.faceOwner()[faceI];
 
-        pointIndexHit surfaceHitInfo;
-
+        start[i] = cellCentres[own];
         if (mesh_.isInternalFace(faceI))
         {
-            surfaceIndex_[faceI] = surfaces_.findAnyIntersection
-            (
-                cellCentres[own],
-                cellCentres[mesh_.faceNeighbour()[faceI]],
-                surfaceHitInfo
-            );
+            end[i] = cellCentres[mesh_.faceNeighbour()[faceI]];
         }
         else
         {
-            surfaceIndex_[faceI] = surfaces_.findAnyIntersection
-            (
-                cellCentres[own],
-                neiCc[faceI-mesh_.nInternalFaces()],
-                surfaceHitInfo
-            );
+            end[i] = neiCc[faceI-mesh_.nInternalFaces()];
         }
     }
 
+    // Do tests in one go
+    labelList surfaceHit;
+    {
+        labelList surfaceLevel;
+        surfaces_.findHigherIntersection
+        (
+            start,
+            end,
+            labelList(start.size(), -1),    // accept any intersection
+            surfaceHit,
+            surfaceLevel
+        );
+    }
+
+    // Keep just surface hit
+    forAll(surfaceHit, i)
+    {
+        surfaceIndex_[changedFaces[i]] = surfaceHit[i];
+    }
+
     // Make sure both sides have same information. This should be
     // case in general since same vectors but just to make sure.
     syncTools::syncFaceList(mesh_, surfaceIndex_, maxEqOp<label>(), false);
@@ -297,7 +224,7 @@ void Foam::meshRefinement::checkData()
         // Compare
         testSyncBoundaryFaceList
         (
-            tol_,
+            mergeDistance_,
             "testing faceCentres : ",
             boundaryFc,
             neiBoundaryFc
@@ -305,67 +232,72 @@ void Foam::meshRefinement::checkData()
     }
     // Check meshRefinement
     {
-        for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
-        {
-            label own = mesh_.faceOwner()[faceI];
-            label nei = mesh_.faceNeighbour()[faceI];
-            const point& ownCc = mesh_.cellCentres()[own];
-            const point& neiCc = mesh_.cellCentres()[nei];
-
-            pointIndexHit surfaceHitInfo;
-            label surfI = surfaces_.findAnyIntersection
-            (
-                ownCc,
-                neiCc,
-                surfaceHitInfo
-            );
-
-            if (surfI != surfaceIndex_[faceI])
-            {
-                WarningIn("meshRefinement::checkData()")
-                    << "Internal face:" << faceI
-                    << " fc:" << mesh_.faceCentres()[faceI]
-                    << " cached surfaceIndex_:" << surfaceIndex_[faceI]
-                    << " current:" << surfI
-                    << " ownCc:" << ownCc << " neiCc:" << neiCc
-                    << endl;
-            }
-        }
-
         // Get boundary face centre and level. Coupled aware.
         labelList neiLevel(mesh_.nFaces()-mesh_.nInternalFaces());
         pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
         calcNeighbourData(neiLevel, neiCc);
 
-        for
-        (
-            label faceI = mesh_.nInternalFaces();
-            faceI < mesh_.nFaces();
-            faceI++
-        )
+        // Collect segments we want to test for
+        pointField start(mesh_.nFaces());
+        pointField end(mesh_.nFaces());
+
+        forAll(start, faceI)
         {
-            label own = mesh_.faceOwner()[faceI];
-            const point& ownCc = mesh_.cellCentres()[own];
+            start[faceI] = mesh_.cellCentres()[mesh_.faceOwner()[faceI]];
 
-            pointIndexHit surfaceHitInfo;
-            label surfI = surfaces_.findAnyIntersection
+            if (mesh_.isInternalFace(faceI))
+            {
+                end[faceI] = mesh_.cellCentres()[mesh_.faceNeighbour()[faceI]];
+            }
+            else
+            {
+                end[faceI] = neiCc[faceI-mesh_.nInternalFaces()];
+            }
+        }
+
+        // Do tests in one go
+        labelList surfaceHit;
+        {
+            labelList surfaceLevel;
+            surfaces_.findHigherIntersection
             (
-                ownCc,
-                neiCc[faceI-mesh_.nInternalFaces()],
-                surfaceHitInfo
+                start,
+                end,
+                labelList(start.size(), -1),    // accept any intersection
+                surfaceHit,
+                surfaceLevel
             );
+        }
 
-            if (surfI != surfaceIndex_[faceI])
+        // Check
+        forAll(surfaceHit, faceI)
+        {
+            if (surfaceHit[faceI] != surfaceIndex_[faceI])
             {
-                WarningIn("meshRefinement::checkData()")
-                    << "Boundary face:" << faceI
-                    << " patch:" << mesh_.boundaryMesh().whichPatch(faceI)
-                    << " fc:" << mesh_.faceCentres()[faceI]
-                    << " cached surfaceIndex_:" << surfaceIndex_[faceI]
-                    << " current:" << surfI
-                    << " ownCc:" << ownCc
-                    << " neiCc:" << neiCc[faceI-mesh_.nInternalFaces()]
-                    << endl;
+                if (mesh_.isInternalFace(faceI))
+                {
+                    WarningIn("meshRefinement::checkData()")
+                        << "Internal face:" << faceI
+                        << " fc:" << mesh_.faceCentres()[faceI]
+                        << " cached surfaceIndex_:" << surfaceIndex_[faceI]
+                        << " current:" << surfaceHit[faceI]
+                        << " ownCc:"
+                        << mesh_.cellCentres()[mesh_.faceOwner()[faceI]]
+                        << " neiCc:"
+                        << mesh_.cellCentres()[mesh_.faceNeighbour()[faceI]]
+                        << endl;
+                }
+                else
+                {
+                    WarningIn("meshRefinement::checkData()")
+                        << "Boundary face:" << faceI
+                        << " fc:" << mesh_.faceCentres()[faceI]
+                        << " cached surfaceIndex_:" << surfaceIndex_[faceI]
+                        << " current:" << surfaceHit[faceI]
+                        << " ownCc:"
+                        << mesh_.cellCentres()[mesh_.faceOwner()[faceI]]
+                        << endl;
+                }
             }
         }
     }
@@ -492,283 +424,6 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::doRemoveCells
 }
 
 
-// Get which are inside any closed surface. Note that all closed surfaces
-// will have already been oriented to have keepPoint outside.
-Foam::labelList Foam::meshRefinement::getInsideCells
-(
-    const word& postfix
-) const
-{
-    const pointField& points = mesh_.points();
-
-    // This test can be done in various ways. The ultimate is the intersection
-    // of any edge of the mesh with any surface and intersection of any edge
-    // of any surface of the mesh with any mesh face.
-    // For now:
-    // - none of the edges of a cell intersects any surface
-    // - any point of the cell is inside.
-
-
-    // Is point inside any closed surface?
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    PackedList<1> pointIsInside;
-    label nPointInside = surfaces_.markInsidePoints
-    (
-       points,
-       pointIsInside
-    );
-
-    if (debug)
-    {
-        Pout<< "getInsideCells : Points internal to closed surfaces :"
-            << " local:" << nPointInside
-            << " global:" << returnReduce(nPointInside, sumOp<label>())
-            << endl;
-    }
-
-
-    // Find cells with all points inside closed surface
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    PackedList<1> cellIsInside(mesh_.nCells(), 1u);
-    label nCellInside = mesh_.nCells();
-
-    // Unmark cells with any point outside
-    forAll(points, pointI)
-    {
-        if (pointIsInside.get(pointI) == 0u)
-        {
-            // Outside point. Unmark cells.
-            const labelList& pCells = mesh_.pointCells()[pointI];
-
-            forAll(pCells, i)
-            {
-                label cellI = pCells[i];
-
-                if (cellIsInside.get(cellI) == 1u)
-                {
-                    cellIsInside.set(cellI, 0u);
-                    nCellInside--;
-                }
-            }
-        }
-    }
-
-    label totNCellInside = nCellInside;
-    reduce(totNCellInside, sumOp<label>());
-    if (debug)
-    {
-        Pout<< "getInsideCells : Cells using inside points only :"
-            << " local:" << nCellInside << " global:" << totNCellInside
-            << endl;
-    }
-
-    // Filter any cells with cc-cc edge intersection
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    const cellList& cells = mesh_.cells();
-
-    cellSet insidePoints(mesh_, "insidePoints_"+postfix, mesh_.nCells()/100);
-    cellSet insidePointsButCut
-    (
-        mesh_,
-        "insidePointsButCut_"+postfix,
-        mesh_.nCells()/100
-    );
-
-    forAll(cells, cellI)
-    {
-        if (cellIsInside.get(cellI) == 1u)
-        {
-            insidePoints.insert(cellI);
-
-            const cell& cFaces = cells[cellI];
-
-            forAll(cFaces, i)
-            {
-                if (surfaceIndex_[cFaces[i]] != -1)
-                {
-                    cellIsInside.set(cellI, 0u);
-                    nCellInside--;
-
-                    insidePointsButCut.insert(cellI);
-
-                    break;
-                }
-            }
-        }
-    }
-
-    if (debug)
-    {
-        Pout<< "getInsideCells : cells with all points inside : "
-            << insidePoints.objectPath() << endl;
-        insidePoints.write();
-        Pout<< "getInsideCells : cells with all points inside"
-            << " but intersecting (cc) surface : "
-            << insidePointsButCut.objectPath() << endl;
-        insidePointsButCut.write();
-    }
-
-    totNCellInside = nCellInside;
-    reduce(totNCellInside, sumOp<label>());
-    if (debug)
-    {
-        Pout<< "getInsideCells : After filtering cc-cc intersections :"
-            << " local:" << nCellInside << " global:" << totNCellInside
-            << endl;
-    }
-
-
-    // Filter cells with any normal edge intersection
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    cellSet insidePointsButEdge
-    (
-        mesh_,
-        "insidePointsButEdge_"+postfix,
-        mesh_.nCells()/100
-    );
-
-    const labelListList& edgeCells = mesh_.edgeCells();
-
-    forAll(edgeCells, edgeI)
-    {
-        const labelList& eCells = edgeCells[edgeI];
-
-        // Does edge use any inside cells? Prefilter since edge intersection
-        // test expensive.
-        bool edgeHasInsideCells = false;
-
-        forAll(eCells, i)
-        {
-            if (cellIsInside.get(eCells[i]) == 1u)
-            {
-                edgeHasInsideCells = true;
-                break;
-            }
-        }
-
-        if (edgeHasInsideCells)
-        {
-            const edge& e = mesh_.edges()[edgeI];
-
-            // Find any pierces of surface by mesh edge.
-            pointIndexHit hit;
-            label surfI = surfaces().findHigherIntersection
-            (
-                points[e[0]],
-                points[e[1]],
-                -1,                     // minimum level.
-                hit
-            );
-
-            if (surfI != -1)
-            {
-                // This edge intersects some surface. Unmark all cells
-                // using this edge.
-
-                forAll(eCells, i)
-                {
-                    if (cellIsInside.get(eCells[i]) == 1u)
-                    {
-                        cellIsInside.set(eCells[i], 0u);
-                        nCellInside--;
-
-                        insidePointsButEdge.insert(eCells[i]);
-                    }
-                }
-            }
-        }
-    }
-
-    if (debug)
-    {
-        Pout<< "getInsideCells : cells with all points inside"
-            << " but intersecting (edges) surface : "
-            << insidePointsButEdge.objectPath() << endl;
-        insidePointsButEdge.write();
-    }
-
-    totNCellInside = nCellInside;
-    reduce(totNCellInside, sumOp<label>());
-    if (debug)
-    {
-        Pout<< "getInsideCells : After filtering edge interesections :"
-            << " local:" << nCellInside << " global:" << totNCellInside
-            << endl;
-    }
-
-
-    // Collect cells
-    // ~~~~~~~~~~~~~
-
-    DynamicList<label> cellsToRemove(nCellInside);
-
-    forAll(cellIsInside, cellI)
-    {
-        if (cellIsInside.get(cellI) == 1u)
-        {
-            cellsToRemove.append(cellI);
-        }
-    }
-    return cellsToRemove.shrink();
-}
-
-
-// Do all to remove inside cells
-Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeInsideCells
-(
-    const string& msg,
-    const label exposedPatchI
-)
-{
-    labelList insideCells
-    (
-        getInsideCells
-        (
-            string::validate<word>(msg)
-        )
-    );
-
-    Info<< "Removing inside cells : "
-        << returnReduce(insideCells.size(), sumOp<label>())
-        << '.' << endl;
-
-    removeCells cellRemover(mesh_);
-
-    labelList exposedFaces(cellRemover.getExposedFaces(insideCells));
-
-    // Debug check
-    forAll(exposedFaces, i)
-    {
-        label faceI = exposedFaces[i];
-
-        if (surfaceIndex_[faceI] != -1)
-        {
-            FatalErrorIn
-            (
-                "removeInsideCells"
-                "(const label, const string&, const label)"
-            )   << "Face:" << faceI
-                << " fc:" << mesh_.faceCentres()[faceI]
-                << " is actually cutting the surface and should never have been"
-                << " included in the cells to remove."
-                << abort(FatalError);
-        }
-    }
-
-    return doRemoveCells
-    (
-        insideCells,
-        exposedFaces,
-        labelList(exposedFaces.size(), exposedPatchI),
-        cellRemover
-    );
-}
-
-
 // Determine for multi-processor regions the lowest numbered cell on the lowest
 // numbered processor.
 void Foam::meshRefinement::getRegionMaster
@@ -834,13 +489,15 @@ void Foam::meshRefinement::getRegionMaster
 Foam::meshRefinement::meshRefinement
 (
     fvMesh& mesh,
-    const scalar tol,
-    const refinementSurfaces& surfaces
+    const scalar mergeDistance,
+    const refinementSurfaces& surfaces,
+    const shellSurfaces& shells
 )
 :
     mesh_(mesh),
-    tol_(tol),
+    mergeDistance_(mergeDistance),
     surfaces_(surfaces),
+    shells_(shells),
     meshCutter_
     (
         mesh,
@@ -926,109 +583,6 @@ Foam::label Foam::meshRefinement::countHits() const
 }
 
 
-//// Determine distribution to keep baffles together
-//Foam::labelList Foam::meshRefinement::decomposePreserveBaffles
-//(
-//    decompositionMethod& decomposer
-//) const
-//{
-//    // Find duplicate faces
-//    labelList duplicateFace
-//    (
-//        localPointRegion::findDuplicateFaces
-//        (
-//            mesh_,
-//            identity(mesh_.nFaces()-mesh_.nInternalFaces())
-//          + mesh_.nInternalFaces()
-//        )
-//    );
-//
-//
-//    // Agglomerate cells on both sides of duplicate face pair
-//
-//    const pointField& cellCentres = mesh_.cellCentres();
-//    const labelList& faceOwner = mesh_.faceOwner();
-//
-//    labelList toAgglom(mesh_.nCells(), -1);
-//    pointField agglomCellCentres(mesh_.nCells());
-//    labelList nAgglomCellCentres(mesh_.nCells(), 0);
-//
-//    label agglomI = 0;
-//
-//    // Do all baffle cells
-//    forAll(duplicateFace, i)
-//    {
-//        if (duplicateFace[i] != -1)
-//        {
-//            label own = faceOwner[i+mesh_.nInternalFaces()];
-//            label otherOwn =
-//                faceOwner[duplicateFace[i]+mesh_.nInternalFaces()];
-//
-//            if (toAgglom[own] == -1 && toAgglom[otherOwn] == -1)
-//            {
-//                // Allocate new agglomeration
-//                agglomCellCentres[agglomI] =
-//                    cellCentres[own]
-//                  + cellCentres[otherOwn];
-//                nAgglomCellCentres[agglomI] = 2;
-//                toAgglom[own] = agglomI;
-//                toAgglom[otherOwn] = agglomI;
-//                agglomI++;
-//            }
-//            else if (toAgglom[own] != -1)
-//            {
-//                // Owner already part of agglomeration. Add otherOwn to it.
-//                label destAgglom = toAgglom[own];
-//                agglomCellCentres[destAgglom] += cellCentres[otherOwn];
-//                nAgglomCellCentres[destAgglom]++;
-//                toAgglom[otherOwn] = destAgglom;
-//            }
-//            else if (toAgglom[otherOwn] != -1)
-//            {
-//                // otherOwn already part of agglomeration. Add own to it.
-//                label destAgglom = toAgglom[otherOwn];
-//                agglomCellCentres[destAgglom] += cellCentres[own];
-//                nAgglomCellCentres[destAgglom]++;
-//                toAgglom[own] = destAgglom;
-//            }
-//        }
-//    }
-//
-//    // Do all other cells
-//    forAll(toAgglom, cellI)
-//    {
-//        if (toAgglom[cellI] == -1)
-//        {
-//            agglomCellCentres[agglomI] = cellCentres[cellI];
-//            nAgglomCellCentres[agglomI] = 1;
-//            toAgglom[cellI] = agglomI;
-//            agglomI++;
-//        }
-//    }
-//
-//    // Average
-//    agglomCellCentres.setSize(agglomI);
-//
-//    forAll(agglomCellCentres, i)
-//    {
-//        agglomCellCentres[i] /= nAgglomCellCentres[i];
-//    }
-//
-//    // Decompose based on agglomerated cell centres
-//    labelList agglomDistribution(decomposer.decompose(agglomCellCentres));
-//
-//    // Rework back into decomposition for original mesh_
-//    labelList distribution(mesh_.nCells());
-//
-//    forAll(toAgglom, cellI)
-//    {
-//        distribution[cellI] = agglomDistribution[toAgglom[cellI]];
-//    }
-//
-//    return distribution;
-//}
-
-
 // Determine distribution to move connected regions onto one processor.
 Foam::labelList Foam::meshRefinement::decomposeCombineRegions
 (
@@ -1164,6 +718,141 @@ Foam::labelList Foam::meshRefinement::decomposeCombineRegions
 }
 
 
+Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::meshRefinement::balance
+(
+    const bool keepZoneFaces,
+    const bool keepBaffles,
+    decompositionMethod& decomposer,
+    fvMeshDistribute& distributor
+)
+{
+    autoPtr<mapDistributePolyMesh> map;
+
+    if (Pstream::parRun())
+    {
+        //Info<< nl
+        //    << "Doing final balancing" << nl
+        //    << "---------------------" << nl
+        //    << endl;
+        //
+        //if (debug_)
+        //{
+        //    const_cast<Time&>(mesh_.time())++;
+        //}
+
+        // Wanted distribution
+        labelList distribution;
+
+        if (keepZoneFaces || keepBaffles)
+        {
+            // Faces where owner and neighbour are not 'connected' so can
+            // go to different processors.
+            boolList blockedFace(mesh_.nFaces(), true);
+            // Pairs of baffles
+            List<labelPair> couples;
+
+            if (keepZoneFaces)
+            {
+                label nNamed = surfaces().getNamedSurfaces().size();
+
+                Info<< "Found " << nNamed << " surfaces with faceZones."
+                    << " Applying special decomposition to keep those together."
+                    << endl;
+
+                // Determine decomposition to keep/move surface zones
+                // on one processor. The reason is that snapping will make these
+                // into baffles, move and convert them back so if they were
+                // proc boundaries after baffling&moving the points might be no
+                // longer snychronised so recoupling will fail. To prevent this
+                // keep owner&neighbour of such a surface zone on the same
+                // processor.
+
+                const wordList& fzNames = surfaces().faceZoneNames();
+                const faceZoneMesh& fZones = mesh_.faceZones();
+
+                // Get faces whose owner and neighbour should stay together,
+                // i.e. they are not 'blocked'.
+
+                label nZoned = 0;
+
+                forAll(fzNames, surfI)
+                {
+                    if (fzNames[surfI].size() > 0)
+                    {
+                        // Get zone
+                        label zoneI = fZones.findZoneID(fzNames[surfI]);
+
+                        const faceZone& fZone = fZones[zoneI];
+
+                        forAll(fZone, i)
+                        {
+                            if (blockedFace[fZone[i]])
+                            {
+                                blockedFace[fZone[i]] = false;
+                                nZoned++;
+                            }
+                        }
+                    }
+                }
+                Info<< "Found " << returnReduce(nZoned, sumOp<label>())
+                    << " zoned faces to keep together."
+                    << endl;
+            }
+
+            if (keepBaffles)
+            {
+                // Get boundary baffles that need to stay together.
+                couples = getDuplicateFaces   // all baffles
+                (
+                    identity(mesh_.nFaces()-mesh_.nInternalFaces())
+                   +mesh_.nInternalFaces()
+                );
+
+                Info<< "Found " << returnReduce(couples.size(), sumOp<label>())
+                    << " baffles to keep together."
+                    << endl;
+            }
+
+            distribution = decomposeCombineRegions
+            (
+                blockedFace,
+                couples,
+                decomposer
+            );
+
+            labelList nProcCells(distributor.countCells(distribution));
+            Pstream::listCombineGather(nProcCells, plusEqOp<label>());
+            Pstream::listCombineScatter(nProcCells);
+
+            Info<< "Calculated decomposition:" << endl;
+            forAll(nProcCells, procI)
+            {
+                Info<< "    " << procI << '\t' << nProcCells[procI] << endl;
+            }
+            Info<< endl;
+        }
+        else
+        {
+            // Normal decomposition
+            distribution = decomposer.decompose(mesh_.cellCentres());
+        }
+
+        if (debug)
+        {
+            Pout<< "Wanted distribution:"
+                << distributor.countCells(distribution)
+                << endl;
+        }
+        // Do actual sending/receiving of mesh
+        map = distributor.distribute(distribution);
+
+        // Update numbering of meshRefiner
+        distribute(map);
+    }
+    return map;
+}
+
+
 // Helper function to get intersected faces
 Foam::labelList Foam::meshRefinement::intersectedFaces() const
 {
@@ -1213,9 +902,8 @@ Foam::labelList Foam::meshRefinement::intersectedPoints
 
             forAll(f, fp)
             {
-                if (isBoundaryPoint.get(f[fp]) == 0u)
+                if (isBoundaryPoint.set(f[fp], 1u))
                 {
-                    isBoundaryPoint.set(f[fp], 1u);
                     nBoundaryPoints++;
                 }
             }
@@ -1239,9 +927,7 @@ Foam::labelList Foam::meshRefinement::intersectedPoints
     //
     //            forAll(f, fp)
     //            {
-    //                if (isBoundaryPoint.get(f[fp]) == 0u)
-    //                {
-    //                    isBoundaryPoint.set(f[fp], 1u);
+    //                if (isBoundaryPoint.set(f[fp], 1u))
     //                    nBoundaryPoints++;
     //                }
     //            }
@@ -1390,6 +1076,101 @@ Foam::tmp<Foam::pointVectorField> Foam::meshRefinement::makeDisplacementField
 }
 
 
+void Foam::meshRefinement::checkCoupledFaceZones(const polyMesh& mesh)
+{
+    const faceZoneMesh& fZones = mesh.faceZones();
+
+    // Check any zones are present anywhere and in same order
+
+    {
+        List<wordList> zoneNames(Pstream::nProcs());
+        zoneNames[Pstream::myProcNo()] = fZones.names();
+        Pstream::gatherList(zoneNames);
+        Pstream::scatterList(zoneNames);
+        // All have same data now. Check.
+        forAll(zoneNames, procI)
+        {
+            if (procI != Pstream::myProcNo())
+            {
+                if (zoneNames[procI] != zoneNames[Pstream::myProcNo()])
+                {
+                    FatalErrorIn
+                    (
+                        "meshRefinement::checkCoupledFaceZones(const polyMesh&)"
+                    )   << "faceZones are not synchronised on processors." << nl
+                        << "Processor " << procI << " has faceZones "
+                        << zoneNames[procI] << nl
+                        << "Processor " << Pstream::myProcNo()
+                        << " has faceZones "
+                        << zoneNames[Pstream::myProcNo()] << nl
+                        << exit(FatalError);
+                }
+            }
+        }
+    }
+
+    // Check that coupled faces are present on both sides.
+
+    labelList faceToZone(mesh.nFaces()-mesh.nInternalFaces(), -1);
+
+    forAll(fZones, zoneI)
+    {
+        const faceZone& fZone = fZones[zoneI];
+
+        forAll(fZone, i)
+        {
+            label bFaceI = fZone[i]-mesh.nInternalFaces();
+
+            if (bFaceI >= 0)
+            {
+                if (faceToZone[bFaceI] == -1)
+                {
+                    faceToZone[bFaceI] = zoneI;
+                }
+                else if (faceToZone[bFaceI] == zoneI)
+                {
+                    FatalErrorIn
+                    (
+                        "meshRefinement::checkCoupledFaceZones(const polyMesh&)"
+                    )   << "Face " << fZone[i] << " in zone "
+                        << fZone.name()
+                        << " is twice in zone!"
+                        << abort(FatalError);
+                }
+                else
+                {
+                    FatalErrorIn
+                    (
+                        "meshRefinement::checkCoupledFaceZones(const polyMesh&)"
+                    )   << "Face " << fZone[i] << " in zone "
+                        << fZone.name()
+                        << " is also in zone "
+                        << fZones[faceToZone[bFaceI]].name()
+                        << abort(FatalError);
+                }
+            }
+        }
+    }
+
+    labelList neiFaceToZone(faceToZone);
+    syncTools::swapBoundaryFaceList(mesh, neiFaceToZone, false);
+
+    forAll(faceToZone, i)
+    {
+        if (faceToZone[i] != neiFaceToZone[i])
+        {
+            FatalErrorIn
+            (
+                "meshRefinement::checkCoupledFaceZones(const polyMesh&)"
+            )   << "Face " << mesh.nInternalFaces()+i
+                << " is in zone " << faceToZone[i]
+                << ", its coupled face is in zone " << neiFaceToZone[i]
+                << abort(FatalError);
+        }
+    }
+}
+
+
 // Adds patch if not yet there. Returns patchID.
 Foam::label Foam::meshRefinement::addPatch
 (
@@ -1910,53 +1691,56 @@ void Foam::meshRefinement::dumpIntersections(const fileName& prefix) const
             << " Writing cellcentre-cellcentre intersections to file "
             << str.name() << endl;
 
-        // Internal faces
-        for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
-        {
-            if (surfaceIndex_[faceI] != -1)
-            {
-                const point& ownCc = cellCentres[mesh_.faceOwner()[faceI]];
-                const point& neiCc = cellCentres[mesh_.faceNeighbour()[faceI]];
-
-                // Re-intersect to get position
-                pointIndexHit surfaceHitInfo;
-                surfaces_.findAnyIntersection(ownCc, neiCc, surfaceHitInfo);
-
-                meshTools::writeOBJ(str, ownCc);
-                vertI++;
-                meshTools::writeOBJ(str, surfaceHitInfo.hitPoint());
-                vertI++;
-                meshTools::writeOBJ(str, neiCc);
-                vertI++;
-                str << "l " << vertI-2 << ' ' << vertI-1 << nl
-                    << "l " << vertI-1 << ' ' << vertI << nl;
-            }
-        }
 
-        // Boundary faces
+        // Redo all intersections
+        // ~~~~~~~~~~~~~~~~~~~~~~
 
         // Get boundary face centre and level. Coupled aware.
         labelList neiLevel(mesh_.nFaces()-mesh_.nInternalFaces());
         pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
         calcNeighbourData(neiLevel, neiCc);
 
-        forAll(neiCc, i)
+        labelList intersectionFaces(intersectedFaces());
+
+        // Collect segments we want to test for
+        pointField start(intersectionFaces.size());
+        pointField end(intersectionFaces.size());
+
+        forAll(intersectionFaces, i)
         {
-            label faceI = i + mesh_.nInternalFaces();
+            label faceI = intersectionFaces[i];
+            start[i] = cellCentres[mesh_.faceOwner()[faceI]];
 
-            if (surfaceIndex_[faceI] != -1)
+            if (mesh_.isInternalFace(faceI))
             {
-                const point& ownCc = cellCentres[mesh_.faceOwner()[faceI]];
+                end[i] = cellCentres[mesh_.faceNeighbour()[faceI]];
+            }
+            else
+            {
+                end[i] = neiCc[faceI-mesh_.nInternalFaces()];
+            }
+        }
 
-                // Re-intersect to get position
-                pointIndexHit surfaceHitInfo;
-                surfaces_.findAnyIntersection(ownCc, neiCc[i], surfaceHitInfo);
+        // Do tests in one go
+        labelList surfaceHit;
+        List<pointIndexHit> surfaceHitInfo;
+        surfaces_.findAnyIntersection
+        (
+            start,
+            end,
+            surfaceHit,
+            surfaceHitInfo
+        );
 
-                meshTools::writeOBJ(str, ownCc);
+        forAll(intersectionFaces, i)
+        {
+            if (surfaceHit[i] != -1)
+            {
+                meshTools::writeOBJ(str, start[i]);
                 vertI++;
-                meshTools::writeOBJ(str, surfaceHitInfo.hitPoint());
+                meshTools::writeOBJ(str, surfaceHitInfo[i].hitPoint());
                 vertI++;
-                meshTools::writeOBJ(str, neiCc[i]);
+                meshTools::writeOBJ(str, end[i]);
                 vertI++;
                 str << "l " << vertI-2 << ' ' << vertI-1 << nl
                     << "l " << vertI-1 << ' ' << vertI << nl;
@@ -1996,13 +1780,4 @@ void Foam::meshRefinement::write
 }
 
 
-// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Functions  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
-
-
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H
index 78fed5349306a3f57daf6a6fe10d17f4c84cbc30..d27eaae413946987954c4a168144fe67b1d64849 100644
--- a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H
+++ b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H
@@ -30,7 +30,7 @@ Description
     (static) surfaces.
 
     Maintains
-    - per face any intersections of this edge with any of the surfaces
+    - per face any intersections of the cc-cc segment with any of the surfaces
 
 SourceFiles
     meshRefinement.C
@@ -62,6 +62,7 @@ class fvMesh;
 class mapDistributePolyMesh;
 class decompositionMethod;
 class refinementSurfaces;
+class shellSurfaces;
 class removeCells;
 class featureEdgeMesh;
 class fvMeshDistribute;
@@ -93,7 +94,7 @@ public:
         {
             MASTERONLY = 1, /*!< maintain master only */
             KEEPALL = 2,    /*!< have slaves (upon refinement) from master */
-            REMOVE = 4      /*!< set value to -1 any face that has been refined */
+            REMOVE = 4      /*!< set value to -1 any face that was refined */
         };
 
 
@@ -105,10 +106,14 @@ private:
         fvMesh& mesh_;
 
         //- tolerance used for sorting coordinates (used in 'less' routine)
-        const scalar tol_;
+        const scalar mergeDistance_;
 
+        //- All surface-intersection interaction
         const refinementSurfaces& surfaces_;
 
+        //- All shell-refinement interaction
+        const shellSurfaces& shells_;
+
         //- refinement engine
         hexRef8 meshCutter_;
 
@@ -229,14 +234,18 @@ private:
             //- Mark cells for refinement-shells based refinement.
             label markInternalRefinement
             (
-                const PtrList<searchableSurface>&,
-                const labelList& shellLevels,
-                const boolList& shellRefineInside,
                 const label nAllowRefine,
                 labelList& refineCell,
                 label& nRefine
             ) const;
 
+            //- Collect faces that are intersected and whose neighbours aren't
+            //  yet marked  for refinement.
+            labelList getRefineCandidateFaces
+            (
+                const labelList& refineCell
+            ) const;
+
             //- Mark cells for surface intersection based refinement.
             label markSurfaceRefinement
             (
@@ -252,17 +261,13 @@ private:
             //  regions.
             bool checkCurvature
             (
-                const labelList& globalToPatch,
                 const scalar curvature,
-                const bool markDifferingRegions,
                 const label nAllowRefine,
-                const label newSurfI,
-                const label newTriI,
+                const label surfaceLevel,
+                const vector& surfaceNormal,
                 const label cellI,
-
-                label& maxCellSurfI,
-                label& maxCellTriI,
-
+                label& cellMaxLevel,
+                vector& cellMaxNormal,
                 labelList& refineCell,
                 label& nRefine
             ) const;
@@ -273,9 +278,7 @@ private:
             //  (markDifferingRegions)
             label markSurfaceCurvatureRefinement
             (
-                const labelList& globalToPatch,
                 const scalar curvature,
-                const bool markDifferingRegions,
                 const label nAllowRefine,
                 const labelList& neiLevel,
                 const pointField& neiCc,
@@ -380,8 +383,9 @@ public:
         meshRefinement
         (
             fvMesh& mesh,
-            const scalar tol,
-            const refinementSurfaces&
+            const scalar mergeDistance,
+            const refinementSurfaces&,
+            const shellSurfaces&
         );
 
 
@@ -399,12 +403,23 @@ public:
                 return mesh_;
             }
 
+            scalar mergeDistance() const
+            {
+                return mergeDistance_;
+            }
+
             //- reference to surface search engines
             const refinementSurfaces& surfaces() const
             {
                 return surfaces_;
             }
 
+            //- reference to refinement shells (regions)
+            const shellSurfaces& shells() const
+            {
+                return shells_;
+            }
+
             //- reference to meshcutting engine
             const hexRef8& meshCutter() const
             {
@@ -454,6 +469,18 @@ public:
                 decompositionMethod&
             ) const;
 
+            //- Redecompose according to cell count
+            //  keepZoneFaces : find all faceZones from zoned surfaces and keep
+            //                  owner and neighbour together
+            //  keepBaffles   : find all baffles and keep them together
+            autoPtr<mapDistributePolyMesh> balance
+            (
+                const bool keepZoneFaces,
+                const bool keepBaffles,
+                decompositionMethod& decomposer,
+                fvMeshDistribute& distributor
+            );
+
             //- Get faces with intersection.
             labelList intersectedFaces() const;
 
@@ -482,22 +509,21 @@ public:
                 const labelList& adaptPatchIDs
             );
 
+            //- Helper function: check that face zones are synced
+            static void checkCoupledFaceZones(const polyMesh&);
+
+
         // Refinement
 
             //- Calculate list of cells to refine.
             labelList refineCandidates
             (
                 const point& keepPoint,
-                const labelList& globalToPatch,
                 const scalar curvature,
 
                 const PtrList<featureEdgeMesh>& featureMeshes,
                 const labelList& featureLevels,
 
-                const PtrList<searchableSurface>&,
-                const labelList& shellLevels,
-                const boolList& shellRefineInside,
-
                 const bool featureRefinement,
                 const bool internalRefinement,
                 const bool surfaceRefinement,
@@ -626,7 +652,7 @@ public:
             template<class T>
             void testSyncBoundaryFaceList
             (
-                const scalar tol,
+                const scalar mergeDistance,
                 const string&,
                 const UList<T>&,
                 const UList<T>&
diff --git a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C
index 17eaaeaa275cce90b0802adcef1712f934a4c154..a92f61a78adbf93d7cdaf3079069c006fe5e9ca8 100644
--- a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C
+++ b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C
@@ -249,7 +249,7 @@ void Foam::meshRefinement::getBafflePatches
             if (debug)
             {
                 Pout<< "getBafflePatches : Not baffling surface "
-                    << surfaces_[surfI].searchableSurface::name() << endl;
+                    << surfaces_.names()[surfI] << endl;
             }
         }
         else
@@ -264,69 +264,95 @@ void Foam::meshRefinement::getBafflePatches
     neiPatch.setSize(mesh_.nFaces());
     neiPatch = -1;
 
-    forAll(surfaceIndex_, faceI)
+
+    // Collect candidate faces
+    // ~~~~~~~~~~~~~~~~~~~~~~~
+
+    labelList testFaces(intersectedFaces());
+
+    // Collect segments
+    // ~~~~~~~~~~~~~~~~
+
+    pointField start(testFaces.size());
+    pointField end(testFaces.size());
+
+    forAll(testFaces, i)
     {
-        if (surfaceIndex_[faceI] != -1)
-        {
-            // Edge between owner and neighbour of face pierces a surface.
-            // Do closer inspection to find nearest intersection on both sides.
+        label faceI = testFaces[i];
 
-            label own = mesh_.faceOwner()[faceI];
+        label own = mesh_.faceOwner()[faceI];
 
-            // Cc of neighbouring cell
-            point end
-            (
-                mesh_.isInternalFace(faceI)
-              ? cellCentres[mesh_.faceNeighbour()[faceI]]
-              : neiCc[faceI-mesh_.nInternalFaces()]
-            );
+        if (mesh_.isInternalFace(faceI))
+        {
+            start[i] = cellCentres[own];
+            end[i] = cellCentres[mesh_.faceNeighbour()[faceI]];
+        }
+        else
+        {
+            start[i] = cellCentres[own];
+            end[i] = neiCc[faceI-mesh_.nInternalFaces()];
+        }
+    }
 
-            label surface1, surface2;
-            pointIndexHit hit1, hit2;
-            surfaces_.findNearestIntersection
-            (
-                surfacesToBaffle,
-                cellCentres[own],
-                end,
 
-                surface1,
-                hit1,
-                surface2,
-                hit2
-            );
+    // Do test for intersections
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~
+    labelList surface1;
+    List<pointIndexHit> hit1;
+    labelList region1;
+    labelList surface2;
+    List<pointIndexHit> hit2;
+    labelList region2;
+    surfaces_.findNearestIntersection
+    (
+        surfacesToBaffle,
+        start,
+        end,
 
-            if (hit1.hit() && hit2.hit())
-            {
-                if (str.valid())
-                {
-                    meshTools::writeOBJ(str(), cellCentres[own]);
-                    vertI++;
-                    meshTools::writeOBJ(str(), hit1.rawPoint());
-                    vertI++;
-                    meshTools::writeOBJ(str(), hit2.rawPoint());
-                    vertI++;
-                    meshTools::writeOBJ(str(), end);
-                    vertI++;
-                    str()<< "l " << vertI-3 << ' ' << vertI-2 << nl;
-                    str()<< "l " << vertI-2 << ' ' << vertI-1 << nl;
-                    str()<< "l " << vertI-1 << ' ' << vertI << nl;
-                }
+        surface1,
+        hit1,
+        region1,
 
+        surface2,
+        hit2,
+        region2
+    );
 
-                // Pick up the patches
-                ownPatch[faceI] = globalToPatch
-                [
-                    surfaces_.triangleRegion(surface1, hit1.index())
-                ];
-                neiPatch[faceI] = globalToPatch
-                [
-                    surfaces_.triangleRegion(surface2, hit2.index())
-                ];
+    forAll(testFaces, i)
+    {
+        label faceI = testFaces[i];
 
-                if (ownPatch[faceI] == -1 || neiPatch[faceI] == -1)
-                {
-                    FatalErrorIn("getBafflePatches()") << abort(FatalError);
-                }
+        if (hit1[i].hit() && hit2[i].hit())
+        {
+            if (str.valid())
+            {
+                meshTools::writeOBJ(str(), start[i]);
+                vertI++;
+                meshTools::writeOBJ(str(), hit1[i].rawPoint());
+                vertI++;
+                meshTools::writeOBJ(str(), hit2[i].rawPoint());
+                vertI++;
+                meshTools::writeOBJ(str(), end[i]);
+                vertI++;
+                str()<< "l " << vertI-3 << ' ' << vertI-2 << nl;
+                str()<< "l " << vertI-2 << ' ' << vertI-1 << nl;
+                str()<< "l " << vertI-1 << ' ' << vertI << nl;
+            }
+
+            // Pick up the patches
+            ownPatch[faceI] = globalToPatch
+            [
+                surfaces_.globalRegion(surface1[i], region1[i])
+            ];
+            neiPatch[faceI] = globalToPatch
+            [
+                surfaces_.globalRegion(surface2[i], region2[i])
+            ];
+
+            if (ownPatch[faceI] == -1 || neiPatch[faceI] == -1)
+            {
+                FatalErrorIn("getBafflePatches(..)")
+                    << "problem." << abort(FatalError);
             }
         }
     }
@@ -1203,15 +1229,20 @@ void Foam::meshRefinement::findCellZoneGeometric
     const labelList& faceOwner = mesh_.faceOwner();
     const labelList& faceNeighbour = mesh_.faceNeighbour();
 
-    forAll(cellToZone, cellI)
+    // Check if cell centre is inside
+    labelList insideSurfaces;
+    surfaces_.findInside
+    (
+        closedNamedSurfaces,
+        cellCentres,
+        insideSurfaces
+    );
+
+    forAll(insideSurfaces, cellI)
     {
         if (cellToZone[cellI] == -2)
         {
-            label surfI = surfaces_.insideZone
-            (
-                closedNamedSurfaces,
-                cellCentres[cellI]
-            );
+            label surfI = insideSurfaces[cellI];
 
             if (surfI != -1)
             {
@@ -1223,6 +1254,32 @@ void Foam::meshRefinement::findCellZoneGeometric
 
     // Some cells with cell centres close to surface might have
     // had been put into wrong surface. Recheck with perturbed cell centre.
+
+
+    // 1. Collect points
+
+    // Count points to test.
+    label nCandidates = 0;
+    forAll(namedSurfaceIndex, faceI)
+    {
+        label surfI = namedSurfaceIndex[faceI];
+
+        if (surfI != -1)
+        {
+            if (mesh_.isInternalFace(faceI))
+            {
+                nCandidates += 2;
+            }
+            else
+            {
+                nCandidates += 1;
+            }
+        }
+    }
+
+    // Collect points.
+    pointField candidatePoints(nCandidates);
+    nCandidates = 0;
     forAll(namedSurfaceIndex, faceI)
     {
         label surfI = namedSurfaceIndex[faceI];
@@ -1236,55 +1293,65 @@ void Foam::meshRefinement::findCellZoneGeometric
             {
                 label nei = faceNeighbour[faceI];
                 const point& neiCc = cellCentres[nei];
-
+                // Perturbed cc
                 const vector d = 1E-4*(neiCc - ownCc);
+                candidatePoints[nCandidates++] = ownCc-d;
+                candidatePoints[nCandidates++] = neiCc+d;
+            }
+            else
+            {
+                const point& neiFc = mesh_.faceCentres()[faceI];
+                // Perturbed cc
+                const vector d = 1E-4*(neiFc - ownCc);
+                candidatePoints[nCandidates++] = ownCc-d;
+            }
+        }
+    }
 
-                // Test perturbed owner
-                {
-                    label ownSurfI = surfaces_.insideZone
-                    (
-                        closedNamedSurfaces,
-                        ownCc-d
-                    );
 
-                    if (ownSurfI != -1)
-                    {
-                        cellToZone[own] = surfaceToCellZone[ownSurfI];
-                    }
+    // 2. Test points for inside
+
+    surfaces_.findInside
+    (
+        closedNamedSurfaces,
+        candidatePoints,
+        insideSurfaces
+    );
+
+
+    // 3. Update zone information
+
+    nCandidates = 0;
+    forAll(namedSurfaceIndex, faceI)
+    {
+        label surfI = namedSurfaceIndex[faceI];
+
+        if (surfI != -1)
+        {
+            label own = faceOwner[faceI];
+
+            if (mesh_.isInternalFace(faceI))
+            {
+                label ownSurfI = insideSurfaces[nCandidates++];
+                if (ownSurfI != -1)
+                {
+                    cellToZone[own] = surfaceToCellZone[ownSurfI];
                 }
 
-                // Test perturbed neighbour
+                label neiSurfI = insideSurfaces[nCandidates++];
+                if (neiSurfI != -1)
                 {
-                    label neiSurfI = surfaces_.insideZone
-                    (
-                        closedNamedSurfaces,
-                        neiCc+d
-                    );
+                    label nei = faceNeighbour[faceI];
 
-                    if (neiSurfI != -1)
-                    {
-                        cellToZone[nei] = surfaceToCellZone[neiSurfI];
-                    }
+                    cellToZone[nei] = surfaceToCellZone[neiSurfI];
                 }
             }
             else
             {
-                const point& neiCc = mesh_.faceCentres()[faceI];
-
-                const vector d = 1E-4*(neiCc - ownCc);
-
-                // Test perturbed owner
+                label ownSurfI = insideSurfaces[nCandidates++];
+                if (ownSurfI != -1)
                 {
-                    label ownSurfI = surfaces_.insideZone
-                    (
-                        closedNamedSurfaces,
-                        ownCc-d
-                    );
-
-                    if (ownSurfI != -1)
-                    {
-                        cellToZone[own] = surfaceToCellZone[ownSurfI];
-                    }
+                    cellToZone[own] = surfaceToCellZone[ownSurfI];
                 }
             }
         }
@@ -1771,7 +1838,11 @@ Foam::autoPtr<Foam::mapPolyMesh>  Foam::meshRefinement::splitMesh
     const labelList& faceNeighbour = mesh_.faceNeighbour();
 
     // Patch for exposed faces for lack of anything sensible.
-    const label defaultPatch = globalToPatch[0];
+    label defaultPatch = 0;
+    if (globalToPatch.size() > 0)
+    {
+        defaultPatch = globalToPatch[0];
+    }
 
     for (label i = 0; i < nBufferLayers; i++)
     {
@@ -2291,7 +2362,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
 
         isNamedSurface[surfI] = true;
 
-        Info<< "Surface : " << surfaces_[surfI].searchableSurface::name() << nl
+        Info<< "Surface : " << surfaces_.names()[surfI] << nl
             << "    faceZone : " << faceZoneNames[surfI] << nl
             << "    cellZone : " << cellZoneNames[surfI] << endl;
     }
@@ -2329,7 +2400,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
 
             if (debug)
             {
-                Pout<< "Faces on " << surfaces_[surfI].searchableSurface::name()
+                Pout<< "Faces on " << surfaces_.names()[surfI]
                     << " will go into faceZone " << zoneI << endl;
             }
             surfaceToFaceZone[surfI] = zoneI;
@@ -2387,8 +2458,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
 
             if (debug)
             {
-                Pout<< "Cells inside "
-                    << surfaces_[surfI].searchableSurface::name()
+                Pout<< "Cells inside " << surfaces_.names()[surfI]
                     << " will go into cellZone " << zoneI << endl;
             }
             surfaceToCellZone[surfI] = zoneI;
@@ -2444,78 +2514,83 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
         // Note: for all internal faces? internal + coupled?
         // Since zonify is run after baffling the surfaceIndex_ on baffles is
         // not synchronised across both baffle faces. Fortunately we don't
-        // do zonify baffle faces anyway.
-        forAll(surfaceIndex_, faceI)
+        // do zonify baffle faces anyway (they are normal boundary faces).
+
+        // Collect candidate faces
+        // ~~~~~~~~~~~~~~~~~~~~~~~
+
+        labelList testFaces(intersectedFaces());
+
+        // Collect segments
+        // ~~~~~~~~~~~~~~~~
+
+        pointField start(testFaces.size());
+        pointField end(testFaces.size());
+
+        forAll(testFaces, i)
         {
-            label surfI = surfaceIndex_[faceI];
+            label faceI = testFaces[i];
 
-            if
-            (
-                surfI != -1
-             && (
-                    mesh_.isInternalFace(faceI)
-                 || patches[patches.whichPatch(faceI)].coupled()
-                )
-            )
+            label own = mesh_.faceOwner()[faceI];
+
+            if (mesh_.isInternalFace(faceI))
             {
-                if (isNamedSurface[surfI])
-                {
-                    namedSurfaceIndex[faceI] = surfI;
-                    nSurfFaces[surfI]++;
-                }
-                else
-                {
-                    // Test more accurately for whether intersection is at
-                    // zoneable surface
+                start[i] = cellCentres[own];
+                end[i] = cellCentres[mesh_.faceNeighbour()[faceI]];
+            }
+            else
+            {
+                start[i] = cellCentres[own];
+                end[i] = neiCc[faceI-mesh_.nInternalFaces()];
+            }
+        }
 
-                    label surface1, surface2;
-                    pointIndexHit hit1, hit2;
 
-                    if (mesh_.isInternalFace(faceI))
-                    {
-                        surfaces_.findNearestIntersection
-                        (
-                            namedSurfaces,
-                            cellCentres[faceOwner[faceI]],
-                            cellCentres[faceNeighbour[faceI]],
-
-                            surface1,
-                            hit1,
-                            surface2,
-                            hit2
-                        );
-                    }
-                    else
-                    {
-                        surfaces_.findNearestIntersection
-                        (
-                            namedSurfaces,
-                            cellCentres[faceOwner[faceI]],
-                            neiCc[faceI-mesh_.nInternalFaces()],
-
-                            surface1,
-                            hit1,
-                            surface2,
-                            hit2
-                        );
-                    }
+        // Do test for intersections
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~
+        // Note that we intersect all intersected faces again. Could reuse
+        // the information already in surfaceIndex_.
 
-                    if (hit1.hit())
-                    {
-                        // If both hit should probably choose nearest. For
-                        // later.
-                        namedSurfaceIndex[faceI] = surface1;
-                        nSurfFaces[surface1]++;
-                    }
-                    else if (hit2.hit())
-                    {
-                        namedSurfaceIndex[faceI] = surface2;
-                        nSurfFaces[surface2]++;
-                    }
-                }
+        labelList surface1;
+        labelList surface2;
+        {
+            List<pointIndexHit> hit1;
+            labelList region1;
+            List<pointIndexHit> hit2;
+            labelList region2;
+            surfaces_.findNearestIntersection
+            (
+                namedSurfaces,
+                start,
+                end,
+
+                surface1,
+                hit1,
+                region1,
+                surface2,
+                hit2,
+                region2
+            );
+        }
+
+        forAll(testFaces, i)
+        {
+            label faceI = testFaces[i];
+
+            if (surface1[i] != -1)
+            {
+                // If both hit should probably choose nearest. For later.
+                namedSurfaceIndex[faceI] = surface1[i];
+                nSurfFaces[surface1[i]]++;
+            }
+            else if (surface2[i] != -1)
+            {
+                namedSurfaceIndex[faceI] = surface2[i];
+                nSurfFaces[surface2[i]]++;
             }
         }
 
+
         // surfaceIndex migh have different surfaces on both sides if
         // there happen to be a (obviously thin) surface with different
         // regions between the cell centres. If one is on a named surface
@@ -2534,7 +2609,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
             forAll(nSurfFaces, surfI)
             {
                 Pout<< "Surface:"
-                    << surfaces_[surfI].searchableSurface::name()
+                    << surfaces_.names()[surfI]
                     << "  nZoneFaces:" << nSurfFaces[surfI] << nl;
             }
             Pout<< endl;
@@ -2591,7 +2666,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
                     (
                         mesh_.faces()[faceI],           // modified face
                         faceI,                          // label of face
-                        faceOwner[faceI],              // owner
+                        faceOwner[faceI],               // owner
                         -1,                             // neighbour
                         false,                          // face flip
                         patchI,                         // patch for face
@@ -2609,20 +2684,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
     // Put the cells into the correct zone
     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    labelList closedNamedSurfaces(namedSurfaces.size());
-    label nClosed = 0;
-
-    forAll(namedSurfaces, i)
-    {
-        label surfI = namedSurfaces[i];
-
-        if (surfaces_.closed()[surfI])
-        {
-            closedNamedSurfaces[nClosed++] = surfI;
-        }
-    }
-    closedNamedSurfaces.setSize(nClosed);
-
+    // Closed surfaces with cellZone specified.
+    labelList closedNamedSurfaces(surfaces_.getClosedNamedSurfaces());
 
     // Zone per cell:
     // -2 : unset
@@ -2704,13 +2767,4 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
 }
 
 
-// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Functions  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
-
-
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C
index 14c3304979b41030960405291825a04bdcfe79c4..d8ddb69bcece7841e39e71751245cc21efd5d5ee 100644
--- a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C
+++ b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C
@@ -30,15 +30,9 @@ License
 #include "removePoints.H"
 
 
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 // Merge faces that are in-line.
@@ -244,13 +238,4 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::mergeEdges
 }
 
 
-// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Functions  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
-
-
 // ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
index bc65921bf5aa74179e90972f61eba7c71b831228..9a5f6cf63379293e884c8c74fa59fa48b981c203 100644
--- a/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
+++ b/src/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
@@ -29,6 +29,7 @@ License
 #include "syncTools.H"
 #include "Time.H"
 #include "refinementSurfaces.H"
+#include "shellSurfaces.H"
 #include "faceSet.H"
 #include "decompositionMethod.H"
 #include "fvMeshDistribute.H"
@@ -37,9 +38,6 @@ License
 #include "featureEdgeMesh.H"
 #include "Cloud.H"
 
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 // Get faces (on the new mesh) that have in some way been affected by the
@@ -369,7 +367,7 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
             {
                 label edgeI = pEdges[i];
 
-                if (featureEdgeVisited[featI].get(edgeI) == 0)
+                if (featureEdgeVisited[featI].set(edgeI, 1u))
                 {
                     // Unvisited edge. Make the particle go to the other point
                     // on the edge.
@@ -377,7 +375,6 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
                     const edge& e = featureMesh.edges()[edgeI];
                     label otherPointI = e.otherVertex(pointI);
 
-                    featureEdgeVisited[featI].set(edgeI, 1u);
                     tp.end() = featureMesh.points()[otherPointI];
                     tp.j() = otherPointI;
                     keepParticle = true;
@@ -438,6 +435,15 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
         }
     }
 
+    if
+    (
+        returnReduce(nRefine, sumOp<label>())
+      > returnReduce(nAllowRefine, sumOp<label>())
+    )
+    {
+        Info<< "Reached refinement limit." << endl;
+    }
+
     return returnReduce(nRefine-oldNRefine,  sumOp<label>());
 }
 
@@ -445,9 +451,6 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
 // Mark cells for non-surface intersection based refinement.
 Foam::label Foam::meshRefinement::markInternalRefinement
 (
-    const PtrList<searchableSurface>& shells,
-    const labelList& shellLevels,
-    const boolList& shellRefineInside,
     const label nAllowRefine,
 
     labelList& refineCell,
@@ -457,94 +460,113 @@ Foam::label Foam::meshRefinement::markInternalRefinement
     const labelList& cellLevel = meshCutter_.cellLevel();
     const pointField& cellCentres = mesh_.cellCentres();
 
-
     label oldNRefine = nRefine;
 
-    // Number of cells marked for refinement per shell.
-    labelList nCellsPerShell(shells.size(), 0);
+    // Collect cells to test
+    pointField testCc(cellLevel.size()-nRefine);
+    labelList testLevels(cellLevel.size()-nRefine);
+    label testI = 0;
 
     forAll(cellLevel, cellI)
     {
         if (refineCell[cellI] == -1)
         {
-            // Cell not marked for refinement. Check if inside any shell
-            // with higher refinement level than cell currently has.
+            testCc[testI] = cellCentres[cellI];
+            testLevels[testI] = cellLevel[cellI];
+            testI++;
+        }
+    }
 
-            bool reachedLimit = false;
+    // Do test to see whether cells is inside/outside shell with higher level
+    labelList maxLevel;
+    shells_.findHigherLevel(testCc, testLevels, maxLevel);
 
-            forAll(shells, shellI)
+    // Mark for refinement. Note that we didn't store the original cellID so
+    // now just reloop in same order.
+    testI = 0;
+    forAll(cellLevel, cellI)
+    {
+        if (refineCell[cellI] == -1)
+        {
+            if (maxLevel[testI] > testLevels[testI])
             {
-                // Cached inside-outside from tree
-                searchableSurface::volumeType t =
-                    shells[shellI].getVolumeType(cellCentres[cellI]);
-
-                //// Uncached inside-outside from treeData
-                //label t = shells[shellI].shapes().getVolumeType
-                //    (
-                //        shells[shellI],
-                //        cellCentres[cellI]
-                //    );
-
-                // Which side of shell is to be refined
-                searchableSurface::volumeType refSide =
+                bool reachedLimit = !markForRefine
                 (
-                    shellRefineInside[shellI]
-                  ? searchableSurface::INSIDE
-                  : searchableSurface::OUTSIDE
+                    maxLevel[testI],    // mark with any positive value
+                    nAllowRefine,
+                    refineCell[cellI],
+                    nRefine
                 );
 
-                if (t == refSide && cellLevel[cellI] < shellLevels[shellI])
+                if (reachedLimit)
                 {
-                    // Cell is inside shell with higher refinement level. Mark
-                    // for refinement.
-
-                    reachedLimit = !markForRefine
-                    (
-                        labelMax,
-                        nAllowRefine,
-                        refineCell[cellI],
-                        nRefine
-                    );
-
-                    if (reachedLimit)
-                    {
-                        if (debug)
-                        {
-                            Pout<< "Stopped refining internal cells"
-                                << " since reaching my cell limit of "
-                                << mesh_.nCells()+7*nRefine << endl;
-                        }
-                        break;
-                    }
-                    else
+                    if (debug)
                     {
-                        // Cell successfully marked for refinement
-                        nCellsPerShell[shellI]++;
+                        Pout<< "Stopped refining internal cells"
+                            << " since reaching my cell limit of "
+                            << mesh_.nCells()+7*nRefine << endl;
                     }
+                    break;
                 }
             }
-
-            if (reachedLimit)
-            {
-                break;
-            }
+            testI++;
         }
     }
 
-    Pstream::listCombineGather(nCellsPerShell, plusEqOp<label>());
-    Pstream::listCombineScatter(nCellsPerShell);
-
-    Info<< "Marked for refinement per shell :" << endl;
-    forAll(nCellsPerShell, shellI)
+    if
+    (
+        returnReduce(nRefine, sumOp<label>())
+      > returnReduce(nAllowRefine, sumOp<label>())
+    )
     {
-        Info<< "    shell:" << shellI << " nCells:" << nCellsPerShell[shellI]
-            << nl;
+        Info<< "Reached refinement limit." << endl;
     }
 
     return returnReduce(nRefine-oldNRefine, sumOp<label>());
 }
 
 
+// Collect faces that are intersected and whose neighbours aren't yet marked
+// for refinement.
+Foam::labelList Foam::meshRefinement::getRefineCandidateFaces
+(
+    const labelList& refineCell
+) const
+{
+    labelList testFaces(mesh_.nCells());
+
+    label nTest = 0;
+
+    forAll(surfaceIndex_, faceI)
+    {
+        if (surfaceIndex_[faceI] != -1)
+        {
+            label own = mesh_.faceOwner()[faceI];
+
+            if (mesh_.isInternalFace(faceI))
+            {
+                label nei = mesh_.faceNeighbour()[faceI];
+
+                if (refineCell[own] == -1 || refineCell[nei] == -1)
+                {
+                    testFaces[nTest++] = faceI;
+                }
+            }
+            else
+            {
+                if (refineCell[own] == -1)
+                {
+                    testFaces[nTest++] = faceI;
+                }
+            }
+        }
+    }
+    testFaces.setSize(nTest);
+
+    return testFaces;
+}
+
+
 // Mark cells for surface intersection based refinement.
 Foam::label Foam::meshRefinement::markSurfaceRefinement
 (
@@ -564,156 +586,149 @@ Foam::label Foam::meshRefinement::markSurfaceRefinement
     // Use cached surfaceIndex_ to detect if any intersection. If so
     // re-intersect to determine level wanted.
 
-    label faceI;
-    for (faceI = 0; faceI < surfaceIndex_.size(); faceI++)
+    // Collect candidate faces
+    // ~~~~~~~~~~~~~~~~~~~~~~~
+
+    labelList testFaces(getRefineCandidateFaces(refineCell));
+
+    // Collect segments
+    // ~~~~~~~~~~~~~~~~
+
+    pointField start(testFaces.size());
+    pointField end(testFaces.size());
+    labelList minLevel(testFaces.size());
+
+    forAll(testFaces, i)
     {
-        if (surfaceIndex_[faceI] != -1)
+        label faceI = testFaces[i];
+
+        label own = mesh_.faceOwner()[faceI];
+
+        if (mesh_.isInternalFace(faceI))
         {
-            label own = mesh_.faceOwner()[faceI];
+            label nei = mesh_.faceNeighbour()[faceI];
 
-            if (mesh_.isInternalFace(faceI))
-            {
-                label nei = mesh_.faceNeighbour()[faceI];
+            start[i] = cellCentres[own];
+            end[i] = cellCentres[nei];
+            minLevel[i] = min(cellLevel[own], cellLevel[nei]);
+        }
+        else
+        {
+            label bFaceI = faceI - mesh_.nInternalFaces();
 
-                // Test if not both sides already marked for refinement.
-                if (refineCell[own] == -1 || refineCell[nei] == -1)
-                {
-                    pointIndexHit hit;
-                    label surfI = surfaces_.findHigherIntersection
-                    (
-                        cellCentres[own],
-                        cellCentres[nei],
-                        min(cellLevel[own], cellLevel[nei]),
-                        hit
-                    );
+            start[i] = cellCentres[own];
+            end[i] = neiCc[bFaceI];
+            minLevel[i] = min(cellLevel[own], neiLevel[bFaceI]);
+        }
+    }
 
-                    if (surfI != -1)
-                    {
-                        // Found intersection with surface with higher wanted
-                        // refinement. Mark cell for refining.
-                        // Note: could do optimization here and only refine the
-                        // side of the face the intersection actually goes
-                        // through.
-                        // This would require us though to find all
-                        // intersections
-                        // first instead of now only the first higher one. Also
-                        // would need the exact intersection of face with cc-cc
-                        // connection?
-
-                        label surfaceMinLevel =
-                            surfaces_.minLevelField(surfI)[hit.index()];
-
-                        if (surfaceMinLevel > cellLevel[own])
-                        {
-                            // Owner needs refining
-                            if
-                            (
-                               !markForRefine
-                                (
-                                    surfI,
-                                    nAllowRefine,
-                                    refineCell[own],
-                                    nRefine
-                                )
-                            )
-                            {
-                                break;
-                            }
-                        }
 
-                        if (surfaceMinLevel > cellLevel[nei])
-                        {
-                            // Neighbour needs refining
-                            if
-                            (
-                               !markForRefine
-                                (
-                                    surfI,
-                                    nAllowRefine,
-                                    refineCell[nei],
-                                    nRefine
-                                )
-                            )
-                            {
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-            else if (refineCell[own] == -1)
-            {
-                // boundary face with unmarked owner
+    // Do test for higher intersections
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    labelList surfaceHit;
+    labelList surfaceMinLevel;
+    surfaces_.findHigherIntersection
+    (
+        start,
+        end,
+        minLevel,
+
+        surfaceHit,
+        surfaceMinLevel
+    );
+
+
+    // Mark cells for refinement
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    forAll(testFaces, i)
+    {
+        label faceI = testFaces[i];
+
+        label surfI = surfaceHit[i];
 
-                label bFaceI = faceI - mesh_.nInternalFaces();
+        if (surfI != -1)
+        {
+            // Found intersection with surface with higher wanted
+            // refinement. Check if the level field on that surface
+            // specifies an even higher level. Note:this is weird. Should
+            // do the check with the surfaceMinLevel whilst intersecting the
+            // surfaces?
+
+            label own = mesh_.faceOwner()[faceI];
 
-                pointIndexHit hit;
-                label surfI = surfaces_.findHigherIntersection
+            if (surfaceMinLevel[i] > cellLevel[own])
+            {
+                // Owner needs refining
+                if
                 (
-                    cellCentres[own],
-                    neiCc[bFaceI],
-                    min(cellLevel[own], neiLevel[bFaceI]),
-                    hit
-                );
+                   !markForRefine
+                    (
+                        surfI,
+                        nAllowRefine,
+                        refineCell[own],
+                        nRefine
+                    )
+                )
+                {
+                    break;
+                }
+            }
 
-                if (surfI != -1)
+            if (mesh_.isInternalFace(faceI))
+            {
+                label nei = mesh_.faceNeighbour()[faceI];
+                if (surfaceMinLevel[i] > cellLevel[nei])
                 {
-                    // Make sure it is my side that wants refinement.
-                    label surfaceMinLevel =
-                        surfaces_.minLevelField(surfI)[hit.index()];
-                   
-                    if (surfaceMinLevel > cellLevel[own])
-                    {
-                        if
+                    // Neighbour needs refining
+                    if
+                    (
+                       !markForRefine
                         (
-                           !markForRefine
-                            (
-                                surfI,
-                                nAllowRefine,
-                                refineCell[own],
-                                nRefine
-                            )
+                            surfI,
+                            nAllowRefine,
+                            refineCell[nei],
+                            nRefine
                         )
-                        {
-                            break;
-                        }
+                    )
+                    {
+                        break;
                     }
                 }
             }
         }
     }
 
-    if (faceI < surfaceIndex_.size())
+    if
+    (
+        returnReduce(nRefine, sumOp<label>())
+      > returnReduce(nAllowRefine, sumOp<label>())
+    )
     {
-        if (debug)
-        {
-            Pout<< "Stopped refining since reaching my cell limit of "
-                << mesh_.nCells()+7*nRefine << endl;
-        }
+        Info<< "Reached refinement limit." << endl;
     }
 
     return returnReduce(nRefine-oldNRefine, sumOp<label>());
 }
 
 
-// Given intersection of (face of) cell by newSurfI, newTriI, check whether
-// it needs to be refined. Updates maxCellSurf, maxCellTri and
-// refineCell,nRefine if it decides to refine the cell. Returns false
-// if the nRefine limit has been reached, true otherwise.
+// Checks if multiple intersections of a cell (by a surface with a higher
+// max than the cell level) and if so if the normals at these intersections
+// make a large angle.
+// Returns false if the nRefine limit has been reached, true otherwise.
 bool Foam::meshRefinement::checkCurvature
 (
-    const labelList& globalToPatch,
     const scalar curvature,
-    const bool markDifferingRegions,
     const label nAllowRefine,
 
-    const label newSurfI,
-    const label newTriI,
+    const label surfaceLevel,   // current intersection max level
+    const vector& surfaceNormal,// current intersection normal
 
     const label cellI,
 
-    label& maxCellSurfI,
-    label& maxCellTriI,
+    label& cellMaxLevel,        // cached max surface level for this cell
+    vector& cellMaxNormal,      // cached surface normal for this cell
 
     labelList& refineCell,
     label& nRefine
@@ -721,59 +736,35 @@ bool Foam::meshRefinement::checkCurvature
 {
     const labelList& cellLevel = meshCutter_.cellLevel();
 
-    if (maxCellSurfI == -1)
-    {
-        // First visit of cell. Store
-        maxCellSurfI = newSurfI;
-        maxCellTriI = newTriI;
-    }
-    else
+    // Test if surface applicable
+    if (surfaceLevel > cellLevel[cellI])
     {
-        // Second or more visit.
-        label cellRegion = surfaces_.triangleRegion(maxCellSurfI, maxCellTriI);
-        label newRegion = surfaces_.triangleRegion(newSurfI, newTriI);
-
-        // Update max
-        label maxLevel = surfaces_.maxLevel()[cellRegion];
-
-        if (surfaces_.maxLevel()[newRegion] > maxLevel)
+        if (cellMaxLevel == -1)
         {
-            maxCellSurfI = newSurfI;
-            maxCellTriI = newTriI;
-            maxLevel = surfaces_.maxLevel()[newRegion];
+            // First visit of cell. Store
+            cellMaxLevel = surfaceLevel;
+            cellMaxNormal = surfaceNormal;
         }
-
-
-        // Check if cell is candidate for refinement
-        if (cellLevel[cellI] < maxLevel)
+        else
         {
-            // Test 1: different regions
-            if (markDifferingRegions && cellRegion != newRegion)
+            // Second or more visit. Check curvature.
+            if ((cellMaxNormal & surfaceNormal) < curvature)
             {
                 return markForRefine
                 (
-                    globalToPatch[newRegion],   // mark with non-neg number.
+                    surfaceLevel,   // mark with any non-neg number.
                     nAllowRefine,
                     refineCell[cellI],
                     nRefine
                 );
             }
 
-            // Test 2: different normals
-            const vector& cellN =
-                surfaces_[maxCellSurfI].faceNormals()[maxCellTriI];
-            const vector& newN =
-                surfaces_[newSurfI].faceNormals()[newTriI];
-
-            if ((cellN & newN) < curvature)
+            // Set normal to that of highest surface. Not really necessary
+            // over here but we reuse cellMax info when doing coupled faces.
+            if (surfaceLevel > cellMaxLevel)
             {
-                return markForRefine
-                (
-                    globalToPatch[newRegion],   // mark with non-neg number.
-                    nAllowRefine,
-                    refineCell[cellI],
-                    nRefine
-                );
+                cellMaxLevel = surfaceLevel;
+                cellMaxNormal = surfaceNormal;
             }
         }
     }
@@ -786,9 +777,7 @@ bool Foam::meshRefinement::checkCurvature
 // Mark cells for surface curvature based refinement.
 Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
 (
-    const labelList& globalToPatch,
     const scalar curvature,
-    const bool markDifferingRegions,
     const label nAllowRefine,
     const labelList& neiLevel,
     const pointField& neiCc,
@@ -802,107 +791,117 @@ Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
 
     label oldNRefine = nRefine;
 
-    // 1. Any cell on more than one surface gets refined (if its current level
-    // is <= max of the surface max level)
-
-    // 2. Any cell on only one surface with a neighbour on a different surface
-    // gets refined (if its current level etc.)
+    // 1. local test: any cell on more than one surface gets refined
+    // (if its current level is < max of the surface max level)
 
+    // 2. 'global' test: any cell on only one surface with a neighbour
+    // on a different surface gets refined (if its current level etc.)
 
 
-    // Index of surface with the max maxLevel and the actual triangle
-    // on the surface. We store these and not e.g. global region so we can get
-    // at:
-    //  - global region
-    //  - global patch
-    //  - face normal
-    labelList cellMaxSurface(mesh_.nCells(), -1);
-    labelList cellMaxTriangle(mesh_.nCells(), -1);
+    // Collect candidate faces (i.e. intersecting any surface and
+    // owner/neighbour not yet refined.
+    labelList testFaces(getRefineCandidateFaces(refineCell));
 
-    // 1.
+    // Collect segments
+    pointField start(testFaces.size());
+    pointField end(testFaces.size());
+    labelList minLevel(testFaces.size());
 
-    forAll(surfaceIndex_, faceI)
+    forAll(testFaces, i)
     {
-        if (surfaceIndex_[faceI] != -1)
+        label faceI = testFaces[i];
+
+        label own = mesh_.faceOwner()[faceI];
+
+        if (mesh_.isInternalFace(faceI))
         {
-            label own = mesh_.faceOwner()[faceI];
+            label nei = mesh_.faceNeighbour()[faceI];
 
-            // There is an intersection. Do more accurate test to get all
-            // intersections
-            labelList surfaceIndices;
-            List<pointIndexHit> hits;
+            start[i] = cellCentres[own];
+            end[i] = cellCentres[nei];
+            minLevel[i] = min(cellLevel[own], cellLevel[nei]);
+        }
+        else
+        {
+            label bFaceI = faceI - mesh_.nInternalFaces();
 
-            if (mesh_.isInternalFace(faceI))
-            {
-                label nei = mesh_.faceNeighbour()[faceI];
+            start[i] = cellCentres[own];
+            end[i] = neiCc[bFaceI];
+            minLevel[i] = min(cellLevel[own], neiLevel[bFaceI]);
+        }
+    }
 
-                surfaces_.findAllIntersections
-                (
-                    cellCentres[own],
-                    cellCentres[nei],
-                    surfaceIndices,
-                    hits
-                );
-            }
-            else
-            {
-                label bFaceI = faceI - mesh_.nInternalFaces();
+    // Test for all intersections (with surfaces of higher max level than
+    // minLevel) and cache per cell the max surface level and the local normal
+    // on that surface.
+    labelList cellMaxLevel(mesh_.nCells(), -1);
+    vectorField cellMaxNormal(mesh_.nCells());
 
-                surfaces_.findAllIntersections
-                (
-                    cellCentres[own],
-                    neiCc[bFaceI],
-                    surfaceIndices,
-                    hits
-                );
-            }
+    {
+        // Per segment the normals of the surfaces
+        List<vectorList> surfaceNormal;
+        // Per segment the list of levels of the surfaces
+        labelListList surfaceLevel;
 
+        surfaces_.findAllHigherIntersections
+        (
+            start,
+            end,
+            minLevel,           // max level of surface has to be bigger
+                                // than min level of neighbouring cells
+            surfaceNormal,
+            surfaceLevel
+        );
+        // Clear out unnecessary data
+        start.clear();
+        end.clear();
+        minLevel.clear();
 
-            // See if intersection adds any new information to the owner or
-            // neighbour cell.
+        // Extract per cell information on the surface with the highest max
+        forAll(testFaces, i)
+        {
+            label faceI = testFaces[i];
+            label own = mesh_.faceOwner()[faceI];
 
-            forAll(surfaceIndices, i)
-            {
-                label surfI = surfaceIndices[i];
-                label triI = hits[i].index();
+            const vectorList& fNormals = surfaceNormal[i];
+            const labelList& fLevels = surfaceLevel[i];
 
+            forAll(fLevels, hitI)
+            {
                 checkCurvature
                 (
-                    globalToPatch,
                     curvature,
-                    markDifferingRegions,
                     nAllowRefine,
 
-                    surfI,
-                    triI,
+                    fLevels[hitI],
+                    fNormals[hitI],
 
                     own,
-
-                    cellMaxSurface[own],
-                    cellMaxTriangle[own],
+                    cellMaxLevel[own],
+                    cellMaxNormal[own],
 
                     refineCell,
                     nRefine
                 );
+            }
 
-                if (mesh_.isInternalFace(faceI))
-                {
-                    label nei = mesh_.faceNeighbour()[faceI];
+            if (mesh_.isInternalFace(faceI))
+            {
+                label nei = mesh_.faceNeighbour()[faceI];
 
+                forAll(fLevels, hitI)
+                {
                     checkCurvature
                     (
-                        globalToPatch,
                         curvature,
-                        markDifferingRegions,
                         nAllowRefine,
 
-                        surfI,
-                        triI,
+                        fLevels[hitI],
+                        fNormals[hitI],
 
                         nei,
-
-                        cellMaxSurface[nei],
-                        cellMaxTriangle[nei],
+                        cellMaxLevel[nei],
+                        cellMaxNormal[nei],
 
                         refineCell,
                         nRefine
@@ -910,93 +909,46 @@ Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
                 }
             }
         }
-
-        if (nRefine > nAllowRefine)
-        {
-            if (debug)
-            {
-                Pout<< "Stopped refining since reaching my cell limit of "
-                    << mesh_.nCells()+7*nRefine << endl;
-            }
-            break;
-        }
     }
 
     // 2. Find out a measure of surface curvature and region edges.
     // Send over surface region and surface normal to neighbour cell.
 
-    // global region
-    labelList ownRegion(mesh_.nFaces(), -1);
-    labelList neiRegion(mesh_.nFaces(), -1);
-    // local normal at hit
-    vectorField ownNormal(mesh_.nFaces(), vector::zero);
-    vectorField neiNormal(mesh_.nFaces(), vector::zero);
-
-    // Internal faces
-    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
-    {
-        label own = mesh_.faceOwner()[faceI];
-
-        if (cellMaxSurface[own] != -1)
-        {
-            label surfI = cellMaxSurface[own];
-            label triI = cellMaxTriangle[own];
-
-            ownRegion[faceI] = surfaces_.triangleRegion(surfI, triI);
-            ownNormal[faceI] = surfaces_[surfI].faceNormals()[triI];
-        }
-
-        label nei = mesh_.faceNeighbour()[faceI];
+    labelList neiBndMaxLevel(mesh_.nFaces()-mesh_.nInternalFaces());
+    vectorField neiBndMaxNormal(mesh_.nFaces()-mesh_.nInternalFaces());
 
-        if (cellMaxSurface[nei] != -1)
-        {
-            label surfI = cellMaxSurface[nei];
-            label triI = cellMaxTriangle[nei];
-
-            neiRegion[faceI] = surfaces_.triangleRegion(surfI, triI);
-            neiNormal[faceI] = surfaces_[surfI].faceNormals()[triI];
-        }
-    }
-    // Boundary faces
     for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
     {
+        label bFaceI = faceI-mesh_.nInternalFaces();
         label own = mesh_.faceOwner()[faceI];
 
-        if (cellMaxSurface[own] != -1)
-        {
-            label surfI = cellMaxSurface[own];
-            label triI = cellMaxTriangle[own];
-
-            ownRegion[faceI] = surfaces_.triangleRegion(surfI, triI);
-            ownNormal[faceI] = surfaces_[surfI].faceNormals()[triI];
-
-            neiRegion[faceI] = ownRegion[faceI];
-            neiNormal[faceI] = ownNormal[faceI];
-        }
+        neiBndMaxLevel[bFaceI] = cellMaxLevel[own];
+        neiBndMaxNormal[bFaceI] = cellMaxNormal[own];
     }
+    syncTools::swapBoundaryFaceList(mesh_, neiBndMaxLevel, false);
+    syncTools::swapBoundaryFaceList(mesh_, neiBndMaxNormal, false);
 
-    syncTools::swapFaceList(mesh_, neiRegion, false);
-    syncTools::swapFaceList(mesh_, neiNormal, false);
+    // Loop over all faces. Could only be checkFaces.. except if they're coupled
 
-    for (label faceI = 0; faceI < mesh_.nFaces(); faceI++)
+    // Internal faces
+    for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
     {
-        if (ownRegion[faceI] != -1 && neiRegion[faceI] != -1)
+        label own = mesh_.faceOwner()[faceI];
+        label nei = mesh_.faceNeighbour()[faceI];
+
+        if (cellMaxLevel[own] != -1 && cellMaxLevel[nei] != -1)
         {
-            if
-            (
-                (markDifferingRegions && (ownRegion[faceI] != neiRegion[faceI]))
-             || ((ownNormal[faceI] & neiNormal[faceI]) < curvature)
-            )
+            // Have valid data on both sides. Check curvature.
+            if ((cellMaxNormal[own] & cellMaxNormal[nei]) < curvature)
             {
-                label own = mesh_.faceOwner()[faceI];
-
-                if (cellLevel[own] < surfaces_.maxLevel()[ownRegion[faceI]])
+                // See which side to refine
+                if (cellLevel[own] < cellMaxLevel[own])
                 {
                     if
                     (
                         !markForRefine
                         (
-                            globalToPatch[ownRegion[faceI]],
+                            cellMaxLevel[own],
                             nAllowRefine,
                             refineCell[own],
                             nRefine
@@ -1011,46 +963,77 @@ Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
                         }
                         break;
                     }
-
                 }
 
-                if (mesh_.isInternalFace(faceI))
+                if (cellLevel[nei] < cellMaxLevel[nei])
                 {
-                    label nei = mesh_.faceNeighbour()[faceI];
-
-                    if (cellLevel[nei] < surfaces_.maxLevel()[neiRegion[faceI]])
-                    {
-                        if
+                    if
+                    (
+                        !markForRefine
                         (
-                            !markForRefine
-                            (
-                                globalToPatch[neiRegion[faceI]],
-                                nAllowRefine,
-
-                                refineCell[nei],
-                                nRefine
-                            )
+                            cellMaxLevel[nei],
+                            nAllowRefine,
+                            refineCell[nei],
+                            nRefine
                         )
+                    )
+                    {
+                        if (debug)
                         {
-                            if (debug)
-                            {
-                                Pout<< "Stopped refining since reaching my cell"
-                                    << " limit of " << mesh_.nCells()+7*nRefine
-                                    << endl;
-                            }
-                            break;
+                            Pout<< "Stopped refining since reaching my cell"
+                                << " limit of " << mesh_.nCells()+7*nRefine
+                                << endl;
                         }
+                        break;
                     }
                 }
             }
         }
     }
+    // Boundary faces
+    for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
+    {
+        label own = mesh_.faceOwner()[faceI];
+        label bFaceI = faceI - mesh_.nInternalFaces();
 
+        if (cellLevel[own] < cellMaxLevel[own] && neiBndMaxLevel[bFaceI] != -1)
+        {
+            // Have valid data on both sides. Check curvature.
+            if ((cellMaxNormal[own] & neiBndMaxNormal[bFaceI]) < curvature)
+            {
+                if
+                (
+                    !markForRefine
+                    (
+                        cellMaxLevel[own],
+                        nAllowRefine,
+                        refineCell[own],
+                        nRefine
+                    )
+                )
+                {
+                    if (debug)
+                    {
+                        Pout<< "Stopped refining since reaching my cell"
+                            << " limit of " << mesh_.nCells()+7*nRefine
+                            << endl;
+                    }
+                    break;
+                }
+            }
+        }
+    }
 
-    return returnReduce(nRefine-oldNRefine, sumOp<label>());
-    label totNRefined = returnReduce(totNRefined, sumOp<label>());
+    if
+    (
+        returnReduce(nRefine, sumOp<label>())
+      > returnReduce(nAllowRefine, sumOp<label>())
+    )
+    {
+        Info<< "Reached refinement limit." << endl;
+    }
 
-    return totNRefined;
+    return returnReduce(nRefine-oldNRefine, sumOp<label>());
 }
 
 
@@ -1064,16 +1047,11 @@ Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
 Foam::labelList Foam::meshRefinement::refineCandidates
 (
     const point& keepPoint,
-    const labelList& globalToPatch,
     const scalar curvature,
 
     const PtrList<featureEdgeMesh>& featureMeshes,
     const labelList& featureLevels,
 
-    const PtrList<searchableSurface>& shells,
-    const labelList& shellLevels,
-    const boolList& shellRefineInside,
-
     const bool featureRefinement,
     const bool internalRefinement,
     const bool surfaceRefinement,
@@ -1086,7 +1064,12 @@ Foam::labelList Foam::meshRefinement::refineCandidates
 
     labelList cellsToRefine;
 
-    if (totNCells < maxGlobalCells)
+    if (totNCells >= maxGlobalCells)
+    {
+        Info<< "No cells marked for refinement since reached limit "
+            << maxGlobalCells << '.' << endl;
+    }
+    else
     {
         // Every cell I refine adds 7 cells. Estimate the number of cells
         // I am allowed to refine.
@@ -1151,9 +1134,6 @@ Foam::labelList Foam::meshRefinement::refineCandidates
         {
             label nShell = markInternalRefinement
             (
-                shells,
-                shellLevels,
-                shellRefineInside,
                 nAllowRefine,
 
                 refineCell,
@@ -1184,13 +1164,11 @@ Foam::labelList Foam::meshRefinement::refineCandidates
         // Refinement based on curvature of surface
         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-        if (curvatureRefinement)
+        if (curvatureRefinement && (curvature >= -1 && curvature <= 1))
         {
             label nCurv = markSurfaceCurvatureRefinement
             (
-                globalToPatch,
                 curvature,
-                false,              // do not refine at multiple regions
                 nAllowRefine,
                 neiLevel,
                 neiCc,
@@ -1287,28 +1265,6 @@ Foam::autoPtr<Foam::mapDistributePolyMesh>
     printMeshInfo(debug, "After refinement " + msg);
 
 
-    //// Remove cells which are inside closed surfaces
-    //// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    //
-    //if (findIndex(surfaces.closed(), true) != -1)
-    //{
-    //    Info<< "Removing cells fully inside closed surfaces."
-    //        << endl;
-    //    removeInsideCells
-    //    (
-    //        msg,               // refinement iteration for statistics only
-    //        exposedFacesPatch  // patch to use for exposed internal faces
-    //    );
-    //    Info<< "Removed inside cells in = "
-    //        << mesh_.time().cpuTimeIncrement() << " s" << endl;
-    //    if (debug)
-    //    {
-    //       // test all is still synced across proc patches
-    //       checkData();
-    //    }
-    //}
-
-
     // Load balancing
     // ~~~~~~~~~~~~~~
 
diff --git a/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C b/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C
index 83816314a7cdc674876b41d03f47239bd831b171..ce09e597e2480c80f1583778639ff7899aac5b57 100644
--- a/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C
+++ b/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C
@@ -27,25 +27,17 @@ License
 #include "refinementSurfaces.H"
 #include "orientedSurface.H"
 #include "Time.H"
-#include "searchableSurface.H"
+#include "searchableSurfaces.H"
+#include "shellSurfaces.H"
+#include "triSurfaceMesh.H"
+#include "labelPair.H"
+#include "searchableSurfacesQueries.H"
+
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-Foam::fileNameList Foam::refinementSurfaces::extractFileNames
-(
-    const PtrList<dictionary>& surfaceDicts
-)
-{
-    fileNameList surfaceFileNames(surfaceDicts.size());
-
-    forAll(surfaceDicts, i)
-    {
-        surfaceFileNames[i] = fileName(surfaceDicts[i].lookup("file"));
-    }
-    return surfaceFileNames;
-}
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
@@ -53,13 +45,13 @@ Foam::fileNameList Foam::refinementSurfaces::extractFileNames
 // Construct from components
 Foam::refinementSurfaces::refinementSurfaces
 (
-    const IOobject& io,
+    const searchableSurfaces& allGeometry,
     const PtrList<dictionary>& surfaceDicts
 )
 :
-    triSurfaceMeshes(io, extractFileNames(surfaceDicts)),
+    allGeometry_(allGeometry),
+    surfaces_(surfaceDicts.size()),
     names_(surfaceDicts.size()),
-    closed_(surfaceDicts.size(), true),
     faceZoneNames_(surfaceDicts.size()),
     cellZoneNames_(surfaceDicts.size()),
     zoneInside_(surfaceDicts.size()),
@@ -67,29 +59,25 @@ Foam::refinementSurfaces::refinementSurfaces
 {
     labelList globalMinLevel(surfaceDicts.size(), 0);
     labelList globalMaxLevel(surfaceDicts.size(), 0);
-    List<HashTable<label> > regionMinLevel(surfaceDicts.size());
-    List<HashTable<label> > regionMaxLevel(surfaceDicts.size());
+    List<Map<label> > regionMinLevel(surfaceDicts.size());
+    List<Map<label> > regionMaxLevel(surfaceDicts.size());
 
-    labelList globalSurfLayers(surfaceDicts.size());
-    List<HashTable<label> > regionSurfLayers(surfaceDicts.size());
-
-    wordList globalPatchType(surfaceDicts.size());
-    List<HashTable<word> > regionPatchType(surfaceDicts.size());
-    List<HashTable<word> > regionPatchName(surfaceDicts.size());
+    //wordList globalPatchType(surfaceDicts.size());
+    //List<HashTable<word> > regionPatchType(surfaceDicts.size());
+    //List<HashTable<word> > regionPatchName(surfaceDicts.size());
 
     forAll(surfaceDicts, surfI)
     {
         const dictionary& dict = surfaceDicts[surfI];
 
-        names_[surfI] = word(dict.lookup("name"));
+        dict.lookup("name") >> names_[surfI];
+
+        surfaces_[surfI] = allGeometry_.findSurfaceID(names_[surfI]);
 
         // Global refinement level
         globalMinLevel[surfI] = readLabel(dict.lookup("minRefinementLevel"));
         globalMaxLevel[surfI] = readLabel(dict.lookup("maxRefinementLevel"));
 
-        // Global number of layers
-        globalSurfLayers[surfI] = readLabel(dict.lookup("surfaceLayers"));
-
         // Global zone names per surface
         if (dict.found("faceZone"))
         {
@@ -98,50 +86,62 @@ Foam::refinementSurfaces::refinementSurfaces
             dict.lookup("zoneInside") >> zoneInside_[surfI];
         }
 
-        // Global patch name per surface
-        if (dict.found("patchType"))
-        {
-            dict.lookup("patchType") >> globalPatchType[surfI];
-        }
+        //// Global patch name per surface
+        //if (dict.found("patchType"))
+        //{
+        //    dict.lookup("patchType") >> globalPatchType[surfI];
+        //}
 
 
         if (dict.found("regions"))
         {
             PtrList<dictionary> regionDicts(dict.lookup("regions"));
 
+            const wordList& regionNames =
+                allGeometry_[surfaces_[surfI]].regions();
+
             forAll(regionDicts, dictI)
             {
                 const dictionary& regionDict = regionDicts[dictI];
 
                 const word regionName(regionDict.lookup("name"));
-                label min = readLabel(regionDict.lookup("minRefinementLevel"));
-                label max = readLabel(regionDict.lookup("maxRefinementLevel"));
 
-                regionMinLevel[surfI].insert(regionName, min);
-                regionMaxLevel[surfI].insert(regionName, max);
+                label regionI = findIndex(regionNames, regionName);
 
-                label nLayers = readLabel(regionDict.lookup("surfaceLayers"));
-                regionSurfLayers[surfI].insert(regionName, nLayers);
-
-                if (regionDict.found("patchType"))
+                if (regionI == -1)
                 {
-                    regionPatchType[surfI].insert
+                    FatalErrorIn
                     (
-                        regionName,
-                        regionDict.lookup("patchType")
-                    );
-                    regionPatchName[surfI].insert
+                        "refinementSurfaces::refinementSurfaces"
+                        "(const IOobject&, const PtrList<dictionary>&)"
+                    )   << "No region called " << regionName << " on surface "
+                        << allGeometry_[surfaces_[surfI]].name() << endl
+                        << "Valid regions are " << regionNames
+                        << exit(FatalError);
+                }
+
+
+                label min = readLabel(regionDict.lookup("minRefinementLevel"));
+                label max = readLabel(regionDict.lookup("maxRefinementLevel"));
+
+                bool hasInserted = regionMinLevel[surfI].insert(regionI, min);
+                if (!hasInserted)
+                {
+                    FatalErrorIn
                     (
-                        regionName,
-                        regionDict.lookup("patchName")
-                    );
+                        "refinementSurfaces::refinementSurfaces"
+                        "(const IOobject&, const PtrList<dictionary>&)"
+                    )   << "Duplicate region name " << regionName
+                        << " on surface " << names_[surfI]
+                        << exit(FatalError);
                 }
+                regionMaxLevel[surfI].insert(regionI, max);
             }
         }
     }
 
 
-    // Check for duplicate surface or region names
+    // Check for duplicate surface names
     {
         HashTable<label> surfaceNames(names_.size());
 
@@ -158,57 +158,173 @@ Foam::refinementSurfaces::refinementSurfaces
                     << surfaceNames[names_[surfI]]
                     << exit(FatalError);
             }
+        }
+    }
+
+    // Calculate local to global region offset
+    label nRegions = 0;
+
+    forAll(surfaceDicts, surfI)
+    {
+        regionOffset_[surfI] = nRegions;
+
+        nRegions += allGeometry_[surfaces_[surfI]].regions().size();
+    }
+
+    // Rework surface specific information into information per global region
+    minLevel_.setSize(nRegions);
+    minLevel_ = 0;
+    maxLevel_.setSize(nRegions);
+    maxLevel_ = 0;
+    //patchName_.setSize(nRegions);
+    //patchType_.setSize(nRegions);
+
+    forAll(surfaceDicts, surfI)
+    {
+        label nRegions = allGeometry_[surfaces_[surfI]].regions().size();
+
+        // Initialise to global (i.e. per surface)
+        for (label i = 0; i < nRegions; i++)
+        {
+            minLevel_[regionOffset_[surfI] + i] = globalMinLevel[surfI];
+            maxLevel_[regionOffset_[surfI] + i] = globalMaxLevel[surfI];
+        }
 
-            // Check for duplicate region names
-            const geometricSurfacePatchList& patches =
-                operator[](surfI).patches();
+        // Overwrite with region specific information
+        forAllConstIter(Map<label>, regionMinLevel[surfI], iter)
+        {
+            label globalRegionI = regionOffset_[surfI] + iter.key();
 
-            HashTable<label> regionNames(patches.size());
-            forAll(patches, i)
+            minLevel_[globalRegionI] = iter();
+            maxLevel_[globalRegionI] = regionMaxLevel[surfI][iter.key()];
+
+            // Check validity
+            if
+            (
+                minLevel_[globalRegionI] < 0
+             || maxLevel_[globalRegionI] < minLevel_[globalRegionI]
+            )
             {
-                if (!regionNames.insert(patches[i].name(), i))
-                {
-                    FatalErrorIn
-                    (
-                        "refinementSurfaces::refinementSurfaces"
-                        "(const IOobject&, const PtrList<dictionary>&)"
-                    )   << "Duplicate region name " << patches[i].name()
-                        << " on surface " << names_[surfI] << endl
-                        << "Previous occurrence of region at index "
-                        << regionNames[patches[i].name()]
-                        << exit(FatalError);
-                }
+                FatalErrorIn
+                (
+                    "refinementSurfaces::refinementSurfaces"
+                    "(const IOobject&, const PtrList<dictionary>&)"
+                )   << "Illegal level or layer specification for surface "
+                    << names_[surfI]
+                    << " : minLevel:" << minLevel_[globalRegionI]
+                    << " maxLevel:" << maxLevel_[globalRegionI]
+                    << exit(FatalError);
             }
         }
+
+        //// Optional patch names and patch types
+        //forAllConstIter(HashTable<word>, regionPatchName[surfI], iter)
+        //{
+        //    label regionI = findIndex(regionNames, iter.key());
+        //    label globalRegionI = regionOffset_[surfI] + regionI;
+        //
+        //    patchName_[globalRegionI] = iter();
+        //    patchType_[globalRegionI] = regionPatchType[surfI][iter.key()];
+        //}
     }
+}
+
 
+Foam::refinementSurfaces::refinementSurfaces
+(
+    const searchableSurfaces& allGeometry,
+    const dictionary& surfacesDict
+)
+:
+    allGeometry_(allGeometry),
+    surfaces_(surfacesDict.size()),
+    names_(surfacesDict.size()),
+    faceZoneNames_(surfacesDict.size()),
+    cellZoneNames_(surfacesDict.size()),
+    zoneInside_(surfacesDict.size()),
+    regionOffset_(surfacesDict.size())
+{
+    labelList globalMinLevel(surfacesDict.size(), 0);
+    labelList globalMaxLevel(surfacesDict.size(), 0);
+    List<Map<label> > regionMinLevel(surfacesDict.size());
+    List<Map<label> > regionMaxLevel(surfacesDict.size());
 
-    // Calculate closedness
-    forAll(closed_, surfI)
+    label surfI = 0;
+    forAllConstIter(dictionary, surfacesDict, iter)
     {
-        const triSurface& s = operator[](surfI);
+        names_[surfI] = iter().keyword();
+
+        surfaces_[surfI] = allGeometry_.findSurfaceID(names_[surfI]);
 
-        const labelListList& edgeFaces = s.edgeFaces();
+        if (surfaces_[surfI] == -1)
+        {
+            FatalErrorIn
+            (
+                "refinementSurfaces::refinementSurfaces"
+                "(const searchableSurfaces&, const dictionary>&"
+            )   << "No surface called " << iter().keyword() << endl
+                << "Valid surfaces are " << allGeometry_.names()
+                << exit(FatalError);
+        }
+        const dictionary& dict = surfacesDict.subDict(iter().keyword());
+
+        const labelPair refLevel(dict.lookup("level"));
+        globalMinLevel[surfI] = refLevel[0];
+        globalMaxLevel[surfI] = refLevel[1];
+
+        // Global zone names per surface
+        if (dict.found("faceZone"))
+        {
+            dict.lookup("faceZone") >> faceZoneNames_[surfI];
+            dict.lookup("cellZone") >> cellZoneNames_[surfI];
+            dict.lookup("zoneInside") >> zoneInside_[surfI];
+        }
 
-        forAll(edgeFaces, edgeI)
+        if (dict.found("regions"))
         {
-            if (edgeFaces[edgeI].size() != 2)
+            const dictionary& regionsDict = dict.subDict("regions");
+            const wordList& regionNames =
+                allGeometry_[surfaces_[surfI]].regions();
+
+            forAllConstIter(dictionary, regionsDict, iter)
             {
-                closed_[surfI] = false;
-                break;
+                const word& key = iter().keyword();
+
+                if (regionsDict.isDict(key))
+                {
+                    // Get the dictionary for region iter.keyword()
+                    const dictionary& regionDict = regionsDict.subDict(key);
+
+                    label regionI = findIndex(regionNames, key);
+                    if (regionI == -1)
+                    {
+                        FatalErrorIn
+                        (
+                            "refinementSurfaces::refinementSurfaces"
+                            "(const searchableSurfaces&, const dictionary>&"
+                        )   << "No region called " << key << " on surface "
+                            << allGeometry_[surfaces_[surfI]].name() << endl
+                            << "Valid regions are " << regionNames
+                            << exit(FatalError);
+                    }
+
+                    const labelPair refLevel(regionDict.lookup("level"));
+
+                    regionMinLevel[surfI].insert(regionI, refLevel[0]);
+                    regionMaxLevel[surfI].insert(regionI, refLevel[1]);
+                }
             }
         }
+        surfI++;
     }
 
-
     // Calculate local to global region offset
     label nRegions = 0;
 
-    forAll(surfaceDicts, surfI)
+    forAll(surfacesDict, surfI)
     {
         regionOffset_[surfI] = nRegions;
-
-        nRegions += operator[](surfI).patches().size();
+        nRegions += allGeometry_[surfaces_[surfI]].regions().size();
     }
 
     // Rework surface specific information into information per global region
@@ -216,92 +332,52 @@ Foam::refinementSurfaces::refinementSurfaces
     minLevel_ = 0;
     maxLevel_.setSize(nRegions);
     maxLevel_ = 0;
-    numLayers_.setSize(nRegions);
-    numLayers_ = 0;
-    patchName_.setSize(nRegions);
-    patchType_.setSize(nRegions);
 
-    forAll(surfaceDicts, surfI)
+
+    forAll(globalMinLevel, surfI)
     {
-        const geometricSurfacePatchList& regions = operator[](surfI).patches();
+        label nRegions = allGeometry_[surfaces_[surfI]].regions().size();
 
         // Initialise to global (i.e. per surface)
-        forAll(regions, i)
+        for (label i = 0; i < nRegions; i++)
         {
             minLevel_[regionOffset_[surfI] + i] = globalMinLevel[surfI];
             maxLevel_[regionOffset_[surfI] + i] = globalMaxLevel[surfI];
-            numLayers_[regionOffset_[surfI] + i] = globalSurfLayers[surfI];
-            patchType_[regionOffset_[surfI] + i] = globalPatchType[surfI];
-        }
-
-        // Get the region names
-        wordList regionNames(regions.size());
-        forAll(regions, regionI)
-        {
-            regionNames[regionI] = regions[regionI].name();
         }
 
         // Overwrite with region specific information
-        forAllConstIter(HashTable<label>, regionMinLevel[surfI], iter)
+        forAllConstIter(Map<label>, regionMinLevel[surfI], iter)
         {
-            // Find the local region number.
-            label regionI = findIndex(regionNames, iter.key());
-
-            if (regionI == -1)
-            {
-                FatalErrorIn
-                (
-                    "refinementSurfaces::refinementSurfaces"
-                    "(const IOobject&, const PtrList<dictionary>&)"
-                )   << "Cannot find region " << iter.key()
-                    << " in surface " << names_[surfI]
-                    << " which has regions " << regionNames
-                    << abort(FatalError);
-            }
-
-            label globalRegionI = regionOffset_[surfI] + regionI;
+            label globalRegionI = regionOffset_[surfI] + iter.key();
 
             minLevel_[globalRegionI] = iter();
             maxLevel_[globalRegionI] = regionMaxLevel[surfI][iter.key()];
-            numLayers_[globalRegionI] = regionSurfLayers[surfI][iter.key()];
 
             // Check validity
             if
             (
                 minLevel_[globalRegionI] < 0
              || maxLevel_[globalRegionI] < minLevel_[globalRegionI]
-             || numLayers_[globalRegionI] < 0
             )
             {
                 FatalErrorIn
                 (
                     "refinementSurfaces::refinementSurfaces"
-                    "(const IOobject&, const PtrList<dictionary>&)"
+                    "(const searchableSurfaces&, const dictionary>&"
                 )   << "Illegal level or layer specification for surface "
                     << names_[surfI]
                     << " : minLevel:" << minLevel_[globalRegionI]
                     << " maxLevel:" << maxLevel_[globalRegionI]
-                    << " numLayers:" << numLayers_[globalRegionI]
                     << exit(FatalError);
             }
         }
-
-        // Optional patch names and patch types
-        forAllConstIter(HashTable<word>, regionPatchName[surfI], iter)
-        {
-            label regionI = findIndex(regionNames, iter.key());
-            label globalRegionI = regionOffset_[surfI] + regionI;
-
-            patchName_[globalRegionI] = iter();
-            patchType_[globalRegionI] = regionPatchType[surfI][iter.key()];
-        }
     }
 }
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-//- Get indices of named surfaces (surfaces with cellZoneNam)
+// Get indices of named surfaces (surfaces with cellZoneName)
 Foam::labelList Foam::refinementSurfaces::getNamedSurfaces() const
 {
    labelList namedSurfaces(cellZoneNames_.size());
@@ -320,16 +396,26 @@ Foam::labelList Foam::refinementSurfaces::getNamedSurfaces() const
 }
 
 
-bool Foam::refinementSurfaces::isSurfaceClosed(const triSurface& s)
+// Get indices of closed named surfaces
+Foam::labelList Foam::refinementSurfaces::getClosedNamedSurfaces() const
 {
-    if (s.nEdges() == s.nInternalEdges())
-    {
-        return true;
-    }
-    else
+    labelList named(getNamedSurfaces());
+
+    labelList closed(named.size());
+    label closedI = 0;
+
+    forAll(named, i)
     {
-        return false;
+        label surfI = named[i];
+
+        if (allGeometry_[surfaces_[surfI]].hasVolumeType())
+        {
+            closed[closedI++] = surfI;
+        }
     }
+    closed.setSize(closedI);
+
+    return closed;
 }
 
 
@@ -373,238 +459,475 @@ Foam::labelList Foam::refinementSurfaces::countRegions(const triSurface& s)
 }
 
 
-// Find intersections of edge. Return -1 or first surface with higher minLevel
-// number.
-Foam::label Foam::refinementSurfaces::findHigherIntersection
+// Precalculate the refinement level for every element of the searchable
+// surface. This is currently hardcoded for triSurfaceMesh only.
+void Foam::refinementSurfaces::setMinLevelFields
 (
-    const point& start,
-    const point& end,
-    const label currentLevel,   // current cell refinement level
-
-    pointIndexHit& hit
-) const
+    const shellSurfaces& shells
+)
 {
-    forAll(*this, surfI)
+    minLevelFields_.setSize(surfaces_.size());
+
+    forAll(surfaces_, surfI)
     {
-        hit = operator[](surfI).findLineAny(start, end);
+        const searchableSurface& geom = allGeometry_[surfaces_[surfI]];
 
-        if (hit.hit())
+        if (isA<triSurfaceMesh>(geom))
         {
-            if (currentLevel == -1)
+            const triSurfaceMesh& triMesh = refCast<const triSurfaceMesh>(geom);
+
+            minLevelFields_.set
+            (
+                surfI,
+                new triSurfaceLabelField
+                (
+                    IOobject
+                    (
+                        triMesh.searchableSurface::name(),
+                        triMesh.objectRegistry::time().constant(),// directory
+                        "triSurface",               // instance
+                        triMesh,
+                        IOobject::NO_READ,
+                        IOobject::AUTO_WRITE,
+                        false
+                    ),
+                    triMesh,
+                    dimless
+                )
+            );
+            triSurfaceLabelField& minLevelField = minLevelFields_[surfI];
+
+            const triSurface& s = static_cast<const triSurface&>(triMesh);
+
+            // Initialise fields to region wise minLevel
+            forAll(s, triI)
             {
-                // Return any.
-                return surfI;
+                minLevelField[triI] = minLevel(surfI, s[triI].region());
             }
-            else
+
+            // Find out if triangle inside shell with higher level
+            pointField fc(s.size());
+            forAll(s, triI)
             {
-                //const triSurface& s = trees()[surfI].shapes().surface();
-                //label region = globalRegion(surfI, s[hit.index()].region());
-                //if (minLevel_[region] > currentLevel)
-                if (minLevelFields_[surfI][hit.index()] > currentLevel)
-                {
-                    return surfI;
-                }
+                fc[triI] = s[triI].centre(s.points());
+            }
+            // What level does shell want to refine fc to?
+            labelList shellLevel;
+            shells.findHigherLevel(fc, minLevelField, shellLevel);
+
+            forAll(minLevelField, triI)
+            {
+                minLevelField[triI] = max
+                (
+                    minLevelField[triI],
+                    shellLevel[triI]
+                );
             }
         }
     }
-
-    hit.setMiss();
-
-    return -1;
 }
 
 
-// Find intersection with max of edge. Return -1 or the surface
-// with the highest maxLevel above currentLevel
-Foam::label Foam::refinementSurfaces::findHighestIntersection
+// Find intersections of edge. Return -1 or first surface with higher minLevel
+// number.
+void Foam::refinementSurfaces::findHigherIntersection
 (
-    const point& start,
-    const point& end,
-    const label currentLevel,   // current cell refinement level
+    const pointField& start,
+    const pointField& end,
+    const labelList& currentLevel,   // current cell refinement level
 
-    pointIndexHit& maxHit
+    labelList& surfaces,
+    labelList& surfaceLevel
 ) const
 {
-    // surface with highest maxlevel
-    label maxSurface = -1;
-    // maxLevel of maxSurface
-    label maxLevel = currentLevel;
+    surfaces.setSize(start.size());
+    surfaces = -1;
+    surfaceLevel.setSize(start.size());
+    surfaceLevel = -1;
+
+    if (surfaces_.size() == 0)
+    {
+        return;
+    }
+
+    // Work arrays
+    labelList hitMap(identity(start.size()));
+    pointField p0(start);
+    pointField p1(end);
+    List<pointIndexHit> hitInfo(start.size());
 
-    forAll(*this, surfI)
+
+    forAll(surfaces_, surfI)
     {
-        pointIndexHit hit = operator[](surfI).findLineAny(start, end);
+        allGeometry_[surfaces_[surfI]].findLineAny(p0, p1, hitInfo);
 
-        if (hit.hit())
+        // Copy all hits into arguments, continue with misses
+        label newI = 0;
+        forAll(hitInfo, hitI)
         {
-            const triSurface& s = operator[](surfI);
+            // Get the minLevel for the point
+            label minLocalLevel = -1;
+
+            if (hitInfo[hitI].hit())
+            {
+                // Check if minLevelField for this surface.
+                if
+                (
+                    minLevelFields_.set(surfI)
+                 && minLevelFields_[surfI].size() > 0
+                )
+                {
+                    minLocalLevel =
+                        minLevelFields_[surfI][hitInfo[hitI].index()];
+                }
+                else
+                {
+                    // Use the min level for the surface instead. Assume
+                    // single region 0. 
+                    minLocalLevel = minLevel(surfI, 0);
+                }
+            }
 
-            label region = globalRegion(surfI, s[hit.index()].region());
+            label pointI = hitMap[hitI];
 
-            if (maxLevel_[region] > maxLevel)
+            if (minLocalLevel > currentLevel[pointI])
+            {
+                surfaces[pointI] = surfI;
+                surfaceLevel[pointI] = minLocalLevel;
+            }
+            else
             {
-                maxSurface = surfI;
-                maxLevel = maxLevel_[region];
-                maxHit = hit;
+                if (hitI != newI)
+                {
+                    hitMap[newI] = hitMap[hitI];
+                    p0[newI] = p0[hitI];
+                    p1[newI] = p1[hitI];
+                }
+                newI++;
             }
         }
-    }
 
-    if (maxSurface == -1)
-    {
-        // maxLevel unchanged. No interesting surface hit.
-        maxHit.setMiss();
-    }
+        // All done? Note that this decision should be synchronised
+        if (newI == 0)
+        {
+            break;
+        }
 
-    return maxSurface;
+        // Trim and continue
+        hitMap.setSize(newI);
+        p0.setSize(newI);
+        p1.setSize(newI);
+        hitInfo.setSize(newI);
+    }
 }
 
 
-Foam::label Foam::refinementSurfaces::insideZone
+void Foam::refinementSurfaces::findAllHigherIntersections
 (
-    const labelList& surfaces,
-    const point& pt
+    const pointField& start,
+    const pointField& end,
+    const labelList& currentLevel,   // current cell refinement level
+
+    List<vectorList>& surfaceNormal,
+    labelListList& surfaceLevel
 ) const
 {
-    forAll(surfaces, i)
+    surfaceLevel.setSize(start.size());
+    surfaceNormal.setSize(start.size());
+
+    if (surfaces_.size() == 0)
     {
-        label surfI = surfaces[i];
+        return;
+    }
+
+    // Work arrays
+    List<List<pointIndexHit> > hitInfo;
+    labelList pRegions;
+    vectorField pNormals;
+
+    forAll(surfaces_, surfI)
+    {
+        allGeometry_[surfaces_[surfI]].findLineAll(start, end, hitInfo);
 
-        if (closed_[surfI])
+        forAll(hitInfo, pointI)
         {
-            const triSurfaceMesh& s = operator[](surfI);
+            const List<pointIndexHit>& pHits = hitInfo[pointI];
+            allGeometry_[surfaces_[surfI]].getRegion(pHits, pRegions);
+            allGeometry_[surfaces_[surfI]].getNormal(pHits, pNormals);
 
-            triSurfaceMesh::volumeType t = s.getVolumeType(pt);
+            // Extract those hits that are on higher levelled surfaces.
+            // Note: should move extraction of region, normal outside of loop
+            // below for if getRegion/getNormal have high overhead.
 
-            if
-            (
-                (t == triSurfaceMesh::INSIDE && zoneInside_[surfI])
-             || (t == triSurfaceMesh::OUTSIDE && !zoneInside_[surfI])
-            )
+            forAll(pHits, pHitI)
             {
-                return surfI;
+                label region = globalRegion(surfI, pRegions[pHitI]);
+
+                if (maxLevel_[region] > currentLevel[pointI])
+                {
+                    // Append to pointI info
+                    label sz = surfaceNormal[pointI].size();
+                    surfaceNormal[pointI].setSize(sz+1);
+                    surfaceNormal[pointI][sz] = pNormals[pHitI];
+
+                    surfaceLevel[pointI].setSize(sz+1);
+                    surfaceLevel[pointI][sz] = maxLevel_[region];
+                }
             }
         }
     }
-    return -1;
 }
 
 
-Foam::label Foam::refinementSurfaces::markInsidePoints
+void Foam::refinementSurfaces::findNearestIntersection
 (
-    const pointField& points,
-    PackedList<1>& isInside
+    const labelList& surfacesToTest,
+    const pointField& start,
+    const pointField& end,
+
+    labelList& surface1,
+    List<pointIndexHit>& hit1,
+    labelList& region1,
+    labelList& surface2,
+    List<pointIndexHit>& hit2,
+    labelList& region2
 ) const
 {
-    isInside.setSize(points.size());
-    isInside = 0u;
+    // 1. intersection from start to end
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    // Initialize arguments
+    surface1.setSize(start.size());
+    surface1 = -1;
+    hit1.setSize(start.size());
+    region1.setSize(start.size());
+
+    // Current end of segment to test.
+    pointField nearest(end);
+    // Work array
+    List<pointIndexHit> nearestInfo(start.size());
+    labelList region;
+
+    forAll(surfacesToTest, testI)
+    {
+        label surfI = surfacesToTest[testI];
 
-    label nPointInside = 0;
+        // See if any intersection between start and current nearest
+        allGeometry_[surfaces_[surfI]].findLine
+        (
+            start,
+            nearest,
+            nearestInfo
+        );
+        allGeometry_[surfaces_[surfI]].getRegion
+        (
+            nearestInfo,
+            region
+        );
 
-    forAll(points, pointI)
-    {
-        forAll(*this, surfI)
+        forAll(nearestInfo, pointI)
         {
-            if (closed()[surfI])
+            if (nearestInfo[pointI].hit())
             {
-                const triSurfaceMesh& s = operator[](surfI);
-
-                triSurfaceMesh::volumeType t = s.getVolumeType(points[pointI]);
-
-                if (t == triSurfaceMesh::INSIDE)
-                {
-                    isInside.set(pointI, 1u);
-                    nPointInside++;
-                    break;
-                }
+                hit1[pointI] = nearestInfo[pointI];
+                surface1[pointI] = surfI;
+                region1[pointI] = region[pointI];
+                nearest[pointI] = hit1[pointI].hitPoint();
             }
         }
     }
 
-    return nPointInside;
-}
 
+    // 2. intersection from end to last intersection
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-void Foam::refinementSurfaces::setMinLevelFields
-(
-    const PtrList<searchableSurface>& shells,
-    const labelList& shellLevels,
-    const boolList& shellRefineInside
-)
-{
-    typedef Foam::DimensionedField<label, triSurfaceGeoMesh>
-        triSurfaceLabelField;
+    // Find the nearest intersection from end to start. Note that we initialize
+    // to the first intersection (if any).
+    surface2 = surface1;
+    hit2 = hit1;
+    region2 = region1;
 
-    minLevelFields_.setSize(size());
+    // Set current end of segment to test.
+    forAll(nearest, pointI)
+    {
+        if (hit1[pointI].hit())
+        {
+            nearest[pointI] = hit1[pointI].hitPoint();
+        }
+        else
+        {
+            // Disable testing by setting to end.
+            nearest[pointI] = end[pointI];
+        }
+    }
 
-    forAll(*this, surfI)
+    forAll(surfacesToTest, testI)
     {
-        const triSurfaceMesh& surfMesh = operator[](surfI);
+        label surfI = surfacesToTest[testI];
 
-        minLevelFields_.set
+        // See if any intersection between end and current nearest
+        allGeometry_[surfaces_[surfI]].findLine
         (
-            surfI,
-            new triSurfaceLabelField
-            (
-                IOobject
-                (
-                    surfMesh.IOobject::name(),
-                    surfMesh.time().constant(), // directory
-                    "triSurface",               // instance
-                    surfMesh,
-                    IOobject::NO_READ,
-                    IOobject::AUTO_WRITE,
-                    false
-                ),
-                surfMesh,
-                dimless
-            )
+            end,
+            nearest,
+            nearestInfo
+        );
+        allGeometry_[surfaces_[surfI]].getRegion
+        (
+            nearestInfo,
+            region
         );
+
+        forAll(nearestInfo, pointI)
+        {
+            if (nearestInfo[pointI].hit())
+            {
+                hit2[pointI] = nearestInfo[pointI];
+                surface2[pointI] = surfI;
+                region2[pointI] = region[pointI];
+                nearest[pointI] = hit2[pointI].hitPoint();
+            }
+        }
     }
+}
 
-    // Initialise fields to region wise minLevel
-    forAll(*this, surfI)
-    {
-        const triSurface& s = operator[](surfI);
 
-        triSurfaceLabelField& minLevelField = minLevelFields_[surfI];
+void Foam::refinementSurfaces::findAnyIntersection
+(
+    const pointField& start,
+    const pointField& end,
+
+    labelList& hitSurface,
+    List<pointIndexHit>& hitInfo
+) const
+{
+    searchableSurfacesQueries::findAnyIntersection
+    (
+        allGeometry_,
+        surfaces_,
+        start,
+        end,
+        hitSurface,
+        hitInfo
+    );
+}
+
 
-        forAll(s, i)
+void Foam::refinementSurfaces::findNearest
+(
+    const labelList& surfacesToTest,
+    const pointField& samples,
+    const  scalarField& nearestDistSqr,
+    labelList& hitSurface,
+    List<pointIndexHit>& hitInfo
+) const
+{
+    labelList geometries(IndirectList<label>(surfaces_, surfacesToTest));
+
+    // Do the tests. Note that findNearest returns index in geometries.
+    searchableSurfacesQueries::findNearest
+    (
+        allGeometry_,
+        geometries,
+        samples,
+        nearestDistSqr,
+        hitSurface,
+        hitInfo
+    );
+
+    // Rework the hitSurface to be surface (i.e. index into surfaces_)
+    forAll(hitSurface, pointI)
+    {
+        if (hitSurface[pointI] != -1)
         {
-            minLevelField[i] = minLevel_[globalRegion(surfI, s[i].region())];
+            hitSurface[pointI] = surfacesToTest[hitSurface[pointI]];
         }
     }
+}
+
 
-    // Adapt for triangles inside shells
-    forAll(*this, surfI)
+//// Find intersection with max of edge. Return -1 or the surface
+//// with the highest maxLevel above currentLevel
+//Foam::label Foam::refinementSurfaces::findHighestIntersection
+//(
+//    const point& start,
+//    const point& end,
+//    const label currentLevel,   // current cell refinement level
+//
+//    pointIndexHit& maxHit
+//) const
+//{
+//    // surface with highest maxlevel
+//    label maxSurface = -1;
+//    // maxLevel of maxSurface
+//    label maxLevel = currentLevel;
+//
+//    forAll(*this, surfI)
+//    {
+//        pointIndexHit hit = operator[](surfI).findLineAny(start, end);
+//
+//        if (hit.hit())
+//        {
+//            const triSurface& s = operator[](surfI);
+//
+//            label region = globalRegion(surfI, s[hit.index()].region());
+//
+//            if (maxLevel_[region] > maxLevel)
+//            {
+//                maxSurface = surfI;
+//                maxLevel = maxLevel_[region];
+//                maxHit = hit;
+//            }
+//        }
+//    }
+//
+//    if (maxSurface == -1)
+//    {
+//        // maxLevel unchanged. No interesting surface hit.
+//        maxHit.setMiss();
+//    }
+//
+//    return maxSurface;
+//}
+
+
+void Foam::refinementSurfaces::findInside
+(
+    const labelList& testSurfaces,
+    const pointField& pt,
+    labelList& insideSurfaces
+) const
+{
+    insideSurfaces.setSize(pt.size());
+    insideSurfaces = -1;
+
+    forAll(testSurfaces, i)
     {
-        const triSurface& s = operator[](surfI);
-        triSurfaceLabelField& minLevelField = minLevelFields_[surfI];
+        label surfI = testSurfaces[i];
 
-        forAll(s, i)
+        if (allGeometry_[surfaces_[surfI]].hasVolumeType())
         {
-            point fc = s[i].centre(s.points());
+            List<searchableSurface::volumeType> volType;
+            allGeometry_[surfaces_[surfI]].getVolumeType(pt, volType);
 
-            forAll(shells, shellI)
+            forAll(volType, pointI)
             {
-                // Which side of shell is to be refined
-                searchableSurface::volumeType refSide =
-                (
-                    shellRefineInside[shellI]
-                  ? searchableSurface::INSIDE
-                  : searchableSurface::OUTSIDE
-                );
-
-                // Find whether point is inside or outside shell
-                searchableSurface::volumeType t =
-                    shells[shellI].getVolumeType(fc);
-
-                if (t == refSide)
+                if (insideSurfaces[pointI] == -1)
                 {
-                    minLevelField[i] = max
+                    if
                     (
-                        minLevelField[i],
-                        shellLevels[shellI]
-                    );
+                        (
+                            volType[pointI] == triSurfaceMesh::INSIDE
+                         && zoneInside_[surfI]
+                        )
+                     || (
+                            volType[pointI] == triSurfaceMesh::OUTSIDE
+                         && !zoneInside_[surfI]
+                        )
+                    )
+                    {
+                        insideSurfaces[pointI] = surfI;
+                    }
                 }
             }
         }
diff --git a/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H b/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H
index dc5bd0adfb6122f66f61830575c5aedf6c2c8d25..edb613d966f11b8b62ab55de4c83765b9ab55bd9 100644
--- a/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H
+++ b/src/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H
@@ -26,8 +26,8 @@ Class
     Foam::refinementSurfaces
 
 Description
-    Container for triSurfaces used for surface-driven refinement.
-    These contain all the data about the level of refinement needed per
+    Container for data on surfaces used for surface-driven refinement.
+    Contains all the data about the level of refinement needed per
     surface.
 
 SourceFiles
@@ -38,34 +38,38 @@ SourceFiles
 #ifndef refinementSurfaces_H
 #define refinementSurfaces_H
 
-#include "triSurfaceMeshes.H"
 #include "PackedList.H"
 #include "triSurfaceGeoMesh.H"
 #include "triSurfaceFields.H"
+#include "vectorList.H"
+#include "pointIndexHit.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
-class searchableSurface;
+class searchableSurfaces;
+class shellSurfaces;
+class triSurfaceMesh;
 
 /*---------------------------------------------------------------------------*\
                            Class refinementSurfaces Declaration
 \*---------------------------------------------------------------------------*/
 
 class refinementSurfaces
-:
-    public triSurfaceMeshes
 {
     // Private data
 
+        //- Reference to all geometry.
+        const searchableSurfaces& allGeometry_;
+
+        //- Indices of surfaces that are refinement ones
+        labelList surfaces_;
+
         //- Surface name (word)
         wordList names_;
 
-        //- Per surface whether is closed
-        boolList closed_;
-
         //- Per 'interface' surface : name of faceZone to put faces into
         wordList faceZoneNames_;
 
@@ -86,24 +90,12 @@ class refinementSurfaces
         //- From global region number to refinement level
         labelList maxLevel_;
 
-        //- From global region number to added layers
-        labelList numLayers_;
-
-        //- From global region number to patch name
-        wordList patchName_;
-
-        //- From global region number to patch name
-        wordList patchType_;
-
-
         //- Per surface refinement level adapted for shells.
         PtrList<triSurfaceLabelField> minLevelFields_;
 
 
     // Private Member Functions
 
-        static fileNameList extractFileNames(const PtrList<dictionary>&);
-
         //- Disallow default bitwise copy construct
         refinementSurfaces(const refinementSurfaces&);
 
@@ -115,28 +107,39 @@ public:
 
     // Constructors
 
-        //- Construct from directory (io.instance()) and dictionaries
+        //- Construct from surfaces and dictionaries
         refinementSurfaces
         (
-            const IOobject& io,
+            const searchableSurfaces& allGeometry,
             const PtrList<dictionary>&
         );
 
+        //- Construct from surfaces and dictionary
+        refinementSurfaces
+        (
+            const searchableSurfaces& allGeometry,
+            const dictionary&
+        );
+
 
     // Member Functions
 
         // Access
 
-            //- Names of surfaces
-            const wordList& names() const
+            const searchableSurfaces& geometry() const
             {
-                return names_;
+                return allGeometry_;
             }
 
-            //- Per surface whether is closed
-            const boolList& closed() const
+            const labelList& surfaces() const
             {
-                return closed_;
+                return surfaces_;
+            }
+
+            //- Names of surfaces
+            const wordList& names() const
+            {
+                return names_;
             }
 
             //- Per 'interface' surface : name of faceZone to put faces into
@@ -151,15 +154,11 @@ public:
                 return cellZoneNames_;
             }
 
-            //- Per 'interface' surface : if closed: zonify cells inside surface
-            const boolList& zoneInside() const
-            {
-                return zoneInside_;
-            }
-
             //- Get indices of named surfaces (surfaces with cellZoneName)
             labelList getNamedSurfaces() const;
 
+            //- Get indices of closed named surfaces
+            labelList getClosedNamedSurfaces() const;
 
             //- From local region number to global region number
             const labelList& regionOffset() const
@@ -179,24 +178,6 @@ public:
                 return maxLevel_;
             }
 
-            //- From global region number to added layers
-            const labelList& numLayers() const
-            {
-                return numLayers_;
-            }
-
-            //- From global region number to patch name. Patchnames can be empty
-            const wordList& patchName() const
-            {
-                return patchName_;
-            }
-
-            //- From global region number to patch name
-            const wordList& patchType() const
-            {
-                return patchType_;
-            }
-
 
         // Helper
 
@@ -206,14 +187,6 @@ public:
                 return regionOffset_[surfI]+regionI;
             }
 
-            //- From triangle on surface to global region
-            label triangleRegion(const label surfI, const label triI) const
-            {
-                const triSurface& s = operator[](surfI);
-
-                return globalRegion(surfI, s[triI].region());
-            }
-
             //- Min level for surface and region on surface
             label minLevel(const label surfI, const label regionI) const
             {
@@ -231,23 +204,12 @@ public:
                 return minLevel_.size();
             }
 
-            //- Minlevel updated for refinement shells
-            const triSurfaceLabelField& minLevelField(const label surfI) const
-            {
-                return minLevelFields_[surfI];
-            }
-
             //- Calculate minLevelFields
             void setMinLevelFields
             (
-                const PtrList<searchableSurface>& shells,
-                const labelList& shellLevels,
-                const boolList& shellRefineInside
+                const shellSurfaces& shells
             );
 
-            //- Helper: is surface closed?
-            static bool isSurfaceClosed(const triSurface&);
-
             //- Helper: orient (closed only) surfaces so keepPoint is outside.
             static void orientSurface
             (
@@ -263,36 +225,71 @@ public:
 
             //- Find intersection of edge. Return -1 or first surface
             //  with higher (than currentLevel) minlevel.
-            //  Return surface number and hit info.
-            label findHigherIntersection
+            //  Return surface number and level.
+            void findHigherIntersection
             (
-                const point& start,
-                const point& end,
-                const label currentLevel,   // current cell refinement level
-                pointIndexHit&
+                const pointField& start,
+                const pointField& end,
+                const labelList& currentLevel,  // current cell refinement level
+
+                labelList& surfaces,
+                labelList& surfaceLevel
             ) const;
 
-            //- Find intersection with max level. Return -1 or the surface
-            //  with the highest maxLevel above currentLevel
-            label findHighestIntersection
+            //- Find all intersections of edge. Unsorted order.
+            void findAllHigherIntersections
             (
-                const point& start,
-                const point& end,
-                const label currentLevel,   // current cell refinement level
-                pointIndexHit&
+                const pointField& start,
+                const pointField& end,
+                const labelList& currentLevel,  // current cell refinement level
+
+                List<vectorList>& surfaceNormal,
+                labelListList& surfaceLevel
             ) const;
 
-            //- Detect if a point is 'inside' (depending on zoneInside flag) a
-            //  zoneable surface. Returns -1 if not, returns first surface it
-            //  is.
-            label insideZone(const labelList& surfaces, const point& pt) const;
+            //- Find intersection nearest to the endpoints. surface1,2 are
+            //  not indices into surfacesToTest but refinement surface indices.
+            void findNearestIntersection
+            (
+                const labelList& surfacesToTest,
+                const pointField& start,
+                const pointField& end,
+
+                labelList& surface1,
+                List<pointIndexHit>& hit1,
+                labelList& region1,
+                labelList& surface2,
+                List<pointIndexHit>& hit2,
+                labelList& region2
+            ) const;
 
+            //- Used for debugging only: find intersection of edge.
+            void findAnyIntersection
+            (
+                const pointField& start,
+                const pointField& end,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            ) const;
 
-            //- Mark for all points whether it is inside any closed surface
-            //  Return number of inside points.
-            label markInsidePoints(const pointField&, PackedList<1>& isInside)
-             const;
+            //- Find nearest point on surfaces.
+            void findNearest
+            (
+                const labelList& surfacesToTest,
+                const pointField& samples,
+                const scalarField& nearestDistSqr,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            ) const;
 
+            //- Detect if a point is 'inside' (closed) surfaces.
+            //  Returns -1 if not, returns first surface it is.
+            void findInside
+            (
+                const labelList& surfacesToTest,
+                const pointField& pt,
+                labelList& insideSurfaces
+            ) const;
 };
 
 
diff --git a/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.C b/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.C
new file mode 100644
index 0000000000000000000000000000000000000000..fe64ea81c3d86d8f57b661ac0b24c4d694d76696
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.C
@@ -0,0 +1,465 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "searchableSurface.H"
+#include "shellSurfaces.H"
+#include "boundBox.H"
+#include "triSurfaceMesh.H"
+#include "refinementSurfaces.H"
+#include "searchableSurfaces.H"
+#include "pointIndexHit.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+
+namespace Foam
+{
+
+template<>
+const char*
+NamedEnum<shellSurfaces::refineMode, 3>::
+names[] =
+{
+    "inside",
+    "outside",
+    "distance"
+};
+
+const NamedEnum<shellSurfaces::refineMode, 3> shellSurfaces::refineModeNames_;
+
+} // End namespace Foam
+
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::shellSurfaces::setAndCheckLevels
+(
+    const scalar shellI,
+    const List<Tuple2<scalar, label> >& distLevels
+)
+{
+    if (modes_[shellI] != DISTANCE && distLevels.size() != 1)
+    {
+        FatalErrorIn
+        (
+            "shellSurfaces::shellSurfaces"
+            "(const searchableSurfaces&, const dictionary&)"
+        )   << "For refinement mode "
+            << refineModeNames_[modes_[shellI]]
+            << " specify only one distance+level."
+            << " (its distance gets discarded)"
+            << exit(FatalError);
+    }
+    // Extract information into separate distance and level
+    distances_[shellI].setSize(distLevels.size());
+    levels_[shellI].setSize(distLevels.size());
+
+    forAll(distLevels, j)
+    {
+        distances_[shellI][j] = distLevels[j].first();
+        levels_[shellI][j] = distLevels[j].second();
+
+        // Check in incremental order
+        if (j > 0)
+        {
+            if
+            (
+                (distances_[shellI][j] <= distances_[shellI][j-1])
+             || (levels_[shellI][j] > levels_[shellI][j-1])
+            )
+            {
+                FatalErrorIn
+                (
+                    "shellSurfaces::shellSurfaces"
+                    "(const searchableSurfaces&, const dictionary&)"
+                )   << "For refinement mode "
+                    << refineModeNames_[modes_[shellI]]
+                    << " : Refinement should be specified in order"
+                    << " of increasing distance"
+                    << " (and decreasing refinement level)." << endl
+                    << "Distance:" << distances_[shellI][j]
+                    << " refinementLevel:" << levels_[shellI][j]
+                    << exit(FatalError);
+            }
+        }
+    }
+
+    const searchableSurface& shell = allGeometry_[shells_[shellI]];
+
+    if (modes_[shellI] == DISTANCE)
+    {
+        Info<< "Refinement level according to distance to "
+            << shell.name() << endl;
+        forAll(levels_[shellI], j)
+        {
+            Info<< "    level " << levels_[shellI][j]
+                << " for all cells within " << distances_[shellI][j]
+                << " meter." << endl;
+        }
+    }
+    else
+    {
+        if (!allGeometry_[shells_[shellI]].hasVolumeType())
+        {
+            FatalErrorIn
+            (
+                "shellSurfaces::shellSurfaces"
+                "(const searchableSurfaces&"
+                ", const PtrList<dictionary>&)"
+            )   << "Shell " << shell.name()
+                << " does not support testing for "
+                << refineModeNames_[modes_[shellI]] << endl
+                << "Probably it is not closed."
+                << exit(FatalError);
+        }
+
+        if (modes_[shellI] == INSIDE)
+        {
+            Info<< "Refinement level " << levels_[shellI][0]
+                << " for all cells inside " << shell.name() << endl;
+        }
+        else
+        {
+            Info<< "Refinement level " << levels_[shellI][0]
+                << " for all cells outside " << shell.name() << endl;
+        }
+    }
+}
+
+
+// Specifically orient triSurfaces using a calculated point outside.
+// Done since quite often triSurfaces not of consistent orientation which
+// is (currently) necessary for sideness calculation
+void Foam::shellSurfaces::orient()
+{
+    // Determine outside point.
+    boundBox overallBb
+    (
+        point(GREAT, GREAT, GREAT),
+        point(-GREAT, -GREAT, -GREAT)
+    );
+
+    bool hasSurface = false;
+
+    forAll(shells_, shellI)
+    {
+        const searchableSurface& s = allGeometry_[shells_[shellI]];
+
+        if (modes_[shellI] != DISTANCE && isA<triSurfaceMesh>(s))
+        {
+            const triSurfaceMesh& shell = refCast<const triSurfaceMesh>(s);
+
+            if (shell.triSurface::size() > 0)
+            {
+                const pointField& points = shell.points();
+
+                hasSurface = true;
+
+                boundBox shellBb(points[0], points[0]);
+                // Assume surface is compact!
+                for (label i = 0; i < points.size(); i++)
+                {
+                    const point& pt = points[i];
+                    shellBb.min() = min(shellBb.min(), pt);
+                    shellBb.max() = max(shellBb.max(), pt);
+                }
+
+                overallBb.min() = min(overallBb.min(), shellBb.min());
+                overallBb.max() = max(overallBb.max(), shellBb.max());
+            }
+        }
+    }
+
+    if (hasSurface)
+    {
+        const point outsidePt(2*overallBb.max() - overallBb.min());
+
+        //Info<< "Using point " << outsidePt << " to orient shells" << endl;
+
+        forAll(shells_, shellI)
+        {
+            const searchableSurface& s = allGeometry_[shells_[shellI]];
+
+            if (modes_[shellI] != DISTANCE && isA<triSurfaceMesh>(s))
+            {
+                triSurfaceMesh& shell = const_cast<triSurfaceMesh&>
+                (
+                    refCast<const triSurfaceMesh>(s)
+                );
+
+                refinementSurfaces::orientSurface(outsidePt, shell);
+            }
+        }
+    }
+}
+
+
+// Find maximum level of a shell.
+void Foam::shellSurfaces::findHigherLevel
+(
+    const pointField& pt,
+    const label shellI,
+    labelList& maxLevel
+) const
+{
+    const labelList& levels = levels_[shellI];
+
+    if (modes_[shellI] == DISTANCE)
+    {
+        // Distance mode.
+
+        const scalarField& distances = distances_[shellI];
+
+        // Collect all those points that have a current maxLevel less than
+        // (any of) the shell. Also collect the furthest distance allowable
+        // to any shell with a higher level.
+
+        pointField candidates(pt.size());
+        labelList candidateMap(pt.size());
+        scalarField candidateDistSqr(pt.size());
+        label candidateI = 0;
+
+        forAll(maxLevel, pointI)
+        {
+            forAllReverse(levels, levelI)
+            {
+                if (levels[levelI] > maxLevel[pointI])
+                {
+                    candidates[candidateI] = pt[pointI];
+                    candidateMap[candidateI] = pointI;
+                    candidateDistSqr[candidateI] = sqr(distances[levelI]);
+                    candidateI++;
+                    break;
+                }
+            }
+        }
+        candidates.setSize(candidateI);
+        candidateMap.setSize(candidateI);
+        candidateDistSqr.setSize(candidateI);
+
+        // Do the expensive nearest test only for the candidate points.
+        List<pointIndexHit> nearInfo;
+        allGeometry_[shells_[shellI]].findNearest
+        (
+            candidates,
+            candidateDistSqr,
+            nearInfo
+        );
+
+        // Update maxLevel
+        forAll(nearInfo, candidateI)
+        {
+            if (nearInfo[candidateI].hit())
+            {
+                // Check which level it actually is in.
+                label minDistI = findLower
+                (
+                    distances,
+                    mag(nearInfo[candidateI].hitPoint()-candidates[candidateI])
+                );
+
+                label pointI = candidateMap[candidateI];
+
+                // pt is inbetween shell[minDistI] and shell[minDistI+1]
+                maxLevel[pointI] = levels[minDistI+1];
+            }
+        }
+    }
+    else
+    {
+        // Inside/outside mode
+
+        // Collect all those points that have a current maxLevel less than the
+        // shell.
+
+        pointField candidates(pt.size());
+        labelList candidateMap(pt.size());
+        label candidateI = 0;
+
+        forAll(maxLevel, pointI)
+        {
+            if (levels[0] > maxLevel[pointI])
+            {
+                candidates[candidateI] = pt[pointI];
+                candidateMap[candidateI] = pointI;
+                candidateI++;
+            }
+        }
+        candidates.setSize(candidateI);
+        candidateMap.setSize(candidateI);
+
+        // Do the expensive nearest test only for the candidate points.
+        List<searchableSurface::volumeType> volType;
+        allGeometry_[shells_[shellI]].getVolumeType(candidates, volType);
+
+        forAll(volType, i)
+        {
+            label pointI = candidateMap[i];
+
+            if
+            (
+                (
+                    modes_[shellI] == INSIDE
+                 && volType[i] == searchableSurface::INSIDE
+                )
+             || (
+                    modes_[shellI] == OUTSIDE
+                 && volType[i] == searchableSurface::OUTSIDE
+                )
+            )
+            {
+                maxLevel[pointI] = levels[0];
+            }
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::shellSurfaces::shellSurfaces
+(
+    const searchableSurfaces& allGeometry,
+    const PtrList<dictionary>& shellDicts
+)
+:
+    allGeometry_(allGeometry)
+{
+    shells_.setSize(shellDicts.size());
+    modes_.setSize(shellDicts.size());
+    distances_.setSize(shellDicts.size());
+    levels_.setSize(shellDicts.size());
+
+    forAll(shellDicts, shellI)
+    {
+        const dictionary& dict = shellDicts[shellI];
+        const word name = dict.lookup("name");
+        const word type = dict.lookup("type");
+
+        shells_[shellI] = allGeometry_.findSurfaceID(name);
+
+        if (shells_[shellI] == -1)
+        {
+            FatalErrorIn
+            (
+                "shellSurfaces::shellSurfaces"
+                "(const searchableSurfaces&, const PtrList<dictionary>&)"
+            )   << "No surface called " << name << endl
+                << "Valid surfaces are " << allGeometry_.names()
+                << exit(FatalError);
+        }
+
+        modes_[shellI] = refineModeNames_.read(dict.lookup("refineMode"));
+
+        // Read pairs of distance+level
+        setAndCheckLevels(shellI, dict.lookup("levels"));
+    }
+
+    // Orient shell surfaces before any searching is done. Note that this
+    // only needs to be done for inside or outside. Orienting surfaces
+    // constructs lots of addressing which we want to avoid.
+    orient();
+}
+
+
+Foam::shellSurfaces::shellSurfaces
+(
+    const searchableSurfaces& allGeometry,
+    const dictionary& shellsDict
+)
+:
+    allGeometry_(allGeometry)
+{
+    shells_.setSize(shellsDict.size());
+    modes_.setSize(shellsDict.size());
+    distances_.setSize(shellsDict.size());
+    levels_.setSize(shellsDict.size());
+
+    label shellI = 0;
+    forAllConstIter(dictionary, shellsDict, iter)
+    {
+        shells_[shellI] = allGeometry_.findSurfaceID(iter().keyword());
+
+        if (shells_[shellI] == -1)
+        {
+            FatalErrorIn
+            (
+                "shellSurfaces::shellSurfaces"
+                "(const searchableSurfaces&, const dictionary>&"
+            )   << "No surface called " << iter().keyword() << endl
+                << "Valid surfaces are " << allGeometry_.names()
+                << exit(FatalError);
+        }
+        const dictionary& dict = shellsDict.subDict(iter().keyword());
+
+        modes_[shellI] = refineModeNames_.read(dict.lookup("mode"));
+
+        // Read pairs of distance+level
+        setAndCheckLevels(shellI, dict.lookup("levels"));
+
+        shellI++;
+    }
+
+    // Orient shell surfaces before any searching is done. Note that this
+    // only needs to be done for inside or outside. Orienting surfaces
+    // constructs lots of addressing which we want to avoid.
+    orient();
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+// Highest shell level
+Foam::label Foam::shellSurfaces::maxLevel() const
+{
+    label overallMax = 0;
+    forAll(levels_, shellI)
+    {
+        overallMax = max(overallMax, max(levels_[shellI]));
+    }
+    return overallMax;
+}
+
+
+void Foam::shellSurfaces::findHigherLevel
+(
+    const pointField& pt,
+    const labelList& ptLevel,
+    labelList& maxLevel
+) const
+{
+    // Maximum level of any shell. Start off with level of point.
+    maxLevel = ptLevel;
+
+    forAll(shells_, shellI)
+    {
+        findHigherLevel(pt, shellI, maxLevel);
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.H b/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.H
new file mode 100644
index 0000000000000000000000000000000000000000..b996e98e8972f76544c73d032babc2a73ffd5778
--- /dev/null
+++ b/src/autoMesh/autoHexMesh/shellSurfaces/shellSurfaces.H
@@ -0,0 +1,182 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    shellSurfaces
+
+Description
+    Encapsulates queries for volume refinement ('refine all cells within
+    shell').
+
+SourceFiles
+    shellSurfaces.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef shellSurfaces_H
+#define shellSurfaces_H
+
+#include "searchableSurface.H"
+#include "Tuple2.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class searchableSurfaces;
+
+/*---------------------------------------------------------------------------*\
+                           Class shellSurfaces Declaration
+\*---------------------------------------------------------------------------*/
+
+class shellSurfaces
+{
+public:
+
+    // Public data types
+
+        //- Volume refinement controls
+        enum refineMode
+        {
+            INSIDE,         // Refine all inside shell
+            OUTSIDE,        // ,,         outside
+            DISTANCE        // Refine based on distance to shell
+        };
+
+
+private:
+
+    // Private data
+
+        //- Reference to all geometry.
+        const searchableSurfaces& allGeometry_;
+
+        //- Indices of surfaces that are shells
+        labelList shells_;
+
+        //- Per shell whether to refine inside or outside
+        List<refineMode> modes_;
+
+        //- Per shell the list of ranges
+        List<scalarField> distances_;
+
+        //- Per shell per distance the refinement level
+        labelListList levels_;
+
+
+    // Private data
+
+        //- refineMode names
+        static const NamedEnum<refineMode, 3> refineModeNames_;
+
+
+    // Private Member Functions
+
+        //- Helper function for initialisation.
+        void setAndCheckLevels
+        (
+            const scalar shellI,
+            const List<Tuple2<scalar, label> >&
+        );
+
+        void orient();
+
+        void findHigherLevel
+        (
+            const pointField& pt,
+            const label shellI,
+            labelList& maxLevel
+        ) const;
+
+public:
+
+    // Constructors
+
+        //- Construct from components
+        shellSurfaces
+        (
+            const searchableSurfaces& allGeometry,
+            const labelList& shells,
+            const List<refineMode>& modes,
+            const List<scalarField>& distances,
+            const labelListList& levels
+        );
+
+        //- Construct from geometry and dictionaries
+        shellSurfaces
+        (
+            const searchableSurfaces& allGeometry,
+            const PtrList<dictionary>& shellDicts
+        );
+
+        //- Construct from geometry and dictionary
+        shellSurfaces
+        (
+            const searchableSurfaces& allGeometry,
+            const dictionary& shellsDict
+        );
+
+
+    // Member Functions
+
+        // Access
+
+            //const List<scalarField>& distances() const
+            //{
+            //    return distances_;
+            //}
+            //
+            ////- Per shell per distance the refinement level
+            //const labelListList& levels() const
+            //{
+            //    return levels_;
+            //}
+
+
+        // Query
+
+            //- Highest shell level
+            label maxLevel() const;
+
+            //- Find shell level higher than ptLevel
+            void findHigherLevel
+            (
+                const pointField& pt,
+                const labelList& ptLevel,
+                labelList& maxLevel
+            ) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C b/src/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
index 508143aaf9729a6ccd63648325f06bacc16fa353..cebb6597c217c15bdcdf5a9780e7dfe365b5f787 100644
--- a/src/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
+++ b/src/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
@@ -256,7 +256,7 @@ Foam::Ostream& Foam::operator<<
     const ExactParticle<ParticleType>& p
 )
 {
-    return operator<<(os, static_cast<Particle<ParticleType> >(p));
+    return operator<<(os, static_cast<const Particle<ParticleType>&>(p));
 }
 
 
diff --git a/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.H b/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.H
index d764c3fa6f2e996b42023eaff80023488ea79194..2db82c8eed8954d395b596580688cf3402c36362 100644
--- a/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.H
+++ b/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.H
@@ -361,7 +361,7 @@ public:
 
     // Member Functions
 
-        //- Helper funntion: count cells per processor in wanted distribution
+        //- Helper function: count cells per processor in wanted distribution
         static labelList countCells(const labelList&);
 
         //- Send cells to neighbours according to distribution
diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C
index c1c0e4f316edb9801cf722a14e11417bb5afade5..0742fd44b50fd83ec1c3f03b563e08c17a6f0e6e 100644
--- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C
+++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C
@@ -553,7 +553,7 @@ Foam::labelListList Foam::addPatchCellLayer::addedCells() const
 
 void Foam::addPatchCellLayer::setRefinement
 (
-    const scalar expansionRatio,
+    const scalarField& expansionRatio,
     const indirectPrimitivePatch& pp,
     const labelList& nFaceLayers,
     const labelList& nPointLayers,
@@ -885,7 +885,7 @@ void Foam::addPatchCellLayer::setRefinement
 
                 addedPoints_[patchPointI][i] = addedVertI;
 
-                disp *= expansionRatio;
+                disp *= expansionRatio[patchPointI];
             }
         }
     }
diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H
index 0e47acb7eaeec67dbdec4334e19b0cc337db57b8..0b5caaba86f8d2b84c6f1605809ecb5925937d0b 100644
--- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H
+++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H
@@ -305,7 +305,7 @@ public:
             //        (instead of e.g. from patch faces)
            void setRefinement
            (
-               const scalar expansionRatio,
+               const scalarField& expansionRatio,
                const indirectPrimitivePatch& pp,
                const labelList& nFaceLayers,
                const labelList& nPointLayers,
@@ -325,7 +325,7 @@ public:
             {
                 setRefinement
                 (
-                    1.0,            // expansion ration
+                    scalarField(pp.nPoints(), 1.0), // expansion ration
                     pp,
                     labelList(pp.size(), nLayers),
                     labelList(pp.nPoints(), nLayers),
diff --git a/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.C b/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.C
index cfedeb6112f868b5a1ef5075bdc1ac4726b50eee..7b035e0e4ad69290d4b0923d6d4cc6f0e9016202 100644
--- a/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.C
+++ b/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.C
@@ -62,7 +62,6 @@ surfaceSlipDisplacementPointPatchVectorField
 )
 :
     pointPatchVectorField(p, iF),
-    surfaceNames_(),
     projectMode_(NEAREST),
     projectDir_(vector::zero),
     wedgePlane_(-1)
@@ -78,7 +77,7 @@ surfaceSlipDisplacementPointPatchVectorField
 )
 :
     pointPatchVectorField(p, iF, dict),
-    surfaceNames_(dict.lookup("projectSurfaces")),
+    surfacesDict_(dict.subDict("geometry")),
     projectMode_(followModeNames_.read(dict.lookup("followMode"))),
     projectDir_(dict.lookup("projectDirection")),
     wedgePlane_(readLabel(dict.lookup("wedgePlane"))),
@@ -96,7 +95,7 @@ surfaceSlipDisplacementPointPatchVectorField
 )
 :
     pointPatchVectorField(p, iF),
-    surfaceNames_(ppf.surfaceNames()),
+    surfacesDict_(ppf.surfacesDict()),
     projectMode_(ppf.projectMode()),
     projectDir_(ppf.projectDir()),
     wedgePlane_(ppf.wedgePlane()),
@@ -111,7 +110,7 @@ surfaceSlipDisplacementPointPatchVectorField
 )
 :
     pointPatchVectorField(ppf),
-    surfaceNames_(ppf.surfaceNames()),
+    surfacesDict_(ppf.surfacesDict()),
     projectMode_(ppf.projectMode()),
     projectDir_(ppf.projectDir()),
     wedgePlane_(ppf.wedgePlane()),
@@ -127,7 +126,7 @@ surfaceSlipDisplacementPointPatchVectorField
 )
 :
     pointPatchVectorField(ppf, iF),
-    surfaceNames_(ppf.surfaceNames()),
+    surfacesDict_(ppf.surfacesDict()),
     projectMode_(ppf.projectMode()),
     projectDir_(ppf.projectDir()),
     wedgePlane_(ppf.wedgePlane()),
@@ -137,14 +136,14 @@ surfaceSlipDisplacementPointPatchVectorField
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-const triSurfaceMeshes& surfaceSlipDisplacementPointPatchVectorField::
+const searchableSurfaces& surfaceSlipDisplacementPointPatchVectorField::
 surfaces() const
 {
     if (!surfacesPtr_.valid())
     {
         surfacesPtr_.reset
         (
-            new triSurfaceMeshes
+            new searchableSurfaces
             (
                 IOobject
                 (
@@ -155,7 +154,7 @@ surfaces() const
                     IOobject::MUST_READ,
                     IOobject::NO_WRITE
                 ),
-                surfaceNames_
+                surfacesDict_
             )
         );
     }
@@ -215,128 +214,180 @@ void surfaceSlipDisplacementPointPatchVectorField::evaluate
         );
     const pointField& points0 = motionSolver.points0();
 
-    forAll(localPoints, i)
+
+//XXXXXX
+
+
+    pointField start(meshPoints.size());
+    forAll(start, i)
+    {
+        start[i] = points0[meshPoints[i]] + displacement[i];
+    }
+
+    if (projectMode_ == NEAREST)
+    {
+        List<pointIndexHit> nearest;
+        labelList hitSurfaces;
+        surfaces().findNearest
+        (
+            start,
+            scalarField(start.size(), sqr(projectLen)),
+            hitSurfaces,
+            nearest
+        );
+
+        forAll(nearest, i)
+        {
+            if (zonePtr && (zonePtr->whichPoint(meshPoints[i]) >= 0))
+            {
+                // Fixed point. Reset to point0 location.
+                displacement[i] = points0[meshPoints[i]] - localPoints[i];
+            }
+            else if (nearest[i].hit())
+            {
+                displacement[i] =
+                    nearest[i].hitPoint()
+                  - points0[meshPoints[i]];
+            }
+            else
+            {
+                Pout<< "    point:" << meshPoints[i]
+                    << " coord:" << localPoints[i]
+                    << "  did not find any surface within " << projectLen
+                    << endl;
+            }
+        }
+    }
+    else
     {
-        if (zonePtr && (zonePtr->whichPoint(meshPoints[i]) >= 0))
+        // Do tests on all points. Combine later on.
+
+        // 1. Check if already on surface
+        List<pointIndexHit> nearest;
         {
-            // Fixed point. Reset to point0 location.
+            labelList nearestSurface;
+            surfaces().findNearest
+            (
+                start,
+                scalarField(start.size(), sqr(SMALL)),
+                nearestSurface,
+                nearest
+            );
+        }
 
-            //Pout<< "    Fixed point:" << meshPoints[i]
-            //    << " coord:" << localPoints[i]
-            //    << " should be at:" << points0[meshPoints[i]]
-            //    << endl;
-            displacement[i] = points0[meshPoints[i]] - localPoints[i];
+        // 2. intersection. (combined later on with information from nearest
+        // above)
+        vectorField projectVecs(start.size(), projectVec);
+
+        if (projectMode_ == POINTNORMAL)
+        {
+            projectVecs = projectLen*patch().pointNormals();
         }
-        else
+
+        // Knock out any wedge component
+        scalarField offset(start.size(), 0.0);
+        if (wedgePlane_ >= 0 && wedgePlane_ <= vector::nComponents)
         {
-            point start(points0[meshPoints[i]] + displacement[i]);
+            forAll(offset, i)
+            {
+                offset[i] = start[i][wedgePlane_];
+                start[i][wedgePlane_] = 0;
+                projectVecs[i][wedgePlane_] = 0;
+            }
+        }
 
-            scalar offset = 0;
-            pointIndexHit intersection;
+        List<pointIndexHit> rightHit;
+        {
+            labelList rightSurf;
+            surfaces().findAnyIntersection
+            (
+                start,
+                start+projectVecs,
+                rightSurf,
+                rightHit
+            );
+        }
+        
+        List<pointIndexHit> leftHit;
+        {
+            labelList leftSurf;
+            surfaces().findAnyIntersection
+            (
+                start,
+                start-projectVecs,
+                leftSurf,
+                leftHit
+            );
+        }
 
-            if (projectMode_ == NEAREST)
+        // 3. Choose either -fixed, nearest, right, left.
+        forAll(displacement, i)
+        {
+            if (zonePtr && (zonePtr->whichPoint(meshPoints[i]) >= 0))
             {
-                surfaces().findNearest(start, sqr(projectLen), intersection);
+                // Fixed point. Reset to point0 location.
+                displacement[i] = points0[meshPoints[i]] - localPoints[i];
+            }
+            else if (nearest[i].hit())
+            {
+                // Found nearest.
+                displacement[i] =
+                    nearest[i].hitPoint()
+                  - points0[meshPoints[i]];
             }
             else
             {
-                // Check if already on surface
-                surfaces().findNearest(start, sqr(SMALL), intersection);
+                pointIndexHit interPt;
 
-                if (!intersection.hit())
+                if (rightHit[i].hit())
                 {
-                    // No nearest found. Do intersection
-
-                    if (projectMode_ == POINTNORMAL)
+                    if (leftHit[i].hit())
                     {
-                        projectVec = projectLen*patch().pointNormals()[i];
-                    }
-
-                    // Knock out any wedge component
-                    if (wedgePlane_ >= 0 && wedgePlane_ <= vector::nComponents)
-                    {
-                        offset = start[wedgePlane_];
-                        start[wedgePlane_] = 0;
-                        projectVec[wedgePlane_] = 0;
-                    }
-
-                    label rightSurf0, rightSurf1;
-                    pointIndexHit rightHit0, rightHit1;
-                    surfaces().findNearestIntersection
-                    (
-                        start,
-                        start+projectVec,
-                        rightSurf0,
-                        rightHit0,
-                        rightSurf1,
-                        rightHit1
-                    );
-
-                    // Do intersection
-                    label leftSurf0, leftSurf1;
-                    pointIndexHit leftHit0, leftHit1;
-                    surfaces().findNearestIntersection
-                    (
-                        start,
-                        start-projectVec,
-                        leftSurf0,
-                        leftHit0,
-                        leftSurf1,
-                        leftHit1
-                    );
-
-                    if (rightHit0.hit())
-                    {
-                        if (leftHit0.hit())
+                        if
+                        (
+                            magSqr(rightHit[i].hitPoint()-start[i])
+                          < magSqr(leftHit[i].hitPoint()-start[i])
+                        )
                         {
-                            if
-                            (
-                                magSqr(rightHit0.hitPoint()-start)
-                              < magSqr(leftHit0.hitPoint()-start)
-                            )
-                            {
-                                intersection = rightHit0;
-                            }
-                            else
-                            {
-                                intersection = leftHit0;
-                            }
+                            interPt = rightHit[i];
                         }
                         else
                         {
-                            intersection = rightHit0;
+                            interPt = leftHit[i];
                         }
                     }
                     else
                     {
-                        if (leftHit0.hit())
-                        {
-                            intersection = leftHit0;
-                        }
+                        interPt = rightHit[i];
+                    }
+                }
+                else
+                {
+                    if (leftHit[i].hit())
+                    {
+                        interPt = leftHit[i];
                     }
                 }
-            }
-
-            // Update *this from intersection point
 
-            if (intersection.hit())
-            {
-                point interPt = intersection.hitPoint();
 
-                if (wedgePlane_ >= 0 && wedgePlane_ <= vector::nComponents)
+                if (interPt.hit())
                 {
-                    interPt[wedgePlane_] += offset;
+                    if (wedgePlane_ >= 0 && wedgePlane_ <= vector::nComponents)
+                    {
+                        interPt.rawPoint()[wedgePlane_] += offset[i];
+                    }
+                    displacement[i] = interPt.rawPoint()-points0[meshPoints[i]];
+                }
+                else
+                {
+                    Pout<< "    point:" << meshPoints[i]
+                        << " coord:" << localPoints[i]
+                        << "  did not find any intersection between ray from "
+                        << start[i]-projectVecs[i]
+                        << " to " << start[i]+projectVecs[i]
+                        << endl;
                 }
-                displacement[i] = interPt-points0[meshPoints[i]];
             }
-            else
-            {
-                Pout<< "    point:" << meshPoints[i]
-                    << " coord:" << localPoints[i]
-                    << "  did not find any intersection between ray from "
-                    << start-projectVec << " to " << start+projectVec
-                    << endl;
-            }  
         }
     }
 
@@ -353,7 +404,7 @@ void surfaceSlipDisplacementPointPatchVectorField::evaluate
 void surfaceSlipDisplacementPointPatchVectorField::write(Ostream& os) const
 {
     pointPatchVectorField::write(os);
-    os.writeKeyword("projectSurfaces") << surfaceNames_
+    os.writeKeyword("geometry") << surfacesDict_
         << token::END_STATEMENT << nl;
     os.writeKeyword("followMode") << followModeNames_[projectMode_]
         << token::END_STATEMENT << nl;
diff --git a/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.H b/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.H
index b164795cac52a02f3db8c341967609bf99e5f8bc..5b8fca9409d0b0f06c81d1a0639e0c281a8cf5b3 100644
--- a/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.H
+++ b/src/fvMotionSolver/pointPatchFields/derived/surfaceSlipDisplacement/surfaceSlipDisplacementPointPatchVectorField.H
@@ -52,7 +52,7 @@ SourceFiles
 #define surfaceSlipDisplacementPointPatchVectorField_H
 
 #include "pointPatchFields.H"
-#include "triSurfaceMeshes.H"
+#include "searchableSurfaces.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -87,7 +87,7 @@ private:
         static const NamedEnum<followMode, 3> followModeNames_;
 
         //- names of surfaces
-        const fileNameList surfaceNames_;
+        const dictionary surfacesDict_;
 
         //- How to follow/project onto surface
         const followMode projectMode_;
@@ -102,7 +102,7 @@ private:
         const word frozenPointsZone_;
 
         //- Demand driven: surface to follow
-        mutable autoPtr<triSurfaceMeshes> surfacesPtr_;
+        mutable autoPtr<searchableSurfaces> surfacesPtr_;
 
 
     // Private Member Functions
@@ -187,13 +187,13 @@ public:
     // Member Functions
 
         //- Surfaces to follow
-        const fileNameList& surfaceNames() const
+        const dictionary& surfacesDict() const
         {
-            return surfaceNames_;
+            return surfacesDict_;
         }
 
         //- Surface to follow. Demand loads surfaceNames.
-        const triSurfaceMeshes& surfaces() const;
+        const searchableSurfaces& surfaces() const;
 
         //- Mode of projection/following
         followMode projectMode() const
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 3a3d2aff830ff305ddb6e6057dc244bcfa7e93f6..90f9de250e298142d18b889e8be63a8bf86dac96 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -53,6 +53,14 @@ indexedOctree/treeDataFace.C
 indexedOctree/treeDataPoint.C
 indexedOctree/treeDataTriSurface.C
 
+searchableSurface = searchableSurface
+$(searchableSurface)/searchableBox.C
+$(searchableSurface)/searchableSphere.C
+$(searchableSurface)/searchableSurface.C
+$(searchableSurface)/searchableSurfaces.C
+$(searchableSurface)/searchableSurfacesQueries.C
+$(searchableSurface)/triSurfaceMesh.C
+
 topoSets = sets/topoSets
 $(topoSets)/cellSet.C
 $(topoSets)/topoSet.C
@@ -117,20 +125,10 @@ $(intersectedSurface)/intersectedSurface.C
 $(intersectedSurface)/edgeSurface.C
 
 triSurface/triSurfaceSearch/triSurfaceSearch.C
-
 triSurface/octreeData/octreeDataTriSurface.C
 triSurface/octreeData/octreeDataTriSurfaceTreeLeaf.C
-
 triSurface/triangleFuncs/triangleFuncs.C
-
-triSurface/searchableSurface/searchableSurface.C
-triSurface/searchableSurface/triSurfaceMesh.C
-triSurface/searchableSurface/searchableBox.C
-
 triSurface/surfaceFeatures/surfaceFeatures.C
-
-triSurface/triSurfaceMeshes/triSurfaceMeshes.C
-
 triSurface/triSurfaceTools/triSurfaceTools.C
 triSurface/triSurfaceTools/geompack/geompack.C
 
diff --git a/src/meshTools/searchableSurface/searchableBox.C b/src/meshTools/searchableSurface/searchableBox.C
new file mode 100644
index 0000000000000000000000000000000000000000..cb664b57575d901a583587b92fa69a1072b4f169
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableBox.C
@@ -0,0 +1,541 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "searchableBox.H"
+#include "addToRunTimeSelectionTable.H"
+#include "SortableList.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(searchableBox, 0);
+addToRunTimeSelectionTable(searchableSurface, searchableBox, dict);
+
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::searchableBox::projectOntoCoordPlane
+(
+    const direction dir,
+    const point& planePt,
+    pointIndexHit& info
+) const
+{
+    // Set point
+    info.rawPoint()[dir] = planePt[dir];
+    // Set face
+    if (planePt[dir] == min()[dir])
+    {
+        info.setIndex(dir*2);
+    }
+    else if (planePt[dir] == max()[dir])
+    {
+        info.setIndex(dir*2+1);
+    }
+    else
+    {
+        FatalErrorIn("searchableBox::projectOntoCoordPlane(..)")
+            << "Point on plane " << planePt
+            << " is not on coordinate " << min()[dir]
+            << " nor " << max()[dir] << abort(FatalError);
+    }
+}
+
+
+// Returns miss or hit with face (0..5) and region(always 0)
+Foam::pointIndexHit Foam::searchableBox::findNearest
+(
+    const point& bbMid,
+    const point& sample,
+    const scalar nearestDistSqr
+) const
+{
+    // Point can be inside or outside. For every component direction can be
+    // left of min, right of max or inbetween.
+    // - outside points: project first one x plane (either min().x()
+    // or max().x()), then onto y plane and finally z. You should be left
+    // with intersection point
+    // - inside point: find nearest side (compare to mid point). Project onto
+    //   that.
+
+    // The face is set to the last projected face.
+
+
+    // Outside point projected onto cube. Assume faces 0..5.
+    pointIndexHit info(true, sample, -1);
+    bool outside = false;
+
+    // (for internal points) per direction what nearest cube side is
+    point near;
+
+    for (direction dir = 0; dir < vector::nComponents; dir++)
+    {
+        if (info.rawPoint()[dir] < min()[dir])
+        {
+            projectOntoCoordPlane(dir, min(), info);
+            outside = true;
+        }
+        else if (info.rawPoint()[dir] > max()[dir])
+        {
+            projectOntoCoordPlane(dir, max(), info);
+            outside = true;
+        }
+        else if (info.rawPoint()[dir] > bbMid[dir])
+        {
+            near[dir] = max()[dir];
+        }
+        else
+        {
+            near[dir] = min()[dir];
+        }
+    }
+
+
+    // For outside points the info will be correct now. Handle inside points
+    // using the three near distances. Project onto the nearest plane.
+    if (!outside)
+    {
+        vector dist(cmptMag(info.rawPoint() - near));
+
+        if (dist.x() < dist.y())
+        {
+            if (dist.x() < dist.z())
+            {
+                // Project onto x plane
+                projectOntoCoordPlane(vector::X, near, info);
+            }
+            else
+            {
+                projectOntoCoordPlane(vector::Z, near, info);
+            }
+        }
+        else
+        {
+            if (dist.y() < dist.z())
+            {
+                projectOntoCoordPlane(vector::Y, near, info);
+            }
+            else
+            {
+                projectOntoCoordPlane(vector::Z, near, info);
+            }
+        }
+    }
+
+
+    // Check if outside. Optimisation: could do some checks on distance already
+    // on components above
+    if (magSqr(info.rawPoint() - sample) > nearestDistSqr)
+    {
+        info.setMiss();
+        info.setIndex(-1);
+    }
+
+    return info;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::searchableBox::searchableBox
+(
+    const IOobject& io,
+    const treeBoundBox& bb
+)
+:
+    searchableSurface(io),
+    treeBoundBox(bb)
+{}
+
+
+Foam::searchableBox::searchableBox
+(
+    const IOobject& io,
+    const dictionary& dict
+)
+:
+    searchableSurface(io),
+    treeBoundBox(dict.lookup("min"), dict.lookup("max"))
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::searchableBox::~searchableBox()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::wordList& Foam::searchableBox::regions() const
+{
+    if (regions_.size() == 0)
+    {
+        regions_.setSize(1);
+        regions_[0] = "region0";
+    }
+    return regions_;
+}
+
+
+Foam::pointIndexHit Foam::searchableBox::findNearest
+(
+    const point& sample,
+    const scalar nearestDistSqr
+) const
+{
+    return findNearest(mid(), sample, nearestDistSqr);
+}
+
+
+Foam::pointIndexHit Foam::searchableBox::findNearestOnEdge
+(
+    const point& sample,
+    const scalar nearestDistSqr
+) const
+{
+    const point bbMid(mid());
+
+    // Outside point projected onto cube. Assume faces 0..5.
+    pointIndexHit info(true, sample, -1);
+    bool outside = false;
+
+    // (for internal points) per direction what nearest cube side is
+    point near;
+
+    for (direction dir = 0; dir < vector::nComponents; dir++)
+    {
+        if (info.rawPoint()[dir] < min()[dir])
+        {
+            projectOntoCoordPlane(dir, min(), info);
+            outside = true;
+        }
+        else if (info.rawPoint()[dir] > max()[dir])
+        {
+            projectOntoCoordPlane(dir, max(), info);
+            outside = true;
+        }
+        else if (info.rawPoint()[dir] > bbMid[dir])
+        {
+            near[dir] = max()[dir];
+        }
+        else
+        {
+            near[dir] = min()[dir];
+        }
+    }
+
+
+    // For outside points the info will be correct now. Handle inside points
+    // using the three near distances. Project onto the nearest two planes.
+    if (!outside)
+    {
+        // Get the per-component distance to nearest wall
+        vector dist(cmptMag(info.rawPoint() - near));
+
+        SortableList<scalar> sortedDist(3);
+        sortedDist[0] = dist[0];
+        sortedDist[1] = dist[1];
+        sortedDist[2] = dist[2];
+        sortedDist.sort();
+
+        // Project onto nearest
+        projectOntoCoordPlane(sortedDist.indices()[0], near, info);
+        // Project onto second nearest
+        projectOntoCoordPlane(sortedDist.indices()[1], near, info);
+    }
+
+
+    // Check if outside. Optimisation: could do some checks on distance already
+    // on components above
+    if (magSqr(info.rawPoint() - sample) > nearestDistSqr)
+    {
+        info.setMiss();
+        info.setIndex(-1);
+    }
+
+    return info;
+}
+
+
+Foam::pointIndexHit Foam::searchableBox::findNearest
+(
+    const linePointRef& ln,
+    treeBoundBox& tightest,
+    point& linePoint
+) const
+{
+    notImplemented
+    (
+        "searchableBox::findNearest"
+        "(const linePointRef&, treeBoundBox&, point&)"
+    );
+    return pointIndexHit();
+}
+
+
+Foam::pointIndexHit Foam::searchableBox::findLine
+(
+    const point& start,
+    const point& end
+) const
+{
+    pointIndexHit info(false, start, -1);
+
+    bool foundInter;
+
+    if (posBits(start) == 0)
+    {
+        if (posBits(end) == 0)
+        {
+            // Both start and end inside.
+            foundInter = false;
+        }
+        else
+        {
+            // end is outside. Clip to bounding box.
+            foundInter = intersects(end, start, info.rawPoint());
+        }
+    }
+    else
+    {
+        // start is outside. Clip to bounding box.
+        foundInter = intersects(start, end, info.rawPoint());
+    }
+
+
+    // Classify point
+    if (foundInter)
+    {
+        info.setHit();
+
+        for (direction dir = 0; dir < vector::nComponents; dir++)
+        {
+            if (info.rawPoint()[dir] == min()[dir])
+            {
+                info.setIndex(2*dir);
+                break;
+            }
+            else if (info.rawPoint()[dir] == max()[dir])
+            {
+                info.setIndex(2*dir+1);
+                break;
+            }
+        }
+
+        if (info.index() == -1)
+        {
+            FatalErrorIn("searchableBox::findLine(const point&, const point&)")
+                << "point " << info.rawPoint()
+                << " on segment " << start << end
+                << " should be on face of " << *this
+                << " but it isn't." << abort(FatalError);
+        }
+    }
+
+    return info;
+}
+
+
+Foam::pointIndexHit Foam::searchableBox::findLineAny
+(
+    const point& start,
+    const point& end
+) const
+{
+    return findLine(start, end);
+}
+
+
+void Foam::searchableBox::findNearest
+(
+    const pointField& samples,
+    const scalarField& nearestDistSqr,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(samples.size());
+
+    const point bbMid(mid());
+
+    forAll(samples, i)
+    {
+        info[i] = findNearest(bbMid, samples[i], nearestDistSqr[i]);
+    }
+}
+
+
+void Foam::searchableBox::findLine
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(start.size());
+
+    forAll(start, i)
+    {
+        info[i] = findLine(start[i], end[i]);
+    }
+}
+
+
+void Foam::searchableBox::findLineAny
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(start.size());
+
+    forAll(start, i)
+    {
+        info[i] = findLineAny(start[i], end[i]);
+    }
+}
+
+
+void Foam::searchableBox::findLineAll
+(
+    const pointField& start,
+    const pointField& end,
+    List<List<pointIndexHit> >& info
+) const
+{
+    info.setSize(start.size());
+
+    // Work array
+    DynamicList<pointIndexHit, 1, 1> hits;
+
+    // Tolerances
+    const vectorField dirVec(end-start);
+    const scalarField magSqrDirVec(magSqr(dirVec));
+    const vectorField smallVec
+    (
+        Foam::sqrt(SMALL)*dirVec
+      + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
+    );
+
+    forAll(start, pointI)
+    {
+        hits.clear();
+
+        // Current starting point of ray.
+        point pt = start[pointI];
+
+        while (true)
+        {
+            // See if any intersection between pt and end
+            pointIndexHit inter = findLine(pt, end[pointI]);
+
+            if (!inter.hit())
+            {
+                break;
+            }
+            hits.append(inter);
+
+            pt = inter.hitPoint() + smallVec[pointI];
+
+            if (((pt-start[pointI])&dirVec[pointI]) > magSqrDirVec[pointI])
+            {
+                // Adding smallVec has taken us beyond end
+                break;
+            }
+        }
+
+        hits.shrink();
+        info[pointI].transfer(hits);
+        hits.clear();
+    }
+}
+
+
+void Foam::searchableBox::getRegion
+(
+    const List<pointIndexHit>& info,
+    labelList& region
+) const
+{
+    region.setSize(info.size());
+    region = 0;
+}
+
+
+void Foam::searchableBox::getNormal
+(
+    const List<pointIndexHit>& info,
+    vectorField& normal
+) const
+{
+    normal.setSize(info.size());
+    normal = vector::zero;
+
+    forAll(info, i)
+    {
+        if (info[i].hit())
+        {
+            normal[i] = treeBoundBox::faceNormals[info[i].index()];
+        }
+        else
+        {
+            // Set to what?
+        }
+    }
+}
+
+
+void Foam::searchableBox::getVolumeType
+(
+    const pointField& points,
+    List<volumeType>& volType
+) const
+{
+    volType.setSize(points.size());
+    volType = INSIDE;
+
+    forAll(points, pointI)
+    {
+        const point& pt = points[pointI];
+
+        for (direction dir = 0; dir < vector::nComponents; dir++)
+        {
+            if (pt[dir] < min()[dir] || pt[dir] > max()[dir])
+            {
+                volType[pointI] = OUTSIDE;
+                break;
+            }
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableBox.H b/src/meshTools/searchableSurface/searchableBox.H
new file mode 100644
index 0000000000000000000000000000000000000000..1c51311511b0840fd27bc81d1d67e26f6bfc67e9
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableBox.H
@@ -0,0 +1,249 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::searchableBox
+
+Description
+    Searching on bounding box
+
+SourceFiles
+    searchableBox.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef searchableBox_H
+#define searchableBox_H
+
+#include "searchableSurface.H"
+#include "treeBoundBox.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+
+/*---------------------------------------------------------------------------*\
+                           Class searchableBox Declaration
+\*---------------------------------------------------------------------------*/
+
+class searchableBox
+:
+    public searchableSurface,
+    public treeBoundBox
+{
+private:
+
+    // Private Member Data
+
+        mutable wordList regions_;
+
+
+    // Private Member Functions
+
+        //- Project onto component dir of planePt and update index() (=face)
+        void projectOntoCoordPlane
+        (
+            const direction dir,
+            const point& planePt,
+            pointIndexHit& info
+        ) const;
+
+        //- Returns miss or hit with face (0..5)
+        pointIndexHit findNearest
+        (
+            const point& bbMid,
+            const point& sample,
+            const scalar nearestDistSqr
+        ) const;
+
+
+        //- Disallow default bitwise copy construct
+        searchableBox(const searchableBox&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const searchableBox&);
+
+
+public:
+
+    //- Runtime type information
+    TypeName("searchableBox");
+
+
+    // Constructors
+
+        //- Construct from components
+        searchableBox(const IOobject& io, const treeBoundBox& bb);
+
+        //- Construct from dictionary (used by searchableSurface)
+        searchableBox
+        (
+            const IOobject& io,
+            const dictionary& dict
+        );
+
+    // Destructor
+
+        virtual ~searchableBox();
+
+
+    // Member Functions
+
+        virtual const wordList& regions() const;
+
+        //- Whether supports volume type below
+        virtual bool hasVolumeType() const
+        {
+            return true;
+        }
+
+
+        // Single point queries.
+
+            //- Calculate nearest point on surface. Returns
+            //  - bool : any point found nearer than nearestDistSqr
+            //  - label: relevant index in surface (=face 0..5)
+            //  - point: actual nearest point found
+            pointIndexHit findNearest
+            (
+                const point& sample,
+                const scalar nearestDistSqr
+            ) const;
+
+            //- Calculate nearest point on edge. Returns
+            //  - bool : any point found nearer than nearestDistSqr
+            //  - label: relevant index in surface(=?)
+            //  - point: actual nearest point found
+            pointIndexHit findNearestOnEdge
+            (
+                const point& sample,
+                const scalar nearestDistSqr
+            ) const;
+
+            //- Find nearest to segment. Returns
+            //  - bool : any point found?
+            //  - label: relevant index in shapes (=face 0..5)
+            //  - point: actual nearest point found
+            //  sets:
+            //  - tightest  : bounding box
+            //  - linePoint : corresponding nearest point on line
+            pointIndexHit findNearest
+            (
+                const linePointRef& ln,
+                treeBoundBox& tightest,
+                point& linePoint
+            ) const;
+
+            //- Find nearest intersection of line between start and end.
+            pointIndexHit findLine
+            (
+                const point& start,
+                const point& end
+            ) const;
+
+            //- Find any intersection of line between start and end.
+            pointIndexHit findLineAny
+            (
+                const point& start,
+                const point& end
+            ) const;
+
+
+        // Multiple point queries.
+
+            virtual void findNearest
+            (
+                const pointField& sample,
+                const scalarField& nearestDistSqr,
+                List<pointIndexHit>&
+            ) const;
+
+            virtual void findLine
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const;
+
+            virtual void findLineAny
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const;
+
+            //- Get all intersections in order from start to end.
+            virtual void findLineAll
+            (
+                const pointField& start,
+                const pointField& end,
+                List<List<pointIndexHit> >&
+            ) const;
+
+            //- From a set of points and indices get the region
+            virtual void getRegion
+            (
+                const List<pointIndexHit>&,
+                labelList& region
+            ) const;
+
+            //- From a set of points and indices get the normal
+            virtual void getNormal
+            (
+                const List<pointIndexHit>&,
+                vectorField& normal
+            ) const;
+
+            //- Determine type (inside/outside/mixed) for point. unknown if
+            //  cannot be determined (e.g. non-manifold surface)
+            virtual void getVolumeType
+            (
+                const pointField&,
+                List<volumeType>&
+            ) const;
+
+
+        // regIOobject implementation
+
+            bool writeData(Ostream&) const
+            {
+                notImplemented("searchableBox::writeData(Ostream&) const");
+                return false;
+            }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSphere.C b/src/meshTools/searchableSurface/searchableSphere.C
new file mode 100644
index 0000000000000000000000000000000000000000..039be46749d05d74d07824334faf587672464df9
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSphere.C
@@ -0,0 +1,328 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "searchableSphere.H"
+#include "addToRunTimeSelectionTable.H"
+#include "SortableList.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(searchableSphere, 0);
+addToRunTimeSelectionTable(searchableSurface, searchableSphere, dict);
+
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::pointIndexHit Foam::searchableSphere::findNearest
+(
+    const point& sample,
+    const scalar nearestDistSqr
+) const
+{
+    pointIndexHit info(false, sample, -1);
+
+    const vector n(sample-centre_);
+    scalar magN = mag(n);
+
+    if (nearestDistSqr > sqr(magN-radius_))
+    {
+        info.rawPoint() = centre_ + n/magN*radius_;
+        info.setHit();
+        info.setIndex(0);
+    }
+
+    return info;
+}
+
+
+// From Graphics Gems - intersection of sphere with ray
+void Foam::searchableSphere::findLineAll
+(
+    const point& start,
+    const point& end,
+    pointIndexHit& near,
+    pointIndexHit& far
+) const
+{
+    near.setMiss();
+    far.setMiss();
+
+    vector dir(end-start);
+    scalar magSqrDir = magSqr(dir);
+
+    if (magSqrDir > ROOTVSMALL)
+    {
+        const vector toCentre(centre_-start);
+        scalar magSqrToCentre = magSqr(toCentre);
+
+        dir /= Foam::sqrt(magSqrDir);
+
+        scalar v = (toCentre & dir);
+
+        scalar disc = sqr(radius_) - (magSqrToCentre - sqr(v));
+
+        if (disc >= 0)
+        {
+            scalar d = Foam::sqrt(disc);
+
+            scalar nearParam = v-d;
+
+            if (nearParam >= 0 && sqr(nearParam) <= magSqrDir)
+            {
+                near.setHit();
+                near.setPoint(start + nearParam*dir);
+                near.setIndex(0);
+            }
+
+            scalar farParam = v+d;
+
+            if (farParam >= 0 && sqr(farParam) <= magSqrDir)
+            {
+                far.setHit();
+                far.setPoint(start + farParam*dir);
+                far.setIndex(0);
+            }
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::searchableSphere::searchableSphere
+(
+    const IOobject& io,
+    const point& centre,
+    const scalar radius
+)
+:
+    searchableSurface(io),
+    centre_(centre),
+    radius_(radius)
+{}
+
+
+Foam::searchableSphere::searchableSphere
+(
+    const IOobject& io,
+    const dictionary& dict
+)
+:
+    searchableSurface(io),
+    centre_(dict.lookup("centre")),
+    radius_(readScalar(dict.lookup("radius")))
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::searchableSphere::~searchableSphere()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::wordList& Foam::searchableSphere::regions() const
+{
+    if (regions_.size() == 0)
+    {
+        regions_.setSize(1);
+        regions_[0] = "region0";
+    }
+    return regions_;
+}
+
+
+
+void Foam::searchableSphere::findNearest
+(
+    const pointField& samples,
+    const scalarField& nearestDistSqr,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(samples.size());
+
+    forAll(samples, i)
+    {
+        info[i] = findNearest(samples[i], nearestDistSqr[i]);
+    }
+}
+
+
+void Foam::searchableSphere::findLine
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(start.size());
+
+    pointIndexHit b;
+
+    forAll(start, i)
+    {
+        // Pick nearest intersection. If none intersected take second one.
+        findLineAll(start[i], end[i], info[i], b);
+        if (!info[i].hit() && b.hit())
+        {
+            info[i] = b;
+        }
+    }
+}
+
+
+void Foam::searchableSphere::findLineAny
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    info.setSize(start.size());
+
+    pointIndexHit b;
+
+    forAll(start, i)
+    {
+        // Discard far intersection
+        findLineAll(start[i], end[i], info[i], b);
+        if (!info[i].hit() && b.hit())
+        {
+            info[i] = b;
+        }
+    }
+}
+
+
+void Foam::searchableSphere::findLineAll
+(
+    const pointField& start,
+    const pointField& end,
+    List<List<pointIndexHit> >& info
+) const
+{
+    info.setSize(start.size());
+
+    pointIndexHit near, far;
+
+    forAll(start, i)
+    {
+        findLineAll(start[i], end[i], near, far);
+
+        if (near.hit())
+        {
+            if (far.hit())
+            {
+                info[i].setSize(2);
+                info[i][0] = near;
+                info[i][1] = far;
+            }
+            else
+            {
+                info[i].setSize(1);
+                info[i][0] = near;
+            }
+        }
+        else
+        {
+            if (far.hit())
+            {
+                info[i].setSize(1);
+                info[i][0] = far;
+            }
+        }
+    }
+}
+
+
+void Foam::searchableSphere::getRegion
+(
+    const List<pointIndexHit>& info,
+    labelList& region
+) const
+{
+    region.setSize(info.size());
+    region = 0;
+}
+
+
+void Foam::searchableSphere::getNormal
+(
+    const List<pointIndexHit>& info,
+    vectorField& normal
+) const
+{
+    normal.setSize(info.size());
+    normal = vector::zero;
+
+    forAll(info, i)
+    {
+        if (info[i].hit())
+        {
+            normal[i] = info[i].hitPoint() - centre_;
+            normal[i] /= mag(normal[i]);
+        }
+        else
+        {
+            // Set to what?
+        }
+    }
+}
+
+
+void Foam::searchableSphere::getVolumeType
+(
+    const pointField& points,
+    List<volumeType>& volType
+) const
+{
+    volType.setSize(points.size());
+    volType = INSIDE;
+
+    forAll(points, pointI)
+    {
+        const point& pt = points[pointI];
+
+        if (magSqr(pt - centre_) <= sqr(radius_))
+        {
+            volType[pointI] = INSIDE;
+        }
+        else
+        {
+            volType[pointI] = OUTSIDE;
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSphere.H b/src/meshTools/searchableSurface/searchableSphere.H
new file mode 100644
index 0000000000000000000000000000000000000000..61b114de4e46c93aa36b975b9764ef9ea9634770
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSphere.H
@@ -0,0 +1,204 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::searchableSphere
+
+Description
+    Searching on sphere
+
+SourceFiles
+    searchableSphere.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef searchableSphere_H
+#define searchableSphere_H
+
+#include "searchableSurface.H"
+#include "treeBoundBox.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+
+/*---------------------------------------------------------------------------*\
+                           Class searchableSphere Declaration
+\*---------------------------------------------------------------------------*/
+
+class searchableSphere
+:
+    public searchableSurface
+{
+private:
+
+    // Private Member Data
+
+        //- Centre point
+        const point centre_;
+
+        //- Radius squared
+        const scalar radius_;
+
+        //- Names of regions
+        mutable wordList regions_;
+
+
+    // Private Member Functions
+
+        //- Find nearest point on sphere.
+        pointIndexHit findNearest
+        (
+            const point& sample,
+            const scalar nearestDistSqr
+        ) const;
+
+        //- Find intersection with sphere
+        void findLineAll
+        (
+            const point& start,
+            const point& end,
+            pointIndexHit& near,
+            pointIndexHit& far
+        ) const;
+
+
+        //- Disallow default bitwise copy construct
+        searchableSphere(const searchableSphere&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const searchableSphere&);
+
+
+public:
+
+    //- Runtime type information
+    TypeName("searchableSphere");
+
+
+    // Constructors
+
+        //- Construct from components
+        searchableSphere(const IOobject& io, const point&, const scalar radius);
+
+        //- Construct from dictionary (used by searchableSurface)
+        searchableSphere
+        (
+            const IOobject& io,
+            const dictionary& dict
+        );
+
+    // Destructor
+
+        virtual ~searchableSphere();
+
+
+    // Member Functions
+
+        virtual const wordList& regions() const;
+
+        //- Whether supports volume type below
+        virtual bool hasVolumeType() const
+        {
+            return true;
+        }
+
+
+        // Multiple point queries.
+
+            virtual void findNearest
+            (
+                const pointField& sample,
+                const scalarField& nearestDistSqr,
+                List<pointIndexHit>&
+            ) const;
+
+            virtual void findLine
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const;
+
+            virtual void findLineAny
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const;
+
+            //- Get all intersections in order from start to end.
+            virtual void findLineAll
+            (
+                const pointField& start,
+                const pointField& end,
+                List<List<pointIndexHit> >&
+            ) const;
+
+            //- From a set of points and indices get the region
+            virtual void getRegion
+            (
+                const List<pointIndexHit>&,
+                labelList& region
+            ) const;
+
+            //- From a set of points and indices get the normal
+            virtual void getNormal
+            (
+                const List<pointIndexHit>&,
+                vectorField& normal
+            ) const;
+
+            //- Determine type (inside/outside/mixed) for point. unknown if
+            //  cannot be determined (e.g. non-manifold surface)
+            virtual void getVolumeType
+            (
+                const pointField&,
+                List<volumeType>&
+            ) const;
+
+
+        // regIOobject implementation
+
+            bool writeData(Ostream&) const
+            {
+                notImplemented("searchableSphere::writeData(Ostream&) const");
+                return false;
+            }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/triSurface/searchableSurface/searchableSurface.C b/src/meshTools/searchableSurface/searchableSurface.C
similarity index 67%
rename from src/meshTools/triSurface/searchableSurface/searchableSurface.C
rename to src/meshTools/searchableSurface/searchableSurface.C
index 11996e75341d3cdae108fb2bd57b65089e90f287..4eba2d573bc892c444ddb7f4b2482bb885511a50 100644
--- a/src/meshTools/triSurface/searchableSurface/searchableSurface.C
+++ b/src/meshTools/searchableSurface/searchableSurface.C
@@ -34,15 +34,13 @@ namespace Foam
 
 defineTypeNameAndDebug(searchableSurface, 0);
 defineRunTimeSelectionTable(searchableSurface, dict);
-//defineRunTimeSelectionTable(searchableSurface, istream);
 
 
 // Construct named object from dictionary
 autoPtr<searchableSurface> searchableSurface::New
 (
     const word& searchableSurfaceType,
-    const word& name,
-    const objectRegistry& obj,
+    const IOobject& io,
     const dictionary& dict
 )
 {
@@ -55,7 +53,7 @@ autoPtr<searchableSurface> searchableSurface::New
         FatalErrorIn
         (
             "searchableSurface::New(const word&, const word&"
-            ", const objectRegistry&, const dictionary&)"
+            ", const IOobject&, const dictionary&)"
         )   << "Unknown searchableSurface type " << searchableSurfaceType
             << endl << endl
             << "Valid searchableSurface types : " << endl
@@ -63,44 +61,15 @@ autoPtr<searchableSurface> searchableSurface::New
             << exit(FatalError);
     }
 
-    return autoPtr<searchableSurface>(cstrIter()(name, obj, dict));
+    return autoPtr<searchableSurface>(cstrIter()(io, dict));
 }
 
 
-//// Construct named object from Istream
-//autoPtr<searchableSurface> searchableSurface::New
-//(
-//    const word& searchableSurfaceType,
-//    const objectRegistry& obj,
-//    Istream& is
-//)
-//{
-//    istreamConstructorTable::iterator cstrIter =
-//        istreamConstructorTablePtr_
-//            ->find(searchableSurfaceType);
-//
-//    if (cstrIter == istreamConstructorTablePtr_->end())
-//    {
-//        FatalErrorIn
-//        (
-//            "searchableSurface::New(const word&, const objectRegistry&"
-//            ", Istream&)"
-//        )   << "Unknown searchableSurface type " << searchableSurfaceType
-//            << endl << endl
-//            << "Valid searchableSurface types : " << endl
-//            << istreamConstructorTablePtr_->toc()
-//            << exit(FatalError);
-//    }
-//
-//    return autoPtr<searchableSurface>(cstrIter()(obj, is));
-//}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::searchableSurface::searchableSurface(const word& name)
+Foam::searchableSurface::searchableSurface(const IOobject& io)
 :
-    name_(name)
+    regIOobject(io)
 {}
 
 
diff --git a/src/meshTools/searchableSurface/searchableSurface.H b/src/meshTools/searchableSurface/searchableSurface.H
new file mode 100644
index 0000000000000000000000000000000000000000..f9dc8b12899bb26b52391c23d11085eeb16ca442
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSurface.H
@@ -0,0 +1,322 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::searchableSurface
+
+Description
+    Base class of (analytical or triangulated) surface.
+    Encapsulates all the search routines. WIP.
+
+    Information returned is usually a pointIndexHit:
+    - bool  : was intersection/nearest found?
+    - point : intersection point or nearest point
+    - index : unique index on surface (e.g. triangle for triSurfaceMesh)
+
+SourceFiles
+    searchableSurface.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef searchableSurface_H
+#define searchableSurface_H
+
+#include "pointField.H"
+#include "typeInfo.H"
+#include "runTimeSelectionTables.H"
+#include "pointIndexHit.H"
+#include "linePointRef.H"
+#include "objectRegistry.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class objectRegistry;
+class treeBoundBox;
+
+/*---------------------------------------------------------------------------*\
+                           Class searchableSurface Declaration
+\*---------------------------------------------------------------------------*/
+
+class searchableSurface
+:
+    public regIOobject
+{
+public:
+
+    // Data types
+
+        //- volume types
+        enum volumeType
+        {
+            UNKNOWN = 0,
+            MIXED = 1,      // not used. only here to maintain consistency with
+                            // indexedOctree volumeType.
+            INSIDE = 2,
+            OUTSIDE = 3
+        };
+
+private:
+
+    // Private data
+
+        const word name_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        searchableSurface(const searchableSurface&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const searchableSurface&);
+
+
+public:
+
+    //- Runtime type information
+    TypeName("searchableSurface");
+
+    // Declare run-time constructor selection table
+
+        // For the dictionary constructor
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            searchableSurface,
+            dict,
+            (
+                const IOobject& io,
+                const dictionary& dict
+            ),
+            (io, dict)
+        );
+
+
+        //- Class used for the read-construction of
+        //  PtrLists of searchableSurface.
+        class iNew
+        {
+            IOobject& io_;
+
+        public:
+
+            iNew(IOobject& io)
+            :
+                io_(io)
+            {}
+
+            autoPtr<searchableSurface> operator()(Istream& is) const
+            {
+                word surfaceType(is);
+                word readName(is);
+                dictionary dict(is);
+
+                autoPtr<IOobject> namedIO(io_.clone());
+                namedIO().rename(readName);
+                return searchableSurface::New(surfaceType, namedIO(), dict);
+            }
+        };
+
+
+    // Constructors
+
+        searchableSurface(const IOobject& io);
+
+        //- Clone
+        virtual autoPtr<searchableSurface> clone() const
+        {
+            notImplemented("autoPtr<searchableSurface> clone() const");
+            return autoPtr<searchableSurface>(NULL);
+        }
+
+
+    // Selectors
+
+        //- Return a reference to the selected searchableSurface
+        static autoPtr<searchableSurface> New
+        (
+            const word& surfaceType,
+            const IOobject& io,
+            const dictionary& dict
+        );
+
+
+    // Destructor
+
+        virtual ~searchableSurface();
+
+
+    // Member Functions
+
+
+        //- Names of regions.
+        virtual const wordList& regions() const = 0;
+
+        //- Whether supports volume type below.
+        virtual bool hasVolumeType() const = 0;
+
+
+        // Single point queries.
+
+            ////- Calculate nearest point on surface. Returns
+            ////  - bool : any point found nearer than nearestDistSqr
+            ////  - label: relevant index in surface
+            ////  - label: region in surface
+            ////  - point: actual nearest point found
+            //virtual pointIndexHit findNearest
+            //(
+            //    const point& sample,
+            //    const scalar nearestDistSqr
+            //) const = 0;
+            //
+            ////- Calculate nearest point on edge. Returns
+            ////  - bool : any point found nearer than nearestDistSqr
+            ////  - label: relevant index in surface
+            ////  - label: region in surface
+            ////  - point: actual nearest point found
+            //virtual pointIndexHit findNearestOnEdge
+            //(
+            //    const point& sample,
+            //    const scalar nearestDistSqr
+            //) const = 0;
+            //
+            ////- Find nearest to segment. Returns
+            ////  - bool : any point found?
+            ////  - label: relevant index in shapes
+            ////  - label: region in surface
+            ////  - point: actual nearest point found
+            ////  sets:
+            ////  - tightest  : bounding box
+            ////  - linePoint : corresponding nearest point on line
+            //virtual pointIndexHit findNearest
+            //(
+            //    const linePointRef& ln,
+            //    treeBoundBox& tightest,
+            //    point& linePoint
+            //) const = 0;
+            //
+            ////- Find nearest intersection of line between start and end.
+            //virtual pointIndexHit findLine
+            //(
+            //    const point& start,
+            //    const point& end
+            //) const = 0;
+            //
+            ////- Find any intersection of line between start and end.
+            //virtual pointIndexHit findLineAny
+            //(
+            //    const point& start,
+            //    const point& end
+            //) const = 0;
+
+
+        // Multiple point queries. When surface is distributed the index
+        // should be a global index. Not done yet.
+
+            virtual void findNearest
+            (
+                const pointField& sample,
+                const scalarField& nearestDistSqr,
+                List<pointIndexHit>&
+            ) const = 0;
+
+            //- Find first intersection on segment from start to end.
+            //  Note: searchableSurfacesQueries expects no
+            //  intersection to be found if start==end. Is problem?
+            virtual void findLine
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const = 0;
+
+            //- Return any intersection on segment from start to end.
+            virtual void findLineAny
+            (
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
+            ) const = 0;
+
+            //- Get all intersections in order from start to end.
+            virtual void findLineAll
+            (
+                const pointField& start,
+                const pointField& end,
+                List<List<pointIndexHit> >&
+            ) const = 0;
+
+            //- From a set of points and indices get the region
+            virtual void getRegion
+            (
+                const List<pointIndexHit>&,
+                labelList& region
+            ) const = 0;
+
+            //- From a set of points and indices get the normal
+            virtual void getNormal
+            (
+                const List<pointIndexHit>&,
+                vectorField& normal
+            ) const = 0;
+
+            //- Determine type (inside/outside) for point. unknown if
+            //  cannot be determined (e.g. non-manifold surface)
+            virtual void getVolumeType
+            (
+                const pointField&,
+                List<volumeType>&
+            ) const = 0;
+
+
+        // Other
+
+            ////- Get bounding box.
+            //const boundBox& bounds() const = 0;
+
+            ////- Set bounding box.
+            //void setBounds
+            //(
+            //    const boundBox&,
+            //    autoPtr<mapDistribute>& faceMap,
+            //    autoPtr<mapDistribute>& pointMap
+            //) = 0;
+
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSurfaces.C b/src/meshTools/searchableSurface/searchableSurfaces.C
new file mode 100644
index 0000000000000000000000000000000000000000..9689744fca45e4c8f6ca84a84125b69625803a1a
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSurfaces.C
@@ -0,0 +1,355 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*----------------------------------------------------------------------------*/
+
+#include "searchableSurfaces.H"
+#include "searchableSurfacesQueries.H"
+#include "ListOps.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(searchableSurfaces, 0);
+
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct with length.
+Foam::searchableSurfaces::searchableSurfaces(const label size)
+:
+    PtrList<searchableSurface>(size),
+    regionNames_(size),
+    allSurfaces_(identity(size))
+{}
+
+
+//Foam::searchableSurfaces::searchableSurfaces
+//(
+//    const IOobject& io,
+//    const PtrList<dictionary>& dicts
+//)
+//:
+//    PtrList<searchableSurface>(dicts.size()),
+//    regionNames_(dicts.size()),
+//    allSurfaces_(identity(dicts.size()))
+//{
+//    forAll(dicts, surfI)
+//    {
+//        const dictionary& dict = dicts[surfI];
+//
+//        // Make IOobject with correct name
+//        autoPtr<IOobject> namedIO(io.clone());
+//        namedIO().rename(dict.lookup("name"));
+//
+//        // Create and hook surface
+//        set
+//        (
+//            surfI,
+//            searchableSurface::New
+//            (
+//                dict.lookup("type"),
+//                namedIO(),
+//                dict
+//            )
+//        );
+//        const searchableSurface& s = operator[](surfI);
+//
+//        // Construct default region names by prepending surface name
+//        // to region name.
+//        const wordList& localNames = s.regions();
+//
+//        wordList globalNames(localNames.size());
+//        forAll(localNames, regionI)
+//        {
+//            globalNames[regionI] = s.name() + '_' + localNames[regionI];
+//        }
+//
+//        // See if dictionary provides any global region names.
+//        if (dict.found("regions"))
+//        {
+//            const dictionary& regionsDict = dict.subDict("regions");
+//
+//            forAllConstIter(dictionary, regionsDict, iter)
+//            {
+//                const word& key = iter().keyword();
+//
+//                if (regionsDict.isDict(key))
+//                {
+//                    // Get the dictionary for region iter.key()
+//                    const dictionary& regionDict = regionsDict.subDict(key);
+//
+//                    label index = findIndex(localNames, key);
+//
+//                    if (index == -1)
+//                    {
+//                        FatalErrorIn
+//                        (
+//                            "searchableSurfaces::searchableSurfaces"
+//                            "( const IOobject&, const dictionary&)"
+//                        )   << "Unknown region name " << key
+//                            << " for surface " << s.name() << endl
+//                            << "Valid region names are " << localNames
+//                            << exit(FatalError);
+//                    }
+//
+//                    globalNames[index] = word(regionDict.lookup("name"));
+//                }
+//            }
+//        }
+//
+//        // Now globalNames contains the names of the regions.
+//        Info<< "Surface:" << s.name() << " has regions:"
+//            << endl;
+//        forAll(globalNames, regionI)
+//        {
+//            Info<< "    " << globalNames[regionI] << endl;
+//        }
+//
+//        // Create reverse lookup
+//        forAll(globalNames, regionI)
+//        {
+//            regionNames_.insert
+//            (
+//                globalNames[regionI],
+//                labelPair(surfI, regionI)
+//            );
+//        }
+//    }
+//}
+
+
+Foam::searchableSurfaces::searchableSurfaces
+(
+    const IOobject& io,
+    const dictionary& topDict
+)
+:
+    PtrList<searchableSurface>(topDict.size()),
+    names_(topDict.size()),
+    regionNames_(topDict.size()),
+    allSurfaces_(identity(topDict.size()))
+{
+    label surfI = 0;
+    forAllConstIter(dictionary, topDict, iter)
+    {
+        const word& key = iter().keyword();
+
+        if (!topDict.isDict(key))
+        {
+            FatalErrorIn
+            (
+                "searchableSurfaces::searchableSurfaces"
+                "( const IOobject&, const dictionary&)"
+            )   << "Found non-dictionary entry " << iter()
+                << " in top-level dictionary " << topDict
+                << exit(FatalError);
+        }
+
+        const dictionary& dict = topDict.subDict(key);
+
+        names_[surfI] = key;
+
+        if (dict.found("name"))
+        {
+            dict.lookup("name") >> names_[surfI];
+        }
+
+
+        // Make IOobject with correct name
+        autoPtr<IOobject> namedIO(io.clone());
+        // Note: we would like to e.g. register triSurface 'sphere.stl' as
+        // 'sphere'. Unfortunately
+        // no support for having object read from different location than
+        // their object name. Maybe have stlTriSurfaceMesh which appends .stl
+        // when reading/writing?
+        namedIO().rename(key);  // names_[surfI]
+
+        // Create and hook surface
+        set
+        (
+            surfI,
+            searchableSurface::New
+            (
+                dict.lookup("type"),
+                namedIO(),
+                dict
+            )
+        );
+        const searchableSurface& s = operator[](surfI);
+
+        // Construct default region names by prepending surface name
+        // to region name.
+        const wordList& localNames = s.regions();
+
+        wordList& rNames = regionNames_[surfI];
+        rNames.setSize(localNames.size());
+
+        forAll(localNames, regionI)
+        {
+            rNames[regionI] = names_[surfI] + '_' + localNames[regionI];
+        }
+
+        // See if dictionary provides any global region names.
+        if (dict.found("regions"))
+        {
+            const dictionary& regionsDict = dict.subDict("regions");
+
+            forAllConstIter(dictionary, regionsDict, iter)
+            {
+                const word& key = iter().keyword();
+
+                if (regionsDict.isDict(key))
+                {
+                    // Get the dictionary for region iter.keyword()
+                    const dictionary& regionDict = regionsDict.subDict(key);
+
+                    label index = findIndex(localNames, key);
+
+                    if (index == -1)
+                    {
+                        FatalErrorIn
+                        (
+                            "searchableSurfaces::searchableSurfaces"
+                            "( const IOobject&, const dictionary&)"
+                        )   << "Unknown region name " << key
+                            << " for surface " << s.name() << endl
+                            << "Valid region names are " << localNames
+                            << exit(FatalError);
+                    }
+
+                    rNames[index] = word(regionDict.lookup("name"));
+                }
+            }
+        }
+
+        surfI++;
+    }
+
+    // Trim (not really necessary since we don't allow non-dictionary entries)
+    PtrList<searchableSurface>::setSize(surfI);
+    names_.setSize(surfI);
+    regionNames_.setSize(surfI);
+    allSurfaces_.setSize(surfI);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::label Foam::searchableSurfaces::findSurfaceID(const word& wantedName)
+ const
+{
+    return findIndex(names_, wantedName);
+}
+
+
+// Find any intersection
+void Foam::searchableSurfaces::findAnyIntersection
+(
+    const pointField& start,
+    const pointField& end,
+    labelList& hitSurfaces,
+    List<pointIndexHit>& hitInfo
+) const
+{
+    searchableSurfacesQueries::findAnyIntersection
+    (
+        *this,
+        allSurfaces_,
+        start,
+        end,
+        hitSurfaces,
+        hitInfo
+    );
+}
+
+
+// Find intersections of edge nearest to both endpoints.
+void Foam::searchableSurfaces::findAllIntersections
+(
+    const pointField& start,
+    const pointField& end,
+    labelListList& hitSurfaces,
+    List<List<pointIndexHit> >& hitInfo
+) const
+{
+    searchableSurfacesQueries::findAllIntersections
+    (
+        *this,
+        allSurfaces_,
+        start,
+        end,
+        hitSurfaces,
+        hitInfo
+    );
+}
+
+
+// Find nearest. Return -1 or nearest point
+void Foam::searchableSurfaces::findNearest
+(
+    const pointField& samples,
+    const scalarField& nearestDistSqr,
+    labelList& nearestSurfaces,
+    List<pointIndexHit>& nearestInfo
+) const
+{
+    return searchableSurfacesQueries::findNearest
+    (
+        *this,
+        allSurfaces_,
+        samples,
+        nearestDistSqr,
+        nearestSurfaces,
+        nearestInfo
+    );
+}
+
+
+//- Calculate point which is on a set of surfaces.
+Foam::pointIndexHit Foam::searchableSurfaces::facesIntersection
+(
+    const scalar initDistSqr,
+    const scalar convergenceDistSqr,
+    const point& start
+) const
+{
+    return searchableSurfacesQueries::facesIntersection
+    (
+        *this,
+        allSurfaces_,
+        initDistSqr,
+        convergenceDistSqr,
+        start
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSurfaces.H b/src/meshTools/searchableSurface/searchableSurfaces.H
new file mode 100644
index 0000000000000000000000000000000000000000..4e6c1bc2edfbc27c468e4b2a3331412332e5f037
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSurfaces.H
@@ -0,0 +1,187 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::searchableSurfaces
+
+Description
+    Container for searchableSurfaces.
+
+SourceFiles
+    searchableSurfaces.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef searchableSurfaces_H
+#define searchableSurfaces_H
+
+#include "searchableSurface.H"
+#include "labelPair.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+
+/*---------------------------------------------------------------------------*\
+                           Class searchableSurfaces Declaration
+\*---------------------------------------------------------------------------*/
+
+class searchableSurfaces
+:
+    public PtrList<searchableSurface>
+{
+    // Private data
+
+        //- Surface names
+        wordList names_;
+
+        //- Region names per surface
+        List<wordList> regionNames_;
+
+        ////- From global region name to surface and region on surface
+        //HashTable<labelPair> regionNames_;
+
+        //- Indices of all surfaces. Precalculated and stored.
+        labelList allSurfaces_;
+
+
+        //- Disallow default bitwise copy construct
+        searchableSurfaces(const searchableSurfaces&);
+
+        //- Disallow default bitwise assignment
+        void operator=(const searchableSurfaces&);
+
+
+public:
+
+    ClassName("searchableSurfaces");
+
+    // Constructors
+
+        //- Construct with length specified. Fill later.
+        explicit searchableSurfaces(const label);
+
+
+        ////- Construct from list of dictionaries
+        //searchableSurfaces(const IOobject&, const PtrList<dictionary>&);
+
+        //- Construct from dictionary
+        searchableSurfaces(const IOobject&, const dictionary&);
+
+
+    // Member Functions
+
+        const wordList& names() const
+        {
+            return names_;
+        }
+        wordList& names()
+        {
+            return names_;
+        }
+
+        const List<wordList>& regionNames() const
+        {
+            return regionNames_;
+        }
+        List<wordList>& regionNames()
+        {
+            return regionNames_;
+        }
+
+
+        ////- If adding surfaces 'by hand'
+        //HashTable<labelPair>& regionNames()
+        //{
+        //    return regionNames_;
+        //}
+        ////- Get surface and region for a name
+        //const labelPair& surfaceRegion(const word& globalRegion) const
+        //{
+        //    return regionNames_[globalRegion];
+        //}
+
+        //- Find index of surface. Return -1 if not found.
+        label findSurfaceID(const word& name) const;
+
+
+        // Multiple point queries.
+
+            //- Find any intersection. Return hit point information and
+            //  surface number. If multiple surfaces hit the first surface
+            //  is returned, not necessarily the nearest (to start).
+            void findAnyIntersection
+            (
+                const pointField& start,
+                const pointField& end,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            ) const;
+
+            //- Find all intersections in order from start to end. Returns for
+            //  every hit the surface and the hit info.
+            void findAllIntersections
+            (
+                const pointField& start,
+                const pointField& end,
+                labelListList& surfaces,
+                List<List<pointIndexHit> >& surfaceHits
+            ) const;
+
+            //- Find nearest. Return -1 (and a miss()) or surface and nearest
+            //  point.
+            void findNearest
+            (
+                const pointField&,
+                const scalarField& nearestDistSqr,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            ) const;
+
+
+        // Single point queries
+
+            //- Calculate point which is on a set of surfaces.
+            pointIndexHit facesIntersection
+            (
+                const scalar initialDistSqr,
+                const scalar convergenceDistSqr,
+                const point& start
+            ) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSurfacesQueries.C b/src/meshTools/searchableSurface/searchableSurfacesQueries.C
new file mode 100644
index 0000000000000000000000000000000000000000..ed807628199668e30a37c2abdea6822a90c9bd9e
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSurfacesQueries.C
@@ -0,0 +1,822 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*----------------------------------------------------------------------------*/
+
+#include "searchableSurfacesQueries.H"
+#include "SortableList.H"
+#include "OFstream.H"
+#include "meshTools.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(searchableSurfacesQueries, 0);
+
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::pointIndexHit Foam::searchableSurfacesQueries::tempFindNearest
+(
+    const searchableSurface& surf,
+    const point& pt,
+    const scalar initDistSqr
+)
+{
+    pointField onePoint(1, pt);
+    scalarField oneDist(1, initDistSqr);
+    List<pointIndexHit> oneHit(1);
+    surf.findNearest(onePoint, oneDist, oneHit);
+    return oneHit[0];
+}
+
+
+// Calculate sum of distance to surfaces.
+Foam::scalar Foam::searchableSurfacesQueries::sumDistSqr
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const scalar initDistSqr,
+    const point& pt
+)
+{
+    scalar sum = 0;
+
+    forAll(surfacesToTest, testI)
+    {
+        label surfI = surfacesToTest[testI];
+
+        pointIndexHit hit
+        (
+            tempFindNearest(allSurfaces[surfI], pt, initDistSqr)
+        );
+
+        // Note: make it fall over if not hit.
+        sum += magSqr(hit.hitPoint()-pt);
+    }
+    return sum;
+}
+
+
+// Reflects the point furthest away around the triangle centre by a factor fac.
+// (triangle centre is the average of all points but the ihi. pSum is running
+//  sum of all points)
+Foam::scalar Foam::searchableSurfacesQueries::tryMorphTet
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const scalar initDistSqr,
+    List<vector>& p,
+    List<scalar>& y,
+    vector& pSum,
+    const label ihi,
+    const scalar fac
+)
+{
+    scalar fac1 = (1.0-fac)/vector::nComponents;
+    scalar fac2 = fac1-fac;
+
+    vector ptry = pSum*fac1-p[ihi]*fac2;
+
+    scalar ytry = sumDistSqr(allSurfaces, surfacesToTest, initDistSqr, ptry);
+
+    if (ytry < y[ihi])
+    {
+        y[ihi] = ytry;
+        pSum += ptry - p[ihi];
+        p[ihi] = ptry;
+    }
+    return ytry;
+}
+
+
+bool Foam::searchableSurfacesQueries::morphTet
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const scalar initDistSqr,
+    const scalar convergenceDistSqr,
+    const label maxIter,
+    List<vector>& p,
+    List<scalar>& y
+)
+{
+    vector pSum = sum(p);
+
+    autoPtr<OFstream> str;
+    label vertI = 0;
+    if (debug)
+    {
+        wordList names(surfacesToTest.size());
+        forAll(surfacesToTest, i)
+        {
+            names[i] = allSurfaces[surfacesToTest[i]].name();
+        }
+        Pout<< "searchableSurfacesQueries::morphTet : intersection of "
+            << names << " starting from points:" << p << endl;
+        str.reset(new OFstream("track.obj"));
+        meshTools::writeOBJ(str(), p[0]);
+        vertI++;
+    }
+
+    for (label iter = 0; iter < maxIter; iter++)
+    {
+        // Get the indices of highest, second-highest and lowest values.
+        label ihi, inhi, ilo;
+        {
+            SortableList<scalar> sortedY(y);
+            ilo = sortedY.indices()[0];
+            ihi = sortedY.indices()[sortedY.size()-1];
+            inhi = sortedY.indices()[sortedY.size()-2];
+        }
+
+        if (debug)
+        {
+            Pout<< "Iteration:" << iter
+                << " lowest:" << y[ilo] << " highest:" << y[ihi]
+                << " points:" << p << endl;
+
+            meshTools::writeOBJ(str(), p[ilo]);
+            vertI++;
+            str()<< "l " << vertI-1 << ' ' << vertI << nl;
+        }
+
+        if (y[ihi] < convergenceDistSqr)
+        {
+            // Get point on 0th surface.
+            Swap(p[0], p[ilo]);
+            Swap(y[0], y[ilo]);
+            return true;
+        }
+
+        // Reflection: point furthest away gets reflected.
+        scalar ytry = tryMorphTet
+        (
+            allSurfaces,
+            surfacesToTest,
+            10*y[ihi],             // search box.
+            p,
+            y,
+            pSum,
+            ihi,
+            -1.0
+        );
+
+        if (ytry <= y[ilo])
+        {
+            // If in right direction (y lower) expand by two.
+            ytry = tryMorphTet
+            (
+                allSurfaces,
+                surfacesToTest,
+                10*y[ihi],
+                p,
+                y,
+                pSum,
+                ihi,
+                2.0
+            );
+        }
+        else if (ytry >= y[inhi])
+        {
+            // If inside tet try contraction.
+
+            scalar ysave = y[ihi];
+
+            ytry = tryMorphTet
+            (
+                allSurfaces,
+                surfacesToTest,
+                10*y[ihi],
+                p,
+                y,
+                pSum,
+                ihi,
+                0.5
+            );
+
+            if (ytry >= ysave)
+            {
+                // Contract around lowest point.
+                forAll(p, i)
+                {
+                    if (i != ilo)
+                    {
+                        p[i] = 0.5*(p[i] + p[ilo]);
+                        y[i] = sumDistSqr
+                        (
+                            allSurfaces,
+                            surfacesToTest,
+                            y[ihi],
+                            p[i]
+                        );
+                    }
+                }
+                pSum = sum(p);
+            }
+        }
+    }
+
+    if (debug)
+    {
+        meshTools::writeOBJ(str(), p[0]);
+        vertI++;
+        str()<< "l " << vertI-1 << ' ' << vertI << nl;
+    }
+
+    // Failure to converge. Return best guess so far.
+    label ilo = findMin(y);
+    Swap(p[0], p[ilo]);
+    Swap(y[0], y[ilo]);
+    return false;
+}
+
+
+//// Get all intersections (in order) for single surface.
+//void Foam::searchableSurfacesQueries::findAllIntersections
+//(
+//    const searchableSurface& s,
+//    const pointField& start,
+//    const pointField& end,
+//    const vectorField& smallVec,
+//    List<List<pointIndexHit> >& surfaceHitInfo
+//)
+//{
+//    surfaceHitInfo.setSize(start.size());
+//
+//    // Current start point of vector
+//    pointField p0(start);
+//
+//    List<pointIndexHit> intersectInfo(start.size());
+//
+//    // For test whether finished doing vector.
+//    const vectorField dirVec(end-start);
+//    const scalarField magSqrDirVec(magSqr(dirVec));
+//
+//    while (true)
+//    {
+//        // Find first intersection. Synced.
+//        s.findLine(p0, end, intersectInfo);
+//
+//        label nHits = 0;
+//
+//        forAll(intersectInfo, i)
+//        {
+//            if (intersectInfo[i].hit())
+//            {
+//                nHits++;
+//
+//                label sz = surfaceHitInfo[i].size();
+//                surfaceHitInfo[i].setSize(sz+1);
+//                surfaceHitInfo[i][sz] = intersectInfo[i];
+//
+//                p0[i] = intersectInfo[i].hitPoint() + smallVec[i];
+//
+//                // If beyond endpoint set to endpoint so as not to pick up
+//                // any intersections. Could instead just filter out hits.
+//                if (((p0[i]-start[i])&dirVec[i]) > magSqrDirVec[i])
+//                {
+//                    p0[i] = end[i];
+//                }
+//            }
+//            else
+//            {
+//                // Set to endpoint to stop intersection test. See above.
+//                p0[i] = end[i];
+//            }
+//        }
+//
+//        // returnReduce(nHits) ?
+//        if (nHits == 0)
+//        {
+//            break;
+//        }
+//    }
+//}
+
+
+// Given current set of hits (allSurfaces, allInfo) merge in those coming from
+// surface surfI.
+void Foam::searchableSurfacesQueries::mergeHits
+(
+    const point& start,
+    const scalar mergeDist,
+
+    const label testI,                          // index of surface
+    const List<pointIndexHit>& surfHits,  // hits on surface
+
+    labelList& allSurfaces,
+    List<pointIndexHit>& allInfo,
+    scalarList& allDistSqr
+)
+{
+    // Precalculate distances
+    scalarList surfDistSqr(surfHits.size());
+    forAll(surfHits, i)
+    {
+        surfDistSqr[i] = magSqr(surfHits[i].hitPoint()-start);
+    }
+
+    forAll(surfDistSqr, i)
+    {
+        label index = findLower(allDistSqr, surfDistSqr[i]);
+
+        // Check if equal to lower.
+        if
+        (
+            index >= 0
+         && (mag(allDistSqr[index]-surfDistSqr[i]) < mergeDist)
+        )
+        {
+            // Same. Do not count.
+            //Pout<< "point:" << surfHits[i].hitPoint()
+            //    << " considered same as:" << allInfo[index].hitPoint()
+            //    << " within tol:" << mergeDist
+            //    << endl;
+        }
+        else
+        {
+            // Check if equal to higher
+            label next = index+1;
+            if
+            (
+                next < allDistSqr.size()
+             && (mag(allDistSqr[next]-surfDistSqr[i]) < mergeDist)
+            )
+            {
+                //Pout<< "point:" << surfHits[i].hitPoint()
+                //    << " considered same as:" << allInfo[next].hitPoint()
+                //    << " within tol:" << mergeDist
+                //    << endl;
+            }
+            else
+            {
+                // Insert after index
+                label sz = allSurfaces.size();
+                allSurfaces.setSize(sz+1);
+                allInfo.setSize(allSurfaces.size());
+                allDistSqr.setSize(allSurfaces.size());
+                // Make space.
+                for (label j = sz-1; j > index; --j)
+                {
+                    allSurfaces[j+1] = allSurfaces[j];
+                    allInfo[j+1] = allInfo[j];
+                    allDistSqr[j+1] = allDistSqr[j];
+                }
+                // Insert new value
+                allSurfaces[index+1] = testI;
+                allInfo[index+1] = surfHits[i];
+                allDistSqr[index+1] = surfDistSqr[i];
+            }
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+// Find any intersection
+void Foam::searchableSurfacesQueries::findAnyIntersection
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const pointField& start,
+    const pointField& end,
+    labelList& hitSurfaces,
+    List<pointIndexHit>& hitInfo
+)
+{
+    hitSurfaces.setSize(start.size());
+    hitSurfaces = -1;
+    hitInfo.setSize(start.size());
+
+    // Work arrays
+    labelList hitMap(identity(start.size()));
+    pointField p0(start);
+    pointField p1(end);
+    List<pointIndexHit> intersectInfo(start.size());
+
+    forAll(surfacesToTest, testI)
+    {
+        // Do synchronised call to all surfaces.
+        allSurfaces[surfacesToTest[testI]].findLineAny(p0, p1, intersectInfo);
+
+        // Copy all hits into arguments, continue with misses
+        label newI = 0;
+        forAll(intersectInfo, i)
+        {
+            if (intersectInfo[i].hit())
+            {
+                hitInfo[hitMap[i]] = intersectInfo[i];
+                hitSurfaces[hitMap[i]] = testI;
+            }
+            else
+            {
+                if (i != newI)
+                {
+                    hitMap[newI] = hitMap[i];
+                    p0[newI] = p0[i];
+                    p1[newI] = p1[i];
+                }
+                newI++;
+            }
+        }
+
+        // All done? Note that this decision should be synchronised
+        if (newI == 0)
+        {
+            break;
+        }
+
+        // Trim and continue
+        hitMap.setSize(newI);
+        p0.setSize(newI);
+        p1.setSize(newI);
+        intersectInfo.setSize(newI);
+    }
+}
+
+
+void Foam::searchableSurfacesQueries::findAllIntersections
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const pointField& start,
+    const pointField& end,
+    labelListList& hitSurfaces,
+    List<List<pointIndexHit> >& hitInfo
+)
+{
+    // Note: maybe move the single-surface all intersections test into
+    // searchable surface? Some of the tolerance issues might be
+    // lessened.
+
+    // 2. Currently calling searchableSurface::findLine with start==end
+    //    is expected to find no intersection. Problem if it does.
+
+    hitSurfaces.setSize(start.size());
+    hitInfo.setSize(start.size());
+
+    if (surfacesToTest.size() == 0)
+    {
+        return;
+    }
+
+    // Test first surface
+    allSurfaces[surfacesToTest[0]].findLineAll(start, end, hitInfo);
+
+    // Set hitSurfaces and distance
+    List<scalarList> hitDistSqr(hitInfo.size());
+    forAll(hitInfo, pointI)
+    {
+        const List<pointIndexHit>& pHits = hitInfo[pointI];
+
+        labelList& pSurfaces = hitSurfaces[pointI];
+        pSurfaces.setSize(pHits.size());
+        pSurfaces = 0;
+
+        scalarList& pDistSqr = hitDistSqr[pointI];
+        pDistSqr.setSize(pHits.size());
+        forAll(pHits, i)
+        {
+            pDistSqr[i] = magSqr(pHits[i].hitPoint() - start[pointI]);
+        }
+    }
+
+
+    if (surfacesToTest.size() > 1)
+    {
+        // Small vector to increment start vector by to find next intersection
+        // along line. Constant factor added to make sure that start==end still
+        // ends iteration in findAllIntersections. Also SMALL is just slightly
+        // too small.
+        const vectorField smallVec
+        (
+            1E2*SMALL*(end-start)
+          + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
+        );
+
+        // Tolerance used to check whether points are equal. Note: used to
+        // compare distance^2. Note that we use the maximum possible tolerance
+        // (reached at intersections close to the end point)
+        const scalarField mergeDist(2*mag(smallVec)*mag(end-start));
+
+        // Test the other surfaces and merge (according to distance from start).
+        for (label testI = 1; testI < surfacesToTest.size(); testI++)
+        {
+            List<List<pointIndexHit> > surfHits;
+            allSurfaces[surfacesToTest[testI]].findLineAll
+            (
+                start,
+                end,
+                surfHits
+            );
+
+            forAll(surfHits, pointI)
+            {
+                mergeHits
+                (
+                    start[pointI],          // Current segment
+                    mergeDist[pointI],
+
+                    testI,                  // Surface and its hits
+                    surfHits[pointI],
+
+                    hitSurfaces[pointI],    // Merge into overall hit info
+                    hitInfo[pointI],
+                    hitDistSqr[pointI]
+                );
+            }
+        }
+    }
+}
+
+
+//// Find intersections of edge nearest to both endpoints.
+//void Foam::searchableSurfacesQueries::findNearestIntersection
+//(
+//    const PtrList<searchableSurface>& allSurfaces,
+//    const labelList& surfacesToTest,
+//    const pointField& start,
+//    const pointField& end,
+//
+//    labelList& surface1,
+//    List<pointIndexHit>& hit1,
+//    labelList& surface2,
+//    List<pointIndexHit>& hit2
+//)
+//{
+//    // 1. intersection from start to end
+//    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+//    // Initialize arguments
+//    surface1.setSize(start.size());
+//    surface1 = -1;
+//    hit1.setSize(start.size());
+//
+//    // Current end of segment to test.
+//    pointField nearest(end);
+//    // Work array
+//    List<pointIndexHit> nearestInfo(start.size());
+//
+//    forAll(surfacesToTest, testI)
+//    {
+//        // See if any intersection between start and current nearest
+//        allSurfaces[surfacesToTest[testI]].findLine
+//        (
+//            start,
+//            nearest,
+//            nearestInfo
+//        );
+//
+//        forAll(nearestInfo, pointI)
+//        {
+//            if (nearestInfo[pointI].hit())
+//            {
+//                hit1[pointI] = nearestInfo[pointI];
+//                surface1[pointI] = testI;
+//                nearest[pointI] = hit1[pointI].hitPoint();
+//            }
+//        }
+//    }
+//
+//
+//    // 2. intersection from end to last intersection
+//    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+//    // Find the nearest intersection from end to start. Note that we
+//    // initialize to the first intersection (if any).
+//    surface2 = surface1;
+//    hit2 = hit1;
+//
+//    // Set current end of segment to test.
+//    forAll(nearest, pointI)
+//    {
+//        if (hit1[pointI].hit())
+//        {
+//            nearest[pointI] = hit1[pointI].hitPoint();
+//        }
+//        else
+//        {
+//            // Disable testing by setting to end.
+//            nearest[pointI] = end[pointI];
+//        }
+//    }
+//
+//    forAll(surfacesToTest, testI)
+//    {
+//        // See if any intersection between end and current nearest
+//        allSurfaces[surfacesToTest[i]].findLine(end, nearest, nearestInfo);
+//
+//        forAll(nearestInfo, pointI)
+//        {
+//            if (nearestInfo[pointI].hit())
+//            {
+//                hit2[pointI] = nearestInfo[pointI];
+//                surface2[pointI] = testI;
+//                nearest[pointI] = hit2[pointI].hitPoint();
+//            }
+//        }
+//    }
+//}
+
+
+// Find nearest. Return -1 or nearest point
+void Foam::searchableSurfacesQueries::findNearest
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const pointField& samples,
+    const scalarField& nearestDistSqr,
+    labelList& nearestSurfaces,
+    List<pointIndexHit>& nearestInfo
+)
+{
+    // Initialise
+    nearestSurfaces.setSize(samples.size());
+    nearestSurfaces = -1;
+    nearestInfo.setSize(samples.size());
+
+    // Work arrays
+    scalarField minDistSqr(nearestDistSqr);
+    List<pointIndexHit> hitInfo(samples.size());
+
+    forAll(surfacesToTest, testI)
+    {
+        allSurfaces[surfacesToTest[testI]].findNearest
+        (   
+            samples,
+            minDistSqr,
+            hitInfo
+        );
+
+        // Update minDistSqr and arguments
+        forAll(hitInfo, pointI)
+        {
+            if (hitInfo[pointI].hit())
+            {
+                minDistSqr[pointI] = magSqr
+                (   
+                    hitInfo[pointI].hitPoint()
+                  - samples[pointI]
+                );
+                nearestInfo[pointI] = hitInfo[pointI];
+                nearestSurfaces[pointI] = testI;
+            }
+        }
+    }
+}
+
+
+//- Calculate point which is on a set of surfaces.
+Foam::pointIndexHit Foam::searchableSurfacesQueries::facesIntersection
+(
+    const PtrList<searchableSurface>& allSurfaces,
+    const labelList& surfacesToTest,
+    const scalar initDistSqr,
+    const scalar convergenceDistSqr,
+    const point& start
+)
+{
+    // Get four starting points. Take these as the projection of the
+    // starting point onto the surfaces and the mid point
+    List<point> nearest(surfacesToTest.size()+1);
+
+    point sumNearest = vector::zero;
+
+    forAll(surfacesToTest, i)
+    {
+        pointIndexHit hit
+        (
+            tempFindNearest(allSurfaces[surfacesToTest[i]], start, initDistSqr)
+        );
+
+        if (hit.hit())
+        {
+            nearest[i] = hit.hitPoint();
+            sumNearest += nearest[i];
+        }
+        else
+        {
+            FatalErrorIn
+            (
+                "searchableSurfacesQueries::facesIntersection"
+                "(const labelList&, const scalar, const scalar, const point&)"
+            )   << "Did not find point within distance "
+                << initDistSqr << " of starting point " << start
+                << " on surface "
+                << allSurfaces[surfacesToTest[i]].IOobject::name()
+                << abort(FatalError);
+        }
+    }
+
+    nearest[nearest.size()-1] = sumNearest / surfacesToTest.size();
+
+
+    // Get the sum of distances (initial evaluation)
+    List<scalar> nearestDist(nearest.size());
+
+    forAll(nearestDist, i)
+    {
+        nearestDist[i] = sumDistSqr
+        (
+            allSurfaces,
+            surfacesToTest,
+            initDistSqr,
+            nearest[i]
+        );
+    }
+
+
+    //- Downhill Simplex method
+
+    bool converged = morphTet
+    (
+        allSurfaces,
+        surfacesToTest,
+        initDistSqr,
+        convergenceDistSqr,
+        2000,
+        nearest,
+        nearestDist
+    );
+
+
+    pointIndexHit intersection;
+
+    if (converged)
+    {
+        // Project nearest onto 0th surface.
+        intersection = tempFindNearest
+        (
+            allSurfaces[surfacesToTest[0]],
+            nearest[0],
+            nearestDist[0]
+        );
+    }
+
+    //if (!intersection.hit())
+    //{
+    //    // Restart
+    //    scalar smallDist = Foam::sqr(convergenceDistSqr);
+    //    nearest[0] = intersection.hitPoint();
+    //    nearest[1] = nearest[0];
+    //    nearest[1].x() += smallDist;
+    //    nearest[2] = nearest[0];
+    //    nearest[2].y() += smallDist;
+    //    nearest[3] = nearest[0];
+    //    nearest[3].z() += smallDist;
+    //
+    //    forAll(nearestDist, i)
+    //    {
+    //        nearestDist[i] = sumDistSqr
+    //        (
+    //            surfacesToTest,
+    //            initDistSqr,
+    //            nearest[i]
+    //        );
+    //    }
+    //
+    //    intersection = morphTet
+    //    (
+    //        allSurfaces,
+    //        surfacesToTest,
+    //        initDistSqr,
+    //        convergenceDistSqr,
+    //        1000,
+    //        nearest,
+    //        nearestDist
+    //    );
+    //}
+
+    return intersection;
+}
+
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/searchableSurfacesQueries.H b/src/meshTools/searchableSurface/searchableSurfacesQueries.H
new file mode 100644
index 0000000000000000000000000000000000000000..599d186baeca4fb7d3899503ec269388e6a08279
--- /dev/null
+++ b/src/meshTools/searchableSurface/searchableSurfacesQueries.H
@@ -0,0 +1,198 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Class
+    Foam::searchableSurfacesQueries
+
+Description
+    A collection of tools for searchableSurfaces.
+
+SourceFiles
+    searchableSurfacesQueries.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef searchableSurfacesQueries_H
+#define searchableSurfacesQueries_H
+
+#include "searchableSurface.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class plane;
+
+/*---------------------------------------------------------------------------*\
+                           Class searchableSurfacesQueries Declaration
+\*---------------------------------------------------------------------------*/
+
+class searchableSurfacesQueries
+{
+    // Private data
+
+    // Private Member Functions
+
+        //- Temporary wrapper around findNearest. Used in facesIntersection only
+        static pointIndexHit tempFindNearest
+        (
+            const searchableSurface&,
+            const point& pt,
+            const scalar initDistSqr
+        );
+
+        //- Calculate sum of distances to nearest point on surfaces. Is used
+        //  in minimisation to find intersection. Returns sum of (square of)
+        //  distances to the surfaces.
+        static scalar sumDistSqr
+        (
+            const PtrList<searchableSurface>&,
+            const labelList& surfacesToTest,
+            const scalar initialDistSqr,    // search box
+            const point& pt
+        );
+
+        //- Takes the tet (points p) and reflects the point with the
+        //  highest value around the centre (pSum). Checks if it gets closer
+        //  and updates p, y if so.
+        static scalar tryMorphTet
+        (
+            const PtrList<searchableSurface>&,
+            const labelList& surfacesToTest,
+            const scalar initialDistSqr,
+            List<vector>& p,
+            List<scalar>& y,
+            vector& pSum,
+            const label ihi,
+            const scalar fac
+        );
+
+        //- Downhill simplex method: find the point with min cumulative
+        //  distance to all surfaces. Does so by morphing a tet (points p).
+        //  Returns the point on the 0th surface or hit if not reached within
+        //  maxIters iterations.
+        static bool morphTet
+        (
+            const PtrList<searchableSurface>&,
+            const labelList& surfacesToTest,
+            const scalar initialDistSqr,
+            const scalar convergenceDistSqr,
+            const label maxIter,
+            List<vector>& p,
+            List<scalar>& y
+        );
+
+        //static void findAllIntersections
+        //(
+        //    const searchableSurface& s,
+        //    const pointField& start,
+        //    const pointField& end,
+        //    const vectorField& smallVec,
+        //    List<List<pointIndexHit> >&
+        //);
+
+        static void mergeHits
+        (
+            const point& start,
+            const scalar mergeDist,
+
+            const label surfI,
+            const List<pointIndexHit>& surfHits,
+
+            labelList& allSurfaces,
+            List<pointIndexHit>& allInfo,
+            scalarList& allDistSqr
+        );
+
+public:
+
+    // Declare name of the class and its debug switch
+    ClassName("searchableSurfacesQueries");
+
+
+        // Multiple point queries.
+
+            //- Find any intersection. Return hit point information and
+            //  index in surfacesToTest. If multiple surfaces hit the first
+            //  surface is returned, not necessarily the nearest (to start).
+            static void findAnyIntersection
+            (
+                const PtrList<searchableSurface>&,
+                const labelList& surfacesToTest,
+                const pointField& start,
+                const pointField& end,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            );
+
+            //- Find all intersections in order from start to end. Returns for
+            //  every hit the index in surfacesToTest and the hit info.
+            static void findAllIntersections
+            (
+                const PtrList<searchableSurface>&,
+                const labelList& surfacesToTest,
+                const pointField& start,
+                const pointField& end,
+                labelListList& surfaces,
+                List<List<pointIndexHit> >& surfaceHits
+            );
+
+            //- Find nearest. Return -1 (and a miss()) or surface and nearest
+            //  point.
+            static void findNearest
+            (
+                const PtrList<searchableSurface>&,
+                const labelList& surfacesToTest,
+                const pointField&,
+                const scalarField& nearestDistSqr,
+                labelList& surfaces,
+                List<pointIndexHit>&
+            );
+
+
+        // Single point queries
+
+            //- Calculate point which is on a set of surfaces. WIP.
+            static pointIndexHit facesIntersection
+            (
+                const PtrList<searchableSurface>& allSurfaces,
+                const labelList& surfacesToTest,
+                const scalar initDistSqr,
+                const scalar convergenceDistSqr,
+                const point& start
+            );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/meshTools/searchableSurface/triSurfaceMesh.C b/src/meshTools/searchableSurface/triSurfaceMesh.C
new file mode 100644
index 0000000000000000000000000000000000000000..9751e3a3eff72fcd7f2d86b13bb964ab13f3ac6e
--- /dev/null
+++ b/src/meshTools/searchableSurface/triSurfaceMesh.C
@@ -0,0 +1,552 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
+    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+\*---------------------------------------------------------------------------*/
+
+#include "triSurfaceMesh.H"
+#include "Random.H"
+#include "addToRunTimeSelectionTable.H"
+#include "EdgeMap.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+defineTypeNameAndDebug(triSurfaceMesh, 0);
+addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict);
+
+}
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+//- Check file existence
+const Foam::fileName& Foam::triSurfaceMesh::checkFile
+(
+    const fileName& fName,
+    const fileName& objectName
+)
+{
+    if (fName == fileName::null)
+    {
+        FatalErrorIn
+        (
+            "triSurfaceMesh::checkFile(const fileName&, const fileName&)"
+        )   << "Cannot find triSurfaceMesh starting from "
+            << objectName << exit(FatalError);
+    }
+    return fName;
+}
+
+
+bool Foam::triSurfaceMesh::isSurfaceClosed() const
+{
+    // Construct pointFaces. Let's hope surface has compact point
+    // numbering ...
+    labelListList pointFaces;
+    invertManyToMany(points().size(), *this, pointFaces);
+
+    // Loop over all faces surrounding point. Count edges emanating from point.
+    // Every edge should be used by two faces exactly.
+    // To prevent doing work twice per edge only look at edges to higher
+    // point
+    EdgeMap<label> facesPerEdge(100);
+    forAll(pointFaces, pointI)
+    {
+        const labelList& pFaces = pointFaces[pointI];
+
+        facesPerEdge.clear();
+        forAll(pFaces, i)
+        {
+            const labelledTri& f = triSurface::operator[](pFaces[i]);
+            label fp = findIndex(f, pointI);
+
+            // Forward edge
+            {
+                label p1 = f[f.fcIndex(fp)];
+
+                if (p1 > pointI)
+                {
+                    const edge e(pointI, p1);
+                    EdgeMap<label>::iterator eFnd = facesPerEdge.find(e);
+                    if (eFnd != facesPerEdge.end())
+                    {
+                        if (eFnd() == 2)
+                        {
+                            return false;
+                        }
+                        eFnd()++;
+                    }
+                    else
+                    {
+                        facesPerEdge.insert(e, 1);
+                    }
+                }
+            }
+            // Reverse edge
+            {
+                label p1 = f[f.rcIndex(fp)];
+
+                if (p1 > pointI)
+                {
+                    const edge e(pointI, p1);
+                    EdgeMap<label>::iterator eFnd = facesPerEdge.find(e);
+                    if (eFnd != facesPerEdge.end())
+                    {
+                        if (eFnd() == 2)
+                        {
+                            return false;
+                        }
+                        eFnd()++;
+                    }
+                    else
+                    {
+                        facesPerEdge.insert(e, 1);
+                    }
+                }
+            }
+        }
+
+        // Check for any edges used only once.
+        forAllConstIter(EdgeMap<label>, facesPerEdge, iter)
+        {
+            if (iter() != 2)
+            {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s)
+:
+    searchableSurface(io),
+    objectRegistry(io),
+    triSurface(s),
+    surfaceClosed_(-1)
+{}
+
+
+Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io)
+:
+    searchableSurface(io),
+    objectRegistry(io),
+    triSurface
+    (
+        checkFile
+        (
+            searchableSurface::filePath(),
+            searchableSurface::objectPath()
+        )
+    ),
+    surfaceClosed_(-1)
+{}
+
+
+Foam::triSurfaceMesh::triSurfaceMesh
+(
+    const IOobject& io,
+    const dictionary& dict
+)
+:
+    searchableSurface(io),
+    objectRegistry(io),
+    triSurface
+    (
+        checkFile
+        (
+            searchableSurface::filePath(),
+            searchableSurface::objectPath()
+        )
+    ),
+    surfaceClosed_(-1)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::triSurfaceMesh::~triSurfaceMesh()
+{
+    clearOut();
+}
+
+
+void Foam::triSurfaceMesh::clearOut()
+{
+    tree_.clear();
+    edgeTree_.clear();
+    triSurface::clearOut();
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::triSurfaceMesh::movePoints(const pointField& newPoints)
+{
+    tree_.clear();
+    edgeTree_.clear();
+    triSurface::movePoints(newPoints);
+}
+
+
+const Foam::indexedOctree<Foam::treeDataTriSurface>&
+    Foam::triSurfaceMesh::tree() const
+{
+    if (!tree_.valid())
+    {
+        treeBoundBox bb(points(), meshPoints());
+
+        // Random number generator. Bit dodgy since not exactly random ;-)
+        Random rndGen(65431);
+
+        tree_.reset
+        (
+            new indexedOctree<treeDataTriSurface>
+            (
+                treeDataTriSurface(*this),
+                bb.extend(rndGen, 1E-3),    // slightly randomize bb
+                10,     // maxLevel
+                10,     // leafsize
+                3.0     // duplicity
+            )
+        );
+    }
+
+    return tree_();
+}
+
+
+const Foam::indexedOctree<Foam::treeDataEdge>&
+    Foam::triSurfaceMesh::edgeTree() const
+{
+    if (!edgeTree_.valid())
+    {
+        treeBoundBox bb(localPoints());
+
+        // Boundary edges
+        labelList bEdges
+        (
+            identity
+            (
+                nEdges()
+               -nInternalEdges()
+            )
+          + nInternalEdges()
+        );
+
+        // Random number generator. Bit dodgy since not exactly random ;-)
+        Random rndGen(65431);
+
+        edgeTree_.reset
+        (
+            new indexedOctree<treeDataEdge>
+            (
+                treeDataEdge
+                (
+                    false,          // cachebb
+                    edges(),        // edges
+                    localPoints(),  // points
+                    bEdges          // selected edges
+                ),
+                bb.extend(rndGen, 1E-3),    // slightly randomize bb
+                8,      // maxLevel
+                10,     // leafsize
+                3.0     // duplicity
+            )
+        );
+    }
+    return edgeTree_();
+}
+
+
+const Foam::wordList& Foam::triSurfaceMesh::regions() const
+{
+    if (regions_.size() == 0)
+    {
+        regions_.setSize(patches().size());
+        forAll(regions_, regionI)
+        {
+            regions_[regionI] = patches()[regionI].name();
+        }
+    }
+    return regions_;
+}
+
+
+//Foam::pointIndexHit Foam::triSurfaceMesh::findNearest
+//(
+//    const point& sample,
+//    const scalar nearestDistSqr
+//) const
+//{
+//    return tree().findNearest(sample, nearestDistSqr);
+//}
+//
+//
+//Foam::pointIndexHit Foam::triSurfaceMesh::findNearestOnEdge
+//(
+//    const point& sample,
+//    const scalar nearestDistSqr
+//) const
+//{
+//    return = edgeTree().findNearest(sample, nearestDistSqr);
+//}
+//
+//
+//Foam::pointIndexHit Foam::triSurfaceMesh::findNearest
+//(
+//    const linePointRef& ln,
+//    treeBoundBox& tightest,
+//    point& linePoint
+//) const
+//{
+//    return tree().findNearest(ln, tightest, linePoint);
+//}
+//
+//
+//Foam::pointIndexHit Foam::triSurfaceMesh::findLine
+//(
+//    const point& start,
+//    const point& end
+//) const
+//{
+//    return tree().findLine(start, end);
+//}
+//
+//
+//Foam::pointIndexHit Foam::triSurfaceMesh::findLineAny
+//(
+//    const point& start,
+//    const point& end
+//) const
+//{
+//    return tree().findLineAny(start, end);
+//}
+
+
+// Find out if surface is closed.
+bool Foam::triSurfaceMesh::hasVolumeType() const
+{
+    if (surfaceClosed_ == -1)
+    {
+        if (isSurfaceClosed())
+        {
+            surfaceClosed_ = 1;
+        }
+        else
+        {
+            surfaceClosed_ = 0;
+        }
+    }
+
+    return surfaceClosed_ == 1;
+}
+
+
+void Foam::triSurfaceMesh::findNearest
+(
+    const pointField& samples,
+    const scalarField& nearestDistSqr,
+    List<pointIndexHit>& info
+) const
+{
+    const indexedOctree<treeDataTriSurface>& octree = tree();
+
+    info.setSize(samples.size());
+
+    forAll(samples, i)
+    {
+        static_cast<pointIndexHit&>(info[i]) =
+            octree.findNearest(samples[i], nearestDistSqr[i]);
+    }
+}
+
+
+void Foam::triSurfaceMesh::findLine
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    const indexedOctree<treeDataTriSurface>& octree = tree();
+
+    info.setSize(start.size());
+
+    forAll(start, i)
+    {
+        static_cast<pointIndexHit&>(info[i]) = octree.findLine
+        (
+            start[i],
+            end[i]
+        );
+    }
+}
+
+
+void Foam::triSurfaceMesh::findLineAny
+(
+    const pointField& start,
+    const pointField& end,
+    List<pointIndexHit>& info
+) const
+{
+    const indexedOctree<treeDataTriSurface>& octree = tree();
+
+    info.setSize(start.size());
+
+    forAll(start, i)
+    {
+        static_cast<pointIndexHit&>(info[i]) =
+            octree.findLineAny(start[i], end[i]);
+    }
+}
+
+
+void Foam::triSurfaceMesh::findLineAll
+(
+    const pointField& start,
+    const pointField& end,
+    List<List<pointIndexHit> >& info
+) const
+{
+    const indexedOctree<treeDataTriSurface>& octree = tree();
+
+    info.setSize(start.size());
+
+    // Work array
+    DynamicList<pointIndexHit, 1, 1> hits;
+
+    // Tolerances
+    const vectorField dirVec(end-start);
+    const scalarField magSqrDirVec(magSqr(dirVec));
+    const vectorField smallVec
+    (
+        Foam::sqrt(SMALL)*dirVec
+      + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
+    );
+
+    forAll(start, pointI)
+    {
+        hits.clear();
+
+        // Current starting point of ray.
+        point pt = start[pointI];
+
+        while (true)
+        {
+            // See if any intersection between pt and end
+            pointIndexHit inter = octree.findLine(pt, end[pointI]);
+
+            if (!inter.hit())
+            {
+                break;
+            }
+            hits.append(inter);
+
+            pt = inter.hitPoint() + smallVec[pointI];
+
+            if (((pt-start[pointI])&dirVec[pointI]) > magSqrDirVec[pointI])
+            {
+                // Adding smallVec has taken us beyond end
+                break;
+            }
+        }
+
+        hits.shrink();
+        info[pointI].transfer(hits);
+        hits.clear();
+    }
+}
+
+
+void Foam::triSurfaceMesh::getRegion
+(
+    const List<pointIndexHit>& info,
+    labelList& region
+) const
+{
+    region.setSize(info.size());
+    forAll(info, i)
+    {
+        if (info[i].hit())
+        {
+            region[i] = triSurface::operator[](info[i].index()).region();
+        }
+        else
+        {
+            region[i] = -1;
+        }
+    }
+}
+
+
+void Foam::triSurfaceMesh::getNormal
+(
+    const List<pointIndexHit>& info,
+    vectorField& normal
+) const
+{
+    normal.setSize(info.size());
+
+    forAll(info, i)
+    {
+        if (info[i].hit())
+        {
+            normal[i] = faceNormals()[info[i].index()];
+        }
+        else
+        {
+            // Set to what?
+            normal[i] = vector::zero;
+        }
+    }
+}
+
+
+void Foam::triSurfaceMesh::getVolumeType
+(
+    const pointField& points,
+    List<volumeType>& volType
+) const
+{
+    volType.setSize(points.size());
+
+    forAll(points, pointI)
+    {
+        const point& pt = points[pointI];
+
+        // - use cached volume type per each tree node
+        // - cheat conversion since same values
+        volType[pointI] = static_cast<volumeType>(tree().getVolumeType(pt));
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/triSurface/searchableSurface/triSurfaceMesh.H b/src/meshTools/searchableSurface/triSurfaceMesh.H
similarity index 65%
rename from src/meshTools/triSurface/searchableSurface/triSurfaceMesh.H
rename to src/meshTools/searchableSurface/triSurfaceMesh.H
index 453b4daa6127456e0077e03568b8688212d6bb3a..8a0709386cd2120eba63e5fd1c15b7fcacc5f93e 100644
--- a/src/meshTools/triSurface/searchableSurface/triSurfaceMesh.H
+++ b/src/meshTools/searchableSurface/triSurfaceMesh.H
@@ -54,7 +54,7 @@ namespace Foam
 class triSurfaceMesh
 :
     public searchableSurface,
-    public objectRegistry,
+    public objectRegistry,      // so we can store fields
     public triSurface
 {
 private:
@@ -67,6 +67,12 @@ private:
         //- Search tree for boundary edges.
         mutable autoPtr<indexedOctree<treeDataEdge> > edgeTree_;
 
+        //- Names of regions
+        mutable wordList regions_;
+
+        //- Is surface closed
+        mutable label surfaceClosed_;
+
     // Private Member Functions
 
         //- Check file existence
@@ -76,6 +82,10 @@ private:
             const fileName& objectName
         );
 
+        //- Check whether surface is closed without calculating any permanent
+        //  addressing.
+        bool isSurfaceClosed() const;
+
         //- Disallow default bitwise copy construct
         triSurfaceMesh(const triSurfaceMesh&);
 
@@ -97,11 +107,10 @@ public:
         //- Construct read
         triSurfaceMesh(const IOobject& io);
 
-        //- Construct as searchableSurface
+        //- Construct from dictionary (used by searchableSurface)
         triSurfaceMesh
         (
-            const word& name,
-            const objectRegistry& obj,
+            const IOobject& io,
             const dictionary& dict
         );
 
@@ -128,57 +137,73 @@ public:
 
         // searchableSurface implementation
 
-            //- Calculate nearest point on surface. Returns
-            //  - bool : any point found nearer than nearestDistSqr
-            //  - label: relevant index in surface
-            //  - point: actual nearest point found
-            virtual pointIndexHit findNearest
+            virtual const wordList& regions() const;
+
+            //- Whether supports volume type below. I.e. whether is closed.
+            virtual bool hasVolumeType() const;
+
+
+        // Multiple point queries.
+
+            virtual void findNearest
             (
-                const point& sample,
-                const scalar nearestDistSqr
+                const pointField& sample,
+                const scalarField& nearestDistSqr,
+                List<pointIndexHit>&
             ) const;
 
-            //- Calculate nearest point on edge. Returns
-            //  - bool : any point found nearer than nearestDistSqr
-            //  - label: relevant index in surface
-            //  - point: actual nearest point found
-            virtual pointIndexHit findNearestOnEdge
+            virtual void findLine
             (
-                const point& sample,
-                const scalar nearestDistSqr
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
             ) const;
 
-            //- Find nearest to line. Returns
-            //  - bool : any point found?
-            //  - label: relevant index in shapes
-            //  - point: actual nearest point found
-            //  sets:
-            //  - tightest  : bounding box
-            //  - linePoint : corresponding nearest point on line
-            virtual pointIndexHit findNearest
+            virtual void findLineAny
             (
-                const linePointRef& ln,
-                treeBoundBox& tightest,
-                point& linePoint
+                const pointField& start,
+                const pointField& end,
+                List<pointIndexHit>&
             ) const;
 
-            //- Find nearest intersection of line between start and end.
-            virtual pointIndexHit findLine
+            //- Get all intersections in order from start to end.
+            virtual void findLineAll
             (
-                const point& start,
-                const point& end
+                const pointField& start,
+                const pointField& end,
+                List<List<pointIndexHit> >&
             ) const;
 
-            //- Find any intersection of line between start and end.
-            virtual pointIndexHit findLineAny
+            //- From a set of points and indices get the region
+            virtual void getRegion
             (
-                const point& start,
-                const point& end
+                const List<pointIndexHit>&,
+                labelList& region
+            ) const;
+
+            //- From a set of points and indices get the normal
+            virtual void getNormal
+            (
+                const List<pointIndexHit>&,
+                vectorField& normal
             ) const;
 
             //- Determine type (inside/outside/mixed) for point. unknown if
             //  cannot be determined (e.g. non-manifold surface)
-            virtual volumeType getVolumeType(const point&) const;
+            virtual void getVolumeType
+            (
+                const pointField&,
+                List<volumeType>&
+            ) const;
+
+
+        // regIOobject implementation
+
+            bool writeData(Ostream&) const
+            {
+                notImplemented("triSurfaceMesh::writeData(Ostream&) const");
+                return false;
+            }
 
 };
 
diff --git a/src/meshTools/triSurface/searchableSurface/searchableBox.C b/src/meshTools/triSurface/searchableSurface/searchableBox.C
deleted file mode 100644
index 77e48c3a8ee80f863675c3a4bb22e68fcc13dc30..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/searchableSurface/searchableBox.C
+++ /dev/null
@@ -1,247 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-\*---------------------------------------------------------------------------*/
-
-#include "searchableBox.H"
-#include "addToRunTimeSelectionTable.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-defineTypeNameAndDebug(searchableBox, 0);
-addToRunTimeSelectionTable(searchableSurface, searchableBox, dict);
-
-}
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::searchableBox::searchableBox
-(
-    const word& name,
-    const treeBoundBox& bb
-)
-:
-    searchableSurface(name),
-    treeBoundBox(bb)
-{}
-
-
-Foam::searchableBox::searchableBox
-(
-    const word& name,
-    const objectRegistry& obj,
-    const dictionary& dict
-)
-:
-    searchableSurface(name),
-    treeBoundBox(dict.lookup("min"), dict.lookup("max"))
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::searchableBox::~searchableBox()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::pointIndexHit Foam::searchableBox::findNearest
-(
-    const point& sample,
-    const scalar nearestDistSqr
-) const
-{
-    // Point can be inside or outside. For every component direction can be
-    // left of min, right of max or inbetween.
-    // - outside points: project first one x plane (either min().x()
-    // or max().x()), then onto y plane and finally z. You should be left
-    // with intersection point
-    // - inside point: find nearest side (compare to mid point). Pick any
-    // one of three points.
-
-    const point bbMid(mid());
-
-    // Outside point projected onto cube
-    point interPt(sample);
-    bool outside = false;
-
-    // (for internal points) Per direction what nearest cube side is
-    point near;
-
-    for (direction dir = 0; dir < vector::nComponents; dir++)
-    {
-        if (interPt[dir] < min()[dir])
-        {
-            interPt[dir] = min()[dir];
-            outside = true;
-        }
-        else if (interPt[dir] > max()[dir])
-        {
-            interPt[dir] = max()[dir];
-            outside = true;
-        }
-        else if (interPt[dir] > bbMid[dir])
-        {
-            near[dir] = max()[dir];
-        }
-        else
-        {
-            near[dir] = min()[dir];
-        }
-    }
-
-
-    // For outside points the interPt will be correct now. Handle inside points
-    // using the three near distances. Project onto the nearest plane.
-    if (!outside)
-    {
-        vector dist(cmptMag(interPt - near));
-
-        if (dist.x() < dist.y())
-        {
-            if (dist.x() < dist.z())
-            {
-                interPt.x() = near.x();
-            }
-            else
-            {
-                interPt.z() = near.z();
-            }
-        }
-        else
-        {
-            if (dist.y() < dist.z())
-            {
-                interPt.y() = near.y();
-            }
-            else
-            {
-                interPt.z() = near.z();
-            }
-        }
-    }
-
-    return pointIndexHit(true, interPt, 0);
-}
-
-
-Foam::pointIndexHit Foam::searchableBox::findNearestOnEdge
-(
-    const point& sample,
-    const scalar nearestDistSqr
-) const
-{
-    const point bbMid(mid());
-
-    point interPt(sample);
-
-    for (direction dir = 0; dir < vector::nComponents; dir++)
-    {
-        // Project onto left or right depending on mid
-        if (interPt[dir] > bbMid[dir])
-        {
-            interPt[dir] = max()[dir];
-        }
-        else
-        {
-            interPt[dir] = min()[dir];
-        }
-    }
-
-    return pointIndexHit(true, interPt, 0);
-}
-
-
-Foam::pointIndexHit Foam::searchableBox::findNearest
-(
-    const linePointRef& ln,
-    treeBoundBox& tightest,
-    point& linePoint
-) const
-{
-    notImplemented
-    (
-        "searchableBox::findNearest"
-        "(const linePointRef&, treeBoundBox&, point&)"
-    );
-    return pointIndexHit();
-}
-
-
-Foam::pointIndexHit Foam::searchableBox::findLine
-(
-    const point& start,
-    const point& end
-) const
-{
-    point intPt;
-    bool foundInter = intersects(start, end, intPt);
-
-    return pointIndexHit(foundInter, intPt, 0);
-}
-
-
-Foam::pointIndexHit Foam::searchableBox::findLineAny
-(
-    const point& start,
-    const point& end
-) const
-{
-    return findLine(start, end);
-}
-
-
-Foam::searchableSurface::volumeType Foam::searchableBox::getVolumeType
-(
-    const point& pt
-) const
-{
-    for (direction dir = 0; dir < vector::nComponents; dir++)
-    {
-        if (pt[dir] < min()[dir] || pt[dir] > max()[dir])
-        {
-            return OUTSIDE;
-        }
-    }
-
-    return INSIDE;
-}
-
-
-// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Functions  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/triSurface/searchableSurface/searchableBox.H b/src/meshTools/triSurface/searchableSurface/searchableBox.H
deleted file mode 100644
index 4a347af46a9761629d4c2985392e0043320ce340..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/searchableSurface/searchableBox.H
+++ /dev/null
@@ -1,161 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Class
-    Foam::searchableBox
-
-Description
-    Searching on bounding box
-
-SourceFiles
-    searchableBox.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef searchableBox_H
-#define searchableBox_H
-
-#include "searchableSurface.H"
-#include "treeBoundBox.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-// Forward declaration of classes
-
-/*---------------------------------------------------------------------------*\
-                           Class searchableBox Declaration
-\*---------------------------------------------------------------------------*/
-
-class searchableBox
-:
-    public searchableSurface,
-    public treeBoundBox
-{
-private:
-
-    // Private member data
-
-
-    // Private Member Functions
-
-        //- Disallow default bitwise copy construct
-        searchableBox(const searchableBox&);
-
-        //- Disallow default bitwise assignment
-        void operator=(const searchableBox&);
-
-
-public:
-
-    //- Runtime type information
-    TypeName("searchableBox");
-
-
-    // Constructors
-
-        //- Construct from components
-        searchableBox(const word& name, const treeBoundBox& bb);
-
-        searchableBox
-        (
-            const word& name,
-            const objectRegistry& obj,
-            const dictionary& dict
-        );
-
-    // Destructor
-
-        virtual ~searchableBox();
-
-
-    // Member Functions
-
-        //- Calculate nearest point on surface. Returns
-        //  - bool : any point found nearer than nearestDistSqr
-        //  - label: relevant index in surface
-        //  - point: actual nearest point found
-        virtual pointIndexHit findNearest
-        (
-            const point& sample,
-            const scalar nearestDistSqr
-        ) const;
-
-        //- Calculate nearest point on edge. Returns
-        //  - bool : any point found nearer than nearestDistSqr
-        //  - label: relevant index in surface
-        //  - point: actual nearest point found
-        virtual pointIndexHit findNearestOnEdge
-        (
-            const point& sample,
-            const scalar nearestDistSqr
-        ) const;
-
-        //- Find nearest to line. Returns
-        //  - bool : any point found?
-        //  - label: relevant index in shapes
-        //  - point: actual nearest point found
-        //  sets:
-        //  - tightest  : bounding box
-        //  - linePoint : corresponding nearest point on line
-        virtual pointIndexHit findNearest
-        (
-            const linePointRef& ln,
-            treeBoundBox& tightest,
-            point& linePoint
-        ) const;
-
-        //- Find nearest intersection of line between start and end.
-        virtual pointIndexHit findLine
-        (
-            const point& start,
-            const point& end
-        ) const;
-
-        //- Find any intersection of line between start and end.
-        virtual pointIndexHit findLineAny
-        (
-            const point& start,
-            const point& end
-        ) const;
-
-        //- Determine type (inside/outside/mixed) for point. unknown if
-        //  cannot be determined (e.g. non-manifold surface)
-        virtual volumeType getVolumeType(const point&) const;
-
-
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/meshTools/triSurface/searchableSurface/searchableSurface.H b/src/meshTools/triSurface/searchableSurface/searchableSurface.H
deleted file mode 100644
index 83f7aaf929cd706ce97ae11560f2804c346018bd..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/searchableSurface/searchableSurface.H
+++ /dev/null
@@ -1,257 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Class
-    Foam::searchableSurface
-
-Description
-    Base class of (analytical or triangulated) surface.
-    Encapsulates all the search routines.
-
-SourceFiles
-    searchableSurface.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef searchableSurface_H
-#define searchableSurface_H
-
-#include "pointField.H"
-#include "typeInfo.H"
-#include "runTimeSelectionTables.H"
-#include "pointIndexHit.H"
-#include "linePointRef.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-// Forward declaration of classes
-class objectRegistry;
-class treeBoundBox;
-
-/*---------------------------------------------------------------------------*\
-                           Class searchableSurface Declaration
-\*---------------------------------------------------------------------------*/
-
-class searchableSurface
-{
-public:
-
-    // Data types
-
-        //- volume types
-        enum volumeType
-        {
-            UNKNOWN = 0,
-            MIXED = 1,
-            INSIDE = 2,
-            OUTSIDE = 3
-        };
-
-private:
-
-    // Private data
-
-        const word name_;
-
-
-    // Private Member Functions
-
-        //- Disallow default bitwise copy construct
-        searchableSurface(const searchableSurface&);
-
-        //- Disallow default bitwise assignment
-        void operator=(const searchableSurface&);
-
-
-public:
-
-    //- Runtime type information
-    TypeName("searchableSurface");
-
-    // Declare run-time constructor selection table
-
-        // For the dictionary constructor
-        declareRunTimeSelectionTable
-        (
-            autoPtr,
-            searchableSurface,
-            dict,
-            (
-                const word& name,
-                const objectRegistry& obj,
-                const dictionary& dict
-            ),
-            (name, obj, dict)
-        );
-
-        //// For the Istream constructor
-        //declareRunTimeSelectionTable
-        //(
-        //    autoPtr,
-        //    searchableSurface,
-        //    istream,
-        //    (
-        //        const objectRegistry& obj,
-        //        Istream& is
-        //    ),
-        //    (obj, is)
-        //);
-
-
-        //- Class used for the read-construction of
-        //  PtrLists of searchableSurface
-        class iNew
-        {
-            const objectRegistry& obj_;
-
-        public:
-
-            iNew(const objectRegistry& obj)
-            :
-                obj_(obj)
-            {}
-
-            autoPtr<searchableSurface> operator()(Istream& is) const
-            {
-                word surfaceType(is);
-                word name(is);
-                dictionary dict(is);
-                return searchableSurface::New(surfaceType, name, obj_, dict);
-            }
-        };
-
-
-    // Constructors
-
-        searchableSurface(const word& name);
-
-        //- Clone
-        virtual autoPtr<searchableSurface> clone() const
-        {
-            notImplemented("autoPtr<searchableSurface> clone() const");
-            return autoPtr<searchableSurface>(NULL);
-        }
-
-
-    // Selectors
-
-        //- Return a reference to the selected searchableSurface
-        static autoPtr<searchableSurface> New
-        (
-            const word& surfaceType,
-            const word& name,
-            const objectRegistry& obj,
-            const dictionary& dict
-        );
-
-        ////- Return a reference to the selected searchableSurface
-        //static autoPtr<searchableSurface> New
-        //(
-        //    const word& surfaceType,
-        //    const objectRegistry& obj,
-        //    Istream& is
-        //);
-
-
-    // Destructor
-
-        virtual ~searchableSurface();
-
-
-    // Member Functions
-
-        //- Return name
-        const word& name() const
-        {
-            return name_;
-        }
-
-        //- Calculate nearest point on surface. Returns
-        //  - bool : any point found nearer than nearestDistSqr
-        //  - label: relevant index in surface
-        //  - point: actual nearest point found
-        virtual pointIndexHit findNearest
-        (
-            const point& sample,
-            const scalar nearestDistSqr
-        ) const = 0;
-
-        //- Calculate nearest point on edge. Returns
-        //  - bool : any point found nearer than nearestDistSqr
-        //  - label: relevant index in surface
-        //  - point: actual nearest point found
-        virtual pointIndexHit findNearestOnEdge
-        (
-            const point& sample,
-            const scalar nearestDistSqr
-        ) const = 0;
-
-        //- Find nearest to segment. Returns
-        //  - bool : any point found?
-        //  - label: relevant index in shapes
-        //  - point: actual nearest point found
-        //  sets:
-        //  - tightest  : bounding box
-        //  - linePoint : corresponding nearest point on line
-        virtual pointIndexHit findNearest
-        (
-            const linePointRef& ln,
-            treeBoundBox& tightest,
-            point& linePoint
-        ) const = 0;
-
-        //- Find nearest intersection of line between start and end.
-        virtual pointIndexHit findLine
-        (
-            const point& start,
-            const point& end
-        ) const = 0;
-
-        //- Find any intersection of line between start and end.
-        virtual pointIndexHit findLineAny
-        (
-            const point& start,
-            const point& end
-        ) const = 0;
-
-        //- Determine type (inside/outside/mixed) for point. unknown if
-        //  cannot be determined (e.g. non-manifold surface)
-        virtual volumeType getVolumeType(const point&) const = 0;
-
-
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/meshTools/triSurface/searchableSurface/triSurfaceMesh.C b/src/meshTools/triSurface/searchableSurface/triSurfaceMesh.C
deleted file mode 100644
index ec17352f99c0b27c3edaad00648b2b8eb290ac41..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/searchableSurface/triSurfaceMesh.C
+++ /dev/null
@@ -1,271 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-\*---------------------------------------------------------------------------*/
-
-#include "triSurfaceMesh.H"
-#include "Random.H"
-#include "addToRunTimeSelectionTable.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-defineTypeNameAndDebug(triSurfaceMesh, 0);
-addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict);
-
-}
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-//- Check file existence
-const Foam::fileName& Foam::triSurfaceMesh::checkFile
-(
-    const fileName& fName,
-    const fileName& objectName
-)
-{
-    if (fName == fileName::null)
-    {
-        FatalErrorIn
-        (
-            "triSurfaceMesh::checkFile(const fileName&, const fileName&)"
-        )   << "Cannot find triSurfaceMesh starting from "
-            << objectName << exit(FatalError);
-    }
-    return fName;
-}
-
-
-const Foam::indexedOctree<Foam::treeDataTriSurface>&
-    Foam::triSurfaceMesh::tree() const
-{
-    if (!tree_.valid())
-    {
-        treeBoundBox bb(points(), meshPoints());
-
-        // Random number generator. Bit dodgy since not exactly random ;-)
-        Random rndGen(65431);
-
-        tree_.reset
-        (
-            new indexedOctree<treeDataTriSurface>
-            (
-                treeDataTriSurface(*this),
-                bb.extend(rndGen, 1E-3),    // slightly randomize bb
-                8,      // maxLevel
-                10,     // leafsize
-                3.0     // duplicity
-            )
-        );
-    }
-
-    return tree_();
-}
-
-
-const Foam::indexedOctree<Foam::treeDataEdge>&
-    Foam::triSurfaceMesh::edgeTree() const
-{
-    if (!edgeTree_.valid())
-    {
-        treeBoundBox bb(localPoints());
-
-        // Boundary edges
-        labelList bEdges
-        (
-            identity
-            (
-                nEdges()
-               -nInternalEdges()
-            )
-          + nInternalEdges()
-        );
-
-        // Random number generator. Bit dodgy since not exactly random ;-)
-        Random rndGen(65431);
-
-        edgeTree_.reset
-        (
-            new indexedOctree<treeDataEdge>
-            (
-                treeDataEdge
-                (
-                    false,          // cachebb
-                    edges(),        // edges
-                    localPoints(),  // points
-                    bEdges          // selected edges
-                ),
-                bb.extend(rndGen, 1E-3),    // slightly randomize bb
-                8,      // maxLevel
-                10,     // leafsize
-                3.0     // duplicity
-            )
-        );
-    }
-    return edgeTree_();
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-//- Construct from triangles, patches, points.
-Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s)
-:
-    searchableSurface(io.name()),
-    objectRegistry(io),
-    triSurface(s)
-{}
-
-
-Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io)
-:
-    searchableSurface(io.name()),
-    objectRegistry(io),
-    triSurface(checkFile(filePath(), objectPath()))
-{}
-
-
-Foam::triSurfaceMesh::triSurfaceMesh
-(
-    const word& name,
-    const objectRegistry& obj,
-    const dictionary& dict
-)
-:
-    searchableSurface(name),
-    objectRegistry
-    (
-        IOobject
-        (
-            name,
-            "constant",
-            "triSurface",
-            obj,
-            IOobject::MUST_READ,
-            IOobject::NO_WRITE
-        )
-    ),
-    triSurface(checkFile(filePath(), objectPath()))
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::triSurfaceMesh::~triSurfaceMesh()
-{}
-
-
-void Foam::triSurfaceMesh::clearOut()
-{
-    tree_.clear();
-    edgeTree_.clear();
-    triSurface::clearOut();
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-void Foam::triSurfaceMesh::movePoints(const pointField& newPoints)
-{
-    tree_.clear();
-    edgeTree_.clear();
-    triSurface::movePoints(newPoints);    
-}
-
-
-Foam::pointIndexHit Foam::triSurfaceMesh::findNearest
-(
-    const point& sample,
-    const scalar nearestDistSqr
-) const
-{
-    return tree().findNearest(sample, nearestDistSqr);
-}
-
-
-Foam::pointIndexHit Foam::triSurfaceMesh::findNearestOnEdge
-(
-    const point& sample,
-    const scalar nearestDistSqr
-) const
-{
-    return edgeTree().findNearest(sample, nearestDistSqr);
-}
-
-
-Foam::pointIndexHit Foam::triSurfaceMesh::findNearest
-(
-    const linePointRef& ln,
-    treeBoundBox& tightest,
-    point& linePoint
-) const
-{
-    return tree().findNearest(ln, tightest, linePoint);
-}
-
-
-Foam::pointIndexHit Foam::triSurfaceMesh::findLine
-(
-    const point& start,
-    const point& end
-) const
-{
-    return tree().findLine(start, end);
-}
-
-
-Foam::pointIndexHit Foam::triSurfaceMesh::findLineAny
-(
-    const point& start,
-    const point& end
-) const
-{
-    return tree().findLineAny(start, end);
-}
-
-
-Foam::searchableSurface::volumeType
- Foam::triSurfaceMesh::getVolumeType
-(
-    const point& pt
-) const
-{
-    // - use cached volume type per each tree node
-    // - cheat conversion since same values
-    return static_cast<volumeType>(tree().getVolumeType(pt));
-}
-
-
-// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Functions  * * * * * * * * * * * * * //
-
-
-// * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.C b/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.C
deleted file mode 100644
index e96c61885437063e78de33697f102120c08673fc..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.C
+++ /dev/null
@@ -1,916 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-\*----------------------------------------------------------------------------*/
-
-#include "triSurfaceMeshes.H"
-#include "Random.H"
-#include "Time.H"
-#include "SortableList.H"
-#include "IOmanip.H"
-#include "plane.H"
-#include "SortableList.H"
-#include "triSurfaceTools.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-defineTypeNameAndDebug(triSurfaceMeshes, 0);
-
-}
-
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-// Calculate sum of distance to surfaces.
-Foam::scalar Foam::triSurfaceMeshes::sumDistSqr
-(
-    const labelList& surfacesToTest,
-    const scalar initDistSqr,
-    const point& pt
-) const
-{
-    scalar sum = 0;
-
-    forAll(surfacesToTest, i)
-    {
-        label surfI = surfacesToTest[i];
-
-        pointIndexHit hit(operator[](surfI).findNearest(pt, initDistSqr));
-
-        // Note: make it fall over if not hit.
-        sum += magSqr(hit.hitPoint()-pt);
-    }
-    return sum;
-}
-
-
-// Reflects the point furthest away around the triangle centre by a factor fac.
-// (triangle centre is the average of all points but the ihi. pSum is running
-//  sum of all points)
-Foam::scalar Foam::triSurfaceMeshes::tryMorphTet
-(
-    const labelList& surfacesToTest,
-    const scalar initDistSqr,
-    List<vector>& p,
-    List<scalar>& y,
-    vector& pSum,
-    const label ihi,
-    const scalar fac
-) const
-{
-    scalar fac1 = (1.0-fac)/vector::nComponents;
-    scalar fac2 = fac1-fac;
-
-    vector ptry = pSum*fac1-p[ihi]*fac2;
-
-    scalar ytry = sumDistSqr(surfacesToTest, initDistSqr, ptry);
-
-    if (ytry < y[ihi])
-    {
-        y[ihi] = ytry;
-        pSum += ptry - p[ihi];
-        p[ihi] = ptry;
-    }
-    return ytry;
-}
-
-
-bool Foam::triSurfaceMeshes::morphTet
-(
-    const labelList& surfacesToTest,
-    const scalar initDistSqr,
-    const scalar convergenceDistSqr,
-    const label maxIter,
-    List<vector>& p,
-    List<scalar>& y
-) const
-{
-    vector pSum = sum(p);
-
-    autoPtr<OFstream> str;
-    label vertI = 0;
-    if (debug)
-    {
-        Pout<< "triSurfaceMeshes::morphTet : intersection of "
-            << IndirectList<fileName>(names(), surfacesToTest)()
-            << " starting from points:" << p << endl;
-        str.reset(new OFstream("track.obj"));
-        meshTools::writeOBJ(str(), p[0]);
-        vertI++;
-    }
-
-    for (label iter = 0; iter < maxIter; iter++)
-    {
-        // Get the indices of highest, second-highest and lowest values.
-        label ihi, inhi, ilo;
-        {
-            SortableList<scalar> sortedY(y);
-            ilo = sortedY.indices()[0];
-            ihi = sortedY.indices()[sortedY.size()-1];
-            inhi = sortedY.indices()[sortedY.size()-2];
-        }
-
-        if (debug)
-        {
-            Pout<< "Iteration:" << iter
-                << " lowest:" << y[ilo] << " highest:" << y[ihi]
-                << " points:" << p << endl;
-
-            meshTools::writeOBJ(str(), p[ilo]);
-            vertI++;
-            str()<< "l " << vertI-1 << ' ' << vertI << nl;
-        }
-
-        if (y[ihi] < convergenceDistSqr)
-        {
-            // Get point on 0th surface.
-            Swap(p[0], p[ilo]);
-            Swap(y[0], y[ilo]);
-            return true;
-        }
-
-        // Reflection: point furthest away gets reflected.
-        scalar ytry = tryMorphTet
-        (
-            surfacesToTest,
-            10*y[ihi],             // search box.
-            p,
-            y,
-            pSum,
-            ihi,
-            -1.0
-        );
-
-        if (ytry <= y[ilo])
-        {
-            // If in right direction (y lower) expand by two.
-            ytry = tryMorphTet(surfacesToTest, 10*y[ihi], p, y, pSum, ihi, 2.0);
-        }
-        else if (ytry >= y[inhi])
-        {
-            // If inside tet try contraction.
-
-            scalar ysave = y[ihi];
-
-            ytry = tryMorphTet(surfacesToTest, 10*y[ihi], p, y, pSum, ihi, 0.5);
-
-            if (ytry >= ysave)
-            {
-                // Contract around lowest point.
-                forAll(p, i)
-                {
-                    if (i != ilo)
-                    {
-                        p[i] = 0.5*(p[i] + p[ilo]);
-                        y[i] = sumDistSqr(surfacesToTest, y[ihi], p[i]);
-                    }
-                }
-                pSum = sum(p);
-            }
-        }
-    }
-
-    if (debug)
-    {
-        meshTools::writeOBJ(str(), p[0]);
-        vertI++;
-        str()<< "l " << vertI-1 << ' ' << vertI << nl;
-    }
-
-    // Failure to converge. Return best guess so far.
-    label ilo = findMin(y);
-    Swap(p[0], p[ilo]);
-    Swap(y[0], y[ilo]);
-    return false;
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-// Construct from components
-Foam::triSurfaceMeshes::triSurfaceMeshes
-(
-    const IOobject& io,
-    const fileNameList& names
-)
-:
-    PtrList<triSurfaceMesh>(names.size()),
-    allSurfaces_(identity(names.size()))
-{
-    forAll(names, i)
-    {
-        autoPtr<IOobject> surfaceIO = io.clone();
-        surfaceIO().rename(names[i]);
-
-        Info<< "Loading surface " << surfaceIO().name() << endl;
-
-        //fileName fullPath = surfaceIO().filePath();
-        //
-        //if (fullPath.size() == 0)
-        //{
-        //    FatalErrorIn
-        //    (
-        //        "triSurfaceMeshes::triSurfaceMeshes"
-        //        "(const IOobject&, const fileNameList&)"
-        //    )   << "Cannot load surface " << surfaceIO().name()
-        //        << " starting from path " << surfaceIO().path()
-        //        << exit(FatalError);
-        //}
-
-        set(i, new triSurfaceMesh(surfaceIO()));
-
-        if (Pstream::master())
-        {
-            string oldPrefix(Pout.prefix());
-            Pout.prefix() += "    ";
-            operator[](i).writeStats(Pout);
-            Pout.prefix() = oldPrefix;
-        }
-    }
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileNameList Foam::triSurfaceMeshes::allNames(const IOobject& io)
-{
-    return readDir(io.path(), fileName::FILE);
-}
-
-
-Foam::fileNameList Foam::triSurfaceMeshes::names() const
-{
-    fileNameList surfNames(size());
-
-    forAll(surfNames, surfI)
-    {
-        surfNames[surfI] = operator[](surfI).IOobject::name();
-    }
-    return surfNames;
-}
-
-
-// Find any intersection
-Foam::label Foam::triSurfaceMeshes::findAnyIntersection
-(
-    const labelList& surfaces,
-    const point& start,
-    const point& end,
-    pointIndexHit& hitInfo
-) const
-{
-    forAll(surfaces, i)
-    {
-        label surfI = surfaces[i];
-
-        hitInfo = operator[](surfI).findLineAny(start, end);
-
-        if (hitInfo.hit())
-        {
-            return surfI;
-        }
-    }
-    return -1;
-}
-
-
-Foam::label Foam::triSurfaceMeshes::findAnyIntersection
-(
-    const point& start,
-    const point& end,
-    pointIndexHit& hitInfo
-) const
-{
-    return findAnyIntersection
-    (
-        allSurfaces_,
-        start,
-        end,
-        hitInfo
-    );
-}
-
-
-// Find intersections of edge nearest to both endpoints.
-void Foam::triSurfaceMeshes::findAllIntersections
-(
-    const labelList& surfaces,
-    const point& start,
-    const point& end,
-
-    labelList& surfacesIndex,
-    List<pointIndexHit>& surfaceHitInfo
-) const
-{
-    DynamicList<label> hitSurfaces(surfaces.size());
-    DynamicList<pointIndexHit> hitInfos(surfaces.size());
-    DynamicList<scalar> hitDistSqr(surfaces.size());
-
-    const vector dirVec(end-start);
-    const scalar magSqrDirVec(magSqr(dirVec));
-    const vector smallVec(Foam::sqrt(SMALL)*dirVec);
-
-    forAll(surfaces, i)
-    {
-        label surfI = surfaces[i];
-
-        // Current starting point of ray.
-        point pt = start;
-
-        while (true)
-        {
-            // See if any intersection between pt and end
-            pointIndexHit inter = operator[](surfI).findLine(pt, end);
-
-            if (!inter.hit())
-            {
-                break;
-            }
-            hitSurfaces.append(surfI);
-            hitInfos.append(inter);
-            hitDistSqr.append(magSqr(inter.hitPoint() - start));
-
-            pt = inter.hitPoint() + smallVec;
-
-            if (((pt-start)&dirVec) > magSqrDirVec)
-            {
-                // Adding smallVec has taken us beyond end
-                break;
-            }
-        }
-    }
-    surfacesIndex.setSize(hitSurfaces.size());
-    surfaceHitInfo.setSize(hitSurfaces.size());
-
-    if (hitSurfaces.size() > 0)
-    {
-        // Sort and transfer to arguments
-
-        hitSurfaces.shrink();
-        hitInfos.shrink();
-        hitDistSqr.shrink();
-
-        // Sort from start to end.
-        SortableList<scalar> sortedDist(hitDistSqr);
-
-        forAll(sortedDist.indices(), newPos)
-        {
-            label oldPos = sortedDist.indices()[newPos];
-            surfacesIndex[newPos] = hitSurfaces[oldPos];
-            surfaceHitInfo[newPos] = hitInfos[oldPos];
-        }
-    }
-}
-
-
-void Foam::triSurfaceMeshes::findAllIntersections
-(
-    const point& start,
-    const point& end,
-
-    labelList& surfacesIndex,
-    List<pointIndexHit>& surfaceHitInfo
-) const
-{
-    findAllIntersections
-    (
-        allSurfaces_,
-        start,
-        end,
-        surfacesIndex,
-        surfaceHitInfo
-    );
-}
-
-
-// Find intersections of edge nearest to both endpoints.
-void Foam::triSurfaceMeshes::findNearestIntersection
-(
-    const labelList& surfaces,
-    const point& start,
-    const point& end,
-
-    label& surface1,
-    pointIndexHit& hit1,
-    label& surface2,
-    pointIndexHit& hit2
-) const
-{
-    surface1 = -1;
-    // Initialize to endpoint
-    hit1 = pointIndexHit(false, end, -1);
-
-    forAll(surfaces, i)
-    {
-        label surfI = surfaces[i];
-
-        if (hit1.rawPoint() == start)
-        {
-            break;
-        }
-
-        // See if any intersection between start and current nearest
-        pointIndexHit inter = operator[](surfI).findLine
-        (
-            start,
-            hit1.rawPoint()
-        );
-
-        if (inter.hit())
-        {
-            hit1 = inter;
-            surface1 = surfI;
-        }
-    }
-
-
-    // Find the nearest intersection from end to start. Note that we initialize
-    // to the first intersection (if any).
-    surface2 = surface1;
-    hit2 = pointIndexHit(hit1);
-
-    if (hit1.hit())
-    {
-        // Test from the end side.
-        forAll(surfaces, i)
-        {
-            label surfI = surfaces[i];
-
-            if (hit2.rawPoint() == end)
-            {
-                break;
-            }
-
-            // See if any intersection between end and current nearest
-            pointIndexHit inter = operator[](surfI).findLine
-            (
-                end,
-                hit2.rawPoint()
-            );
-
-            if (inter.hit())
-            {
-                hit2 = inter;
-                surface2 = surfI;
-            }
-        }
-    }
-}
-
-
-void Foam::triSurfaceMeshes::findNearestIntersection
-(
-    const point& start,
-    const point& end,
-
-    label& surface1,
-    pointIndexHit& hit1,
-    label& surface2,
-    pointIndexHit& hit2
-) const
-{
-    findNearestIntersection
-    (
-        allSurfaces_,
-        start,
-        end,
-        surface1,
-        hit1,
-        surface2,
-        hit2
-    );
-}
-
-
-// Find nearest. Return -1 or nearest point
-Foam::label Foam::triSurfaceMeshes::findNearest
-(
-    const labelList& surfaces,
-    const point& pt,
-    const scalar nearestDistSqr,
-    pointIndexHit& nearestHit
-) const
-{
-    // nearest surface
-    label minSurface = -1;
-    scalar minDistSqr = Foam::sqr(GREAT);
-
-    forAll(surfaces, i)
-    {
-        label surfI = surfaces[i];
-
-        pointIndexHit hit
-        (
-            operator[](surfI).findNearest(pt, nearestDistSqr)
-        );
-
-        if (hit.hit())
-        {
-            scalar distSqr = magSqr(hit.hitPoint()-pt);
-
-            if (distSqr < minDistSqr)
-            {
-                minDistSqr = distSqr;
-                minSurface = surfI;
-                nearestHit = hit;
-            }
-        }
-    }
-
-    if (minSurface == -1)
-    {
-        // maxLevel unchanged. No interesting surface hit.
-        nearestHit.setMiss();
-    }
-
-    return minSurface;
-}
-
-
-// Find nearest. Return -1 or nearest point
-Foam::label Foam::triSurfaceMeshes::findNearest
-(
-    const point& pt,
-    const scalar nearestDistSqr,
-    pointIndexHit& nearestHit
-) const
-{
-    return findNearest
-    (
-        allSurfaces_,
-        pt,
-        nearestDistSqr,
-        nearestHit
-    );
-}
-
-
-Foam::label Foam::triSurfaceMeshes::findNearestAndClassify
-(
-    const labelList& surfacesToTest,
-    const point& pt,
-    const scalar nearestDistSqr,
-    surfaceLocation& nearest
-) const
-{
-    pointIndexHit nearestInfo;
-    label surfI = findNearest
-    (
-        surfacesToTest,
-        pt,
-        nearestDistSqr,
-        nearestInfo
-    );
-
-    if (surfI != -1)
-    {
-        nearest = triSurfaceTools::classify
-        (
-            operator[](surfI),
-            nearestInfo.index(),        // triangle
-            nearestInfo.rawPoint()      // point on edge/inside triangle
-        );
-    }
-
-    return surfI;
-}
-
-
-//- Calculate point which is on a set of surfaces.
-Foam::surfaceLocation Foam::triSurfaceMeshes::facesIntersectionTrack
-(
-    const labelList& surfacesToTest,
-    const scalar initialDistSqr,
-    const scalar convergenceDistSqr,
-    const point& start
-) const
-{
-    autoPtr<OFstream> str;
-    label vertI = 0;
-
-    if (debug)
-    {
-        str.reset(new OFstream("track.obj"));
-    }
-
-    surfaceLocation current
-    (
-        pointIndexHit
-        (
-            false,
-            start,
-            -1
-        ),
-        triPointRef::NONE,
-        -1
-    );
-
-    // Dump start point
-    if (str.valid())
-    {
-        Pout<< "Starting at " << current.info()
-            << " written as point " << vertI
-            << endl;
-        meshTools::writeOBJ(str(), current.rawPoint());
-        vertI++;
-    }
-
-    // Now slide current along all faces until it settles
-    label iter = 0;
-
-    const label maxIter = 10;
-
-    for (; iter < maxIter; iter++)
-    {
-        // - store old position
-        // - slide it along all faces
-        // - if it hasn't changed position exit loop
-
-        if (debug)
-        {
-            Pout<< endl
-                << "Iteration " << iter << endl
-                << "-----------" << endl;
-        }
-
-        point oldCurrent = current.rawPoint();
-
-        forAll(surfacesToTest, i)
-        {
-            label surfI = surfacesToTest[i];
-
-            // Project pt onto surf
-            // ~~~~~~~~~~~~~~~~~~~~
-            point copy(current.rawPoint()); // need to work on copy
-            if
-            (
-                findNearestAndClassify
-                (
-                    labelList(1, surfI),
-                    copy,
-                    initialDistSqr,             // initialDistSqr
-                    current
-                )
-             == -1
-            )
-            {
-                FatalErrorIn("triSurfaceMeshes::facesIntersectionTrack(..)")
-                    << "Did not find a point on surface " << surfI
-                    << " which is within sqrt(" << initialDistSqr
-                    << ") of " << copy
-                    << abort(FatalError);
-            }
-
-
-            if (debug)
-            {
-                Pout<< "Nearest onto surface " << surfI
-                    << ' ' << operator[](surfI).IOobject::name() << " : "
-                    << current.info() << " written as point " << vertI
-                    << endl;
-            }
-
-            // Dump current
-            if (str.valid())
-            {
-                meshTools::writeOBJ(str(), current.rawPoint());
-                vertI++;
-                str()<< "l " << vertI-1 << ' ' << vertI << nl;
-            }
-
-            // Find corresponding point on next surface
-            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-            label nextSurfI = surfacesToTest[surfacesToTest.fcIndex(i)];
-
-            surfaceLocation next;
-            findNearestAndClassify
-            (
-                labelList(1, nextSurfI),
-                point(current.rawPoint()),
-                initialDistSqr,                 // initialDistSqr
-                next
-            );
-
-            // Since next is on a different surface we cannot compare
-            // indices (like in snapToEnd) so make sure this does not
-            // happen.
-            next.elementType() = triPointRef::NONE;
-            next.setIndex(-123);
-
-            if (debug)
-            {
-                Pout<< "Nearest onto next surface " << nextSurfI
-                    << ' ' << operator[](nextSurfI).IOobject::name() << " : "
-                    << next.info() << endl;
-            }
-
-            if
-            (
-                magSqr(current.rawPoint() - next.rawPoint())
-              > convergenceDistSqr
-            )
-            {
-                // Track from current to next
-                // ~~~~~~~~~~~~~~~~~~~~~~~~~~
-                //vector n = current.normal(surfaces[surfI]);
-
-                current = triSurfaceTools::trackToEdge
-                (
-                    operator[](surfI),
-                    current,
-                    next,
-                    plane(start, current.rawPoint(), next.rawPoint())
-                );
-
-                if (debug)
-                {
-                    Pout<< "Sliding along surface "
-                        << surfI << ' ' << operator[](surfI).IOobject::name()
-                        << " in direction of "
-                        << nextSurfI << ' '
-                        << operator[](nextSurfI).IOobject::name()
-                        << " stopped at " << current.info()
-                        << " written as point " << vertI << endl;
-                }
-                if (str.valid())
-                {
-                    meshTools::writeOBJ(str(), current.rawPoint());
-                    vertI++;
-                    str()<< "l " << vertI-1 << ' ' << vertI << nl;
-                }
-            }
-        }
-
-        scalar distSqr = magSqr(oldCurrent - current.rawPoint());
-
-        if (debug)
-        {
-            Pout<< "distSqr:" << distSqr
-                << " convergenceDistSqr:" << convergenceDistSqr << endl;
-        }
-
-        if (distSqr < convergenceDistSqr)
-        {
-            break;
-        }
-    }
-
-    if (iter == maxIter)
-    {
-        FatalErrorIn("triSurfaceMeshes::facesIntersectionTrack(..)")
-            << "Did not converge in " << iter
-            << " iterations to find a point which is on surfaces "
-            << IndirectList<fileName>(names(), surfacesToTest)() << nl
-            << "Start point:" << start << nl
-            << "Current nearest:" << current.info() << nl
-            << "Please check that your surfaces actually intersect and"
-            << " that the starting point is close to the intersection point."
-            << abort(FatalError);
-    }
-
-    return current;
-}
-
-
-//- Calculate point which is on a set of surfaces.
-Foam::pointIndexHit Foam::triSurfaceMeshes::facesIntersection
-(
-    const labelList& surfacesToTest,
-    const scalar initDistSqr,
-    const scalar convergenceDistSqr,
-    const point& start
-) const
-{
-    // Get four starting points. Take these as the projection of the
-    // starting point onto the surfaces and the mid point
-    List<point> nearest(surfacesToTest.size()+1);
-
-    point sumNearest = vector::zero;
-
-    forAll(surfacesToTest, i)
-    {
-        label surfI = surfacesToTest[i];
-
-        pointIndexHit hit
-        (
-            operator[](surfI).findNearest(start, initDistSqr)
-        );
-
-        if (hit.hit())
-        {
-            nearest[i] = hit.hitPoint();
-            sumNearest += nearest[i];
-        }
-        else
-        {
-            FatalErrorIn
-            (
-                "triSurfaceMeshes::facesIntersection"
-                "(const labelList&, const scalar, const scalar, const point&)"
-            )   << "Did not find point within distance "
-                << initDistSqr << " of starting point " << start
-                << " on surface " << operator[](surfI).IOobject::name()
-                << abort(FatalError);
-        }
-    }
-
-    nearest[nearest.size()-1] = sumNearest / surfacesToTest.size();
-
-
-    // Get the sum of distances (initial evaluation)
-    List<scalar> nearestDist(nearest.size());
-
-    forAll(nearestDist, i)
-    {
-        nearestDist[i] = sumDistSqr(surfacesToTest, initDistSqr, nearest[i]);
-    }
-
-
-    //- Downhill Simplex method
-
-    bool converged = morphTet
-    (
-        surfacesToTest,
-        initDistSqr,
-        convergenceDistSqr,
-        2000,
-        nearest,
-        nearestDist
-    );
-
-
-    pointIndexHit intersection;
-
-    if (converged)
-    {
-        // Project nearest onto 0th surface.
-        intersection = operator[](surfacesToTest[0]).findNearest
-        (
-            nearest[0],
-            nearestDist[0]
-        );
-    }
-
-    //if (!intersection.hit())
-    //{
-    //    // Restart
-    //    scalar smallDist = Foam::sqr(convergenceDistSqr);
-    //    nearest[0] = intersection.hitPoint();
-    //    nearest[1] = nearest[0];
-    //    nearest[1].x() += smallDist;
-    //    nearest[2] = nearest[0];
-    //    nearest[2].y() += smallDist;
-    //    nearest[3] = nearest[0];
-    //    nearest[3].z() += smallDist;
-    //
-    //    forAll(nearestDist, i)
-    //    {
-    //        nearestDist[i] = sumDistSqr
-    //        (
-    //            surfacesToTest,
-    //            initDistSqr,
-    //            nearest[i]
-    //        );
-    //    }
-    //
-    //    intersection = morphTet
-    //    (
-    //        surfacesToTest,
-    //        initDistSqr,
-    //        convergenceDistSqr,
-    //        1000,
-    //        nearest,
-    //        nearestDist
-    //    );
-    //}
-
-    return intersection;
-}
-
-
-
-// ************************************************************************* //
diff --git a/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.H b/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.H
deleted file mode 100644
index 4269dba6a7bb4593adb7bc39d009e7d4fde7a38b..0000000000000000000000000000000000000000
--- a/src/meshTools/triSurface/triSurfaceMeshes/triSurfaceMeshes.H
+++ /dev/null
@@ -1,261 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2008 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 2 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, write to the Free Software Foundation,
-    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Class
-    Foam::triSurfaceMeshes
-
-Description
-    Container for triSurfaces read from files.
-
-SourceFiles
-    triSurfaceMeshes.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef triSurfaceMeshes_H
-#define triSurfaceMeshes_H
-
-#include "triSurfaceMesh.H"
-#include "fileNameList.H"
-#include "treeDataTriSurface.H"
-#include "indexedOctree.H"
-#include "surfaceLocation.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-// Forward declaration of classes
-class plane;
-
-/*---------------------------------------------------------------------------*\
-                           Class triSurfaceMeshes Declaration
-\*---------------------------------------------------------------------------*/
-
-class triSurfaceMeshes
-:
-    public PtrList<triSurfaceMesh>
-{
-    // Private data
-
-        //- Indices of all surfaces. Precalculated and stored.
-        const labelList allSurfaces_;
-
-
-    // Private Member Functions
-
-        //- Calculate sum of distances to nearest point on surfaces. Is used
-        //  in minimisation to find intersection. Returns sum of (square of)
-        // distances to the surfaces.
-        scalar sumDistSqr
-        (
-            const labelList& surfacesToTest,
-            const scalar initialDistSqr,    // search box
-            const point& pt
-        ) const;
-
-        //- Takes the tet (points p) and reflects the point with the
-        //  highest value around the centre (pSum). Checks if it gets closer
-        //  and updates p, y if so.
-        scalar tryMorphTet
-        (
-            const labelList& surfacesToTest,
-            const scalar initialDistSqr,
-            List<vector>& p,
-            List<scalar>& y,
-            vector& pSum,
-            const label ihi,
-            const scalar fac
-        ) const;
-
-        //- Downhill simplex method: find the point with min cumulative
-        //  distance to all surfaces. Does so by morphing a tet (points p).
-        //  Returns the point on the 0th surface or hit if not reached within
-        //  maxIters iterations.
-        bool morphTet
-        (
-            const labelList& surfacesToTest,
-            const scalar initialDistSqr,
-            const scalar convergenceDistSqr,
-            const label maxIter,
-            List<vector>& p,
-            List<scalar>& y
-        ) const;
-
-        //- Disallow default bitwise copy construct
-        triSurfaceMeshes(const triSurfaceMeshes&);
-
-        //- Disallow default bitwise assignment
-        void operator=(const triSurfaceMeshes&);
-
-
-public:
-
-    ClassName("triSurfaceMeshes");
-
-    // Constructors
-
-        //- Construct from directory (io.instance()) and list of local filenames
-        triSurfaceMeshes(const IOobject& io, const fileNameList&);
-
-
-    // Member Functions
-
-        //- Get all surfaces in directory
-        static fileNameList allNames(const IOobject&);
-
-        //- Get names of surfaces
-        fileNameList names() const;
-
-
-        //- Find any intersection. Return hit point information and surface
-        //  number
-        label findAnyIntersection
-        (
-            const labelList& surfacesToTest,
-            const point& start,
-            const point& end,
-            pointIndexHit&
-        ) const;
-
-        label findAnyIntersection
-        (
-            const point& start,
-            const point& end,
-            pointIndexHit&
-        ) const;
-
-
-        //- Find all intersections in order from start to end. Returns for
-        //  every hit the surface and the hit info.
-        void findAllIntersections
-        (
-            const labelList& surfacesToTest,
-            const point& start,
-            const point& end,
-            labelList& surfaces,
-            List<pointIndexHit>& surfaceHits
-        ) const;
-
-        void findAllIntersections
-        (
-            const point& start,
-            const point& end,
-            labelList& surfaces,
-            List<pointIndexHit>& surfaceHits
-        ) const;
-
-
-        //- Find intersections of edge nearest to both endpoints.
-        void findNearestIntersection
-        (
-            const labelList& surfacesToTest,
-            const point& start,
-            const point& end,
-
-            label& surface1,        // surface index
-            pointIndexHit& hit1,    // hit point information
-            label& surface2,
-            pointIndexHit& hit2
-        ) const;
-
-        void findNearestIntersection
-        (
-            const point& start,
-            const point& end,
-
-            label& surface1,        // surface index
-            pointIndexHit& hit1,    // hit point information
-            label& surface2,
-            pointIndexHit& hit2
-        ) const;
-
-
-        //- Find nearest. Return -1 (and a miss()) or surface and nearest point.
-        label findNearest
-        (
-            const labelList& surfacesToTest,
-            const point&,
-            const scalar nearestDistSqr,
-            pointIndexHit&
-        ) const;
-
-        label findNearest
-        (
-            const point&,
-            const scalar nearestDistSqr,
-            pointIndexHit&
-        ) const;
-
-
-        //- Find nearest point (like findNearest) but also return whether
-        //  point is on edge or on point. Returns -1 if not found
-        //  (within nearestDistSqr) on any of the surfaces.
-        //  Returns surfaceLocation
-        //  - hit : nearest point is inside triangle
-        //  - elementType : none, edge  or point
-        //  - index       : triI, edgeI or localPointI
-        label findNearestAndClassify
-        (
-            const labelList& surfacesToTest,
-            const point& pt,
-            const scalar nearestDistSqr,
-            surfaceLocation& nearest
-        ) const;
-
-        //- Calculate point which is on a set of surfaces. Takes
-        //  - initialDistSqr : search dimensions to find point on surface
-        //  - convergenceDistSqr : when point is converged
-        // Will bomb out if no stable point reached after certain number
-        // of iterations.
-        surfaceLocation facesIntersectionTrack
-        (
-            const labelList& surfacesToTest,
-            const scalar initialDistSqr,
-            const scalar convergenceDistSqr,
-            const point& start
-        ) const;
-
-        //- Calculate point which is on a set of surfaces.
-        pointIndexHit facesIntersection
-        (
-            const labelList& surfacesToTest,
-            const scalar initialDistSqr,
-            const scalar convergenceDistSqr,
-            const point& start
-        ) const;
-
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/tutorials/compressibleLesInterFoam/depthCharge3D/Allclean b/tutorials/compressibleLesInterFoam/depthCharge3D/Allclean
index 7a7b7f16fe19a6df1c05baa3bb818430d0240cf1..c422ce058e162967e4924ab9cd8404a7ca807133 100755
--- a/tutorials/compressibleLesInterFoam/depthCharge3D/Allclean
+++ b/tutorials/compressibleLesInterFoam/depthCharge3D/Allclean
@@ -2,4 +2,4 @@
 
 foamCleanTutorials cases
 rm -rf processor*
-rm 0/pd.gz 0/alpha1.gz
+rm -rf 0/pd.gz 0/alpha1.gz
diff --git a/tutorials/gnemdFoam/Allclean b/tutorials/gnemdFoam/Allclean
index 827eacb6ae57fce6c071229216ccde65b3bd8f04..11f901ce8e07f87b4c4e41b1be30311c5ecd5589 100755
--- a/tutorials/gnemdFoam/Allclean
+++ b/tutorials/gnemdFoam/Allclean
@@ -4,16 +4,13 @@
 . $WM_PROJECT_DIR/bin/tools/CleanFunctions
 
 cd constrictedChannel
-    rm -rf 0 > /dev/null 2>&1
-    rm Ar-Ar Ar-Ne Ne-Ne > /dev/null 2>&1
-    rm constant/idList
-
+    rm -rf 0
+    rm -rf Ar-Ar Ar-Ne Ne-Ne
+    rm -rf constant/idList
     cleanCase
 cd ..
 
 cd nanoNozzle
-    rm -rf processor[0-9] > /dev/null 2>&1
-
+    rm -rf processor[0-9]
     cleanCase
 cd ..
-
diff --git a/tutorials/mdEquilibrationFoam/Allclean b/tutorials/mdEquilibrationFoam/Allclean
index 7880b1387e0bea443782452bcd1172445f5432e1..00dd874d7638805cf31b2c56b01b659e0792e1c4 100755
--- a/tutorials/mdEquilibrationFoam/Allclean
+++ b/tutorials/mdEquilibrationFoam/Allclean
@@ -4,10 +4,8 @@
 . $WM_PROJECT_DIR/bin/tools/CleanFunctions
 
 cd periodicCube
-    rm -rf 0 > /dev/null 2>&1
-    rm Ar-Ar > /dev/null 2>&1
-    rm constant/idList
-
+    rm -rf 0
+    rm -rf Ar-Ar
+    rm -rf constant/idList
     cleanCase
 cd ..
-
diff --git a/tutorials/rhoCentralFoam/Allclean b/tutorials/rhoCentralFoam/Allclean
index cbb826cb1d964f21068e9c44b62bdae567243c5b..c783ce638816a8524a36cdb294a5bc8390b786ee 100755
--- a/tutorials/rhoCentralFoam/Allclean
+++ b/tutorials/rhoCentralFoam/Allclean
@@ -24,7 +24,7 @@ do
 
     if [ "$case" = "biconic25-55Run35" ]
     then
-        rm $case/constant/polyMesh/boundary
+        rm -rf $case/constant/polyMesh/boundary
         wclean $case/datToFoam
     fi
 done