diff --git a/applications/utilities/mesh/generation/cvMesh/Make/options b/applications/utilities/mesh/generation/cvMesh/Make/options
index a0a776d977b1f754a01560aa9e671c9b108dd7c7..5efdb125725138a1df13a4eae74625e3a0fc8cc0 100644
--- a/applications/utilities/mesh/generation/cvMesh/Make/options
+++ b/applications/utilities/mesh/generation/cvMesh/Make/options
@@ -20,7 +20,8 @@ EXE_LIBS = \
     $(CGAL_LIBS) \
     -lconformalVoronoiMesh \
     -lmeshTools \
-    -ldecompositionMethods -L$(FOAM_LIBBIN)/dummy -lscotchDecomp \
+    -ldecompositionMethods \
+    -L$(FOAM_LIBBIN)/dummy -lptscotchDecomp \
     -ledgeMesh \
     -ltriSurface \
     -ldynamicMesh
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/Make/options b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/Make/options
index 1a1902b559ddda2a3ff6c37acca1316f89b6b7ea..2d2d1a1178abede674023f52331496cd82e291a6 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/Make/options
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/Make/options
@@ -19,7 +19,6 @@ EXE_INC = \
 
 EXE_LIBS = \
     -lmeshTools \
-    -ldecompositionMethods -L$(FOAM_LIBBIN)/dummy -lscotchDecomp \
     -ledgeMesh \
     -ltriSurface \
     -ldynamicMesh
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/cellSizeCalculationType/fieldFromFile/fieldFromFile.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/cellSizeCalculationType/fieldFromFile/fieldFromFile.C
index d40427abf7e5fa9bb6f60945b9b66a57a13f3133..788c1d69c6852f1c51e71143c59f5cb1b07f7303 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/cellSizeCalculationType/fieldFromFile/fieldFromFile.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/cellSizeCalculationType/fieldFromFile/fieldFromFile.C
@@ -63,7 +63,7 @@ Foam::fieldFromFile::fieldFromFile
 
 Foam::triSurfaceScalarField Foam::fieldFromFile::load()
 {
-    Info<< "Loading: " << fileName_ << endl;
+    Info<< indent << "Loading: " << fileName_ << endl;
 
     triSurfaceScalarField surfaceCellSize
     (
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/nonUniformField/nonUniformField.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/nonUniformField/nonUniformField.C
index 5e2d032d91e1c66979f1c646bc836845620dd4d7..fc91ed279b04a7979eface6cf063fec1ab5b36c4 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/nonUniformField/nonUniformField.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/nonUniformField/nonUniformField.C
@@ -82,9 +82,9 @@ Foam::nonUniformField::nonUniformField
     Info<< decrIndent;
 
     Info<< indent << "Cell size field statistics:" << nl
-        << indent << "   Minimum: " << min(surfaceCellSize_).value() << nl
-        << indent << "   Average: " << average(surfaceCellSize_).value() << nl
-        << indent << "   Maximum: " << max(surfaceCellSize_).value() << endl;
+        << indent << "    Minimum: " << min(surfaceCellSize_).value() << nl
+        << indent << "    Average: " << average(surfaceCellSize_).value() << nl
+        << indent << "    Maximum: " << max(surfaceCellSize_).value() << endl;
 
     Info<< decrIndent;
 }
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/surfaceCellSizeFunction/surfaceCellSizeFunction.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/surfaceCellSizeFunction/surfaceCellSizeFunction.C
index 7022511c4c2dbdf594ecc6666cf4c6fa5712a833..04d9af824f0980840a2e1846f9144efb404b891f 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/surfaceCellSizeFunction/surfaceCellSizeFunction.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cellSizeControlSurfaces/surfaceCellSizeFunction/surfaceCellSizeFunction/surfaceCellSizeFunction.C
@@ -46,7 +46,10 @@ Foam::surfaceCellSizeFunction::surfaceCellSizeFunction
     dictionary(surfaceCellSizeFunctionDict),
     surface_(surface),
     coeffsDict_(subDict(type + "Coeffs")),
-    refinementFactor_(readScalar(lookup("refinementFactor")))
+    refinementFactor_
+    (
+        lookupOrDefault<scalar>("refinementFactor", 1.0)
+    )
 {}
 
 
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
index 09d27dfa3620f9e110a7704e2a56d06ae88c6069..de23690ea43b26ca788d49724cb42e2d17a6e192 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
@@ -44,6 +44,142 @@ const Foam::scalar Foam::conformalVoronoiMesh::tolParallel = 1e-3;
 
 // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
 
+Foam::scalar Foam::conformalVoronoiMesh::requiredSize
+(
+    const Foam::point& pt
+) const
+{
+    pointIndexHit surfHit;
+    label hitSurface;
+
+    DynamicList<scalar> cellSizeHits;
+
+    geometryToConformTo_.findSurfaceNearest
+    (
+        pt,
+        sqr(GREAT),
+        surfHit,
+        hitSurface
+    );
+
+    if (!surfHit.hit())
+    {
+        FatalErrorIn
+        (
+            "Foam::tensor Foam::conformalVoronoiMesh::requiredAlignment"
+        )
+            << "findSurfaceNearest did not find a hit across the surfaces."
+            << exit(FatalError) << endl;
+    }
+
+    cellSizeHits.append(cellSizeControl().cellSize(pt));
+
+    // Primary alignment
+
+    vectorField norm(1);
+
+    allGeometry_[hitSurface].getNormal
+    (
+        List<pointIndexHit>(1, surfHit),
+        norm
+    );
+
+    const vector np = norm[0];
+
+    // Generate equally spaced 'spokes' in a circle normal to the
+    // direction from the vertex to the closest point on the surface
+    // and look for a secondary intersection.
+
+    const vector d = surfHit.hitPoint() - pt;
+
+    const tensor Rp = rotationTensor(vector(0,0,1), np);
+
+    const label s = cvMeshControls().alignmentSearchSpokes();
+
+    const scalar spanMag = geometryToConformTo_.globalBounds().mag();
+
+    scalar totalDist = 0;
+
+    for (label i = 0; i < s; i++)
+    {
+        vector spoke
+        (
+            Foam::cos(i*constant::mathematical::twoPi/s),
+            Foam::sin(i*constant::mathematical::twoPi/s),
+            0
+        );
+
+        spoke *= spanMag;
+
+        spoke = Rp & spoke;
+
+        pointIndexHit spokeHit;
+
+        label spokeSurface = -1;
+
+        // internal spoke
+
+        geometryToConformTo_.findSurfaceNearestIntersection
+        (
+            pt,
+            pt + spoke,
+            spokeHit,
+            spokeSurface
+        );
+
+        if (spokeHit.hit())
+        {
+            const Foam::point& hitPt = spokeHit.hitPoint();
+
+            scalar spokeHitDistance = mag(hitPt - pt);
+
+            cellSizeHits.append
+            (
+                cellSizeControl().cellSize(hitPt)
+            );
+
+            totalDist += spokeHitDistance;
+        }
+
+        //external spoke
+
+        Foam::point mirrorPt = pt + 2*d;
+
+        geometryToConformTo_.findSurfaceNearestIntersection
+        (
+            mirrorPt,
+            mirrorPt + spoke,
+            spokeHit,
+            spokeSurface
+        );
+
+        if (spokeHit.hit())
+        {
+            const Foam::point& hitPt = spokeHit.hitPoint();
+
+            scalar spokeHitDistance = mag(hitPt - mirrorPt);
+
+            cellSizeHits.append
+            (
+                cellSizeControl().cellSize(hitPt)
+            );
+
+            totalDist += spokeHitDistance;
+        }
+    }
+
+    scalar cellSize = 0;
+
+    forAll(cellSizeHits, hitI)
+    {
+        cellSize += cellSizeHits[hitI];
+    }
+
+    return cellSize/cellSizeHits.size();
+    //return cellSizeControl().cellSize(pt);
+}
+
+
 Foam::tensor Foam::conformalVoronoiMesh::requiredAlignment
 (
     const Foam::point& pt
@@ -828,10 +964,7 @@ void Foam::conformalVoronoiMesh::storeSizesAndAlignments
     {
         sizeAndAlignmentLocations_[i] = topoint(*pit);
 
-        storedSizes_[i] = cellSizeControl().cellSize
-        (
-            sizeAndAlignmentLocations_[i]
-        );
+        storedSizes_[i] = requiredSize(sizeAndAlignmentLocations_[i]);
 
         storedAlignments_[i] = requiredAlignment(sizeAndAlignmentLocations_[i]);
 
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
index 81ce496c7173d96a4ef4f021d9d4a80d440603db..f9648dc1e7749c6cd78f444c5a872d8aefbc6fe2 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
@@ -268,6 +268,9 @@ private:
         //- Return the local maximum surface protrusion distance
         inline scalar maxSurfaceProtrusion(const Foam::point& pt) const;
 
+        //- Return the required cell size at the given location
+        scalar requiredSize(const Foam::point& pt) const;
+
         //- Return the required alignment directions at the given location
         tensor requiredAlignment(const Foam::point& pt) const;
 
@@ -533,6 +536,11 @@ private:
             const Delaunay::Finite_edges_iterator& eit
         ) const;
 
+        boolList dualFaceBoundaryPoints
+        (
+            const Delaunay::Finite_edges_iterator& eit
+        ) const;
+
         //- Finds the maximum filterCount of the dual vertices
         //  (Delaunay cells) that form the dual face produced by the
         //  supplied edge
@@ -839,6 +847,13 @@ private:
             const Delaunay::Finite_facets_iterator& fit
         ) const;
 
+        //- Merge adjacent edges that are not attached to other faces
+        label mergeNearlyParallelEdges
+        (
+            const pointField& pts,
+            const scalar maxCosAngle
+        );
+
         //- Merge vertices that are very close together
         void mergeCloseDualVertices
         (
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
index 5586446cb8a17934c284d07a017d50ff32565908..e0fe697e88e437c90713bb15ba378c9b5d1ca30d 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
@@ -212,6 +212,20 @@ void Foam::conformalVoronoiMesh::calcDualMesh
                     Info<< nl << "Collapsing unnecessary faces" << endl;
 
                     collapseFaces(points, boundaryPts, deferredCollapseFaces);
+
+                    const scalar maxCosAngle
+                        = cos(degToRad(cvMeshControls().edgeMergeAngle()));
+
+                    Info<< nl << "Merging adjacent edges which have an angle "
+                        << "of greater than "
+                        << cvMeshControls().edgeMergeAngle() << ": " << endl;
+
+                    label nRemovedEdges =
+                        mergeNearlyParallelEdges(points, maxCosAngle);
+
+                    reduce(nRemovedEdges, sumOp<label>());
+
+                    Info<< "    Merged " << nRemovedEdges << " edges" << endl;
                 }
 
                 labelHashSet wrongFaces = checkPolyMeshQuality(points);
@@ -652,6 +666,113 @@ Foam::label Foam::conformalVoronoiMesh::mergeCloseDualVertices
 }
 
 
+Foam::label Foam::conformalVoronoiMesh::mergeNearlyParallelEdges
+(
+    const pointField& pts,
+    const scalar maxCosAngle
+)
+{
+    List<HashSet<label> > pointFaceCount(number_of_cells());
+    labelList pointNeighbour(number_of_cells(), -1);
+
+    Map<label> dualPtIndexMap;
+
+    for
+    (
+        Delaunay::Finite_edges_iterator eit = finite_edges_begin();
+        eit != finite_edges_end();
+        ++eit
+    )
+    {
+        Cell_handle c = eit->first;
+        Vertex_handle vA = c->vertex(eit->second);
+        Vertex_handle vB = c->vertex(eit->third);
+
+        if (isBoundaryDualFace(eit))
+        {
+            const face f = buildDualFace(eit);
+
+            forAll(f, pI)
+            {
+                const label pIndex = f[pI];
+
+                const label prevPointI = f.prevLabel(pI);
+                const label nextPointI = f.nextLabel(pI);
+
+                pointFaceCount[pIndex].insert(prevPointI);
+                pointFaceCount[pIndex].insert(nextPointI);
+                pointNeighbour[pIndex] = nextPointI;
+            }
+        }
+        else if
+        (
+            vA->internalOrBoundaryPoint()
+         || vB->internalOrBoundaryPoint()
+        )
+        {
+            const face f = buildDualFace(eit);
+            const boolList faceBoundaryPoints = dualFaceBoundaryPoints(eit);
+
+            forAll(f, pI)
+            {
+                const label pIndex = f[pI];
+
+                const label prevPointI = f.prevLabel(pI);
+                const label nextPointI = f.nextLabel(pI);
+
+                pointFaceCount[pIndex].insert(prevPointI);
+                pointFaceCount[pIndex].insert(nextPointI);
+
+                if (faceBoundaryPoints[pI] == false)
+                {
+                    pointNeighbour[pIndex] = nextPointI;
+                }
+                else
+                {
+                    if (faceBoundaryPoints[prevPointI] == true)
+                    {
+                        pointNeighbour[pIndex] = prevPointI;
+                    }
+                    else if (faceBoundaryPoints[nextPointI] == true)
+                    {
+                        pointNeighbour[pIndex] = nextPointI;
+                    }
+                    else
+                    {
+                        pointNeighbour[pIndex] = pIndex;
+                    }
+                }
+            }
+        }
+    }
+
+    forAll(pointFaceCount, pI)
+    {
+        if (pointFaceCount[pI].size() == 2)
+        {
+            List<vector> edges(2, vector(0, 0, 0));
+
+            label count = 0;
+            forAllConstIter(HashSet<label>, pointFaceCount[pI], iter)
+            {
+                edges[count] = pts[pI] - pts[iter.key()];
+                edges[count] /= mag(edges[count]) + VSMALL;
+                count++;
+            }
+
+            if (mag(edges[0] & edges[1]) > maxCosAngle)
+            {
+                dualPtIndexMap.insert(pI, pointNeighbour[pI]);
+            }
+        }
+    }
+
+    reindexDualVertices(dualPtIndexMap);
+
+    return dualPtIndexMap.size();
+}
+
+
 void Foam::conformalVoronoiMesh::smoothSurface
 (
     pointField& pts,
@@ -696,7 +817,6 @@ void Foam::conformalVoronoiMesh::smoothSurface
     } while (nCollapsedFaces > 0);
 
     // Force all points of boundary faces to be on the surface
-
     for
     (
         Delaunay::Finite_cells_iterator cit = finite_cells_begin();
@@ -917,6 +1037,7 @@ void Foam::conformalVoronoiMesh::collapseFaces
 
         mergeCloseDualVertices(pts, boundaryPts);
 
+
         if (nCollapsedFaces > 0)
         {
             Info<< "    Collapsed " << nCollapsedFaces << " faces" << endl;
@@ -932,6 +1053,7 @@ void Foam::conformalVoronoiMesh::collapseFaces
         }
 
     } while (nCollapsedFaces > 0);
+
 }
 
 
@@ -1064,6 +1186,25 @@ Foam::conformalVoronoiMesh::collapseFace
 
     bool allowEarlyCollapseToPoint = true;
 
+
+//    // Quick exit
+//    label smallEdges = 0;
+//    const edgeList& fEdges = f.edges();
+//    forAll(fEdges, eI)
+//    {
+//        const edge& e = fEdges[eI];
+//
+//        if (e.mag(pts) < 0.2*targetFaceSize)
+//        {
+//            smallEdges++;
+//        }
+//    }
+//    if (smallEdges == 0)
+//    {
+//        return fcmNone;
+//    }
+
+
     // if (maxFC > cvMeshControls().filterCountSkipThreshold() - 3)
     // {
     //     limitToQuadsOrTris = true;
@@ -1071,6 +1212,7 @@ Foam::conformalVoronoiMesh::collapseFace
     //     allowEarlyCollapseToPoint = false;
     // }
 
+
     collapseSizeLimitCoeff *= pow
     (
         cvMeshControls().filterErrorReductionCoeff(),
@@ -1158,6 +1300,91 @@ Foam::conformalVoronoiMesh::collapseFace
         }
     }
 
+
+    scalar maxDist = 0;
+    scalar minDist = GREAT;
+//
+//    if (f.size() <= 3)
+//    {
+//        const edgeList& fEdges = f.edges();
+//
+//        forAll(fEdges, eI)
+//        {
+//            const edge& e = fEdges[eI];
+//            const scalar d = e.mag(pts);
+//
+//            if (d > maxDist)
+//            {
+//                maxDist = d;
+//                collapseAxis = e.vec(pts);
+//            }
+//            else if (d < minDist && d != 0)
+//            {
+//                minDist = d;
+//            }
+//        }
+//    }
+//    else
+//    {
+//        forAll(f, pI)
+//        {
+//            for (label i = pI + 1; i < f.size(); ++i)
+//            {
+//                if
+//                (
+//                    f[i] != f.nextLabel(pI)
+//                 && f[i] != f.prevLabel(pI)
+//                )
+//                {
+//                    scalar d = mag(pts[f[pI]] - pts[f[i]]);
+//
+//                    if (d > maxDist)
+//                    {
+//                        maxDist = d;
+//                        collapseAxis = pts[f[pI]] - pts[f[i]];
+//                    }
+//                    else if (d < minDist && d != 0)
+//                    {
+//                        minDist = d;
+//                    }
+//                }
+//            }
+//        }
+//    }
+//
+//    const edgeList& fEdges = f.edges();
+//
+//    scalar perimeter = 0;
+//
+//    forAll(fEdges, eI)
+//    {
+//        const edge& e = fEdges[eI];
+//        const scalar d = e.mag(pts);
+//
+//        perimeter += d;
+//
+////        collapseAxis += e.vec(pts);
+//
+//        if (d > maxDist)
+//        {
+//            collapseAxis = e.vec(pts);
+//            maxDist = d;
+//        }
+//        else if (d < minDist && d != 0)
+//        {
+//            minDist = d;
+//        }
+//    }
+//
+//    collapseAxis /= mag(collapseAxis);
+//
+////    Info<< f.size() << " " << minDist << " " << maxDist << " | "
+////        << collapseAxis << endl;
+//
+//    aspectRatio = maxDist/minDist;
+//
+//    aspectRatio = min(aspectRatio, sqr(perimeter)/(16.0*fA));
+
     if (magSqr(collapseAxis) < VSMALL)
     {
         WarningIn
@@ -1170,9 +1397,9 @@ Foam::conformalVoronoiMesh::collapseFace
         // Output face and collapse axis for visualisation
 
         Pout<< "# Aspect ratio = " << aspectRatio << nl
-            << "# inertia = " << J << nl
-            << "# determinant = " << detJ << nl
-            << "# eigenvalues = " << eigenValues(J) << nl
+//            << "# inertia = " << J << nl
+//            << "# determinant = " << detJ << nl
+//            << "# eigenvalues = " << eigenValues(J) << nl
             << "# collapseAxis = " << collapseAxis << nl
             << "# facePts = " << facePts << nl
             << endl;
@@ -1280,8 +1507,6 @@ Foam::conformalVoronoiMesh::collapseFace
     {
         scalar guardFraction = cvMeshControls().edgeCollapseGuardFraction();
 
-        cvMeshControls().maxCollapseFaceToPointSideLengthCoeff();
-
         if
         (
             allowEarlyCollapseToPoint
@@ -1337,6 +1562,8 @@ Foam::conformalVoronoiMesh::collapseFace
             Foam::point collapseToPt =
                 collapseAxis*(sum(dNeg)/dNeg.size() - dShift) + fC;
 
+//            DynamicList<label> faceBoundaryPts(f.size());
+
             forAll(facePtsNeg, fPtI)
             {
                 if (boundaryPts[facePtsNeg[fPtI]] == true)
@@ -1351,9 +1578,36 @@ Foam::conformalVoronoiMesh::collapseFace
                     collapseToPt = pts[collapseToPtI];
 
                     break;
+
+//                    faceBoundaryPts.append(facePtsNeg[fPtI]);
                 }
             }
 
+//            if (!faceBoundaryPts.empty())
+//            {
+//                if (faceBoundaryPts.size() == 2)
+//                {
+//                    collapseToPtI = faceBoundaryPts[0];
+//
+//                    collapseToPt =
+//                        0.5
+//                       *(
+//                            pts[faceBoundaryPts[0]]
+//                          + pts[faceBoundaryPts[1]]
+//                        );
+//                }
+//                else if (faceBoundaryPts.size() < f.size())
+//                {
+//                    face bFace(faceBoundaryPts);
+//
+//                    collapseToPtI = faceBoundaryPts.first();
+//
+//                    collapseToPt = bFace.centre(pts);
+//                }
+//            }
+//
+//            faceBoundaryPts.clear();
+
             // ...otherwise arbitrarily choosing the most distant
             // point as the index to collapse to.
 
@@ -1384,9 +1638,34 @@ Foam::conformalVoronoiMesh::collapseFace
                     collapseToPt = pts[collapseToPtI];
 
                     break;
+
+//                    faceBoundaryPts.append(facePtsNeg[fPtI]);
                 }
             }
 
+//            if (!faceBoundaryPts.empty())
+//            {
+//                if (faceBoundaryPts.size() == 2)
+//                {
+//                    collapseToPtI = faceBoundaryPts[0];
+//
+//                    collapseToPt =
+//                        0.5
+//                       *(
+//                            pts[faceBoundaryPts[0]]
+//                          + pts[faceBoundaryPts[1]]
+//                        );
+//                }
+//                else if (faceBoundaryPts.size() < f.size())
+//                {
+//                    face bFace(faceBoundaryPts);
+//
+//                    collapseToPtI = faceBoundaryPts.first();
+//
+//                    collapseToPt = bFace.centre(pts);
+//                }
+//            }
+
             // ...otherwise arbitrarily choosing the most distant
             // point as the index to collapse to.
 
@@ -1406,6 +1685,8 @@ Foam::conformalVoronoiMesh::collapseFace
 
             Foam::point collapseToPt = fC;
 
+            DynamicList<label> faceBoundaryPts(f.size());
+
             forAll(facePts, fPtI)
             {
                 if (boundaryPts[facePts[fPtI]] == true)
@@ -1415,11 +1696,32 @@ Foam::conformalVoronoiMesh::collapseFace
                     // use the first boundary point encountered if
                     // there are multiple boundary points.
 
-                    collapseToPtI = facePts[fPtI];
+//                    collapseToPtI = facePts[fPtI];
+//
+//                    collapseToPt = pts[collapseToPtI];
+//
+//                    break;
 
-                    collapseToPt = pts[collapseToPtI];
+                    faceBoundaryPts.append(facePts[fPtI]);
+                }
+            }
 
-                    break;
+            if (!faceBoundaryPts.empty())
+            {
+                if (faceBoundaryPts.size() == 2)
+                {
+                    collapseToPtI = faceBoundaryPts[0];
+
+                    collapseToPt =
+                        0.5*(pts[faceBoundaryPts[0]] + pts[faceBoundaryPts[1]]);
+                }
+                else if (faceBoundaryPts.size() < f.size())
+                {
+                    face bFace(faceBoundaryPts);
+
+                    collapseToPtI = faceBoundaryPts.first();
+
+                    collapseToPt = bFace.centre(pts);
                 }
             }
 
@@ -1791,9 +2093,9 @@ void Foam::conformalVoronoiMesh::checkCellSizing()
         }
 
         Info<< "    Automatically re-sizing " << cellsToResize.size()
-            << " cells that are attached to the bad faces: DISABLED" << endl;
+            << " cells that are attached to the bad faces: " << endl;
 
-        //cellSizeControl_.setCellSizes(cellsToResize);
+        cellSizeControl_.setCellSizes(cellsToResize);
     }
 
     timeCheck("End of Cell Sizing");
@@ -1858,7 +2160,10 @@ Foam::labelHashSet Foam::conformalVoronoiMesh::findOffsetPatchFaces
         }
     }
 
-    offsetBoundaryCells.write();
+    if (cvMeshControls().objOutput())
+    {
+        offsetBoundaryCells.write();
+    }
 
     return offsetBoundaryCells;
 }
@@ -2092,13 +2397,7 @@ void Foam::conformalVoronoiMesh::indexDualVertices
 
             pts[dualVertI] = cit->dual();
 
-            if
-            (
-                !cit->vertex(0)->internalOrBoundaryPoint()
-             || !cit->vertex(1)->internalOrBoundaryPoint()
-             || !cit->vertex(2)->internalOrBoundaryPoint()
-             || !cit->vertex(3)->internalOrBoundaryPoint()
-            )
+            if (cit->boundaryDualVertex())
             {
                 // This is a boundary dual vertex
                 boundaryPts[dualVertI] = true;
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
index bee04b2fbddc8a165fc72f6f46cdee5b9db1185b..989fe2cf95c42f70514e866d3b50bcb4f8d03898 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
@@ -719,12 +719,15 @@ bool Foam::conformalVoronoiMesh::dualCellSurfaceAllIntersections
 
                         const Foam::point& p = infoList[hitI].hitPoint();
 
-                        const scalar separationDistance
-                            = mag(p - info.hitPoint());
+                        const scalar separationDistance =
+                            mag(p - info.hitPoint());
 
-                        const scalar minSepDist
-                            = sqr(cvMeshControls().removalDistCoeff()
-                             *targetCellSize(p));
+                        const scalar minSepDist =
+                            sqr
+                            (
+                                cvMeshControls().removalDistCoeff()
+                               *targetCellSize(p)
+                            );
 
                         // Reject the point if it is too close to another
                         // surface point.
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
index 1e24b7d8ef29cf030425647cfc70fc03f50bf26a..43d8c077fc785724ad83b37ac8e48a7a76f77ba9 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
@@ -366,6 +366,49 @@ inline bool Foam::conformalVoronoiMesh::isBoundaryDualFace
 }
 
 
+inline Foam::List<bool> Foam::conformalVoronoiMesh::dualFaceBoundaryPoints
+(
+    const Delaunay::Finite_edges_iterator& eit
+) const
+{
+    Cell_circulator ccStart = incident_cells(*eit);
+    Cell_circulator cc1 = ccStart;
+    Cell_circulator cc2 = cc1;
+
+    // Advance the second circulator so that it always stays on the next
+    // cell around the edge;
+    cc2++;
+
+    DynamicList<bool> tmpFaceBoundaryPoints;
+
+    do
+    {
+        label cc1I = cc1->cellIndex();
+
+        label cc2I = cc2->cellIndex();
+
+        if (cc1I != cc2I)
+        {
+            if (cc1->boundaryDualVertex())
+            {
+                tmpFaceBoundaryPoints.append(true);
+            }
+            else
+            {
+                tmpFaceBoundaryPoints.append(false);
+            }
+        }
+
+        cc1++;
+
+        cc2++;
+
+    } while (cc1 != ccStart);
+
+    return tmpFaceBoundaryPoints;
+}
+
+
 inline Foam::List<Foam::label> Foam::conformalVoronoiMesh::processorsAttached
 (
     const Delaunay::Finite_facets_iterator& fit
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.C
index c99f9324ff1c02da60ee84c7275f76ba7415941e..996aa71abfcf13e62ba123e3519090121d5ff095 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.C
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.C
@@ -291,6 +291,11 @@ Foam::cvControls::cvControls
         filteringDict.lookup("mergeClosenessCoeff")
     );
 
+    edgeMergeAngle_ = readScalar
+    (
+        filteringDict.lookup("edgeMergeAngle")
+    );
+
     continueFilteringOnBadInitialPolyMesh_ = Switch
     (
         filteringDict.lookupOrDefault<Switch>
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.H
index dae83ed06dcec1bec4b9b5be5b5c0053953e19c3..58dd06fc741ee6b1c88dc14a5d1cd6756de93692 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.H
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControls.H
@@ -234,6 +234,11 @@ class cvControls
         //  being merged, fraction of the local target cell size
         scalar mergeClosenessCoeff_;
 
+        //- If the angle between two dual edges that are connected by a single
+        //  point is less than this angle, then the edges will be merged into a
+        //  single edge.
+        scalar edgeMergeAngle_;
+
         //- If the mesh quality criteria cannot be satisfied, continue
         //  with filtering anyway?
         Switch continueFilteringOnBadInitialPolyMesh_;
@@ -405,6 +410,9 @@ public:
             //- Return the mergeClosenessCoeff
             inline scalar mergeClosenessCoeff() const;
 
+            //- Return the edgeMergeAngle
+            inline scalar edgeMergeAngle() const;
+
             //- Return the continueFilteringOnBadInitialPolyMesh Switch
             inline Switch continueFilteringOnBadInitialPolyMesh() const;
 
diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControlsI.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControlsI.H
index ce45e5f52242a1f8b38c6d8285802ca72e18e859..50df7e8f5841b36d61322b0931f6840a4de52cf1 100644
--- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControlsI.H
+++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/cvControls/cvControlsI.H
@@ -166,6 +166,12 @@ inline Foam::scalar Foam::cvControls::mergeClosenessCoeff() const
 }
 
 
+inline Foam::scalar Foam::cvControls::edgeMergeAngle() const
+{
+    return edgeMergeAngle_;
+}
+
+
 inline Foam::Switch
 Foam::cvControls::continueFilteringOnBadInitialPolyMesh() const
 {
diff --git a/applications/utilities/surface/surfaceFeatureExtract/Allwmake b/applications/utilities/surface/surfaceFeatureExtract/Allwmake
index e54f15ae2829ee050db4964162262739637b8226..369428e1713ee11a3f49aadc110376bcaf2aaa79 100755
--- a/applications/utilities/surface/surfaceFeatureExtract/Allwmake
+++ b/applications/utilities/surface/surfaceFeatureExtract/Allwmake
@@ -3,30 +3,25 @@ cd ${0%/*} || exit 1    # run from this directory
 
 set -x
 
-if [ ! -e "Make/files" ] || [ ! -e "Make/options" ]
+if [ -n "$CGAL_ARCH_PATH" ]
 then
-    mkdir -p Make
-
-    if [ -n "$CGAL_ARCH_PATH" ]
-    then
-        cp -r MakeWithCGAL/* Make
-
-        echo
-        echo Compiling surfaceFeatureExtract with CGAL support for curvature
-        echo
-
-        wmake
-    else
-        cp -r MakeWithoutCGAL/* Make
-
-        echo
-        echo Compiling surfaceFeatureExtract without CGAL support for curvature
-        echo
-
-        wmake
-    fi
+    echo
+    echo "Compiling surfaceFeatureExtract with CGAL curvature support"
+    echo
+
+    wmake "ENABLE_CURVATURE=-DENABLE_CURVATURE \
+           EXE_FROUNDING_MATH=-frounding-math \
+           USE_F2C=-DCGAL_USE_F2C \
+           CGAL_LIBDIR=-L$CGAL_ARCH_PATH/lib \
+           LAPACK_LIB=-llapack \
+           BLAS_LIB=-lblas \
+           CGAL_LIB=-lCGAL"
 else
-    echo surfaceFeatureExtract already has a Make folder
+    echo
+    echo "Compiling surfaceFeatureExtract without CGAL curvature support"
+    echo
+
+    wmake
 fi
 
 
diff --git a/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.H b/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.H
index 3001a247d3ee8435b64545e7e63b5e39e95ef8ae..30627c7463ffd0a91f2b3b5cc8bf0f3deaf260ec 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.H
+++ b/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.H
@@ -53,7 +53,7 @@ namespace Foam
 {
 
 /*---------------------------------------------------------------------------*\
-                     Class buildCGALPolyhedron Declaration
+                    Class buildCGALPolyhedron Declaration
 \*---------------------------------------------------------------------------*/
 
 class buildCGALPolyhedron
@@ -80,18 +80,56 @@ public:
     // Constructors
 
         //- Construct with reference to triSurface
-        buildCGALPolyhedron(const triSurface& surf);
+        explicit buildCGALPolyhedron(const triSurface& surf)
+        :
+            CGAL::Modifier_base<HalfedgeDS>(),
+            surf_(surf)
+        {}
 
 
     //- Destructor
-    ~buildCGALPolyhedron();
+    ~buildCGALPolyhedron(){}
 
 
     // Member Operators
 
         //- operator() of this `modifier' called by delegate function of
         //  Polyhedron
-        void operator()(HalfedgeDS& hds);
+        void operator()(HalfedgeDS& hds)
+        {
+            typedef HalfedgeDS::Traits     Traits;
+            typedef Traits::Point_3        Point;
+
+            // Postcondition: `hds' is a valid polyhedral surface.
+            CGAL::Polyhedron_incremental_builder_3<HalfedgeDS> B(hds, false);
+
+            B.begin_surface
+            (
+                surf_.points().size(),      // n points
+                surf_.size(),               // n facets
+                2*surf_.edges().size()      // n halfedges
+            );
+
+            forAll(surf_.points(), pI)
+            {
+                const Foam::point& p = surf_.points()[pI];
+
+                B.add_vertex(Point(p.x(), p.y(), p.z()));
+            }
+
+            forAll(surf_, fI)
+            {
+                B.begin_facet();
+
+                B.add_vertex_to_facet(surf_[fI][0]);
+                B.add_vertex_to_facet(surf_[fI][1]);
+                B.add_vertex_to_facet(surf_[fI][2]);
+
+                B.end_facet();
+            }
+
+            B.end_surface();
+        }
 };
 
 
diff --git a/applications/utilities/surface/surfaceFeatureExtract/Make/options b/applications/utilities/surface/surfaceFeatureExtract/Make/options
index b733d5fde961f788415c0aa33e9e74bd1caa539d..b2e5dcf5f7abef5f52c4472ffc0030b91944366e 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/Make/options
+++ b/applications/utilities/surface/surfaceFeatureExtract/Make/options
@@ -1,4 +1,11 @@
+include $(GENERAL_RULES)/CGAL
+
 EXE_INC = \
+    ${ENABLE_CURVATURE}\
+    ${EXE_FROUNDING_MATH} \
+    ${USE_F2C} \
+    ${CGAL_INC} \
+    -ICGALPolyhedron \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/edgeMesh/lnInclude \
@@ -7,6 +14,11 @@ EXE_INC = \
     -I$(LIB_SRC)/sampling/lnInclude
 
 EXE_LIBS = \
+    $(CGAL_LIBS) \
+    ${CGAL_LIBDIR} \
+    ${LAPACK_LIB} \
+    ${BLAS_LIB} \
+    ${CGAL_LIB} \
     -lmeshTools \
     -ledgeMesh \
     -ltriSurface \
diff --git a/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/files b/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/files
deleted file mode 100644
index 0c0f6f7966efb238b4a4b605e978696384379fc5..0000000000000000000000000000000000000000
--- a/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/files
+++ /dev/null
@@ -1,4 +0,0 @@
-surfaceFeatureExtract.C
-CGALPolyhedron/buildCGALPolyhedron.C
-
-EXE = $(FOAM_APPBIN)/surfaceFeatureExtract
diff --git a/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/options b/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/options
deleted file mode 100644
index 60833572b75ba153f30697475b5ba1eb623b209f..0000000000000000000000000000000000000000
--- a/applications/utilities/surface/surfaceFeatureExtract/MakeWithCGAL/options
+++ /dev/null
@@ -1,29 +0,0 @@
-EXE_FROUNDING_MATH = -frounding-math
-EXE_NDEBUG = -DNDEBUG
-USE_F2C = -DCGAL_USE_F2C
-include $(GENERAL_RULES)/CGAL
-
-EXE_INC = \
-    -DENABLE_CURVATURE \
-    ${EXE_FROUNDING_MATH} \
-    ${EXE_NDEBUG} \
-    ${USE_F2C} \
-    ${CGAL_INC} \
-    -ICGALPolyhedron \
-    -I$(LIB_SRC)/finiteVolume/lnInclude \
-    -I$(LIB_SRC)/meshTools/lnInclude \
-    -I$(LIB_SRC)/edgeMesh/lnInclude \
-    -I$(LIB_SRC)/triSurface/lnInclude \
-    -I$(LIB_SRC)/surfMesh/lnInclude \
-    -I$(LIB_SRC)/sampling/lnInclude
-
-EXE_LIBS = \
-    $(CGAL_LIBS) \
-    -L$(CGAL_ARCH_PATH)/lib \
-    -llapack \
-    -lblas \
-    -lCGAL \
-    -lmeshTools \
-    -ledgeMesh \
-    -ltriSurface \
-    -lsampling
diff --git a/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/files b/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/files
deleted file mode 100644
index a57125cd6a7c24a2892c292ee358cce52a6529ce..0000000000000000000000000000000000000000
--- a/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/files
+++ /dev/null
@@ -1,3 +0,0 @@
-surfaceFeatureExtract.C
-
-EXE = $(FOAM_APPBIN)/surfaceFeatureExtract
diff --git a/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/options b/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/options
deleted file mode 100644
index b733d5fde961f788415c0aa33e9e74bd1caa539d..0000000000000000000000000000000000000000
--- a/applications/utilities/surface/surfaceFeatureExtract/MakeWithoutCGAL/options
+++ /dev/null
@@ -1,13 +0,0 @@
-EXE_INC = \
-    -I$(LIB_SRC)/finiteVolume/lnInclude \
-    -I$(LIB_SRC)/meshTools/lnInclude \
-    -I$(LIB_SRC)/edgeMesh/lnInclude \
-    -I$(LIB_SRC)/triSurface/lnInclude \
-    -I$(LIB_SRC)/surfMesh/lnInclude \
-    -I$(LIB_SRC)/sampling/lnInclude
-
-EXE_LIBS = \
-    -lmeshTools \
-    -ledgeMesh \
-    -ltriSurface \
-    -lsampling
diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
index da71cafc90b8e63cc3f0d0b0fbb72d2c071c1e23..b02c4c815d77ab492ed5c57d745ae0416a95dcf7 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
+++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
@@ -74,30 +74,6 @@ scalarField calcCurvature(const triSurface& surf)
 
     // The rest of this function adapted from
     //     CGAL-3.7/examples/Jet_fitting_3/Mesh_estimation.cpp
-    // Licensed under CGAL-3.7/LICENSE.FREE_USE
-
-    // Copyright (c) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007
-    // Utrecht University (The Netherlands), ETH Zurich (Switzerland), Freie
-    // Universitaet Berlin (Germany), INRIA Sophia-Antipolis (France),
-    // Martin-Luther-University Halle-Wittenberg (Germany), Max-Planck-Institute
-    // Saarbruecken (Germany), RISC Linz (Austria), and Tel-Aviv University
-    // (Israel).  All rights reserved.
-
-    // Permission is hereby granted, free of charge, to any person obtaining a
-    // copy of this software and associated documentation files (the
-    // "Software"), to deal in the Software without restriction, including
-    // without limitation the rights to use, copy, modify, merge, publish,
-    // distribute, sublicense, and/or sell copies of the Software, and to permit
-    // persons to whom the Software is furnished to do so, subject to the
-    // following conditions:
-
-    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-    // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-    // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-    // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-    // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
-    // OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
-    // THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
      //Vertex property map, with std::map
     typedef std::map<Vertex*, int> Vertex2int_map_type;
@@ -181,12 +157,12 @@ scalarField calcCurvature(const triSurface& surf)
 //                 << monge_fit.condition_number() << nl << std::endl;
 
         // Use the maximum curvature to give smaller cell sizes later.
-        k[vertI++]
-            = max
-              (
-                   mag(monge_form.principal_curvatures(0)),
-                   mag(monge_form.principal_curvatures(1))
-              );
+        k[vertI++] =
+            max
+            (
+                mag(monge_form.principal_curvatures(0)),
+                mag(monge_form.principal_curvatures(1))
+            );
     }
 
     return k;
@@ -198,8 +174,10 @@ bool edgesConnected(const edge& e1, const edge& e2)
 {
     if
     (
-        e1.start() == e2.start() || e1.start() == e2.end()
-     || e1.end() == e2.start() || e1.end() == e2.end()
+        e1.start() == e2.start()
+     || e1.start() == e2.end()
+     || e1.end() == e2.start()
+     || e1.end() == e2.end()
     )
     {
         return true;
@@ -289,8 +267,8 @@ scalar calcProximityOfFeatureEdges
                     // Don't refine if the edges are connected to each other
                     if (!edgesConnected(e1, e2))
                     {
-                        scalar curDist
-                            = mag(pHit1.hitPoint() - pHit2.hitPoint());
+                        scalar curDist =
+                            mag(pHit1.hitPoint() - pHit2.hitPoint());
 
                         minDist = min(curDist, minDist);
                     }
@@ -503,353 +481,344 @@ int main(int argc, char *argv[])
         "extract and write surface features to file"
     );
     argList::noParallel();
-    argList::validArgs.append("surface");
-    argList::validArgs.append("output set");
 
     argList::addOption
     (
-        "includedAngle",
-        "degrees",
-        "construct feature set from included angle [0..180]"
-    );
-    argList::addOption
-    (
-        "set",
-        "name",
-        "use existing feature set from file"
-    );
-    argList::addOption
-    (
-        "minLen",
-        "scalar",
-        "remove features shorter than the specified cumulative length"
-    );
-    argList::addOption
-    (
-        "minElem",
-        "int",
-        "remove features with fewer than the specified number of edges"
-    );
-    argList::addOption
-    (
-        "subsetBox",
-        "((x0 y0 z0)(x1 y1 z1))",
-        "remove edges outside specified bounding box"
-    );
-    argList::addOption
-    (
-        "deleteBox",
-        "((x0 y0 z0)(x1 y1 z1))",
-        "remove edges within specified bounding box"
+        "dict",
+        "word",
+        "name of dictionary to provide feature extraction information"
     );
-    argList::addBoolOption
-    (
-        "writeObj",
-        "write extendedFeatureEdgeMesh obj files"
-    );
-    argList::addBoolOption
-    (
-        "writeVTK",
-        "write extendedFeatureEdgeMesh vtk files"
-    );
-    argList::addBoolOption
-    (
-        "closeness",
-        "span to look for surface closeness"
-    );
-    argList::addOption
-    (
-        "featureProximity",
-        "scalar",
-        "distance to look for close features"
-    );
-    argList::addBoolOption
-    (
-        "manifoldEdgesOnly",
-        "remove any non-manifold (open or more than two connected faces) edges"
-    );
-    argList::addOption
+    
+#   include "setRootCase.H"
+#   include "createTime.H"
+
+    word dictName
     (
-        "plane",
-        "(nx ny nz)(z0 y0 z0)",
-        "use a plane to create feature edges for 2D meshing"
+        args.optionLookupOrDefault<word>("dict", "surfaceFeatureExtractDict")
     );
 
-#   ifdef ENABLE_CURVATURE
-    argList::addBoolOption
+    Info<< "Reading " << dictName << nl << endl;
+
+    IOdictionary dict
     (
-        "curvature",
-        "calculate curvature and closeness fields"
+        IOobject
+        (
+            dictName,
+            runTime.system(),
+            runTime,
+            IOobject::MUST_READ,
+            IOobject::NO_WRITE,
+            false
+        )
     );
-#   endif
 
+    forAllConstIter(dictionary, dict, iter)
+    {
+        dictionary surfaceDict = dict.subDict(iter().keyword());
 
-#   include "setRootCase.H"
-#   include "createTime.H"
+        const fileName surfFileName = iter().keyword();
+        const fileName sFeatFileName = surfFileName.lessExt().name();
 
-    bool writeVTK = args.optionFound("writeVTK");
+        Info<< "Surface            : " << surfFileName << nl << endl;
 
-    bool writeObj = args.optionFound("writeObj");
+        const Switch writeVTK =
+            surfaceDict.lookupOrAddDefault<Switch>("writeVTK", "off");
+        const Switch writeObj =
+            surfaceDict.lookupOrAddDefault<Switch>("writeObj", "off");
+        const Switch writeFeatureEdgeMesh =
+            surfaceDict.lookupOrAddDefault<Switch>
+            (
+                "writeFeatureEdgeMesh",
+                "off"
+            );
 
-    bool curvature = args.optionFound("curvature");
+        const Switch curvature =
+            surfaceDict.lookupOrAddDefault<Switch>("curvature", "off");
+        const Switch featureProximity =
+            surfaceDict.lookupOrAddDefault<Switch>("featureProximity", "off");
+        const Switch closeness =
+            surfaceDict.lookupOrAddDefault<Switch>("closeness", "off");
 
-    if (curvature && env("FOAM_SIGFPE"))
-    {
-        WarningIn(args.executable())
-            << "Detected floating point exception trapping (FOAM_SIGFPE)."
-            << " This might give" << nl
-            << "    problems when calculating curvature on straight angles"
-            << " (infinite curvature)" << nl
-            << "    Switch it off in case of problems." << endl;
-    }
+        const word extractionMethod = surfaceDict.lookup("extractionMethod");
 
 
-    Info<< "Feature line extraction is only valid on closed manifold surfaces."
-        << endl;
+#ifndef ENABLE_CURVATURE
+        if (curvature)
+        {
+            WarningIn(args.executable())
+                << "Curvature calculation has been requested but "
+                << args.executable() << " has not " << nl
+                << "    been compiled with CGAL. "
+                << "Skipping the curvature calculation." << endl;
+        }
+#else
+        if (curvature && env("FOAM_SIGFPE"))
+        {
+            WarningIn(args.executable())
+                << "Detected floating point exception trapping (FOAM_SIGFPE)."
+                << " This might give" << nl
+                << "    problems when calculating curvature on straight angles"
+                << " (infinite curvature)" << nl
+                << "    Switch it off in case of problems." << endl;
+        }
+#endif
 
-    const fileName surfFileName = args[1];
-    const fileName outFileName  = args[2];
 
-    Info<< "Surface            : " << surfFileName << nl
-        << "Output feature set : " << outFileName << nl
-        << endl;
+        Info<< nl << "Feature line extraction is only valid on closed manifold "
+            << "surfaces." << endl;
 
-    fileName sFeatFileName = surfFileName.lessExt().name();
+        // Read
+        // ~~~~
 
+        triSurface surf("constant/triSurface/" + surfFileName);
 
-    // Read
-    // ~~~~
+        Info<< "Statistics:" << endl;
+        surf.writeStats(Info);
+        Info<< endl;
 
-    triSurface surf(surfFileName);
+        faceList faces(surf.size());
 
-    Info<< "Statistics:" << endl;
-    surf.writeStats(Info);
-    Info<< endl;
+        forAll(surf, fI)
+        {
+            faces[fI] = surf[fI].triFaceFace();
+        }
 
-    faceList faces(surf.size());
 
-    forAll(surf, fI)
-    {
-        faces[fI] = surf[fI].triFaceFace();
-    }
+        // Either construct features from surface & featureAngle or read set.
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    // Either construct features from surface&featureangle or read set.
-    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        surfaceFeatures set(surf);
 
-    surfaceFeatures set(surf);
+        if (extractionMethod == "extractFromFile")
+        {
+            const fileName featureEdgeFile =
+                surfaceDict.subDict("extractFromFile").lookup
+                (
+                    "featureEdgeFile"
+                );
 
-    if (args.optionFound("set"))
-    {
-        const fileName setName = args["set"];
+            edgeMesh eMesh(featureEdgeFile);
 
-        Info<< "Reading existing feature set from file " << setName << endl;
+            // Sometimes duplicate edges are present. Remove them.
+            eMesh.mergeEdges();
 
-        set = surfaceFeatures(surf, setName);
-    }
-    else if (args.optionFound("includedAngle"))
-    {
-        const scalar includedAngle = args.optionRead<scalar>("includedAngle");
+            Info<< nl << "Reading existing feature edges from file "
+                << featureEdgeFile << endl;
+
+            set = surfaceFeatures(surf, eMesh.points(), eMesh.edges());
+        }
+        else if (extractionMethod == "extractFromSurface")
+        {
+            const scalar includedAngle =
+                readScalar
+                (
+                    surfaceDict.subDict("extractFromSurface").lookup
+                    (
+                        "includedAngle"
+                    )
+                );
+
+            Info<< nl << "Constructing feature set from included angle "
+                << includedAngle << endl;
 
-        Info<< "Constructing feature set from included angle " << includedAngle
+            set = surfaceFeatures(surf, includedAngle);
+        }
+        else
+        {
+            FatalErrorIn(args.executable())
+                << "No initial feature set. Provide either one"
+                << " of extractFromFile (to read existing set)" << nl
+                << " or extractFromSurface (to construct new set from angle)"
+                << exit(FatalError);
+        }
+
+        Info<< nl
+            << "Initial feature set:" << nl
+            << "    feature points : " << set.featurePoints().size() << nl
+            << "    feature edges  : " << set.featureEdges().size() << nl
+            << "    of which" << nl
+            << "        region edges   : " << set.nRegionEdges() << nl
+            << "        external edges : " << set.nExternalEdges() << nl
+            << "        internal edges : " << set.nInternalEdges() << nl
             << endl;
 
-        set = surfaceFeatures(surf, includedAngle);
 
-        // Info<< nl << "Writing initial features" << endl;
-        // set.write("initial.fSet");
-        // set.writeObj("initial");
-    }
-    else
-    {
-        FatalErrorIn(args.executable())
-            << "No initial feature set. Provide either one"
-            << " of -set (to read existing set)" << nl
-            << " or -includedAngle (to new set construct from angle)"
-            << exit(FatalError);
-    }
+        // Trim set
+        // ~~~~~~~~
 
-    Info<< nl
-        << "Initial feature set:" << nl
-        << "    feature points : " << set.featurePoints().size() << nl
-        << "    feature edges  : " << set.featureEdges().size() << nl
-        << "    of which" << nl
-        << "        region edges   : " << set.nRegionEdges() << nl
-        << "        external edges : " << set.nExternalEdges() << nl
-        << "        internal edges : " << set.nInternalEdges() << nl
-        << endl;
+        if (surfaceDict.isDict("trimFeatures"))
+        {
+            dictionary trimDict = surfaceDict.subDict("trimFeatures");
 
+            scalar minLen =
+                trimDict.lookupOrAddDefault<scalar>("minLen", -GREAT);
 
-    // Trim set
-    // ~~~~~~~~
+            label minElem = trimDict.lookupOrAddDefault<label>("minElem", 0);
 
-    scalar minLen = -GREAT;
-    if (args.optionReadIfPresent("minLen", minLen))
-    {
-        Info<< "Removing features of length < " << minLen << endl;
-    }
+            // Trim away small groups of features
+            if (minElem > 0 || minLen > 0)
+            {
+                Info<< "Removing features of length < "
+                    << minLen << endl;
+                Info<< "Removing features with number of edges < "
+                    << minElem << endl;
 
-    label minElem = 0;
-    if (args.optionReadIfPresent("minElem", minElem))
-    {
-        Info<< "Removing features with number of edges < " << minElem << endl;
-    }
+                set.trimFeatures(minLen, minElem);
+            }
+        }
 
-    // Trim away small groups of features
-    if (minElem > 0 || minLen > 0)
-    {
-        set.trimFeatures(minLen, minElem);
-        Info<< endl << "Removed small features" << endl;
-    }
 
+        // Subset
+        // ~~~~~~
 
-    // Subset
-    // ~~~~~~
+        // Convert to marked edges, points
+        List<surfaceFeatures::edgeStatus> edgeStat(set.toStatus());
 
-    // Convert to marked edges, points
-    List<surfaceFeatures::edgeStatus> edgeStat(set.toStatus());
+        if (surfaceDict.isDict("subsetFeatures"))
+        {
+            dictionary subsetDict = surfaceDict.subDict("subsetFeatures");
 
-    if (args.optionFound("subsetBox"))
-    {
-        treeBoundBox bb
-        (
-            args.optionLookup("subsetBox")()
-        );
+            if (subsetDict.found("insideBox"))
+            {
+                treeBoundBox bb(subsetDict.lookup("insideBox")());
 
-        Info<< "Removing all edges outside bb " << bb << endl;
-        dumpBox(bb, "subsetBox.obj");
+                Info<< "Removing all edges outside bb " << bb << endl;
+                dumpBox(bb, "subsetBox.obj");
 
-        deleteBox
-        (
-            surf,
-            bb,
-            false,
-            edgeStat
-        );
-    }
-    else if (args.optionFound("deleteBox"))
-    {
-        treeBoundBox bb
-        (
-            args.optionLookup("deleteBox")()
-        );
+                deleteBox
+                (
+                    surf,
+                    bb,
+                    false,
+                    edgeStat
+                );
+            }
+            else if (subsetDict.found("outsideBox"))
+            {
+                treeBoundBox bb(subsetDict.lookup("outsideBox")());
 
-        Info<< "Removing all edges inside bb " << bb << endl;
-        dumpBox(bb, "deleteBox.obj");
+                Info<< "Removing all edges inside bb " << bb << endl;
+                dumpBox(bb, "deleteBox.obj");
 
-        deleteBox
-        (
-            surf,
-            bb,
-            true,
-            edgeStat
-        );
-    }
+                deleteBox
+                (
+                    surf,
+                    bb,
+                    true,
+                    edgeStat
+                );
+            }
 
-    if (args.optionFound("manifoldEdgesOnly"))
-    {
-        Info<< "Removing all non-manifold edges" << endl;
+            const Switch manifoldEdges =
+                subsetDict.lookupOrAddDefault<Switch>("manifoldEdges", "no");
 
-        forAll(edgeStat, edgeI)
-        {
-            if (surf.edgeFaces()[edgeI].size() != 2)
+            if (manifoldEdges)
             {
-                edgeStat[edgeI] = surfaceFeatures::NONE;
+                Info<< "Removing all non-manifold edges" << endl;
+
+                forAll(edgeStat, edgeI)
+                {
+                    if (surf.edgeFaces()[edgeI].size() != 2)
+                    {
+                        edgeStat[edgeI] = surfaceFeatures::NONE;
+                    }
+                }
             }
-        }
-    }
 
-    if (args.optionFound("plane"))
-    {
-        plane cutPlane(args.optionLookup("plane")());
+            if (subsetDict.found("plane"))
+            {
+                plane cutPlane(subsetDict.lookup("plane")());
 
-        deleteEdges
-        (
-            surf,
-            cutPlane,
-            edgeStat
-        );
+                deleteEdges
+                (
+                    surf,
+                    cutPlane,
+                    edgeStat
+                );
 
-        Info<< "Only edges that intersect the plane with normal "
-            << cutPlane.normal() << " and base point " << cutPlane.refPoint()
-            << " will be included as feature edges."<< endl;
-    }
+                Info<< "Only edges that intersect the plane with normal "
+                    << cutPlane.normal()
+                    << " and base point " << cutPlane.refPoint()
+                    << " will be included as feature edges."<< endl;
+            }
+        }
 
 
-    surfaceFeatures newSet(surf);
-    newSet.setFromStatus(edgeStat);
+        surfaceFeatures newSet(surf);
+        newSet.setFromStatus(edgeStat);
 
-    //Info<< endl << "Writing trimmed features to " << outFileName << endl;
-    //newSet.write(outFileName);
+        if (writeObj)
+        {
+            newSet.writeObj("final");
+        }
 
-    // Info<< endl << "Writing edge objs." << endl;
-    // newSet.writeObj("final");
+        Info<< nl
+            << "Final feature set after trimming and subsetting:" << nl
+            << "    feature points : " << newSet.featurePoints().size() << nl
+            << "    feature edges  : " << newSet.featureEdges().size() << nl
+            << "    of which" << nl
+            << "        region edges   : " << newSet.nRegionEdges() << nl
+            << "        external edges : " << newSet.nExternalEdges() << nl
+            << "        internal edges : " << newSet.nInternalEdges() << nl
+            << endl;
 
-    Info<< nl
-        << "Final feature set:" << nl
-        << "    feature points : " << newSet.featurePoints().size() << nl
-        << "    feature edges  : " << newSet.featureEdges().size() << nl
-        << "    of which" << nl
-        << "        region edges   : " << newSet.nRegionEdges() << nl
-        << "        external edges : " << newSet.nExternalEdges() << nl
-        << "        internal edges : " << newSet.nInternalEdges() << nl
-        << endl;
+        // Extracting and writing a extendedFeatureEdgeMesh
+        extendedFeatureEdgeMesh feMesh
+        (
+            newSet,
+            runTime,
+            sFeatFileName + ".extendedFeatureEdgeMesh"
+        );
 
-    // Extracting and writing a extendedFeatureEdgeMesh
-    extendedFeatureEdgeMesh feMesh
-    (
-        newSet,
-        runTime,
-        sFeatFileName + ".extendedFeatureEdgeMesh"
-    );
+        Info<< nl << "Writing extendedFeatureEdgeMesh to "
+            << feMesh.objectPath() << endl;
 
-    Info<< nl << "Writing extendedFeatureEdgeMesh to " << feMesh.objectPath()
-        << endl;
+        if (writeObj)
+        {
+            feMesh.writeObj(surfFileName.lessExt().name());
+        }
 
-    if (writeObj)
-    {
-        feMesh.writeObj(surfFileName.lessExt().name());
-    }
+        feMesh.write();
+
+        // Write a featureEdgeMesh for backwards compatibility
+        if (writeFeatureEdgeMesh)
+        {
+            featureEdgeMesh bfeMesh
+            (
+                IOobject
+                (
+                    surfFileName.lessExt().name() + ".eMesh",   // name
+                    runTime.constant(),                         // instance
+                    "triSurface",
+                    runTime,                                    // registry
+                    IOobject::NO_READ,
+                    IOobject::AUTO_WRITE,
+                    false
+                ),
+                feMesh.points(),
+                feMesh.edges()
+            );
 
-    feMesh.write();
+            Info<< nl << "Writing featureEdgeMesh to "
+                << bfeMesh.objectPath() << endl;
 
-    // Write a featureEdgeMesh for backwards compatibility
-    {
-        featureEdgeMesh bfeMesh
+            bfeMesh.regIOobject::write();
+        }
+
+        triSurfaceMesh searchSurf
         (
             IOobject
             (
-                surfFileName.lessExt().name() + ".eMesh",   // name
-                runTime.constant(), // instance
+                sFeatFileName + ".closeness",
+                runTime.constant(),
                 "triSurface",
-                runTime,            // registry
+                runTime,
                 IOobject::NO_READ,
-                IOobject::AUTO_WRITE,
-                false
+                IOobject::NO_WRITE
             ),
-            feMesh.points(),
-            feMesh.edges()
+            surf
         );
 
-        Info<< nl << "Writing featureEdgeMesh to "
-            << bfeMesh.objectPath() << endl;
-
-        bfeMesh.regIOobject::write();
-    }
-
-    triSurfaceMesh searchSurf
-    (
-        IOobject
-        (
-            sFeatFileName + ".closeness",
-            runTime.constant(),
-            "triSurface",
-            runTime,
-            IOobject::NO_READ,
-            IOobject::NO_WRITE
-        ),
-        surf
-    );
-
     // Find close features
 
     // // Dummy trim operation to mark features
@@ -917,72 +886,57 @@ int main(int argc, char *argv[])
     //     )
     // );
 
-    if (args.optionFound("closeness"))
-    {
-        Info<< nl << "Extracting internal and external closeness of surface."
-            << endl;
-
-        // Internal and external closeness
-
-        // Prepare start and end points for intersection tests
+        if (closeness)
+        {
+            Info<< nl << "Extracting internal and external closeness of "
+                << "surface." << endl;
 
-        const vectorField& normals = searchSurf.faceNormals();
+            // Internal and external closeness
 
-        scalar span = searchSurf.bounds().mag();
+            // Prepare start and end points for intersection tests
 
-        //args.optionReadIfPresent("closeness", span);
+            const vectorField& normals = searchSurf.faceNormals();
 
-        scalar externalAngleTolerance = 10;
-        scalar externalToleranceCosAngle = Foam::cos
-        (
-            degToRad(180 - externalAngleTolerance)
-        );
+            scalar span = searchSurf.bounds().mag();
 
-        scalar internalAngleTolerance = 45;
-        scalar internalToleranceCosAngle = Foam::cos
-        (
-            degToRad(180 - internalAngleTolerance)
-        );
+            scalar externalAngleTolerance = 10;
+            scalar externalToleranceCosAngle =
+                Foam::cos
+                (
+                    degToRad(180 - externalAngleTolerance)
+                );
 
-        Info<< "externalToleranceCosAngle: " << externalToleranceCosAngle << nl
-            << "internalToleranceCosAngle: " << internalToleranceCosAngle
-            << endl;
+            scalar internalAngleTolerance = 45;
+            scalar internalToleranceCosAngle =
+                Foam::cos
+                (
+                    degToRad(180 - internalAngleTolerance)
+                );
 
-        // Info<< "span " << span << endl;
+            Info<< "externalToleranceCosAngle: " << externalToleranceCosAngle
+                << nl
+                << "internalToleranceCosAngle: " << internalToleranceCosAngle
+                << endl;
 
-        pointField start = searchSurf.faceCentres() - span*normals;
-        pointField end = searchSurf.faceCentres() + span*normals;
-        const pointField& faceCentres = searchSurf.faceCentres();
+            // Info<< "span " << span << endl;
 
-        List<List<pointIndexHit> > allHitInfo;
+            pointField start = searchSurf.faceCentres() - span*normals;
+            pointField end = searchSurf.faceCentres() + span*normals;
+            const pointField& faceCentres = searchSurf.faceCentres();
 
-        // Find all intersections (in order)
-        searchSurf.findLineAll(start, end, allHitInfo);
+            List<List<pointIndexHit> > allHitInfo;
 
-        scalarField internalCloseness(start.size(), GREAT);
-        scalarField externalCloseness(start.size(), GREAT);
+            // Find all intersections (in order)
+            searchSurf.findLineAll(start, end, allHitInfo);
 
-        forAll(allHitInfo, fI)
-        {
-            const List<pointIndexHit>& hitInfo = allHitInfo[fI];
+            scalarField internalCloseness(start.size(), GREAT);
+            scalarField externalCloseness(start.size(), GREAT);
 
-            if (hitInfo.size() < 1)
+            forAll(allHitInfo, fI)
             {
-                drawHitProblem(fI, surf, start, faceCentres, end, hitInfo);
+                const List<pointIndexHit>& hitInfo = allHitInfo[fI];
 
-                // FatalErrorIn(args.executable())
-                //     << "findLineAll did not hit its own face."
-                //     << exit(FatalError);
-            }
-            else if (hitInfo.size() == 1)
-            {
-                if (!hitInfo[0].hit())
-                {
-                    // FatalErrorIn(args.executable())
-                    //     << "findLineAll did not hit any face."
-                    //     << exit(FatalError);
-                }
-                else if (hitInfo[0].index() != fI)
+                if (hitInfo.size() < 1)
                 {
                     drawHitProblem(fI, surf, start, faceCentres, end, hitInfo);
 
@@ -990,283 +944,339 @@ int main(int argc, char *argv[])
                     //     << "findLineAll did not hit its own face."
                     //     << exit(FatalError);
                 }
-            }
-            else
-            {
-                label ownHitI = -1;
-
-                forAll(hitInfo, hI)
+                else if (hitInfo.size() == 1)
                 {
-                    // Find the hit on the triangle that launched the ray
-
-                    if (hitInfo[hI].index() == fI)
+                    if (!hitInfo[0].hit())
                     {
-                        ownHitI = hI;
-
-                        break;
+                        // FatalErrorIn(args.executable())
+                        //     << "findLineAll did not hit any face."
+                        //     << exit(FatalError);
                     }
-                }
-
-                if (ownHitI < 0)
-                {
-                    drawHitProblem(fI, surf, start, faceCentres, end, hitInfo);
-
-                    // FatalErrorIn(args.executable())
-                    //     << "findLineAll did not hit its own face."
-                    //     << exit(FatalError);
-                }
-                else if (ownHitI == 0)
-                {
-                    // There are no internal hits, the first hit is the closest
-                    // external hit
-
-                    if
-                    (
-                        (normals[fI] & normals[hitInfo[ownHitI + 1].index()])
-                      < externalToleranceCosAngle
-                    )
+                    else if (hitInfo[0].index() != fI)
                     {
-                        externalCloseness[fI] = mag
+                        drawHitProblem
                         (
-                            faceCentres[fI] - hitInfo[ownHitI + 1].hitPoint()
+                            fI,
+                            surf,
+                            start,
+                            faceCentres,
+                            end,
+                            hitInfo
                         );
+
+                        // FatalErrorIn(args.executable())
+                        //     << "findLineAll did not hit its own face."
+                        //     << exit(FatalError);
                     }
                 }
-                else if (ownHitI == hitInfo.size() - 1)
+                else
                 {
-                    // There are no external hits, the last but one hit is the
-                    // closest internal hit
+                    label ownHitI = -1;
 
-                    if
-                    (
-                        (normals[fI] & normals[hitInfo[ownHitI - 1].index()])
-                      < internalToleranceCosAngle
-                    )
+                    forAll(hitInfo, hI)
+                    {
+                        // Find the hit on the triangle that launched the ray
+
+                        if (hitInfo[hI].index() == fI)
+                        {
+                            ownHitI = hI;
+
+                            break;
+                        }
+                    }
+
+                    if (ownHitI < 0)
                     {
-                        internalCloseness[fI] = mag
+                        drawHitProblem
                         (
-                            faceCentres[fI] - hitInfo[ownHitI - 1].hitPoint()
+                            fI,
+                            surf,
+                            start,
+                            faceCentres,
+                            end,
+                            hitInfo
                         );
+
+                        // FatalErrorIn(args.executable())
+                        //     << "findLineAll did not hit its own face."
+                        //     << exit(FatalError);
                     }
-                }
-                else
-                {
-                    if
-                    (
-                        (normals[fI] & normals[hitInfo[ownHitI + 1].index()])
-                      < externalToleranceCosAngle
-                    )
+                    else if (ownHitI == 0)
                     {
-                        externalCloseness[fI] = mag
+                        // There are no internal hits, the first hit is the
+                        // closest external hit
+
+                        if
                         (
-                            faceCentres[fI] - hitInfo[ownHitI + 1].hitPoint()
-                        );
+                            (
+                                normals[fI]
+                              & normals[hitInfo[ownHitI + 1].index()]
+                            )
+                          < externalToleranceCosAngle
+                        )
+                        {
+                            externalCloseness[fI] =
+                                mag
+                                (
+                                    faceCentres[fI]
+                                  - hitInfo[ownHitI + 1].hitPoint()
+                                );
+                        }
                     }
+                    else if (ownHitI == hitInfo.size() - 1)
+                    {
+                        // There are no external hits, the last but one hit is
+                        // the closest internal hit
 
-                    if
-                    (
-                        (normals[fI] & normals[hitInfo[ownHitI - 1].index()])
-                      < internalToleranceCosAngle
-                    )
+                        if
+                        (
+                            (
+                                normals[fI]
+                              & normals[hitInfo[ownHitI - 1].index()]
+                            )
+                          < internalToleranceCosAngle
+                        )
+                        {
+                            internalCloseness[fI] =
+                                mag
+                                (
+                                    faceCentres[fI]
+                                  - hitInfo[ownHitI - 1].hitPoint()
+                                );
+                        }
+                    }
+                    else
                     {
-                        internalCloseness[fI] = mag
+                        if
                         (
-                            faceCentres[fI] - hitInfo[ownHitI - 1].hitPoint()
-                        );
+                            (
+                                normals[fI]
+                              & normals[hitInfo[ownHitI + 1].index()]
+                            )
+                          < externalToleranceCosAngle
+                        )
+                        {
+                            externalCloseness[fI] =
+                                mag
+                                (
+                                    faceCentres[fI]
+                                  - hitInfo[ownHitI + 1].hitPoint()
+                                );
+                        }
+
+                        if
+                        (
+                            (
+                                normals[fI]
+                              & normals[hitInfo[ownHitI - 1].index()]
+                            )
+                          < internalToleranceCosAngle
+                        )
+                        {
+                            internalCloseness[fI] =
+                                mag
+                                (
+                                    faceCentres[fI]
+                                  - hitInfo[ownHitI - 1].hitPoint()
+                                );
+                        }
                     }
                 }
             }
-        }
 
-        triSurfaceScalarField internalClosenessField
-        (
-            IOobject
+            triSurfaceScalarField internalClosenessField
             (
-                sFeatFileName + ".internalCloseness",
-                runTime.constant(),
-                "triSurface",
-                runTime,
-                IOobject::NO_READ,
-                IOobject::NO_WRITE
-            ),
-            surf,
-            dimLength,
-            internalCloseness
-        );
+                IOobject
+                (
+                    sFeatFileName + ".internalCloseness",
+                    runTime.constant(),
+                    "triSurface",
+                    runTime,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE
+                ),
+                surf,
+                dimLength,
+                internalCloseness
+            );
 
-        internalClosenessField.write();
+            internalClosenessField.write();
 
-        triSurfaceScalarField externalClosenessField
-        (
-            IOobject
+            triSurfaceScalarField externalClosenessField
             (
-                sFeatFileName + ".externalCloseness",
-                runTime.constant(),
-                "triSurface",
-                runTime,
-                IOobject::NO_READ,
-                IOobject::NO_WRITE
-            ),
-            surf,
-            dimLength,
-            externalCloseness
-        );
+                IOobject
+                (
+                    sFeatFileName + ".externalCloseness",
+                    runTime.constant(),
+                    "triSurface",
+                    runTime,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE
+                ),
+                surf,
+                dimLength,
+                externalCloseness
+            );
 
-        externalClosenessField.write();
+            externalClosenessField.write();
 
-        if (writeVTK)
-        {
-            vtkSurfaceWriter().write
-            (
-                runTime.constant()/"triSurface",    // outputDir
-                sFeatFileName,                      // surfaceName
-                surf.points(),
-                faces,
-                "internalCloseness",                // fieldName
-                internalCloseness,
-                false,                              // isNodeValues
-                true                                // verbose
-            );
+            if (writeVTK)
+            {
+                vtkSurfaceWriter().write
+                (
+                    runTime.constant()/"triSurface",    // outputDir
+                    sFeatFileName,                      // surfaceName
+                    surf.points(),
+                    faces,
+                    "internalCloseness",                // fieldName
+                    internalCloseness,
+                    false,                              // isNodeValues
+                    true                                // verbose
+                );
 
-            vtkSurfaceWriter().write
-            (
-                runTime.constant()/"triSurface",    // outputDir
-                sFeatFileName,                      // surfaceName
-                surf.points(),
-                faces,
-                "externalCloseness",                // fieldName
-                externalCloseness,
-                false,                              // isNodeValues
-                true                                // verbose
-            );
+                vtkSurfaceWriter().write
+                (
+                    runTime.constant()/"triSurface",    // outputDir
+                    sFeatFileName,                      // surfaceName
+                    surf.points(),
+                    faces,
+                    "externalCloseness",                // fieldName
+                    externalCloseness,
+                    false,                              // isNodeValues
+                    true                                // verbose
+                );
+            }
         }
-    }
 
 
 #ifdef ENABLE_CURVATURE
-    if (args.optionFound("curvature"))
-    {
-        Info<< nl << "Extracting curvature of surface at the points." << endl;
+        if (curvature)
+        {
+            Info<< nl << "Extracting curvature of surface at the points."
+                << endl;
 
-        scalarField k = calcCurvature(surf);
+            scalarField k = calcCurvature(surf);
 
         // Modify the curvature values on feature edges and points to be zero.
 
-    //    forAll(newSet.featureEdges(), fEI)
-    //    {
-    //        const edge& e = surf.edges()[newSet.featureEdges()[fEI]];
-    //
-    //        k[surf.meshPoints()[e.start()]] = 0.0;
-    //        k[surf.meshPoints()[e.end()]] = 0.0;
-    //    }
+        //    forAll(newSet.featureEdges(), fEI)
+        //    {
+        //        const edge& e = surf.edges()[newSet.featureEdges()[fEI]];
+        //
+        //        k[surf.meshPoints()[e.start()]] = 0.0;
+        //        k[surf.meshPoints()[e.end()]] = 0.0;
+        //    }
 
-        triSurfacePointScalarField kField
-        (
-            IOobject
+            triSurfacePointScalarField kField
             (
-                sFeatFileName + ".curvature",
-                runTime.constant(),
-                "triSurface",
-                runTime,
-                IOobject::NO_READ,
-                IOobject::NO_WRITE
-            ),
-            surf,
-            dimLength,
-            k
-        );
+                IOobject
+                (
+                    sFeatFileName + ".curvature",
+                    runTime.constant(),
+                    "triSurface",
+                    runTime,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE
+                ),
+                surf,
+                dimLength,
+                k
+            );
 
-        kField.write();
+            kField.write();
 
-        if (writeVTK)
-        {
-            vtkSurfaceWriter().write
-            (
-                runTime.constant()/"triSurface",    // outputDir
-                sFeatFileName,                      // surfaceName
-                surf.points(),
-                faces,
-                "curvature",                        // fieldName
-                k,
-                true,                               // isNodeValues
-                true                                // verbose
-            );
+            if (writeVTK)
+            {
+                vtkSurfaceWriter().write
+                (
+                    runTime.constant()/"triSurface",    // outputDir
+                    sFeatFileName,                      // surfaceName
+                    surf.points(),
+                    faces,
+                    "curvature",                        // fieldName
+                    k,
+                    true,                               // isNodeValues
+                    true                                // verbose
+                );
+            }
         }
-    }
 #endif
 
 
-    if (args.optionFound("featureProximity"))
-    {
-        Info<< nl << "Extracting proximity of close feature points and edges "
-            << "to the surface" << endl;
+        if (featureProximity)
+        {
+            Info<< nl << "Extracting proximity of close feature points and "
+                << "edges to the surface" << endl;
 
-        const scalar searchDistance =
-            args.optionRead<scalar>("featureProximity");
+            const scalar searchDistance =
+                readScalar(surfaceDict.lookup("maxFeatureProximity"));
 
-        const scalar radiusSqr = sqr(searchDistance);
+            const scalar radiusSqr = sqr(searchDistance);
 
-        scalarField featureProximity(surf.size(), searchDistance);
+            scalarField featureProximity(surf.size(), searchDistance);
 
-        forAll(surf, fI)
-        {
-            const triPointRef& tri = surf[fI].tri(surf.points());
-            const point& triCentre = tri.circumCentre();
+            forAll(surf, fI)
+            {
+                const triPointRef& tri = surf[fI].tri(surf.points());
+                const point& triCentre = tri.circumCentre();
 
-            List<pointIndexHit> hitList;
+                List<pointIndexHit> hitList;
 
-            feMesh.allNearestFeatureEdges(triCentre, radiusSqr, hitList);
+                feMesh.allNearestFeatureEdges(triCentre, radiusSqr, hitList);
 
-            featureProximity[fI] =
-                calcProximityOfFeatureEdges
-                (
-                    feMesh,
-                    hitList,
-                    featureProximity[fI]
-                );
+                featureProximity[fI] =
+                    calcProximityOfFeatureEdges
+                    (
+                        feMesh,
+                        hitList,
+                        featureProximity[fI]
+                    );
 
-            feMesh.allNearestFeaturePoints(triCentre, radiusSqr, hitList);
+                feMesh.allNearestFeaturePoints(triCentre, radiusSqr, hitList);
 
-            featureProximity[fI] =
-                calcProximityOfFeaturePoints
-                (
-                    hitList,
-                    featureProximity[fI]
-                );
-        }
+                featureProximity[fI] =
+                    calcProximityOfFeaturePoints
+                    (
+                        hitList,
+                        featureProximity[fI]
+                    );
+            }
 
-        triSurfaceScalarField featureProximityField
-        (
-            IOobject
+            triSurfaceScalarField featureProximityField
             (
-                sFeatFileName + ".featureProximity",
-                runTime.constant(),
-                "triSurface",
-                runTime,
-                IOobject::NO_READ,
-                IOobject::NO_WRITE
-            ),
-            surf,
-            dimLength,
-            featureProximity
-        );
+                IOobject
+                (
+                    sFeatFileName + ".featureProximity",
+                    runTime.constant(),
+                    "triSurface",
+                    runTime,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE
+                ),
+                surf,
+                dimLength,
+                featureProximity
+            );
 
-        featureProximityField.write();
+            featureProximityField.write();
 
-        if (writeVTK)
-        {
-            vtkSurfaceWriter().write
-            (
-                runTime.constant()/"triSurface",    // outputDir
-                sFeatFileName,                      // surfaceName
-                surf.points(),
-                faces,
-                "featureProximity",                // fieldName
-                featureProximity,
-                false,                              // isNodeValues
-                true                                // verbose
-            );
+            if (writeVTK)
+            {
+                vtkSurfaceWriter().write
+                (
+                    runTime.constant()/"triSurface",    // outputDir
+                    sFeatFileName,                      // surfaceName
+                    surf.points(),
+                    faces,
+                    "featureProximity",                 // fieldName
+                    featureProximity,
+                    false,                              // isNodeValues
+                    true                                // verbose
+                );
+            }
         }
+
+        Info<< endl;
     }
 
     Info<< "End\n" << endl;
diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict
new file mode 100644
index 0000000000000000000000000000000000000000..3621d64a293ffa8a1a2daf3be441818036555e92
--- /dev/null
+++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict
@@ -0,0 +1,86 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      surfaceFeatureExtractDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+surface1.stl
+{
+    // extractFromFile || extractFromSurface
+    extractionMethod    extractFromFile;
+
+    extractFromFile
+    {
+        // Load from an existing feature edge file
+        featureEdgeFile "constant/triSurface/featureEdges.nas";
+    }
+    
+    trimFeatures
+    {
+        // Remove features with fewer than the specified number of edges
+        minElem         0;
+        
+        // Remove features shorter than the specified cumulative length
+        minLen          0.0;
+    }  
+    
+    subsetFeatures
+    {        
+        // Use a plane to select feature edges
+        // (normal)(basePoint)
+        plane               (1 0 0)(0 0 0);
+        
+        // Select feature edges using a box
+        // (minPt)(maxPt)
+        insideBox           (0 0 0)(1 1 1);
+        outsideBox          (0 0 0)(1 1 1);
+        
+        // Remove any non-manifold (open or > 2 connected faces) edges
+        manifoldEdges       no;
+    }
+    
+    // Output the curvature of the surface
+    curvature               no;
+    
+    // Output the proximity of feature points and edges to each other
+    featureProximity        no;
+    // The maximum search distance to use when looking for other feature
+    // points and edges
+    maxFeatureProximity     1;
+    
+    // Out put the closeness of surface elements to other surface elements.
+    closeness               no;
+
+    // Write options
+    writeVTK                no;
+    writeObj                yes;
+    writeFeatureEdgeMesh    no;
+}
+
+
+surface2.nas
+{
+    extractionMethod    extractFromSurface;
+
+    extractFromSurface
+    {
+        // Mark edges whose adjacent surface normals are at an angle less
+        // than includedAngle as features
+        // - 0  : selects no edges
+        // - 180: selects all edges
+        includedAngle   120;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/algorithms/indexedOctree/indexedOctree.C b/src/OpenFOAM/algorithms/indexedOctree/indexedOctree.C
index 024f7d27ecf4203c1684e3295bc7e20ad52692fc..f16dd2595c523183104a52a8330d62588e8f2758 100644
--- a/src/OpenFOAM/algorithms/indexedOctree/indexedOctree.C
+++ b/src/OpenFOAM/algorithms/indexedOctree/indexedOctree.C
@@ -2832,7 +2832,7 @@ Foam::pointIndexHit Foam::indexedOctree<Type>::findNearest
 ) const
 {
     label nearestShapeI = -1;
-    point nearestPoint;
+    point nearestPoint = vector::zero;
 
     if (nodes_.size())
     {
@@ -2847,10 +2847,6 @@ Foam::pointIndexHit Foam::indexedOctree<Type>::findNearest
             nearestPoint
         );
     }
-    else
-    {
-        nearestPoint = vector::zero;
-    }
 
     return pointIndexHit(nearestShapeI != -1, nearestPoint, nearestShapeI);
 }
diff --git a/src/edgeMesh/edgeFormats/nas/NASedgeFormat.C b/src/edgeMesh/edgeFormats/nas/NASedgeFormat.C
index 93196c0e7b537c046089811f91cf5aaeba6d194f..c043ae3c0bb38bba14596fcb06f91afebb4b012e 100644
--- a/src/edgeMesh/edgeFormats/nas/NASedgeFormat.C
+++ b/src/edgeMesh/edgeFormats/nas/NASedgeFormat.C
@@ -113,6 +113,17 @@ bool Foam::fileFormats::NASedgeFormat::read
             // discard groupID
             dynEdges.append(e);
         }
+        else if (cmd == "PLOTEL")
+        {
+            edge e;
+
+            // label groupId = readLabel(IStringStream(line.substr(16,8))());
+            e[0] = readLabel(IStringStream(line.substr(16,8))());
+            e[1] = readLabel(IStringStream(line.substr(24,8))());
+
+            // discard groupID
+            dynEdges.append(e);
+        }
         else if (cmd == "GRID")
         {
             label index = readLabel(IStringStream(line.substr(8,8))());
diff --git a/src/edgeMesh/edgeMesh.C b/src/edgeMesh/edgeMesh.C
index 113112fa288200c568776d3324b77052df627d0e..bc63e3d117a66e28586db39a78cc81e8d289da4e 100644
--- a/src/edgeMesh/edgeMesh.C
+++ b/src/edgeMesh/edgeMesh.C
@@ -28,6 +28,7 @@ License
 #include "addToRunTimeSelectionTable.H"
 #include "addToMemberFunctionSelectionTable.H"
 #include "ListOps.H"
+#include "EdgeMap.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -327,10 +328,7 @@ void Foam::edgeMesh::mergePoints(const scalar mergeDist)
         }
 
         // Compact using a hashtable and commutative hash of edge.
-        HashTable<label, edge, Hash<edge> > edgeToLabel
-        (
-            2*edges_.size()
-        );
+        EdgeMap<label> edgeToLabel(2*edges_.size());
 
         label newEdgeI = 0;
 
@@ -349,13 +347,7 @@ void Foam::edgeMesh::mergePoints(const scalar mergeDist)
 
         edges_.setSize(newEdgeI);
 
-        for
-        (
-            HashTable<label, edge, Hash<edge> >::const_iterator iter =
-                edgeToLabel.begin();
-            iter != edgeToLabel.end();
-            ++iter
-        )
+        forAllConstIter(EdgeMap<label>, edgeToLabel, iter)
         {
             edges_[iter()] = iter.key();
         }
@@ -363,4 +355,35 @@ void Foam::edgeMesh::mergePoints(const scalar mergeDist)
 }
 
 
+void Foam::edgeMesh::mergeEdges()
+{
+    EdgeMap<label> existingEdges(2*edges_.size());
+
+    label curEdgeI = 0;
+    forAll(edges_, edgeI)
+    {
+        const edge& e = edges_[edgeI];
+
+        if (existingEdges.insert(e, curEdgeI))
+        {
+            curEdgeI++;
+        }
+    }
+
+    if (debug)
+    {
+        Info<< "Merging duplicate edges: "
+            << edges_.size() - existingEdges.size()
+            << " edges will be deleted." << endl;
+    }
+
+    edges_.setSize(existingEdges.size());
+
+    forAllConstIter(EdgeMap<label>, existingEdges, iter)
+    {
+        edges_[iter()] = iter.key();
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/edgeMesh/edgeMesh.H b/src/edgeMesh/edgeMesh.H
index baa6142815464b4872c5b1f52a09ec927f45741f..66574e9fac6866fac894d3fc9478ea130a270c05 100644
--- a/src/edgeMesh/edgeMesh.H
+++ b/src/edgeMesh/edgeMesh.H
@@ -246,6 +246,9 @@ public:
         //- Merge common points (points within mergeDist)
         void mergePoints(const scalar mergeDist);
 
+        //- Merge similar edges
+        void mergeEdges();
+
 
     // Write
 
diff --git a/src/finiteVolume/Make/files b/src/finiteVolume/Make/files
index 3652bea89a44b4caf93d16ac97cd4e3ff58eb7f4..0e2de76c07e9afe4c38740a43dfcbf49a9079ec8 100644
--- a/src/finiteVolume/Make/files
+++ b/src/finiteVolume/Make/files
@@ -124,6 +124,7 @@ $(derivedFvPatchFields)/mappedFixedPushedInternalValue/mappedFixedPushedInternal
 $(derivedFvPatchFields)/mappedFixedValue/mappedFixedValueFvPatchFields.C
 $(derivedFvPatchFields)/mappedVelocityFluxFixedValue/mappedVelocityFluxFixedValueFvPatchField.C
 $(derivedFvPatchFields)/mappedFlowRate/mappedFlowRateFvPatchVectorField.C
+$(derivedFvPatchFields)/fixedMean/fixedMeanFvPatchFields.C
 $(derivedFvPatchFields)/fan/fanFvPatchFields.C
 $(derivedFvPatchFields)/fanPressure/fanPressureFvPatchScalarField.C
 $(derivedFvPatchFields)/buoyantPressure/buoyantPressureFvPatchScalarField.C
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.C
new file mode 100644
index 0000000000000000000000000000000000000000..e59f98910eb8a1f23cdaf39b7f48f59205b09a73
--- /dev/null
+++ b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.C
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2012 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fixedMeanFvPatchField.H"
+#include "volFields.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+template<class Type>
+fixedMeanFvPatchField<Type>::fixedMeanFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    fixedValueFvPatchField<Type>(p, iF),
+    meanValue_(pTraits<Type>::zero)
+{}
+
+
+template<class Type>
+fixedMeanFvPatchField<Type>::fixedMeanFvPatchField
+(
+    const fixedMeanFvPatchField<Type>& ptf,
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const fvPatchFieldMapper& mapper
+)
+:
+    fixedValueFvPatchField<Type>(ptf, p, iF, mapper),
+    meanValue_(ptf.meanValue_)
+{}
+
+
+template<class Type>
+fixedMeanFvPatchField<Type>::fixedMeanFvPatchField
+(
+    const fvPatch& p,
+    const DimensionedField<Type, volMesh>& iF,
+    const dictionary& dict
+)
+:
+    fixedValueFvPatchField<Type>(p, iF, dict),
+    meanValue_(pTraits<Type>(dict.lookup("meanValue")))
+{}
+
+
+template<class Type>
+fixedMeanFvPatchField<Type>::fixedMeanFvPatchField
+(
+    const fixedMeanFvPatchField<Type>& ptf
+)
+:
+    fixedValueFvPatchField<Type>(ptf),
+    meanValue_(ptf.meanValue_)
+{}
+
+
+template<class Type>
+fixedMeanFvPatchField<Type>::fixedMeanFvPatchField
+(
+    const fixedMeanFvPatchField<Type>& ptf,
+    const DimensionedField<Type, volMesh>& iF
+)
+:
+    fixedValueFvPatchField<Type>(ptf, iF),
+    meanValue_(ptf.meanValue_)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+void fixedMeanFvPatchField<Type>::updateCoeffs()
+{
+    if (this->updated())
+    {
+        return;
+    }
+
+    Field<Type> newValues(this->patchInternalField());
+
+    Type meanValuePsi =
+        gSum(this->patch().magSf()*newValues)
+       /gSum(this->patch().magSf());
+
+    if (mag(meanValue_) > SMALL && mag(meanValuePsi)/mag(meanValue_) > 0.5)
+    {
+        newValues *= mag(meanValue_)/mag(meanValuePsi);
+    }
+    else
+    {
+        newValues += (meanValue_ - meanValuePsi);
+    }
+
+    this->operator==(newValues);
+
+    fixedValueFvPatchField<Type>::updateCoeffs();
+}
+
+
+template<class Type>
+void fixedMeanFvPatchField<Type>::write(Ostream& os) const
+{
+    fvPatchField<Type>::write(os);
+    os.writeKeyword("meanValue") << meanValue_ << token::END_STATEMENT << nl;
+    this->writeEntry("value", os);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.H b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.H
new file mode 100644
index 0000000000000000000000000000000000000000..b59969a967210d0a31a76751e57fbf2996cb6301
--- /dev/null
+++ b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchField.H
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2012 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::fixedMeanFvPatchField
+
+Description
+    Extrapolates field to the patch using the near-cell values and adjusts
+    the distribution to match the specified meanValue.
+
+SourceFiles
+    fixedMeanFvPatchField.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef fixedMeanFvPatchField_H
+#define fixedMeanFvPatchField_H
+
+#include "fixedValueFvPatchFields.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                  Class fixedMeanFvPatch Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Type>
+class fixedMeanFvPatchField
+:
+    public fixedValueFvPatchField<Type>
+{
+
+protected:
+
+    // Protected data
+
+        //- MeanValue value the field is adjusted to maintain
+        Type meanValue_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("fixedMean");
+
+
+    // Constructors
+
+        //- Construct from patch and internal field
+        fixedMeanFvPatchField
+        (
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct from patch, internal field and dictionary
+        fixedMeanFvPatchField
+        (
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const dictionary&
+        );
+
+        //- Construct by mapping given fixedMeanFvPatchField
+        //  onto a new patch
+        fixedMeanFvPatchField
+        (
+            const fixedMeanFvPatchField<Type>&,
+            const fvPatch&,
+            const DimensionedField<Type, volMesh>&,
+            const fvPatchFieldMapper&
+        );
+
+        //- Construct as copy
+        fixedMeanFvPatchField
+        (
+            const fixedMeanFvPatchField<Type>&
+        );
+
+        //- Construct and return a clone
+        virtual tmp<fvPatchField<Type> > clone() const
+        {
+            return tmp<fvPatchField<Type> >
+            (
+                new fixedMeanFvPatchField<Type>(*this)
+            );
+        }
+
+        //- Construct as copy setting internal field reference
+        fixedMeanFvPatchField
+        (
+            const fixedMeanFvPatchField<Type>&,
+            const DimensionedField<Type, volMesh>&
+        );
+
+        //- Construct and return a clone setting internal field reference
+        virtual tmp<fvPatchField<Type> > clone
+        (
+            const DimensionedField<Type, volMesh>& iF
+        ) const
+        {
+            return tmp<fvPatchField<Type> >
+            (
+                new fixedMeanFvPatchField<Type>(*this, iF)
+            );
+        }
+
+
+    // Member functions
+
+        // Evaluation functions
+
+            //- Update the coefficients associated with the patch field
+            virtual void updateCoeffs();
+
+        //- Write
+        virtual void write(Ostream&) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+#   include "fixedMeanFvPatchField.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.C b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..e8e269c8a5b4689fdf26d1306f6b5cf0ce59f834
--- /dev/null
+++ b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.C
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2012 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fixedMeanFvPatchFields.H"
+#include "volMesh.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+makePatchFields(fixedMean);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.H b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.H
new file mode 100644
index 0000000000000000000000000000000000000000..64996c3a682db52980833172f8c9104cc9862fee
--- /dev/null
+++ b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFields.H
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2012 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef fixedMeanFvPatchFields_H
+#define fixedMeanFvPatchFields_H
+
+#include "fixedMeanFvPatchField.H"
+#include "fieldTypes.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+makePatchTypeFieldTypedefs(fixedMean);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.C b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFieldsFwd.H
similarity index 50%
rename from applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.C
rename to src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFieldsFwd.H
index 63e2b904bf5adff04fc1efad940e17d14ad52ffb..9f04dc965bfe98be0cb1e99fcc22a644c4613a1f 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/CGALPolyhedron/buildCGALPolyhedron.C
+++ b/src/finiteVolume/fields/fvPatchFields/derived/fixedMean/fixedMeanFvPatchFieldsFwd.H
@@ -23,67 +23,28 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "buildCGALPolyhedron.H"
+#ifndef fixedMeanFvPatchFieldsFwd_H
+#define fixedMeanFvPatchFieldsFwd_H
 
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+#include "fieldTypes.H"
 
-Foam::buildCGALPolyhedron::buildCGALPolyhedron
-(
-    const Foam::triSurface& surf
-)
-:
-    CGAL::Modifier_base<HalfedgeDS>(),
-    surf_(surf)
-{}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::buildCGALPolyhedron::~buildCGALPolyhedron()
-{}
-
-
-// * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * * //
-
-void Foam::buildCGALPolyhedron::operator()
-(
-    HalfedgeDS& hds
-)
+namespace Foam
 {
-    typedef HalfedgeDS::Traits     Traits;
-    typedef Traits::Point_3        Point;
-
-    // Postcondition: `hds' is a valid polyhedral surface.
-    CGAL::Polyhedron_incremental_builder_3<HalfedgeDS> B(hds, false);
-
-    B.begin_surface
-    (
-        surf_.points().size(),      // n points
-        surf_.size(),               // n facets
-        2*surf_.edges().size()      // n halfedges
-    );
 
-    forAll(surf_.points(), pI)
-    {
-        const Foam::point& p = surf_.points()[pI];
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-        B.add_vertex(Point(p.x(), p.y(), p.z()));
-    }
+template<class Type> class fixedMeanFvPatchField;
 
-    forAll(surf_, fI)
-    {
-        B.begin_facet();
+makePatchTypeFieldTypedefs(fixedMean);
 
-        B.add_vertex_to_facet(surf_[fI][0]);
-        B.add_vertex_to_facet(surf_[fI][1]);
-        B.add_vertex_to_facet(surf_[fI][2]);
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-        B.end_facet();
-    }
+} // End namespace Foam
 
-    B.end_surface();
-}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+#endif
 
 // ************************************************************************* //
diff --git a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
index e39eae5704a61cbc53438060ac1a8ca77154144e..465734ff879bd4aa2507b05fe3779cb1d4984ea9 100644
--- a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
+++ b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
@@ -33,11 +33,15 @@ License
 #include "OFstream.H"
 #include "IFstream.H"
 #include "unitConversion.H"
+#include "EdgeMap.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 defineTypeNameAndDebug(Foam::surfaceFeatures, 0);
 
+const Foam::scalar Foam::surfaceFeatures::parallelTolerance =
+    sin(degToRad(1.0));
+
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
@@ -170,7 +174,10 @@ void Foam::surfaceFeatures::setFromStatus(const List<edgeStatus>& edgeStat)
 
 
 //construct feature points where more than 2 feature edges meet
-void Foam::surfaceFeatures::calcFeatPoints(const List<edgeStatus>& edgeStat)
+void Foam::surfaceFeatures::calcFeatPoints
+(
+    const List<edgeStatus>& edgeStat
+)
 {
     DynamicList<label> featurePoints(surf_.nPoints()/1000);
 
@@ -200,6 +207,60 @@ void Foam::surfaceFeatures::calcFeatPoints(const List<edgeStatus>& edgeStat)
 }
 
 
+void Foam::surfaceFeatures::classifyFeatureAngles
+(
+    const labelListList& edgeFaces,
+    List<edgeStatus>& edgeStat,
+    const scalar minCos
+) const
+{
+    const vectorField& faceNormals = surf_.faceNormals();
+    const pointField& points = surf_.points();
+
+    forAll(edgeFaces, edgeI)
+    {
+        const labelList& eFaces = edgeFaces[edgeI];
+
+        if (eFaces.size() != 2)
+        {
+            // Non-manifold. What to do here? Is region edge? external edge?
+            edgeStat[edgeI] = REGION;
+        }
+        else
+        {
+            label face0 = eFaces[0];
+            label face1 = eFaces[1];
+
+            if (surf_[face0].region() != surf_[face1].region())
+            {
+                edgeStat[edgeI] = REGION;
+            }
+            else
+            {
+                if ((faceNormals[face0] & faceNormals[face1]) < minCos)
+                {
+
+                    // Check if convex or concave by looking at angle
+                    // between face centres and normal
+                    vector f0Tof1 =
+                        surf_[face1].centre(points)
+                      - surf_[face0].centre(points);
+
+                    if ((f0Tof1 & faceNormals[face0]) > 0.0)
+                    {
+                        edgeStat[edgeI] = INTERNAL;
+                    }
+                    else
+                    {
+                        edgeStat[edgeI] = EXTERNAL;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
 // Returns next feature edge connected to pointI with correct value.
 Foam::label Foam::surfaceFeatures::nextFeatEdge
 (
@@ -436,6 +497,80 @@ Foam::surfaceFeatures::surfaceFeatures
 }
 
 
+Foam::surfaceFeatures::surfaceFeatures
+(
+    const triSurface& surf,
+    const pointField& points,
+    const edgeList& edges,
+    const scalar mergeTol
+)
+:
+    surf_(surf),
+    featurePoints_(0),
+    featureEdges_(0),
+    externalStart_(0),
+    internalStart_(0)
+{
+    // Match edge mesh edges with the triSurface edges
+
+    const labelListList& surfEdgeFaces = surf_.edgeFaces();
+    const edgeList& surfEdges = surf_.edges();
+
+    scalar mergeTolSqr = sqr(mergeTol);
+
+    EdgeMap<label> dynFeatEdges(2*edges.size());
+    DynamicList<labelList> dynFeatureEdgeFaces(edges.size());
+
+    labelList edgeLabel;
+
+    nearestFeatEdge
+    (
+        edges,
+        points,
+        mergeTolSqr,
+        edgeLabel     // label of surface edge or -1
+    );
+
+    label count = 0;
+    forAll(edgeLabel, sEdgeI)
+    {
+        const label sEdge = edgeLabel[sEdgeI];
+
+        if (sEdge == -1)
+        {
+            continue;
+        }
+
+        dynFeatEdges.insert(surfEdges[sEdge], count++);
+        dynFeatureEdgeFaces.append(surfEdgeFaces[sEdge]);
+    }
+
+    // Find whether an edge is external or internal
+    List<edgeStatus> edgeStat(dynFeatEdges.size(), NONE);
+
+    classifyFeatureAngles(dynFeatureEdgeFaces, edgeStat, GREAT);
+
+    // Transfer the edge status to a list encompassing all edges in the surface
+    // so that calcFeatPoints can be used.
+    List<edgeStatus> allEdgeStat(surf_.nEdges(), NONE);
+
+    forAll(allEdgeStat, eI)
+    {
+        EdgeMap<label>::const_iterator iter = dynFeatEdges.find(surfEdges[eI]);
+
+        if (iter != dynFeatEdges.end())
+        {
+            allEdgeStat[eI] = edgeStat[iter()];
+        }
+    }
+
+    edgeStat.clear();
+    dynFeatEdges.clear();
+
+    setFromStatus(allEdgeStat);
+}
+
+
 //- Construct as copy
 Foam::surfaceFeatures::surfaceFeatures(const surfaceFeatures& sf)
 :
@@ -496,54 +631,10 @@ void Foam::surfaceFeatures::findFeatures(const scalar includedAngle)
 {
     scalar minCos = Foam::cos(degToRad(180.0 - includedAngle));
 
-    const labelListList& edgeFaces = surf_.edgeFaces();
-    const vectorField& faceNormals = surf_.faceNormals();
-    const pointField& points = surf_.points();
-
     // Per edge whether is feature edge.
     List<edgeStatus> edgeStat(surf_.nEdges(), NONE);
 
-    forAll(edgeFaces, edgeI)
-    {
-        const labelList& eFaces = edgeFaces[edgeI];
-
-        if (eFaces.size() != 2)
-        {
-            // Non-manifold. What to do here? Is region edge? external edge?
-            edgeStat[edgeI] = REGION;
-        }
-        else
-        {
-            label face0 = eFaces[0];
-            label face1 = eFaces[1];
-
-            if (surf_[face0].region() != surf_[face1].region())
-            {
-                edgeStat[edgeI] = REGION;
-            }
-            else
-            {
-                if ((faceNormals[face0] & faceNormals[face1]) < minCos)
-                {
-
-                    // Check if convex or concave by looking at angle
-                    // between face centres and normal
-                    vector f0Tof1 =
-                        surf_[face1].centre(points)
-                        - surf_[face0].centre(points);
-
-                    if ((f0Tof1 & faceNormals[face0]) > 0.0)
-                    {
-                        edgeStat[edgeI] = INTERNAL;
-                    }
-                    else
-                    {
-                        edgeStat[edgeI] = EXTERNAL;
-                    }
-                }
-            }
-        }
-    }
+    classifyFeatureAngles(surf_.edgeFaces(), edgeStat, minCos);
 
     setFromStatus(edgeStat);
 }
@@ -1147,6 +1238,9 @@ void Foam::surfaceFeatures::nearestSurfEdge
 
     const pointField& localPoints = surf_.localPoints();
 
+    treeBoundBox searchDomain(localPoints);
+    searchDomain.inflate(0.1);
+
     indexedOctree<treeDataEdge> ppTree
     (
         treeDataEdge
@@ -1156,7 +1250,7 @@ void Foam::surfaceFeatures::nearestSurfEdge
             localPoints,
             selectedEdges
         ),          // all information needed to do geometric checks
-        treeBoundBox(localPoints),  // overall search domain
+        searchDomain,  // overall search domain
         8,      // maxLevel
         10,     // leafsize
         3.0     // duplicity
@@ -1216,6 +1310,8 @@ void Foam::surfaceFeatures::nearestSurfEdge
     pointOnEdge.setSize(selectedSampleEdges.size());
     pointOnFeature.setSize(selectedSampleEdges.size());
 
+    treeBoundBox searchDomain(surf_.localPoints());
+
     indexedOctree<treeDataEdge> ppTree
     (
         treeDataEdge
@@ -1224,11 +1320,11 @@ void Foam::surfaceFeatures::nearestSurfEdge
             surf_.edges(),
             surf_.localPoints(),
             selectedEdges
-        ),          // all information needed to do geometric checks
-        treeBoundBox(surf_.localPoints()),  // overall search domain
-        8,      // maxLevel
-        10,     // leafsize
-        3.0     // duplicity
+        ),              // all information needed to do geometric checks
+        searchDomain,   // overall search domain
+        8,              // maxLevel
+        10,             // leafsize
+        3.0             // duplicity
     );
 
     forAll(selectedSampleEdges, i)
@@ -1262,6 +1358,67 @@ void Foam::surfaceFeatures::nearestSurfEdge
 }
 
 
+void Foam::surfaceFeatures::nearestFeatEdge
+(
+    const edgeList& edges,
+    const pointField& points,
+    scalar searchSpanSqr,   // Search span
+    labelList& edgeLabel
+) const
+{
+    edgeLabel = labelList(surf_.nEdges(), -1);
+
+    treeBoundBox searchDomain(points);
+    searchDomain.inflate(0.1);
+
+    indexedOctree<treeDataEdge> ppTree
+    (
+        treeDataEdge
+        (
+            false,
+            edges,
+            points,
+            identity(edges.size())
+        ),          // all information needed to do geometric checks
+        searchDomain,  // overall search domain
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
+    );
+
+    const edgeList& surfEdges = surf_.edges();
+    const pointField& surfLocalPoints = surf_.localPoints();
+
+    forAll(surfEdges, edgeI)
+    {
+        const edge& sample = surfEdges[edgeI];
+
+        const point& startPoint = surfLocalPoints[sample.start()];
+        const point& midPoint = sample.centre(surfLocalPoints);
+
+        pointIndexHit infoMid = ppTree.findNearest
+        (
+            midPoint,
+            searchSpanSqr
+        );
+
+        if (infoMid.hit())
+        {
+            const vector surfEdgeDir = midPoint - startPoint;
+
+            const edge& featEdge = edges[infoMid.index()];
+            const vector featEdgeDir = featEdge.vec(points);
+
+            // Check that the edges are nearly parallel
+            if (mag(surfEdgeDir ^ featEdgeDir) < parallelTolerance)
+            {
+                edgeLabel[edgeI] = edgeI;
+            }
+        }
+    }
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 void Foam::surfaceFeatures::operator=(const surfaceFeatures& rhs)
diff --git a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
index 7f7101b662a04046d8888d0b698f09735812141d..0fca9798a2e312c613ace576f1ba12b19c443069 100644
--- a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
+++ b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
@@ -95,6 +95,11 @@ private:
         {}
     };
 
+    // Static data
+
+        //- Tolerance for determining whether two vectors are parallel
+        static const scalar parallelTolerance;
+
 
     // Private data
 
@@ -130,6 +135,14 @@ private:
         //- Construct feature points where more than 2 feature edges meet
         void calcFeatPoints(const List<edgeStatus>&);
 
+        //- Classify the angles of the feature edges
+        void classifyFeatureAngles
+        (
+            const labelListList& edgeFaces,
+            List<edgeStatus>& edgeStat,
+            const scalar minCos
+        ) const;
+
         //- Choose next unset feature edge.
         label nextFeatEdge
         (
@@ -186,6 +199,15 @@ public:
         //- Construct from file
         surfaceFeatures(const triSurface&, const fileName& fName);
 
+        //- Construct from pointField and edgeList (edgeMesh)
+        surfaceFeatures
+        (
+            const triSurface&,
+            const pointField& points,
+            const edgeList& edges,
+            const scalar mergeTol = 1e-6
+        );
+
         //- Construct as copy
         surfaceFeatures(const surfaceFeatures&);
 
@@ -327,9 +349,8 @@ public:
                 pointField& edgePoint
             ) const;
 
-
             //- Find nearest surface edge (out of selectedEdges) for each
-            // sample edge.
+            //  sample edge.
             //  Sets:
             //  - edgeLabel         : label of surface edge.
             //  - pointOnEdge       : exact position of nearest point on edge.
@@ -347,6 +368,16 @@ public:
                 pointField& pointOnFeature  // point on sample edge
             ) const;
 
+            //- Find nearest feature edge to each surface edge. Uses the
+            //  mid-point of the surface edges.
+            void nearestFeatEdge
+            (
+                const edgeList& edges,
+                const pointField& points,
+                scalar searchSpanSqr,
+                labelList& edgeLabel
+            ) const;
+
 
         // Write