diff --git a/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C b/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
index 1aa1113615a0487eb488c34d4dbf547ecad14b4b..ac3b5a4511de9f6b631a8202e14d10dd895a2e5a 100644
--- a/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
+++ b/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
@@ -2047,33 +2047,41 @@ void Foam::conformalVoronoiMesh::calcDualMesh
     // Loop over all dual faces and merge points to remove faces that
     // are not wanted.
 
-    for
-    (
-        Triangulation::Finite_edges_iterator eit = finite_edges_begin();
-        eit != finite_edges_end();
-        ++eit
-    )
+    Info<< nl << "    Collapsing unnecessary faces" << endl;
+
+    label nCollapsedFaces = 0;
+
+    do
     {
-        Cell_handle c = eit->first;
-        Vertex_handle vA = c->vertex(eit->second);
-        Vertex_handle vB = c->vertex(eit->third);
+        nCollapsedFaces = 0;
 
-        if
+        for
         (
-            vA->internalOrBoundaryPoint()
-         || vB->internalOrBoundaryPoint()
+            Triangulation::Finite_edges_iterator eit = finite_edges_begin();
+            eit != finite_edges_end();
+            ++eit
         )
         {
-            face newDualFace = buildDualFace(eit);
-
-            bool keepFace = assessFace(newDualFace, vA, vB, points);
+            Cell_handle c = eit->first;
+            Vertex_handle vA = c->vertex(eit->second);
+            Vertex_handle vB = c->vertex(eit->third);
 
-            if (!keepFace)
+            if
+            (
+                vA->internalOrBoundaryPoint()
+                || vB->internalOrBoundaryPoint()
+            )
             {
-
+                if (collapseFace(eit, points))
+                {
+                    nCollapsedFaces++;
+                }
             }
         }
-    }
+
+        Info<< "        Collapsed " << nCollapsedFaces << " faces" << endl;
+
+    } while (nCollapsedFaces > 0);
 
     // ~~~~~~~~~~~~ dual face and owner neighbour construction ~~~~~~~~~~~~~~~~~
 
@@ -2116,8 +2124,14 @@ void Foam::conformalVoronoiMesh::calcDualMesh
         {
             face newDualFace = buildDualFace(eit);
 
+            //bool keepFace = assessFace(newDualFace, vA, vB, points);
+
+            // if (newDualFace.size() >= 3 && keepFace)
+            // {
+
             if (newDualFace.size() >= 3)
             {
+
                 label dcA = vA->index();
 
                 if (!vA->internalOrBoundaryPoint())
@@ -2221,8 +2235,12 @@ void Foam::conformalVoronoiMesh::calcDualMesh
     owner.setSize(nInternalFaces);
     neighbour.setSize(nInternalFaces);
 
+    timeCheck();
+
     sortFaces(faces, owner, neighbour);
 
+    timeCheck();
+
     addPatches
     (
         nInternalFaces,
@@ -2235,6 +2253,37 @@ void Foam::conformalVoronoiMesh::calcDualMesh
         patchOwners,
         false
     );
+
+    removeUnusedPoints(faces, points);
+
+    timeCheck();
+
+    // Write out faces to be removed as a list of labels to be used in
+    // faceSet
+
+    // DynamicList<label> facesToBeRemoved;
+
+    // labelList nEdgeHistogram(12, 0);
+
+    // forAll(faces, fI)
+    // {
+    //     const face& f = faces[fI];
+
+    //     if (!assessFace(f, targetCellSize(f.centre(points)), points))
+    //     {
+    //         facesToBeRemoved.append(fI);
+
+    //         nEdgeHistogram[f.size()]++;
+    //     }
+    // }
+
+    // fileName fName = "facesToBeRemoved";
+
+    // OFstream str(fName);
+
+    // str << facesToBeRemoved;
+
+    // Info<< nEdgeHistogram << endl;
 }
 
 
@@ -2318,6 +2367,7 @@ void Foam::conformalVoronoiMesh::calcTetMesh
     label faceI = 0;
 
     labelList verticesOnTriFace(3, -1);
+
     face newFace(verticesOnTriFace);
 
     for
@@ -2513,10 +2563,21 @@ bool Foam::conformalVoronoiMesh::assessFace
 
     scalar averageCellSize = averageAnyCellSize(vA, vB);
 
-    scalar smallFaceAreaCoeff = 0.01;
+    return assessFace(f, averageCellSize, pts);
+}
+
+
+bool Foam::conformalVoronoiMesh::assessFace
+(
+    const face& f,
+    scalar targetFaceSize,
+    const pointField& pts
+) const
+{
+    scalar smallFaceAreaCoeff = 0.003;
     scalar highAspectRatioFaceAreaCoeff = 0.1;
     scalar aspectRatioLimit = 2.0;
-    scalar targetArea = sqr(averageCellSize);
+    scalar targetArea = sqr(targetFaceSize);
 
     const edgeList& eds = f.edges();
 
@@ -2550,24 +2611,243 @@ bool Foam::conformalVoronoiMesh::assessFace
         keepFace = false;
     }
 
-    if (!keepFace)
+    // if (!keepFace)
+    // {
+    //     Info<< nl << "Area " << area << nl
+    //         << "targetFaceSize " << targetFaceSize << nl
+    //         << "Area ratio "
+    //         << area/max(sqr(targetFaceSize), VSMALL) << nl
+    //         << "aspectRatio " << aspectRatio << nl
+    //         << endl;
+
+    //     forAll(f, i)
+    //     {
+    //         meshTools::writeOBJ(Info, pts[f[i]]);
+    //     }
+
+    //     Info<< nl;
+    // }
+
+    return keepFace;
+}
+
+
+bool Foam::conformalVoronoiMesh::collapseFace
+(
+    const Triangulation::Finite_edges_iterator& eit,
+    pointField& pts
+)
+{
+    scalar smallEdgeLengthCoeff = 1e-6;
+    scalar smallFaceAreaCoeff = sqr(smallEdgeLengthCoeff);
+    scalar collapseToEdgeCoeff = 10;
+
+    Cell_handle c = eit->first;
+    Vertex_handle vA = c->vertex(eit->second);
+    Vertex_handle vB = c->vertex(eit->third);
+
+    scalar targetFaceSize = averageAnyCellSize(vA, vB);
+    scalar targetArea = sqr(targetFaceSize);
+
+    face dualFace = buildDualFace(eit);
+
+    if (dualFace.size() < 3)
+    {
+        // This face has been collapsed already
+        return false;
+    }
+
+    scalar area = dualFace.mag(pts);
+
+    if (area < smallFaceAreaCoeff*targetArea)
     {
-        Info<< nl << "Area " << area << nl
-            << "averageCellSize " << averageCellSize << nl
-            << "Area ratio "
-            << area/max(sqr(averageCellSize), VSMALL) << nl
-            << "aspectRatio " << aspectRatio << nl
-            << endl;
+        // Collapse the dual face
+
+        // Determine if the face should be collapsed to a line or a
+        // point
+
+        const edgeList& eds = dualFace.edges();
 
-        forAll(f, i)
+        label longestEdgeI = -1;
+
+        scalar longestEdgeLength = -SMALL;
+
+        forAll(eds, edI)
+        {
+            if (eds[edI].mag(pts) > longestEdgeLength)
+            {
+                longestEdgeI = edI;
+
+                longestEdgeLength = eds[edI].mag(pts);
+            }
+        }
+
+        if
+        (
+            longestEdgeLength
+          > collapseToEdgeCoeff*smallEdgeLengthCoeff*targetFaceSize
+        )
         {
-            meshTools::writeOBJ(Info, pts[f[i]]);
+            // Collapse to edge
+
+            // Start at either end of the longest edge and consume the
+            // rest of the points of the face
+
+            const edge& longestEd = eds[longestEdgeI];
+
+            label longestEdStartPtI = longestEd.start();
+            label longestEdEndPtI = longestEd.end();
+
+            label revEdI = longestEdgeI;
+            label fwdEdI = longestEdgeI;
+
+            point revPt = pts[longestEdStartPtI];
+            point fwdPt = pts[longestEdEndPtI];
+
+            // Circulate around the face
+
+            Info<< nl << "# Before" << dualFace << endl;
+
+            forAll(dualFace, fPtI)
+            {
+                meshTools::writeOBJ(Info, pts[dualFace[fPtI]]);
+            }
+
+            for (label fcI = 1; fcI <= label(eds.size()/2); fcI++)
+            {
+                revEdI = eds.rcIndex(revEdI);
+                fwdEdI = eds.fcIndex(fwdEdI);
+
+                const edge& revEd = eds[revEdI];
+                const edge& fwdEd = eds[fwdEdI];
+
+                if (fcI < label(eds.size()/2))
+                {
+                    revPt += pts[revEd.start()];
+                    fwdPt += pts[fwdEd.end()];
+
+                    // THE EDGE IS INDEXING THE POINTS DIRECTLY, NOT
+                    // VIA THE FACE
+                    // dualFace[revEd.start()] =
+                    // longestEdStartPtI; dualFace[fwdEd.end()] =
+                    // longestEdEndPtI;
+                }
+                else
+                {
+                    // Final circulation
+
+                    if
+                    (
+                        eds.size() % 2 == 1
+                     && revEd.start() == fwdEd.end()
+                    )
+                    {
+                        // Odd number of edges, give final point to
+                        // the edge direction that has the shorter
+                        // final edge
+
+                        if (fwdEd.mag(pts) < revEd.mag(pts))
+                        {
+                            fwdPt += pts[fwdEd.end()];
+                            // dualFace[fwdEd.end()] = longestEdEndPtI;
+
+                            fwdPt /= (fcI + 1);
+                            revPt /= fcI;
+                        }
+                        else
+                        {
+                            revPt += pts[revEd.start()];
+                            // dualFace[revEd.start()] = longestEdStartPtI;
+
+                            revPt /= (fcI + 1);
+                            fwdPt /= fcI;
+                        }
+                    }
+                    else if
+                    (
+                        eds.size() % 2 == 0
+                     && revEd.start() == fwdEd.start()
+                     && revEd.end() == fwdEd.end()
+                    )
+                    {
+                        // Even number of edges
+
+                        revPt /= fcI;
+                        fwdPt /= fcI;
+                    }
+                    else
+                    {
+                        FatalErrorIn("Foam::conformalVoronoiMesh::collapseFace")
+                            << "Face circulation failed for face "
+                            << dualFace << nl
+                            << exit(FatalError);
+                    }
+                }
+            }
+
+            Info<< "# Collapsed" << endl;
+
+            meshTools::writeOBJ(Info, revPt);
+            meshTools::writeOBJ(Info, fwdPt);
+
+            return false;
         }
+        else
+        {
+            // Collapse to point
+
+            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++;
+
+            label nPts = 0;
+
+            point resultantPt = vector::zero;
+
+            label ccStartI = cc1->cellIndex();
 
-        Info<< nl;
+            do
+            {
+                label& cc1I = cc1->cellIndex();
+                label& cc2I = cc2->cellIndex();
+
+                if (cc1I < 0 || cc2I < 0)
+                {
+                    FatalErrorIn("Foam::conformalVoronoiMesh::collapseFace")
+                        << "Dual face uses circumcenter defined by a "
+                        << "Delaunay tetrahedron with no internal "
+                        << "or boundary points.  Defining Delaunay edge ends: "
+                        << topoint(vA->point()) << " "
+                        << topoint(vB->point()) << nl
+                        << exit(FatalError);
+                }
+
+                if (cc1I != cc2I)
+                {
+                    resultantPt += pts[cc1I];
+                    nPts++;
+                }
+
+                cc1I = ccStartI;
+                cc2I = ccStartI;
+                cc1++;
+                cc2++;
+
+            } while (cc1 != ccStart);
+
+            resultantPt /= nPts;
+
+            pts[ccStartI] = resultantPt;
+
+            return true;
+        }
     }
 
-    return keepFace;
+    return false;
 }
 
 
@@ -2583,7 +2863,7 @@ void Foam::conformalVoronoiMesh::sortFaces
     // + within each block of equal value for owner, neighbour is sorted in
     //   ascending cell order.
     // + faces sorted to correspond
-    // i.e:
+    // e.g.
     // owner | neighbour
     // 0     | 2
     // 0     | 23
@@ -2595,6 +2875,10 @@ void Foam::conformalVoronoiMesh::sortFaces
     // Two stage sort:
     // 1) sort by owner
 
+    Info<< nl
+        << "    Sorting faces, owner and neighbour into upper triangular order"
+        << endl;
+
     labelList oldToNew;
 
     sortedOrder(owner, oldToNew);
@@ -2752,6 +3036,60 @@ void Foam::conformalVoronoiMesh::addPatches
     }
 }
 
+
+void Foam::conformalVoronoiMesh::removeUnusedPoints
+(
+    faceList& faces,
+    pointField& pts
+) const
+{
+    Info<< nl << "    Removing unused points after filtering" << endl;
+
+    PackedBoolList ptUsed(pts.size(), false);
+
+    // Scan all faces to find all of the points that are used
+
+    forAll(faces, fI)
+    {
+        const face& f = faces[fI];
+
+        forAll(f, fPtI)
+        {
+            ptUsed[f[fPtI]] = true;
+        }
+    }
+
+    label pointI = 0;
+    labelList oldToNew(pts.size(), -1);
+
+    // Move all of the used faces to the start of the pointField and
+    // truncate it
+
+    forAll(ptUsed, ptUI)
+    {
+        if (ptUsed[ptUI] == true)
+        {
+            oldToNew[ptUI] = pointI++;
+        }
+    }
+
+    inplaceReorder(oldToNew, pts);
+
+    Info<< "        Removing "
+        << pts.size() - pointI
+        << " unused points" << endl;
+
+    pts.setSize(pointI);
+
+    // Renumber the faces to use the new point numbers
+
+    forAll(faces, fI)
+    {
+        inplaceRenumber(oldToNew, faces[fI]);
+    }
+}
+
+
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
 void Foam::conformalVoronoiMesh::move()
diff --git a/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H b/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
index a2d72c146e53c5e6a372288422c107d4a385d5da..a35b1e60ee464e0040bf86d6c9ba72de6e25d448 100644
--- a/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
+++ b/src/mesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H
@@ -458,15 +458,23 @@ private:
             const pointField& pts
         ) const;
 
+        //- Assess face to see if it is a candidate for removal, self
+        //  determined target size
+        bool assessFace
+        (
+            const face& f,
+            scalar targetFaceSize,
+            const pointField& pts
+        ) const;
+
         //- Collapse a face to a point or an edge, modifying and
-        //  mapping the points
-        // void collapseFace
-        // (
-        //     const face& f,
-        //     const Vertex_handle& vA,
-        //     const Vertex_handle& vB
-        //     pointField& pts,
-        // ) const;
+        //  mapping the points, returns the true if the face was
+        //  collapsed in this operation
+        bool collapseFace
+        (
+            const Triangulation::Finite_edges_iterator& eit,
+            pointField& pts
+        );
 
         //- Sort the faces, owner and neighbour lists into
         //  upper-triangular order.  For internal faces only, use
@@ -493,6 +501,13 @@ private:
             bool includeEmptyPatches
         ) const;
 
+        //- Remove points that are no longer used by any faces.
+        void removeUnusedPoints
+        (
+            faceList& faces,
+            pointField& pts
+        ) const;
+
         //- Disallow default bitwise copy construct
         conformalVoronoiMesh(const conformalVoronoiMesh&);