diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
index 5c8f4da2d7de4f7c34483ec0dcf0107970de619d..4f1f821681022aaafc0cee51a41dde3840976b90 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
@@ -8,7 +8,6 @@
 #include "wedgePolyPatch.H"
 #include "unitConversion.H"
 #include "polyMeshTetDecomposition.H"
-#include "surfaceWriter.H"
 #include "checkTools.H"
 #include "functionObject.H"
 
@@ -482,7 +481,7 @@ Foam::label Foam::checkGeometry
 (
     const polyMesh& mesh,
     const bool allGeometry,
-    const autoPtr<surfaceWriter>& surfWriter,
+    autoPtr<surfaceWriter>& surfWriter,
     const autoPtr<writer<scalar>>& setWriter
 )
 {
@@ -542,7 +541,7 @@ Foam::label Foam::checkGeometry
                 nonAlignedPoints.write();
                 if (setWriter.valid())
                 {
-                    mergeAndWrite(setWriter(), nonAlignedPoints);
+                    mergeAndWrite(*setWriter, nonAlignedPoints);
                 }
             }
         }
@@ -576,7 +575,7 @@ Foam::label Foam::checkGeometry
                 cells.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), cells);
+                    mergeAndWrite(*surfWriter, cells);
                 }
             }
         }
@@ -592,7 +591,7 @@ Foam::label Foam::checkGeometry
             aspectCells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), aspectCells);
+                mergeAndWrite(*surfWriter, aspectCells);
             }
         }
     }
@@ -613,7 +612,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -635,7 +634,7 @@ Foam::label Foam::checkGeometry
                 cells.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), cells);
+                    mergeAndWrite(*surfWriter, cells);
                 }
             }
         }
@@ -658,7 +657,7 @@ Foam::label Foam::checkGeometry
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -680,7 +679,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -702,7 +701,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -726,7 +725,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -759,7 +758,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -784,7 +783,7 @@ Foam::label Foam::checkGeometry
                 points.write();
                 if (setWriter.valid())
                 {
-                    mergeAndWrite(setWriter(), points);
+                    mergeAndWrite(*setWriter, points);
                 }
             }
         }
@@ -807,7 +806,7 @@ Foam::label Foam::checkGeometry
                 nearPoints.write();
                 if (setWriter.valid())
                 {
-                    mergeAndWrite(setWriter(), nearPoints);
+                    mergeAndWrite(*setWriter, nearPoints);
                 }
             }
         }
@@ -831,7 +830,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -854,7 +853,7 @@ Foam::label Foam::checkGeometry
                 faces.write();
                 if (surfWriter.valid())
                 {
-                    mergeAndWrite(surfWriter(), faces);
+                    mergeAndWrite(*surfWriter, faces);
                 }
             }
         }
@@ -875,7 +874,7 @@ Foam::label Foam::checkGeometry
             cells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), cells);
+                mergeAndWrite(*surfWriter, cells);
             }
         }
     }
@@ -895,7 +894,7 @@ Foam::label Foam::checkGeometry
             cells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), cells);
+                mergeAndWrite(*surfWriter, cells);
             }
         }
     }
@@ -916,7 +915,7 @@ Foam::label Foam::checkGeometry
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -937,7 +936,7 @@ Foam::label Foam::checkGeometry
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -952,14 +951,10 @@ Foam::label Foam::checkGeometry
         autoPtr<surfaceWriter> patchWriter;
         if (!surfWriter.valid())
         {
-            patchWriter.reset(new vtkSurfaceWriter());
+            patchWriter.reset(new surfaceWriters::vtkWriter());
         }
-        const surfaceWriter& wr =
-        (
-            surfWriter.valid()
-          ? surfWriter()
-          : patchWriter()
-        );
+
+        surfaceWriter& wr = (surfWriter.valid() ? *surfWriter : *patchWriter);
 
         // Currently only do AMI checks
 
@@ -1017,22 +1012,22 @@ Foam::label Foam::checkGeometry
 
                         if (Pstream::master())
                         {
-                            wr.write
+                            const word fName
                             (
-                                outputDir,
-                                (
-                                    "patch" + Foam::name(cpp.index())
-                                  + "-src_" + tmName
-                                ),
-                                meshedSurfRef
-                                (
-                                    mergedPoints,
-                                    mergedFaces
-                                ),
-                                "weightsSum",
-                                mergedWeights,
-                                false
+                                "patch" + Foam::name(cpp.index())
+                              + "-src_" + tmName
+                            );
+
+                            wr.open
+                            (
+                                mergedPoints,
+                                mergedFaces,
+                                (outputDir / fName),
+                                false  // serial - already merged
                             );
+
+                            wr.write("weightsSum", mergedWeights);
+                            wr.clear();
                         }
 
                         if (isA<cyclicACMIPolyPatch>(pbm[patchi]))
@@ -1049,22 +1044,22 @@ Foam::label Foam::checkGeometry
 
                             if (Pstream::master())
                             {
-                                wr.write
+                                const word fName
                                 (
-                                    outputDir,
-                                    (
-                                        "patch" + Foam::name(cpp.index())
-                                      + "-src_" + tmName
-                                    ),
-                                    meshedSurfRef
-                                    (
-                                        mergedPoints,
-                                        mergedFaces
-                                    ),
-                                    "mask",
-                                    mergedMask,
-                                    false
+                                    "patch" + Foam::name(cpp.index())
+                                  + "-src_" + tmName
+                                );
+
+                                wr.open
+                                (
+                                    mergedPoints,
+                                    mergedFaces,
+                                    (outputDir / fName),
+                                    false  // serial - already merged
                                 );
+
+                                wr.write("mask", mergedMask);
+                                wr.clear();
                             }
                         }
                     }
@@ -1101,22 +1096,22 @@ Foam::label Foam::checkGeometry
 
                         if (Pstream::master())
                         {
-                            wr.write
+                            const word fName
                             (
-                                outputDir,
-                                (
-                                    "patch" + Foam::name(cpp.index())
-                                  + "-tgt_" + tmName
-                                ),
-                                meshedSurfRef
-                                (
-                                    mergedPoints,
-                                    mergedFaces
-                                ),
-                                "weightsSum",
-                                mergedWeights,
-                                false
+                                "patch" + Foam::name(cpp.index())
+                              + "-tgt_" + tmName
+                            );
+
+                            wr.open
+                            (
+                                mergedPoints,
+                                mergedFaces,
+                                (outputDir / fName),
+                                false  // serial - already merged
                             );
+
+                            wr.write("weightsSum", mergedWeights);
+                            wr.clear();
                         }
 
                         if (isA<cyclicACMIPolyPatch>(pbm[patchi]))
@@ -1129,24 +1124,25 @@ Foam::label Foam::checkGeometry
                                 pp.neighbPatch().mask(),
                                 mergedMask
                             );
+
                             if (Pstream::master())
                             {
-                                wr.write
+                                const word fName
                                 (
-                                    outputDir,
-                                    (
-                                        "patch" + Foam::name(cpp.index())
-                                      + "-tgt_" + tmName
-                                    ),
-                                    meshedSurfRef
-                                    (
-                                        mergedPoints,
-                                        mergedFaces
-                                    ),
-                                    "mask",
-                                    mergedMask,
-                                    false
+                                    "patch" + Foam::name(cpp.index())
+                                  + "-tgt_" + tmName
                                 );
+
+                                wr.open
+                                (
+                                    mergedPoints,
+                                    mergedFaces,
+                                    (outputDir / fName),
+                                    false  // serial - already merged
+                                );
+
+                                wr.write("mask", mergedMask);
+                                wr.clear();
                             }
                         }
                     }
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.H b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.H
index d0111fd1e1972d6495ab0854cbfd5a2e843c39af..acd3d401be5c03c42fb1a7a36e42df7f0d383a77 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.H
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.H
@@ -27,7 +27,7 @@ namespace Foam
     (
         const polyMesh& mesh,
         const bool allGeometry,
-        const autoPtr<surfaceWriter>&,
-        const autoPtr<writer<scalar>>&
+        autoPtr<surfaceWriter>& surfWriter,
+        const autoPtr<writer<scalar>>& setWriter
     );
 }
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkMesh.C b/applications/utilities/mesh/manipulation/checkMesh/checkMesh.C
index 8b3eb25cc72c64d3643c3efe80b3f6a917baabf4..fbd24180c46b35a6c3ae5751bc3718b3432c9db1 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkMesh.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkMesh.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2017 OpenFOAM Foundation
@@ -67,8 +67,8 @@ Usage
 #include "Time.H"
 #include "fvMesh.H"
 #include "globalMeshData.H"
-#include "surfaceWriter.H"
 #include "vtkSetWriter.H"
+#include "vtkSurfaceWriter.H"
 #include "IOdictionary.H"
 
 #include "checkTools.H"
@@ -149,16 +149,19 @@ int main(int argc, char *argv[])
     );
     if (!writeFields && args.found("writeAllFields"))
     {
-        selectedFields.insert("nonOrthoAngle");
-        selectedFields.insert("faceWeight");
-        selectedFields.insert("skewness");
-        selectedFields.insert("cellDeterminant");
-        selectedFields.insert("aspectRatio");
-        selectedFields.insert("cellShapes");
-        selectedFields.insert("cellVolume");
-        selectedFields.insert("cellVolumeRatio");
-        selectedFields.insert("minTetVolume");
-        selectedFields.insert("cellRegion");
+        selectedFields.insert
+        ({
+            "nonOrthoAngle",
+            "faceWeight",
+            "skewness",
+            "cellDeterminant",
+            "aspectRatio",
+            "cellShapes",
+            "cellVolume",
+            "cellVolumeRatio",
+            "minTetVolume",
+            "cellRegion"
+        });
     }
 
 
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.C b/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.C
index a6a01c7336144d40f1793381b6e4eda0ad5c942c..26dfbb6d20fd2725788052f3749e1e0cbb72dcb6 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.C
@@ -10,7 +10,7 @@ Foam::label Foam::checkMeshQuality
 (
     const polyMesh& mesh,
     const dictionary& dict,
-    const autoPtr<surfaceWriter>& writer
+    autoPtr<surfaceWriter>& writer
 )
 {
     label noFailedChecks = 0;
@@ -29,9 +29,10 @@ Foam::label Foam::checkMeshQuality
                 << " faces in error to set " << faces.name() << endl;
             faces.instance() = mesh.pointsInstance();
             faces.write();
+
             if (writer.valid())
             {
-                mergeAndWrite(writer(), faces);
+                mergeAndWrite(*writer, faces);
             }
         }
     }
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.H b/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.H
index 52507986bf0bb68dd10b30afc2fce8e01beedf45..0ad3f7448792ac0421d7bbca69feffc0bbc24cdc 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.H
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkMeshQuality.H
@@ -6,8 +6,8 @@ namespace Foam
 
     label checkMeshQuality
     (
-        const polyMesh&,
-        const dictionary&,
-        const autoPtr<surfaceWriter>&
+        const polyMesh& mesh,
+        const dictionary& dict,
+        autoPtr<surfaceWriter>& writer
     );
 }
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkTools.C b/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
index 060c60b0fc83eba7d3c89cfafb125f67b78d2bb6..4b530ee9efe29b223e51ab85e3d421d9bb6487a7 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
@@ -209,7 +209,7 @@ void Foam::printMeshStats(const polyMesh& mesh, const bool allTopology)
 void Foam::mergeAndWrite
 (
     const polyMesh& mesh,
-    const surfaceWriter& writer,
+    surfaceWriter& writer,
     const word& name,
     const indirectPrimitivePatch& setPatch,
     const fileName& outputDir
@@ -242,37 +242,37 @@ void Foam::mergeAndWrite
         // Write
         if (Pstream::master())
         {
-            writer.write
+            writer.open
             (
-                outputDir,
-                name,
-                meshedSurfRef
-                (
-                    mergedPoints,
-                    mergedFaces
-                )
+                mergedPoints,
+                mergedFaces,
+                (outputDir / name),
+                false  // serial - already merged
             );
+
+            writer.writeGeometry();
+            writer.clear();
         }
     }
     else
     {
-        writer.write
+        writer.open
         (
-            outputDir,
-            name,
-            meshedSurfRef
-            (
-                setPatch.localPoints(),
-                setPatch.localFaces()
-            )
+            setPatch.localPoints(),
+            setPatch.localFaces(),
+            (outputDir / name),
+            false  // serial - already merged
         );
+
+        writer.writeGeometry();
+        writer.clear();
     }
 }
 
 
 void Foam::mergeAndWrite
 (
-    const surfaceWriter& writer,
+    surfaceWriter& writer,
     const faceSet& set
 )
 {
@@ -299,7 +299,7 @@ void Foam::mergeAndWrite
 
 void Foam::mergeAndWrite
 (
-    const surfaceWriter& writer,
+    surfaceWriter& writer,
     const cellSet& set
 )
 {
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkTools.H b/applications/utilities/mesh/manipulation/checkMesh/checkTools.H
index adb98089b5bc9af76daad422e42c5c58731d9765..79c68b6673a136130ba55a1c071e019f0fe094fd 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkTools.H
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkTools.H
@@ -5,12 +5,12 @@
 namespace Foam
 {
     class polyMesh;
-    class surfaceWriter;
     class pointSet;
     class faceSet;
     class cellSet;
     class fileName;
     class polyMesh;
+    class surfaceWriter;
 
     void printMeshStats(const polyMesh& mesh, const bool allTopology);
 
@@ -19,7 +19,7 @@ namespace Foam
     void mergeAndWrite
     (
         const polyMesh& mesh,
-        const surfaceWriter& writer,
+        surfaceWriter& writer,
         const word& name,
         const indirectPrimitivePatch& setPatch,
         const fileName& outputDir
@@ -27,15 +27,15 @@ namespace Foam
 
     //- Write vtk representation of (assembled) faceSet to surface file in
     //  postProcessing/ directory
-    void mergeAndWrite(const surfaceWriter&, const faceSet&);
+    void mergeAndWrite(surfaceWriter& writer, const faceSet& set);
 
     //- Write vtk representation of (assembled) cellSet to surface file in
     //  postProcessing/ directory
-    void mergeAndWrite(const surfaceWriter&, const cellSet&);
+    void mergeAndWrite(surfaceWriter& writer, const cellSet& set);
 
     //- Write vtk representation of (assembled) pointSet to 'set' file in
     //  postProcessing/ directory
-    void mergeAndWrite(const writer<scalar>&, const pointSet&);
+    void mergeAndWrite(const writer<scalar>& writer, const pointSet& set);
 }
 
 
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkTopology.C b/applications/utilities/mesh/manipulation/checkMesh/checkTopology.C
index fb0d1c6ccf53daa0f2b84c39f6a98e6e4c44b1b1..25c8e43de8b995e73f9d3c23b3f419b3298eb969 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkTopology.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkTopology.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2017-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2016 OpenFOAM Foundation
@@ -35,7 +35,7 @@ License
 #include "IOmanip.H"
 #include "emptyPolyPatch.H"
 #include "processorPolyPatch.H"
-#include "surfaceWriter.H"
+#include "vtkSurfaceWriter.H"
 #include "checkTools.H"
 #include "treeBoundBox.H"
 
@@ -114,7 +114,7 @@ Foam::label Foam::checkTopology
     const polyMesh& mesh,
     const bool allTopology,
     const bool allGeometry,
-    const autoPtr<surfaceWriter>& surfWriter,
+    autoPtr<surfaceWriter>& surfWriter,
     const autoPtr<writer<scalar>>& setWriter
 )
 {
@@ -203,7 +203,7 @@ Foam::label Foam::checkTopology
             cells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), cells);
+                mergeAndWrite(*surfWriter, cells);
             }
         }
         else
@@ -227,7 +227,7 @@ Foam::label Foam::checkTopology
             points.write();
             if (setWriter.valid())
             {
-                mergeAndWrite(setWriter(), points);
+                mergeAndWrite(*setWriter, points);
             }
         }
     }
@@ -249,7 +249,7 @@ Foam::label Foam::checkTopology
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -269,7 +269,7 @@ Foam::label Foam::checkTopology
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -290,7 +290,7 @@ Foam::label Foam::checkTopology
             cells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), cells);
+                mergeAndWrite(*surfWriter, cells);
             }
 
         }
@@ -314,7 +314,7 @@ Foam::label Foam::checkTopology
             faces.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), faces);
+                mergeAndWrite(*surfWriter, faces);
             }
         }
     }
@@ -369,7 +369,7 @@ Foam::label Foam::checkTopology
             oneCells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), oneCells);
+                mergeAndWrite(*surfWriter, oneCells);
             }
         }
 
@@ -385,7 +385,7 @@ Foam::label Foam::checkTopology
             twoCells.write();
             if (surfWriter.valid())
             {
-                mergeAndWrite(surfWriter(), twoCells);
+                mergeAndWrite(*surfWriter, twoCells);
             }
         }
     }
@@ -530,7 +530,7 @@ Foam::label Foam::checkTopology
                 points.write();
                 if (setWriter.valid())
                 {
-                    mergeAndWrite(setWriter(), points);
+                    mergeAndWrite(*setWriter, points);
                 }
             }
         }
@@ -641,7 +641,7 @@ Foam::label Foam::checkTopology
         points.write();
         if (setWriter.valid())
         {
-            mergeAndWrite(setWriter(), points);
+            mergeAndWrite(*setWriter, points);
         }
     }
 
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkTopology.H b/applications/utilities/mesh/manipulation/checkMesh/checkTopology.H
index 7320cb0e563b1b436af88ae724f5d35d170c2d6e..bd93afc3dbe1dba9a310f101fd8abc639b030c26 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkTopology.H
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkTopology.H
@@ -5,8 +5,8 @@
 namespace Foam
 {
     class polyMesh;
-    class surfaceWriter;
     class pointSet;
+    class surfaceWriter;
 
     template<class PatchType>
     void checkPatch
@@ -19,10 +19,10 @@ namespace Foam
 
     label checkTopology
     (
-        const polyMesh&,
-        const bool,
-        const bool,
-        const autoPtr<surfaceWriter>&,
-        const autoPtr<writer<scalar>>&
+        const polyMesh& mesh,
+        const bool allTopology,
+        const bool allGeometry,
+        autoPtr<surfaceWriter>& surfWriter,
+        const autoPtr<writer<scalar>>& setWriter
     );
 }
diff --git a/applications/utilities/surface/surfaceCheck/surfaceCheck.C b/applications/utilities/surface/surfaceCheck/surfaceCheck.C
index 57afe5931faac00141417bf161d62da149c0f900..c42f3e3fe87e652e7abddd0f4590a98c2a9393f1 100644
--- a/applications/utilities/surface/surfaceCheck/surfaceCheck.C
+++ b/applications/utilities/surface/surfaceCheck/surfaceCheck.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2016 OpenFOAM Foundation
@@ -128,7 +128,7 @@ labelList countBins
 
 void writeZoning
 (
-    const surfaceWriter& writer,
+    surfaceWriter& writer,
     const triSurface& surf,
     const labelList& faceZone,
     const word& fieldName,
@@ -136,43 +136,23 @@ void writeZoning
     const fileName& surfFileNameBase
 )
 {
-    Info<< "Writing zoning to "
-        <<  fileName
-            (
-                surfFilePath
-              / fieldName
-              + '_'
-              + surfFileNameBase
-              + '.'
-              + writer.type()
-            )
-        << " ..." << endl << endl;
-
-    // Convert data
-    scalarField scalarFaceZone(faceZone.size());
-    forAll(faceZone, i)
-    {
-        scalarFaceZone[i] = faceZone[i];
-    }
-    faceList faces(surf.size());
-    forAll(surf, i)
-    {
-        faces[i] = surf[i];
-    }
+    // Transcribe faces
+    faceList faces;
+    surf.triFaceFaces(faces);
 
-    writer.write
+    writer.open
     (
-        surfFilePath,
-        surfFileNameBase,
-        meshedSurfRef
-        (
-            surf.points(),
-            faces
-        ),
-        fieldName,
-        scalarFaceZone,
-        false               // face based data
+        surf.points(),
+        faces,
+        (surfFilePath / surfFileNameBase),
+        false  // serial - already merged
     );
+
+    fileName outputName = writer.write(fieldName, labelField(faceZone));
+
+    writer.clear();
+
+    Info<< "Wrote zoning to " << outputName << nl << endl;
 }
 
 
@@ -213,7 +193,7 @@ void writeParts
         fileName subName
         (
             surfFilePath
-           /surfFileNameBase + "_" + name(zone) + ".obj"
+          / surfFileNameBase + "_" + name(zone) + ".obj"
         );
 
         Info<< "writing part " << zone << " size " << subSurf.size()
@@ -377,6 +357,7 @@ int main(int argc, char *argv[])
     if (writeSets)
     {
         surfWriter = surfaceWriter::New(surfaceFormat);
+
         // Option1: hard-coded format
         edgeFormat = "obj";
         //// Option2: same type as surface format. Problem is e.g. .obj format
@@ -514,38 +495,29 @@ int main(int argc, char *argv[])
                     )
                 );
 
-                const fileName qualityName
+
+                // Transcribe faces
+                faceList faces;
+                subSurf.triFaceFaces(faces);
+
+                surfWriter->open
                 (
-                    surfFilePath
-                  / "illegal"
-                  + '_'
-                  + surfFileNameBase
-                  + '.'
-                  + surfWriter().type()
+                    subSurf.points(),
+                    faces,
+                    (surfFilePath / surfFileNameBase),
+                    false // serial - already merged
                 );
-                Info<< "Writing illegal triangles to "
-                    << qualityName << " ..." << endl << endl;
-
-                // Convert data
-                faceList faces(subSurf.size());
-                forAll(subSurf, i)
-                {
-                    faces[i] = subSurf[i];
-                }
 
-                surfWriter().write
+                fileName outputName = surfWriter->write
                 (
-                    surfFilePath,
-                    surfFileNameBase,
-                    meshedSurfRef
-                    (
-                        subSurf.points(),
-                        faces
-                    ),
                     "illegal",
-                    scalarField(subSurf.size(), Zero),
-                    false               // face based data
+                    scalarField(subSurf.size(), Zero)
                 );
+
+                surfWriter->clear();
+
+                Info<< "Wrote illegal triangles to "
+                    << outputName << nl << endl;
             }
             else if (outputThreshold > 0)
             {
@@ -645,38 +617,24 @@ int main(int argc, char *argv[])
         // Dump for subsetting
         if (surfWriter.valid())
         {
-            const fileName qualityName
-            (
-                surfFilePath
-              / "quality"
-              + '_'
-              + surfFileNameBase
-              + '.'
-              + surfWriter().type()
-            );
-            Info<< "Writing triangle-quality to "
-                << qualityName << " ..." << endl << endl;
-
-            // Convert data
+            // Transcribe faces
             faceList faces(surf.size());
-            forAll(surf, i)
-            {
-                faces[i] = surf[i];
-            }
+            surf.triFaceFaces(faces);
 
-            surfWriter().write
+            surfWriter->open
             (
-                surfFilePath,
-                surfFileNameBase,
-                meshedSurfRef
-                (
-                    surf.points(),
-                    faces
-                ),
-                "quality",
-                triQ,
-                false               // face based data
+                surf.points(),
+                faces,
+                (surfFilePath / surfFileNameBase),
+                false // serial - already merged
             );
+
+            fileName outputName = surfWriter->write("quality", triQ);
+
+            surfWriter->clear();
+
+            Info<< "Wrote triangle-quality to "
+                << outputName << nl << endl;
         }
         else if (outputThreshold > 0)
         {
@@ -926,11 +884,12 @@ int main(int argc, char *argv[])
 
             if (!surfWriter.valid())
             {
-                surfWriter.reset(new vtkSurfaceWriter());
+                surfWriter.reset(new surfaceWriters::vtkWriter());
             }
+
             writeZoning
             (
-                surfWriter(),
+                *surfWriter,
                 surf,
                 faceZone,
                 "zone",
@@ -991,11 +950,12 @@ int main(int argc, char *argv[])
         {
             if (!surfWriter.valid())
             {
-                surfWriter.reset(new vtkSurfaceWriter());
+                surfWriter.reset(new surfaceWriters::vtkWriter());
             }
+
             writeZoning
             (
-                surfWriter(),
+                *surfWriter,
                 surf,
                 normalZone,
                 "normal",
diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
index 1df74fc79d1c5f8a6dee8060e9c110ae80f03a72..056b00a0c5b299006f5b21e7e1f0576df1760e4c 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
@@ -953,20 +953,23 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
     {
         const word formatName(dict.get<word>("surfaceFormat"));
 
-        if (formatName != "none")
-        {
-            surfaceWriterPtr_.reset
+        surfaceWriterPtr_.reset
+        (
+            surfaceWriter::New
             (
-                surfaceWriter::New
-                (
-                    formatName,
-                    dict.subOrEmptyDict("formatOptions")
-                        .subOrEmptyDict(formatName)
-                )
-            );
+                formatName,
+                dict.subOrEmptyDict("formatOptions").subOrEmptyDict(formatName)
+            )
+        );
 
+        if (surfaceWriterPtr_->enabled())
+        {
             Info<< "    surfaceFormat = " << formatName << nl;
         }
+        else
+        {
+            surfaceWriterPtr_->clear();
+        }
     }
 
     Info<< nl << endl;
diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValueTemplates.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValueTemplates.C
index 56c3bc8ed9e038346aff585adf5ad42926338eee..d665e74d5866ee48d18cb571f8f573c8f1c191d8 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValueTemplates.C
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValueTemplates.C
@@ -384,22 +384,26 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::writeValues
         Field<Type> values(getFieldValues<Type>(fieldName, true));
 
         // Write raw values on surface if specified
-        if (surfaceWriterPtr_.valid())
+        if (surfaceWriterPtr_.valid() && surfaceWriterPtr_->enabled())
         {
             Field<Type> allValues(values);
             combineFields(allValues);
 
             if (Pstream::master())
             {
-                surfaceWriterPtr_->write
+                surfaceWriterPtr_->open
                 (
-                    outputDir(),
-                    regionTypeNames_[regionType_] + ("_" + regionName_),
                     surfToWrite,
-                    fieldName,
-                    allValues,
-                    false
+                    (
+                        outputDir()
+                      / regionTypeNames_[regionType_] + ("_" + regionName_)
+                    ),
+                    false  // serial - already merged
                 );
+
+                surfaceWriterPtr_->write(fieldName, allValues);
+
+                surfaceWriterPtr_->clear();
             }
         }
 
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/FacePostProcessing/FacePostProcessing.C b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/FacePostProcessing/FacePostProcessing.C
index 5c228621bd27827f2d340a0293118137c35b92bb..4c859a885fb10a75ef5800d309cb1d71f3f3991f 100644
--- a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/FacePostProcessing/FacePostProcessing.C
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/FacePostProcessing/FacePostProcessing.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2017 OpenFOAM Foundation
@@ -28,8 +28,8 @@ License
 #include "FacePostProcessing.H"
 #include "Pstream.H"
 #include "ListListOps.H"
-#include "surfaceWriter.H"
 #include "globalIndex.H"
+#include "surfaceWriter.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
@@ -194,35 +194,26 @@ void Foam::FacePostProcessing<CloudType>::write()
                     )
                 );
 
-                autoPtr<surfaceWriter> writer
+                auto writer = surfaceWriter::New
                 (
-                    surfaceWriter::New
-                    (
-                        surfaceFormat_,
-                        this->coeffDict().subOrEmptyDict("formatOptions").
-                            subOrEmptyDict(surfaceFormat_)
-                    )
+                    surfaceFormat_,
+                    this->coeffDict().subOrEmptyDict("formatOptions")
+                        .subOrEmptyDict(surfaceFormat_)
                 );
 
-                writer->write
+                writer->open
                 (
-                    this->writeTimeDir(),
-                    fZone.name(),
-                    meshedSurfRef(allPoints, allFaces),
-                    "massTotal",
-                    zoneMassTotal[zoneI],
-                    false
+                    allPoints,
+                    allFaces,
+                    (this->writeTimeDir() / fZone.name()),
+                    false  // serial - already merged
                 );
 
-                writer->write
-                (
-                    this->writeTimeDir(),
-                    fZone.name(),
-                    meshedSurfRef(allPoints, allFaces),
-                    "massFlowRate",
-                    zoneMassFlowRate[zoneI],
-                    false
-                );
+                writer->write("massTotal", zoneMassTotal[zoneI]);
+
+                writer->write("massFlowRate", zoneMassFlowRate[zoneI]);
+
+                writer->clear();
             }
         }
     }
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleCollector/ParticleCollector.C b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleCollector/ParticleCollector.C
index 381c36077b9bd3fe2b2f94550f38cfff09f90bcf..6f0eaf15c583a16dfc8f26533d3f0c9d7556a96e 100644
--- a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleCollector/ParticleCollector.C
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleCollector/ParticleCollector.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2012-2017 OpenFOAM Foundation
@@ -462,40 +462,26 @@ void Foam::ParticleCollector<CloudType>::write()
         << endl;
 
 
-    if (surfaceFormat_ != "none")
+    if (surfaceFormat_ != "none" && Pstream::master())
     {
-        if (Pstream::master())
-        {
-            autoPtr<surfaceWriter> writer
-            (
-                surfaceWriter::New
-                (
-                    surfaceFormat_,
-                    this->coeffDict().subOrEmptyDict("formatOptions").
-                        subOrEmptyDict(surfaceFormat_)
-                )
-            );
+        auto writer = surfaceWriter::New
+        (
+            surfaceFormat_,
+            this->coeffDict().subOrEmptyDict("formatOptions")
+                .subOrEmptyDict(surfaceFormat_)
+        );
 
-            writer->write
-            (
-                this->writeTimeDir(),
-                "collector",
-                meshedSurfRef(points_, faces_),
-                "massTotal",
-                faceMassTotal,
-                false
-            );
+        writer->open
+        (
+            points_,
+            faces_,
+            (this->writeTimeDir() / "collector"),
+            false  // serial - already merged
+        );
 
-            writer->write
-            (
-                this->writeTimeDir(),
-                "collector",
-                meshedSurfRef(points_, faces_),
-                "massFlowRate",
-                faceMassFlowRate,
-                false
-            );
-        }
+        writer->write("massFlowRate", faceMassFlowRate);
+
+        writer->write("massTotal", faceMassTotal);
     }
 
 
diff --git a/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C
index bf53217d1a19b6fdf33727067f204b5c913a481f..b97622dd4ae65c4500c81849cbfcb4686ba9bca8 100644
--- a/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C
+++ b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -275,22 +275,19 @@ Foam::scalar surfaceNoise::writeSurfaceData
                 }
             }
 
-            // Could also have meshedSurface implement meshedSurf
             if (writeSurface)
             {
-                fileName outFileName = writerPtr_->write
+                writerPtr_->open
                 (
-                    outDir,
-                    fName,
-                    meshedSurfRef
-                    (
-                        surf.points(),
-                        surf.surfFaces()
-                    ),
-                    title,
-                    allData,
-                    false
+                    surf.points(),
+                    surf.surfFaces(),
+                    (outDir / fName),
+                    false  // serial - already merged
                 );
+
+                writerPtr_->write(title, allData);
+
+                writerPtr_->clear();
             }
 
             // TO BE VERIFIED: area-averaged values
@@ -305,22 +302,19 @@ Foam::scalar surfaceNoise::writeSurfaceData
     {
         const meshedSurface& surf = readerPtr_->geometry();
 
-        // Could also have meshedSurface implement meshedSurf
         if (writeSurface)
         {
-            writerPtr_->write
+            writerPtr_->open
             (
-                outDir,
-                fName,
-                meshedSurfRef
-                (
-                    surf.points(),
-                    surf.surfFaces()
-                ),
-                title,
-                data,
-                false
+                surf.points(),
+                surf.surfFaces(),
+                (outDir / fName),
+                false  // serial - already merged
             );
+
+            writerPtr_->write(title, data);
+
+            writerPtr_->clear();
         }
 
         // TO BE VERIFIED: area-averaged values
@@ -442,13 +436,12 @@ bool surfaceNoise::read(const dictionary& dict)
 
         const word writerType(dict.get<word>("writer"));
 
-        dictionary optDict
+        writerPtr_ = surfaceWriter::New
         (
+            writerType,
             dict.subOrEmptyDict("writeOptions").subOrEmptyDict(writerType)
         );
 
-        writerPtr_ = surfaceWriter::New(writerType, optDict);
-
         return true;
     }
 
diff --git a/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.H b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.H
index 55d4e6e3348a7dc2d35a0e6e66f27c9a9e2ed438..38073c5f2b4d65a15888f2d046a7859a4157fde8 100644
--- a/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.H
+++ b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -111,7 +111,7 @@ SeeAlso
 namespace Foam
 {
 
-// Forward declaration of classes
+// Forward declarations
 class surfaceReader;
 class surfaceWriter;
 
diff --git a/src/sampling/Make/files b/src/sampling/Make/files
index dbef3b398732dd14b0e76791bc91b60c81ca2d62..83403b64a29d4f3ff468c2dd4893bb7ea472f1f8 100644
--- a/src/sampling/Make/files
+++ b/src/sampling/Make/files
@@ -50,8 +50,8 @@ sampledSurface/distanceSurface/sampledDistanceSurface.C
 sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C
 sampledSurface/sampledCuttingSurface/sampledCuttingSurface.C
 sampledSurface/sampledSurface/sampledSurface.C
+sampledSurface/sampledSurface/sampledSurfaceRegister.C
 sampledSurface/sampledSurfaces/sampledSurfaces.C
-sampledSurface/sampledSurfaces/sampledSurfacesGrouping.C
 sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMesh.C
 sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMeshNormal.C
 sampledSurface/thresholdCellFaces/sampledThresholdCellFaces.C
@@ -59,24 +59,11 @@ sampledSurface/thresholdCellFaces/sampledThresholdCellFaces.C
 /* Proof-of-concept: */
 /* sampledSurface/triSurfaceMesh/sampledDiscreteSurface.C */
 
+readers = sampledSurface/readers
 
-surfWriters = sampledSurface/writers
-
-$(surfWriters)/surfaceWriter.C
-$(surfWriters)/ensight/ensightSurfaceWriter.C
-$(surfWriters)/foam/foamSurfaceWriter.C
-$(surfWriters)/nastran/nastranSurfaceWriter.C
-$(surfWriters)/proxy/proxySurfaceWriter.C
-$(surfWriters)/raw/rawSurfaceWriter.C
-$(surfWriters)/starcd/starcdSurfaceWriter.C
-$(surfWriters)/vtk/vtkSurfaceWriter.C
-$(surfWriters)/boundaryData/boundaryDataSurfaceWriter.C
-
-surfReaders = sampledSurface/readers
-
-$(surfReaders)/surfaceReader.C
-$(surfReaders)/surfaceReaderNew.C
-$(surfReaders)/ensight/ensightSurfaceReader.C
+$(readers)/surfaceReader.C
+$(readers)/surfaceReaderNew.C
+$(readers)/ensight/ensightSurfaceReader.C
 
 graphField/writePatchGraph.C
 graphField/writeCellGraph.C
diff --git a/src/sampling/sampledSurface/sampledSurface/sampledSurface.C b/src/sampling/sampledSurface/sampledSurface/sampledSurface.C
index d326e7e3cab5f9c4e6e85bd75a1ee12e5071c365..ebb5048e53a3ee5a6bb1f1cca183631085b69cf7 100644
--- a/src/sampling/sampledSurface/sampledSurface/sampledSurface.C
+++ b/src/sampling/sampledSurface/sampledSurface/sampledSurface.C
@@ -38,6 +38,17 @@ namespace Foam
 }
 
 
+const Foam::wordList Foam::sampledSurface::surfaceFieldTypes
+({
+    "surfaceScalarField",
+    "surfaceVectorField",
+    "surfaceSphericalTensorField",
+    "surfaceSymmTensorField",
+    "surfaceTensorField"
+});
+
+
+
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
 void Foam::sampledSurface::clearGeom() const
@@ -57,10 +68,8 @@ Foam::autoPtr<Foam::sampledSurface> Foam::sampledSurface::New
 {
     const word sampleType(dict.get<word>("type"));
 
-    if (debug)
-    {
-        Info<< "Selecting sampledType " << sampleType << endl;
-    }
+    DebugInfo
+        << "Selecting sampledType " << sampleType << endl;
 
     auto cstrIter = wordConstructorTablePtr_->cfind(sampleType);
 
diff --git a/src/sampling/sampledSurface/sampledSurface/sampledSurface.H b/src/sampling/sampledSurface/sampledSurface/sampledSurface.H
index bf279805db750e72cab41356afd338978e4f4f0c..ba6a84a15f3d811b6f0aefa78c2a2c41fbf7ec07 100644
--- a/src/sampling/sampledSurface/sampledSurface/sampledSurface.H
+++ b/src/sampling/sampledSurface/sampledSurface/sampledSurface.H
@@ -65,17 +65,15 @@ SourceFiles
 #ifndef sampledSurface_H
 #define sampledSurface_H
 
-#include "meshedSurf.H"
-#include "word.H"
-#include "labelList.H"
-#include "faceList.H"
+#include "polySurface.H"
+#include "surfMesh.H"
 #include "typeInfo.H"
 #include "runTimeSelectionTables.H"
 #include "autoPtr.H"
+#include "polyMesh.H"
 #include "volFieldsFwd.H"
 #include "surfaceFieldsFwd.H"
 #include "surfaceMesh.H"
-#include "polyMesh.H"
 #include "interpolation.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -84,18 +82,26 @@ namespace Foam
 {
 
 /*---------------------------------------------------------------------------*\
-                      Class sampledSurface Declaration
+                       Class sampledSurface Declaration
 \*---------------------------------------------------------------------------*/
 
 class sampledSurface
 :
     public meshedSurf
 {
+public:
+
+    // Public Static Data
+
+        //- Class names for surface field types
+        static const wordList surfaceFieldTypes;
+
+
 private:
 
     // Private Data
 
-        //- Name of sample surface
+        //- The name of the sample surface
         word name_;
 
         //- Reference to mesh
@@ -104,15 +110,13 @@ private:
         //- Should surface sampling be enabled?
         bool enabled_;
 
-        //- Do we intend to interpolate the information?
-        const bool interpolate_;
+        //- Interpolate information to the nodes?
+        bool interpolate_;
 
         //- Total surface area (demand-driven)
         mutable scalar area_;
 
 
-
-
 protected:
 
     // Protected Member Functions
@@ -163,9 +167,7 @@ public:
     );
 
 
-    // iNew helper class
-
-        //- Class for PtrList read-construction
+        //- PtrList read-construction helper
         class iNew
         {
             //- Reference to the volume mesh
@@ -188,6 +190,34 @@ public:
         };
 
 
+        //- PtrList read-construction helper that captures dictionaries used
+        //- during creation.
+        class iNewCapture
+        {
+            //- Reference to the volume mesh
+            const polyMesh& mesh_;
+
+            //- Captured (recorded) dictionaries
+            DynamicList<dictionary>& capture_;
+
+        public:
+
+            iNewCapture(const polyMesh& mesh, DynamicList<dictionary>& capture)
+            :
+                mesh_(mesh),
+                capture_(capture)
+            {}
+
+            autoPtr<sampledSurface> operator()(Istream& is) const
+            {
+                word name(is);
+                capture_.append(dictionary(is));
+
+                return sampledSurface::New(name, mesh_, capture_.last());
+            }
+        };
+
+
     // Constructors
 
         //- Construct from name, mesh
@@ -245,13 +275,13 @@ public:
             return name_;
         }
 
-        //- Sampling is enabled
+        //- Surface is enabled
         bool enabled() const
         {
             return enabled_;
         }
 
-        //- Interpolation requested for surface
+        //- interpolation to nodes requested for surface
         bool interpolate() const
         {
             return interpolate_;
@@ -301,6 +331,99 @@ public:
         }
 
 
+    // General registry storage (optional)
+
+        //- Get surface from registry if available.
+        //  \param obr The objectRegistry to use
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return surface or nullptr
+        polySurface* getRegistrySurface
+        (
+            const objectRegistry& obr,
+            word lookupName = ""
+        ) const;
+
+        //- Copy surface into registry.
+        //  \param obr The objectRegistry to use
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return surface or nullptr it surface should not be stored
+        polySurface* storeRegistrySurface
+        (
+            objectRegistry& obr,
+            word lookupName = ""
+        ) const;
+
+        //- Remove surface from registry.
+        //  \param obr The objectRegistry to use
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return True if surface existed and was removed
+        bool removeRegistrySurface
+        (
+            objectRegistry& obr,
+            word lookupName = ""
+        ) const;
+
+        //- Copy/store sampled field onto registered surface (if it exists)
+        template<class Type, class GeoMeshType>
+        bool storeRegistryField
+        (
+            const objectRegistry& obr,
+            const word& fieldName,
+            const dimensionSet& dims,
+            const Field<Type>& values,
+            word lookupName = ""
+        ) const;
+
+        //- Move/store sampled field onto registered surface (if it exists)
+        template<class Type, class GeoMeshType>
+        bool storeRegistryField
+        (
+            const objectRegistry& obr,
+            const word& fieldName,
+            const dimensionSet& dims,
+            Field<Type>&& values,
+            word lookupName = ""
+        ) const;
+
+
+    // Specialized surfMesh storage (optional)
+
+        //- Get surface from registry if available.
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return surface or nullptr
+        surfMesh* getSurfMesh(word lookupName = "") const;
+
+        //- Copy surface into registry.
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return surface or nullptr it surface should not be stored
+        surfMesh* storeSurfMesh(word lookupName = "") const;
+
+        //- Remove surface from registry.
+        //  \param lookupName Optional lookup name, use surface name if empty
+        //  \return True if surface existed and was removed
+        bool removeSurfMesh(word lookupName = "") const;
+
+        //- Copy/store sampled Face field onto surfMesh (if it exists)
+        template<class Type, class GeoMeshType>
+        bool storeSurfMeshField
+        (
+            const word& fieldName,
+            const dimensionSet& dims,
+            const Field<Type>& values,
+            word lookupName = ""
+        ) const;
+
+        //- Move/store sampled Face field onto surfMesh (if it exists)
+        template<class Type, class GeoMeshType>
+        bool storeSurfMeshField
+        (
+            const word& fieldName,
+            const dimensionSet& dims,
+            Field<Type>&& values,
+            word lookupName = ""
+        ) const;
+
+
     // Sample (faces)
 
         //- Sample volume field onto surface faces
diff --git a/src/sampling/sampledSurface/sampledSurface/sampledSurfaceRegister.C b/src/sampling/sampledSurface/sampledSurface/sampledSurfaceRegister.C
index c7fa0297d0ca15073c737b0824086f8da78057e4..ab2438fc643336589e1b02ac0bf13128b8ead249 100644
--- a/src/sampling/sampledSurface/sampledSurface/sampledSurfaceRegister.C
+++ b/src/sampling/sampledSurface/sampledSurface/sampledSurfaceRegister.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2004-2010, 2018-2019 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,269 +24,114 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "sampledSurface.H"
-#include "polyMesh.H"
+#include "fvMesh.H"
+#include "MeshedSurface.H"
 #include "demandDrivenData.H"
 
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    defineTypeNameAndDebug(sampledSurface, 0);
-    defineRunTimeSelectionTable(sampledSurface, word);
-}
-
-
-const Foam::wordList Foam::sampledSurface::surfaceFieldTypes
-({
-    "surfaceScalarField",
-    "surfaceVectorField",
-    "surfaceSphericalTensorField",
-    "surfaceSymmTensorField",
-    "surfaceTensorField"
-});
-
-
-
-// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
-
-void Foam::sampledSurface::clearGeom() const
-{
-    area_ = -1;
-}
-
-
-// * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::autoPtr<Foam::sampledSurface> Foam::sampledSurface::New
+Foam::polySurface* Foam::sampledSurface::getRegistrySurface
 (
-    const word& name,
-    const polyMesh& mesh,
-    const dictionary& dict
-)
+    const objectRegistry& obr,
+    word lookupName
+) const
 {
-    const word sampleType(dict.get<word>("type"));
-
-    if (debug)
-    {
-        Info<< "Selecting sampledType " << sampleType << endl;
-    }
-
-    auto cstrIter = wordConstructorTablePtr_->cfind(sampleType);
-
-    if (!cstrIter.found())
+    if (lookupName.empty())
     {
-        FatalErrorInFunction
-            << "Unknown sample type "
-            << sampleType << nl << nl
-            << "Valid sample types :" << endl
-            << wordConstructorTablePtr_->sortedToc()
-            << exit(FatalError);
+        lookupName = this->name();
     }
 
-    return autoPtr<sampledSurface>(cstrIter()(name, mesh, dict));
+    return obr.getObjectPtr<polySurface>(lookupName);
 }
 
 
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::sampledSurface::sampledSurface(const word& name, std::nullptr_t)
-:
-    name_(name),
-    mesh_(NullObjectRef<polyMesh>()),
-    enabled_(true),
-    interpolate_(false),
-    area_(-1),
-    writerType_(),
-    formatOptions_()
-{}
-
-
-Foam::sampledSurface::sampledSurface
-(
-    const word& name,
-    const polyMesh& mesh,
-    const bool interpolate
-)
-:
-    name_(name),
-    mesh_(mesh),
-    enabled_(true),
-    interpolate_(interpolate),
-    area_(-1),
-    writerType_(),
-    formatOptions_()
-{}
-
-
-Foam::sampledSurface::sampledSurface
+Foam::polySurface* Foam::sampledSurface::storeRegistrySurface
 (
-    const word& name,
-    const polyMesh& mesh,
-    const dictionary& dict
-)
-:
-    name_(dict.lookupOrDefault<word>("name", name)),
-    mesh_(mesh),
-    enabled_(dict.lookupOrDefault("enabled", true)),
-    interpolate_(dict.lookupOrDefault("interpolate", false)),
-    area_(-1),
-    writerType_(dict.lookupOrDefault<word>("surfaceFormat", "")),
-    formatOptions_(dict.subOrEmptyDict("formatOptions"))
-{}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::sampledSurface::~sampledSurface()
-{
-    clearGeom();
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::scalar Foam::sampledSurface::area() const
+    objectRegistry& obr,
+    word lookupName
+) const
 {
-    if (area_ < 0)
+    if (lookupName.empty())
     {
-        area_ = gSum(magSf());
+        lookupName = this->name();
     }
 
-    return area_;
-}
-
-
-bool Foam::sampledSurface::withSurfaceFields() const
-{
-    return false;
-}
-
-
-Foam::tmp<Foam::scalarField> Foam::sampledSurface::sample
-(
-    const surfaceScalarField& sField
-) const
-{
-    NotImplemented;
-    return nullptr;
-}
-
-
-Foam::tmp<Foam::vectorField> Foam::sampledSurface::sample
-(
-    const surfaceVectorField& sField
-) const
-{
-    NotImplemented;
-    return nullptr;
-}
-
+    polySurface* surfptr = getRegistrySurface(obr, lookupName);
 
-Foam::tmp<Foam::sphericalTensorField> Foam::sampledSurface::sample
-(
-    const surfaceSphericalTensorField& sField
-) const
-{
-    NotImplemented;
-    return nullptr;
-}
+    if (!surfptr)
+    {
+        // Construct null and add to registry (owned by registry)
+        surfptr = new polySurface(lookupName, obr, true);
+    }
 
+    surfptr->copySurface(*this);   // Copy in geometry (removes old fields)
 
-Foam::tmp<Foam::symmTensorField> Foam::sampledSurface::sample
-(
-    const surfaceSymmTensorField& sField
-) const
-{
-    NotImplemented;
-    return nullptr;
+    return surfptr;
 }
 
 
-Foam::tmp<Foam::tensorField> Foam::sampledSurface::sample
+bool Foam::sampledSurface::removeRegistrySurface
 (
-    const surfaceTensorField& sField
+    objectRegistry& obr,
+    word lookupName
 ) const
 {
-    NotImplemented;
-    return nullptr;
-}
+    polySurface* surfptr = getRegistrySurface(obr, lookupName);
 
+    if (surfptr)
+    {
+        return obr.checkOut(*surfptr);
+    }
 
-void Foam::sampledSurface::print(Ostream& os) const
-{
-    os << type();
+    return false;
 }
 
 
-Foam::polySurface* Foam::sampledSurface::getRegistrySurface
-(
-    const objectRegistry& obr,
-    word lookupName
-) const
+Foam::surfMesh* Foam::sampledSurface::getSurfMesh(word lookupName) const
 {
     if (lookupName.empty())
     {
         lookupName = this->name();
     }
 
-    return obr.getObjectPtr<polySurface>(lookupName);
+    return mesh().getObjectPtr<surfMesh>(lookupName);
 }
 
 
-Foam::polySurface* Foam::sampledSurface::storeRegistrySurface
-(
-    objectRegistry& obr,
-    word lookupName
-) const
+Foam::surfMesh* Foam::sampledSurface::storeSurfMesh(word lookupName) const
 {
     if (lookupName.empty())
     {
         lookupName = this->name();
     }
 
-    polySurface* surfptr = getRegistrySurface(obr, lookupName);
+    surfMesh* surfptr = getSurfMesh();
 
     if (!surfptr)
     {
-        surfptr = new polySurface
-        (
-            lookupName,
-            obr,
-            true  // Add to registry - owned by registry
-        );
+        // Construct null and add owned by registry
+        surfptr = new surfMesh(lookupName, mesh());
+
+        surfptr->store();       // Add to registry - owned by registry
     }
 
-    surfptr->deepCopy(*this);   // Copy in geometry (removes old fields)
+    surfptr->copySurface(*this);   // Copy in geometry (removes old fields)
 
     return surfptr;
 }
 
 
-bool Foam::sampledSurface::removeRegistrySurface
-(
-    objectRegistry& obr,
-    word lookupName
-) const
+bool Foam::sampledSurface::removeSurfMesh(word lookupName) const
 {
-    polySurface* surfptr = getRegistrySurface(obr, lookupName);
+    surfMesh* surfptr = getSurfMesh(lookupName);
 
     if (surfptr)
     {
-        return obr.checkOut(*surfptr);
+        return mesh().checkOut(*surfptr);
     }
 
     return false;
 }
 
 
-// * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
-
-Foam::Ostream& Foam::operator<<(Ostream& os, const sampledSurface& s)
-{
-    s.print(os);
-    os.check(FUNCTION_NAME);
-    return os;
-}
-
-
 // ************************************************************************* //
diff --git a/src/sampling/sampledSurface/sampledSurface/sampledSurfaceTemplates.C b/src/sampling/sampledSurface/sampledSurface/sampledSurfaceTemplates.C
index 31794db89817464bf332593ba13c36759dc943ad..3d07a7a2ae30ece9a053b488d57327fd20852ecb 100644
--- a/src/sampling/sampledSurface/sampledSurface/sampledSurfaceTemplates.C
+++ b/src/sampling/sampledSurface/sampledSurface/sampledSurfaceTemplates.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2018-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2016 OpenFOAM Foundation
@@ -116,4 +116,100 @@ Foam::sampledSurface::pointAverage
 }
 
 
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type, class GeoMeshType>
+bool Foam::sampledSurface::storeRegistryField
+(
+    const objectRegistry& obr,
+    const word& fieldName,
+    const dimensionSet& dims,
+    const Field<Type>& values,
+    word lookupName
+) const
+{
+    polySurface* surfptr = this->getRegistrySurface(obr, lookupName);
+
+    if (surfptr)
+    {
+        surfptr->storeField<Type, GeoMeshType>
+        (
+            fieldName, dims, values
+        );
+    }
+
+    return surfptr;
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::sampledSurface::storeRegistryField
+(
+    const objectRegistry& obr,
+    const word& fieldName,
+    const dimensionSet& dims,
+    Field<Type>&& values,
+    word lookupName
+) const
+{
+    polySurface* surfptr = this->getRegistrySurface(obr, lookupName);
+
+    if (surfptr)
+    {
+        surfptr->storeField<Type, GeoMeshType>
+        (
+            fieldName, dims, std::move(values)
+        );
+    }
+
+    return surfptr;
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::sampledSurface::storeSurfMeshField
+(
+    const word& fieldName,
+    const dimensionSet& dims,
+    const Field<Type>& values,
+    word lookupName
+) const
+{
+    surfMesh* surfptr = this->getSurfMesh(lookupName);
+
+    if (surfptr)
+    {
+        surfptr->storeField<Type, GeoMeshType>
+        (
+            fieldName, dims, values
+        );
+    }
+
+    return surfptr;
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::sampledSurface::storeSurfMeshField
+(
+    const word& fieldName,
+    const dimensionSet& dims,
+    Field<Type>&& values,
+    word lookupName
+) const
+{
+    surfMesh* surfptr = this->getSurfMesh(lookupName);
+
+    if (surfptr)
+    {
+        surfptr->storeField<Type, GeoMeshType>
+        (
+            fieldName, dims, std::move(values)
+        );
+    }
+
+    return surfptr;
+}
+
+
 // ************************************************************************* //
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
index c72f0ce2eca7bfef5d4f997ea7012d48c7162973..5256a377ef2de7a03f7a39c69525a7450d01112c 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.C
@@ -26,15 +26,15 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "sampledSurfaces.H"
+#include "polySurface.H"
+
+#include "mapPolyMesh.H"
+#include "stringListOps.H"
 #include "volFields.H"
-#include "dictionary.H"
+#include "HashOps.H"
+#include "PstreamCombineReduceOps.H"
 #include "Time.H"
-#include "IOmanip.H"
-#include "interpolationCell.H"
-#include "volPointInterpolation.H"
-#include "PatchTools.H"
-#include "mapPolyMesh.H"
-#include "sampledTriSurfaceMesh.H"
+#include "UIndirectList.H"
 #include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@@ -51,69 +51,157 @@ namespace Foam
     );
 }
 
-bool Foam::sampledSurfaces::verbose_ = false;
 Foam::scalar Foam::sampledSurfaces::mergeTol_ = 1e-10;
 
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-void Foam::sampledSurfaces::writeGeometry() const
+Foam::polySurface* Foam::sampledSurfaces::getRegistrySurface
+(
+    const sampledSurface& s
+) const
 {
-    // Write to time directory under outputPath_
-    // Skip surfaces without faces (eg, a failed cut-plane)
+    return s.getRegistrySurface
+    (
+        storedObjects(),
+        IOobject::groupName(name(), s.name())
+    );
+}
 
-    const fileName outputDir = outputPath_/time_.timeName();
 
-    forAll(*this, surfi)
+Foam::polySurface* Foam::sampledSurfaces::storeRegistrySurface
+(
+    const sampledSurface& s
+)
+{
+    return s.storeRegistrySurface
+    (
+        storedObjects(),
+        IOobject::groupName(name(), s.name())
+    );
+}
+
+
+bool Foam::sampledSurfaces::removeRegistrySurface
+(
+    const sampledSurface& s
+)
+{
+    return s.removeRegistrySurface
+    (
+        storedObjects(),
+        IOobject::groupName(name(), s.name())
+    );
+}
+
+
+void Foam::sampledSurfaces::countFields()
+{
+    wordList allFields;    // Just needed for warnings
+    HashTable<wordHashSet> selected;
+
+    if (loadFromFiles_)
     {
-        const sampledSurface& s = operator[](surfi);
+        // Check files for a particular time
+        IOobjectList objects(obr_, obr_.time().timeName());
 
-        if (Pstream::parRun())
-        {
-            if (Pstream::master() && mergedList_[surfi].size())
-            {
-                formatter_->write
-                (
-                    outputDir,
-                    s.name(),
-                    mergedList_[surfi]
-                );
-            }
-        }
-        else if (s.faces().size())
+        allFields = objects.names();
+        selected = objects.classes(fieldSelection_);
+    }
+    else
+    {
+        // Check currently available fields
+        allFields = obr_.names();
+        selected = obr_.classes(fieldSelection_);
+    }
+
+    if (Pstream::parRun())
+    {
+        Pstream::mapCombineGather(selected, HashSetOps::plusEqOp<word>());
+        Pstream::mapCombineScatter(selected);
+    }
+
+
+    DynamicList<label> missed(fieldSelection_.size());
+
+    // Detect missing fields
+    forAll(fieldSelection_, i)
+    {
+        if (findStrings(fieldSelection_[i], allFields).empty())
         {
-            formatter_->write(outputDir, s.name(), s);
+            missed.append(i);
         }
     }
-}
 
+    if (missed.size())
+    {
+        WarningInFunction
+            << nl
+            << "Cannot find "
+            << (loadFromFiles_ ? "field file" : "registered field")
+            << " matching "
+            << UIndirectList<wordRe>(fieldSelection_, missed) << endl;
+    }
 
-void Foam::sampledSurfaces::writeOriginalIds()
-{
-    const word fieldName = "Ids";
-    const fileName outputDir = outputPath_/time_.timeName();
 
-    forAll(*this, surfi)
+    // Currently only support volume and surface field types
+    label nVolumeFields = 0;
+    label nSurfaceFields = 0;
+
+    forAllConstIters(selected, iter)
     {
-        const sampledSurface& s = operator[](surfi);
+        const word& clsName = iter.key();
+        const label n = iter.val().size();
 
-        if (s.hasFaceIds())
+        if (fieldTypes::volume.found(clsName))
+        {
+            nVolumeFields += n;
+        }
+        else if (sampledSurface::surfaceFieldTypes.found(clsName))
         {
-            const labelList& idLst = s.originalIds();
+            nSurfaceFields += n;
+        }
+    }
 
-            // Transcribe from label to scalar
-            Field<scalar> ids(idLst.size());
-            forAll(idLst, i)
-            {
-                ids[i] = idLst[i];
-            }
+    // Now propagate field counts (per surface)
 
-            writeSurface(ids, surfi, fieldName, outputDir);
-        }
+    label surfi = 0;
+
+    for (const sampledSurface& s : surfaces())
+    {
+        writers_[surfi].nFields() =
+        (
+            nVolumeFields
+          + (s.withSurfaceFields() ? nSurfaceFields : 0)
+          + ((s.hasFaceIds() && !s.interpolate()) ? 1 : 0)
+        );
+
+        ++surfi;
     }
 }
 
 
+Foam::autoPtr<Foam::surfaceWriter> Foam::sampledSurfaces::newWriter
+(
+    word writeType,
+    const dictionary& formatOptions,
+    const dictionary& surfDict
+)
+{
+    // Per-surface adjustment
+    surfDict.readIfPresent<word>("surfaceFormat", writeType);
+
+    dictionary options = formatOptions.subOrEmptyDict(writeType);
+
+    options.merge
+    (
+        surfDict.subOrEmptyDict("formatOptions").subOrEmptyDict(writeType)
+    );
+
+    return surfaceWriter::New(writeType, options);
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::sampledSurfaces::sampledSurfaces
@@ -126,6 +214,8 @@ Foam::sampledSurfaces::sampledSurfaces
     functionObjects::fvMeshFunctionObject(name, runTime, dict),
     PtrList<sampledSurface>(),
     loadFromFiles_(false),
+    verbose_(false),
+    onExecute_(false),
     outputPath_
     (
         time_.globalPath()/functionObject::outputPrefix/name
@@ -133,9 +223,9 @@ Foam::sampledSurfaces::sampledSurfaces
     fieldSelection_(),
     sampleFaceScheme_(),
     sampleNodeScheme_(),
-    mergedList_(),
-    changedGeom_(),
-    formatter_(nullptr)
+    writers_(),
+    actions_(),
+    nFaces_()
 {
     outputPath_.clean();  // Remove unneeded ".."
 
@@ -154,6 +244,8 @@ Foam::sampledSurfaces::sampledSurfaces
     functionObjects::fvMeshFunctionObject(name, obr, dict),
     PtrList<sampledSurface>(),
     loadFromFiles_(loadFromFiles),
+    verbose_(false),
+    onExecute_(false),
     outputPath_
     (
         time_.globalPath()/functionObject::outputPrefix/name
@@ -161,9 +253,9 @@ Foam::sampledSurfaces::sampledSurfaces
     fieldSelection_(),
     sampleFaceScheme_(),
     sampleNodeScheme_(),
-    mergedList_(),
-    changedGeom_(),
-    formatter_(nullptr)
+    writers_(),
+    actions_(),
+    nFaces_()
 {
     outputPath_.clean();  // Remove unneeded ".."
 
@@ -179,60 +271,19 @@ void Foam::sampledSurfaces::verbose(const bool verbosity)
 }
 
 
-bool Foam::sampledSurfaces::execute()
-{
-    return true;
-}
-
-
-bool Foam::sampledSurfaces::write()
-{
-    if (empty())
-    {
-        return true;
-    }
-
-
-    // Finalize surfaces, merge points etc.
-    update();
-
-    const label nFields = classifyFields();
-
-    // Write geometry first if required,
-    // or when no fields would otherwise be written
-    if (formatter_->separateGeometry() || !nFields)
-    {
-        writeGeometry();
-        changedGeom_ = false;
-    }
-
-    const IOobjectList objects(obr_, obr_.time().timeName());
-
-    sampleAndWrite<volScalarField>(objects);
-    sampleAndWrite<volVectorField>(objects);
-    sampleAndWrite<volSphericalTensorField>(objects);
-    sampleAndWrite<volSymmTensorField>(objects);
-    sampleAndWrite<volTensorField>(objects);
-
-    sampleAndWrite<surfaceScalarField>(objects);
-    sampleAndWrite<surfaceVectorField>(objects);
-    sampleAndWrite<surfaceSphericalTensorField>(objects);
-    sampleAndWrite<surfaceSymmTensorField>(objects);
-    sampleAndWrite<surfaceTensorField>(objects);
-
-    return true;
-}
-
-
 bool Foam::sampledSurfaces::read(const dictionary& dict)
 {
     fvMeshFunctionObject::read(dict);
 
     PtrList<sampledSurface>::clear();
-    mergedList_.clear();
-    changedGeom_.clear();
+    writers_.clear();
+    actions_.clear();
+    nFaces_.clear();
     fieldSelection_.clear();
 
+    verbose_ = dict.lookupOrDefault("verbose", false);
+    onExecute_ = dict.lookupOrDefault("sampleOnExecute", false);
+
     sampleFaceScheme_ =
         dict.lookupOrDefault<word>("sampleScheme", "cell");
 
@@ -241,82 +292,160 @@ bool Foam::sampledSurfaces::read(const dictionary& dict)
 
     const entry* eptr = dict.findEntry("surfaces");
 
+    // Surface writer type and format options
+    const word writerType =
+        (eptr ? dict.get<word>("surfaceFormat") : word::null);
+
+    const dictionary formatOptions(dict.subOrEmptyDict("formatOptions"));
+
+    // Store on registry?
+    const bool dfltStore = dict.lookupOrDefault("store", false);
+
     if (eptr && eptr->isDict())
     {
         PtrList<sampledSurface> surfs(eptr->dict().size());
 
+        actions_.resize(surfs.size(), ACTION_WRITE); // Default action
+        writers_.resize(surfs.size());
+        nFaces_.resize(surfs.size(), Zero);
+
         label surfi = 0;
 
         for (const entry& dEntry : eptr->dict())
         {
-            if (dEntry.isDict())
+            if (!dEntry.isDict())
+            {
+                continue;
+            }
+
+            const dictionary& surfDict = dEntry.dict();
+
+            autoPtr<sampledSurface> surf =
+            sampledSurface::New
+                (
+                    dEntry.keyword(),
+                    mesh_,
+                    surfDict
+                );
+
+            if (!surf.valid() || !surf->enabled())
             {
-                autoPtr<sampledSurface> surf =
-                    sampledSurface::New
-                    (
-                        dEntry.keyword(),
-                        mesh_,
-                        dEntry.dict()
-                    );
-
-                if (surf.valid() && surf->enabled())
-                {
-                    surfs.set(surfi, surf);
-                    ++surfi;
-                }
+                continue;
             }
+
+            // Define the surface
+            surfs.set(surfi, surf);
+
+            // Define additional action(s)
+            if (surfDict.lookupOrDefault("store", dfltStore))
+            {
+                actions_[surfi] |= ACTION_STORE;
+            }
+            if (surfDict.lookupOrDefault("surfMeshStore", false))
+            {
+                actions_[surfi] |= ACTION_SURF_MESH;
+            }
+
+            // Define surface writer, but do NOT yet attach a surface
+            writers_.set
+            (
+                surfi,
+                newWriter(writerType, formatOptions, surfDict)
+            );
+
+            writers_[surfi].isPointData() = surfs[surfi].interpolate();
+
+            // Use outputDir/TIME/surface-name
+            writers_[surfi].useTimeDir() = true;
+            writers_[surfi].verbose() = verbose_;
+
+            ++surfi;
         }
 
         surfs.resize(surfi);
+        actions_.resize(surfi);
+        writers_.resize(surfi);
         surfaces().transfer(surfs);
     }
     else if (eptr)
     {
-        PtrList<sampledSurface> surfs
+        // This is slightly trickier.
+        // We want access to the individual dictionaries used for construction
+
+        DynamicList<dictionary> capture;
+
+        PtrList<sampledSurface> input
         (
             eptr->stream(),
-            sampledSurface::iNew(mesh_)
+            sampledSurface::iNewCapture(mesh_, capture)
         );
 
-        forAll(surfs, surfi)
+        PtrList<sampledSurface> surfs(input.size());
+
+        actions_.resize(surfs.size(), ACTION_WRITE); // Default action
+        writers_.resize(surfs.size());
+        nFaces_.resize(surfs.size(), Zero);
+
+        label surfi = 0;
+
+        forAll(input, inputi)
         {
-            if (!surfs[surfi].enabled())
+            const dictionary& surfDict = capture[inputi];
+
+            autoPtr<sampledSurface> surf = input.set(inputi, nullptr);
+
+            if (!surf.valid() || !surf->enabled())
             {
-                surfs.set(surfi, nullptr);
+                continue;
             }
-        }
 
-        surfs.resize(surfs.squeezeNull());
+            // Define the surface
+            surfs.set(surfi, surf);
 
+            // Define additional action(s)
+            if (surfDict.lookupOrDefault("store", dfltStore))
+            {
+                actions_[surfi] |= ACTION_STORE;
+            }
+            if (surfDict.lookupOrDefault("surfMeshStore", false))
+            {
+                actions_[surfi] |= ACTION_SURF_MESH;
+            }
+
+            // Define surface writer, but do NOT yet attach a surface
+            writers_.set
+            (
+                surfi,
+                newWriter(writerType, formatOptions, surfDict)
+            );
+
+            writers_[surfi].isPointData() = surfs[surfi].interpolate();
+
+            // Use outputDir/TIME/surface-name
+            writers_[surfi].useTimeDir() = true;
+            writers_[surfi].verbose() = verbose_;
+
+            ++surfi;
+        }
+
+        surfs.resize(surfi);
+        actions_.resize(surfi);
+        writers_.resize(surfi);
         surfaces().transfer(surfs);
     }
 
 
     const auto& surfs = surfaces();
+
+    // Have some surfaces, so sort out which fields are needed and report
+
     if (surfs.size())
     {
+        nFaces_.resize(surfs.size(), Zero);
+
         dict.readEntry("fields", fieldSelection_);
         fieldSelection_.uniq();
 
-        // The surface writer and format options
-        const word writerType(dict.get<word>("surfaceFormat"));
-
-        // Define the surface formatter
-        // Optionally defined extra controls for the output formats
-        formatter_ = surfaceWriter::New
-        (
-            writerType,
-            dict.subOrEmptyDict("formatOptions").subOrEmptyDict(writerType)
-        );
-
-        if (Pstream::parRun())
-        {
-            mergedList_.resize(size());
-        }
-
-        // Ensure all surfaces and merge information are expired
-        expire();
-
         label surfi = 0;
         for (const sampledSurface& s : surfs)
         {
@@ -324,7 +453,18 @@ bool Foam::sampledSurfaces::read(const dictionary& dict)
             {
                 Info<< "Sampled surface:" << nl;
             }
-            Info<< "    " << s.name() << " -> " << writerType << nl;
+
+            Info<< "    " << s.name() << " -> " << writers_[surfi].type();
+            if (actions_[surfi] & ACTION_STORE)
+            {
+                Info<< ", store on registry ("
+                    << IOobject::groupName(name(), s.name()) << ')';
+            }
+            if (actions_[surfi] & ACTION_SURF_MESH)
+            {
+                Info<< ", store as surfMesh (deprecated)";
+            }
+            Info<< nl;
 
             ++surfi;
         }
@@ -334,24 +474,171 @@ bool Foam::sampledSurfaces::read(const dictionary& dict)
     if (debug && Pstream::master())
     {
         Pout<< "sample fields:" << fieldSelection_ << nl
-            << "sample surfaces:" << nl << "(" << nl;
+            << "sample surfaces:" << nl << '(' << nl;
 
         for (const sampledSurface& s : surfaces())
         {
             Pout<< "  " << s << nl;
         }
+        Pout<< ')' << endl;
+    }
 
-        Pout<< ")" << endl;
+    // Ensure all surfaces and merge information are expired
+    expire();
+
+    return true;
+}
+
+
+bool Foam::sampledSurfaces::performAction(unsigned request)
+{
+    if
+    (
+        empty()
+     || (request == ACTION_NONE)
+     || !testAny
+        (
+            actions_,
+            [=] (unsigned action) { return (request & action); }
+        )
+    )
+    {
+        return true;
     }
 
-    // New geometry
-    changedGeom_.resize(size());
-    changedGeom_ = true;
+
+    // Finalize surfaces, update information, writer associations etc.
+    update();
+
+    bool noFaces = true;
+    for (const label n : nFaces_)
+    {
+        if (n) noFaces = false;
+    }
+
+    if (noFaces)
+    {
+        // No surfaces with faces at all.
+        return true;
+    }
+
+    // Determine the per-surface number of fields, including Ids etc.
+    // Only seems to be needed for VTK legacy
+    countFields();
+
+
+    forAll(*this, surfi)
+    {
+        const sampledSurface& s = (*this)[surfi];
+
+        if (!nFaces_[surfi])
+        {
+            continue;
+        }
+
+        if ((request & actions_[surfi]) & ACTION_STORE)
+        {
+            storeRegistrySurface(s);
+        }
+
+        if ((request & actions_[surfi]) & ACTION_SURF_MESH)
+        {
+            s.storeSurfMesh();
+        }
+
+        if ((request & actions_[surfi]) & ACTION_WRITE)
+        {
+            // Output writers
+            surfaceWriter& outWriter = writers_[surfi];
+
+            if (outWriter.needsUpdate())
+            {
+                outWriter.setSurface(s);
+            }
+
+            outWriter.open(outputPath_/s.name());
+
+            outWriter.beginTime(obr_.time());
+
+
+            // Write geometry if no fields would otherwise be written
+            if (!outWriter.nFields() || outWriter.separateGeometry())
+            {
+                outWriter.writeGeometry();
+                continue;
+            }
+
+            // Write original ids - as label or scalar field
+
+            const word fieldName("Ids");
+            if (s.hasFaceIds() && !s.interpolate())
+            {
+                writeSurface
+                (
+                    outWriter,
+                    Field<label>(s.originalIds()),
+                    fieldName
+                );
+            }
+        }
+    }
+
+    const IOobjectList objects(obr_, obr_.time().timeName());
+
+    performAction<volScalarField>(objects, request);
+    performAction<volVectorField>(objects, request);
+    performAction<volSphericalTensorField>(objects, request);
+    performAction<volSymmTensorField>(objects, request);
+    performAction<volTensorField>(objects, request);
+
+    // Only bother with surface fields if a sampler supports them
+    if
+    (
+        testAny
+        (
+            surfaces(),
+            [] (const sampledSurface& s) { return s.withSurfaceFields(); }
+        )
+    )
+    {
+        performAction<surfaceScalarField>(objects, request);
+        performAction<surfaceVectorField>(objects, request);
+        performAction<surfaceSphericalTensorField>(objects, request);
+        performAction<surfaceSymmTensorField>(objects, request);
+        performAction<surfaceTensorField>(objects, request);
+    }
+
+
+    // Finish this time step
+    forAll(writers_, surfi)
+    {
+        if ((request & actions_[surfi]) & ACTION_WRITE)
+        {
+            writers_[surfi].endTime();
+        }
+    }
+
+    return true;
+}
+
+
+bool Foam::sampledSurfaces::execute()
+{
+    if (onExecute_)
+    {
+        return performAction(ACTION_ALL & ~ACTION_WRITE);
+    }
 
     return true;
 }
 
 
+bool Foam::sampledSurfaces::write()
+{
+    return performAction(ACTION_ALL);
+}
+
+
 void Foam::sampledSurfaces::updateMesh(const mapPolyMesh& mpm)
 {
     if (&mpm.mesh() == &mesh_)
@@ -383,9 +670,9 @@ void Foam::sampledSurfaces::readUpdate(const polyMesh::readUpdateState state)
 
 bool Foam::sampledSurfaces::needsUpdate() const
 {
-    forAll(*this, surfi)
+    for (const sampledSurface& s : surfaces())
     {
-        if (operator[](surfi).needsUpdate())
+        if (s.needsUpdate())
         {
             return true;
         }
@@ -397,26 +684,29 @@ bool Foam::sampledSurfaces::needsUpdate() const
 
 bool Foam::sampledSurfaces::expire()
 {
-    bool justExpired = false;
+    // Dimension as fraction of mesh bounding box
+    const scalar mergeDim = mergeTol_ * mesh_.bounds().mag();
 
-    forAll(*this, surfi)
+    label nChanged = 0;
+
+    label surfi = 0;
+
+    for (sampledSurface& s : surfaces())
     {
-        if (operator[](surfi).expire())
+        if (s.expire())
         {
-            justExpired = true;
+            ++nChanged;
         }
 
-        // Clear merge information
-        if (Pstream::parRun())
-        {
-            mergedList_[surfi].clear();
-        }
-    }
+        writers_[surfi].expire();
+        writers_[surfi].mergeDim() = mergeDim;
+        nFaces_[surfi] = 0;
 
-    changedGeom_ = true;
+        ++surfi;
+    }
 
-    // true if any surfaces just expired
-    return justExpired;
+    // True if any surfaces just expired
+    return nChanged;
 }
 
 
@@ -427,48 +717,24 @@ bool Foam::sampledSurfaces::update()
         return false;
     }
 
-    bool updated = false;
+    label nUpdated = 0;
 
-    // Serial: quick and easy, no merging required
-    if (!Pstream::parRun())
-    {
-        forAll(*this, surfi)
-        {
-            sampledSurface& s = operator[](surfi);
+    label surfi = 0;
 
-            if (s.update())
-            {
-                updated = true;
-                changedGeom_[surfi] = true;
-            }
-        }
-
-        return updated;
-    }
-
-
-    // Dimension as fraction of mesh bounding box
-    const scalar mergeDim = mergeTol_*mesh_.bounds().mag();
-
-    if (Pstream::master() && debug)
-    {
-        Pout<< nl << "Merging all points within "
-            << mergeDim << " metre" << endl;
-    }
-
-    forAll(*this, surfi)
+    for (sampledSurface& s : surfaces())
     {
-        sampledSurface& s = operator[](surfi);
-
         if (s.update())
         {
-            updated = true;
-            changedGeom_[surfi] = true;
-            mergedList_[surfi].merge(s, mergeDim);
+            ++nUpdated;
+            writers_[surfi].expire();
         }
+
+        nFaces_[surfi] = returnReduce(s.faces().size(), sumOp<label>());
+
+        ++surfi;
     }
 
-    return updated;
+    return nUpdated;
 }
 
 
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
index 902b55c2c956d3e2aed8f148b223d9a8f39161de..49498f458cfad3147eac47828ea9eec255e1a917 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfaces.H
@@ -53,8 +53,12 @@ Description
         // (only used if interpolate=true for the surfaces below)
         interpolationScheme cell;
 
+        // Optional: registry storage
+        store           true
+
         // Output surface format
         surfaceFormat   vtk;
+
         formatOptions
         {
             vtk
@@ -76,6 +80,12 @@ Description
 
                 // Optional: generate values on points instead of faces
                 interpolate true;
+
+                // Optional: alternative output type
+                surfaceFormat   ensight;
+
+                // Optional: registry storage
+                store       true
             }
         );
     }
@@ -84,13 +94,24 @@ Description
     Entries:
     \table
         Property | Description                              | Required | Default
-        type     | surfaces                                 | yes |
-        surfaces | list or dictionary of sample surfaces    | recommended |
-        fields   | word/regex list of fields to sampled     | yes |
+        type     | Type-name: surfaces                      | yes |
+        surfaces | Dictionary or list of sample surfaces    | expected |
+        fields   | word/regex list of fields to sample      | yes |
         sampleScheme | scheme to obtain face centre value   | no  | cell
         interpolationScheme | scheme to obtain node values  | no  | cellPoint
         surfaceFormat | output surface format               | yes |
         formatOptions | dictionary of format options        | no  |
+        sampleOnExecute | Sample (store) on execution as well | no | false
+        store    | Store surface/fields on registry         | no  | false
+    \endtable
+
+    Additional per-surface entries:
+    \table
+        Property | Description                              | Required | Default
+        store    | Store surface/fields on registry         | no  |
+        surfaceFormat | output surface format               | no  |
+        formatOptions | dictionary of format options        | no  |
+        surfMeshStore | Store surface/fields as surfMesh (transitional) | no  |
     \endtable
 
 Note
@@ -111,10 +132,8 @@ SourceFiles
 #include "fvMeshFunctionObject.H"
 #include "sampledSurface.H"
 #include "surfaceWriter.H"
-#include "mergedSurf.H"
 #include "volFieldsFwd.H"
 #include "surfaceFieldsFwd.H"
-#include "wordRes.H"
 #include "IOobjectList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -122,6 +141,9 @@ SourceFiles
 namespace Foam
 {
 
+// Forward Declarations
+class polySurface;
+
 /*---------------------------------------------------------------------------*\
                        Class sampledSurfaces Declaration
 \*---------------------------------------------------------------------------*/
@@ -133,23 +155,36 @@ class sampledSurfaces
 {
     // Static Data Members
 
-        //- Output verbosity
-        static bool verbose_;
-
         //- Tolerance for merging points (fraction of mesh bounding box)
         static scalar mergeTol_;
 
+        //- Local control for sampling actions
+        enum sampleActionType : unsigned
+        {
+            ACTION_NONE  = 0,
+            ACTION_WRITE = 0x1,
+            ACTION_STORE = 0x2,
+            ACTION_SURF_MESH = 0x4,
+            ACTION_ALL = 0xF
+        };
+
 
     // Private Data
 
         //- Load fields from files (not from objectRegistry)
         const bool loadFromFiles_;
 
+        //- Output verbosity
+        bool verbose_;
+
+        //- Perform sample/store actions on execute as well
+        bool onExecute_;
+
         //- Output path
         fileName outputPath_;
 
 
-    // Read from dictonary
+    // Read from dictionary
 
         //- Names of fields to sample
         wordRes fieldSelection_;
@@ -161,19 +196,16 @@ class sampledSurfaces
         word sampleNodeScheme_;
 
 
-    // Surfaces
-
-        //- Merged meshed surfaces (parallel only)
-        List<mergedSurf> mergedList_;
+    // Output control
 
-        //- Track which surfaces have changed
-        List<bool> changedGeom_;
+        //- Surface writers (one per surface)
+        PtrList<surfaceWriter> writers_;
 
+        //- Per-surface selection of store/write actions
+        List<unsigned> actions_;
 
-    // Calculated
-
-        //- Surface formatter
-        autoPtr<surfaceWriter> formatter_;
+        //- Cached values of the global number of faces per-surface
+        labelList nFaces_;
 
 
     // Private Member Functions
@@ -190,42 +222,82 @@ class sampledSurfaces
             return *this;
         }
 
-        //- Return number of fields
-        label classifyFields();
+        //- A new surfaceWriter, with per-surface formatOptions
+        static autoPtr<surfaceWriter> newWriter
+        (
+            word writeType,
+            const dictionary& formatOptions,
+            const dictionary& surfDict
+        );
 
-        //- Write geometry only
-        void writeGeometry() const;
 
-        //- Write scalar field with original ids
-        void writeOriginalIds();
+        //- Perform sampling action with store/write
+        bool performAction(unsigned request);
+
+        //- Count selected/sampled fields per surface
+        void countFields();
 
         //- Write sampled fieldName on surface and on outputDir path
         template<class Type>
         void writeSurface
         (
+            surfaceWriter& writer,
             const Field<Type>& values,
-            const label surfi,
-            const word& fieldName,
-            const fileName& outputDir
+            const word& fieldName
         );
 
-        //- Sample and write a particular volume field
+        //- Sample and store/write a specific volume field
         template<class Type>
-        void sampleAndWrite
+        void performAction
         (
-            const GeometricField<Type, fvPatchField, volMesh>& vField
+            const GeometricField<Type, fvPatchField, volMesh>& fld,
+            unsigned request
         );
 
-        //- Sample and write a particular surface field
+        //- Sample and store/write a specific surface field
         template<class Type>
-        void sampleAndWrite
+        void performAction
         (
-            const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
+            const GeometricField<Type, fvsPatchField, surfaceMesh>& fld,
+            unsigned request
+        );
+
+        //- Sample and write all applicable sampled fields
+        template<class GeoField>
+        void performAction
+        (
+            const IOobjectList& objects,
+            unsigned request
+        );
+
+
+        //- Get surface from registry if available.
+        //  \return surface or nullptr
+        polySurface* getRegistrySurface(const sampledSurface& s) const;
+
+        //- Put surface onto registry, when enabled.
+        //  \return surface or nullptr it surface should not be stored
+        polySurface* storeRegistrySurface(const sampledSurface& s);
+
+        //- Remove surface from registry.
+        //  \return True if surface existed and was removed
+        bool removeRegistrySurface(const sampledSurface& s);
+
+
+        //- Store sampled field onto surface registry if it exists
+        template<class Type, class GeoMeshType>
+        bool storeRegistryField
+        (
+            const sampledSurface& s,
+            const word& fieldName,
+            const dimensionSet& dims,
+            Field<Type>&& values
         );
 
-        //- Sample and write all sampled fields
-        template<class Type> void sampleAndWrite(const IOobjectList& objects);
 
+        //- Test surfaces for condition
+        template<class Container, class Predicate>
+        static bool testAny(const Container& items, const Predicate& pred);
 
         //- No copy construct
         sampledSurfaces(const sampledSurfaces&) = delete;
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesGrouping.C b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesGrouping.C
deleted file mode 100644
index b36710e3dccca392c7c345e4ea769a0414e45a4c..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesGrouping.C
+++ /dev/null
@@ -1,89 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2015 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "sampledSurfaces.H"
-#include "IOobjectList.H"
-#include "UIndirectList.H"
-#include "ListOps.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-Foam::label Foam::sampledSurfaces::classifyFields()
-{
-    label nFields = 0;
-
-    wordList allFields;    // Just needed for warnings
-    HashTable<wordHashSet> available;
-
-    if (loadFromFiles_)
-    {
-        // Check files for a particular time
-        IOobjectList objects(obr_, obr_.time().timeName());
-
-        allFields = objects.names();
-        available = objects.classes(fieldSelection_);
-    }
-    else
-    {
-        // Check currently available fields
-        allFields = obr_.names();
-        available = obr_.classes(fieldSelection_);
-    }
-
-    DynamicList<label> missed(fieldSelection_.size());
-
-    // Detect missing fields
-    forAll(fieldSelection_, i)
-    {
-        if (!ListOps::found(allFields, fieldSelection_[i]))
-        {
-            missed.append(i);
-        }
-    }
-
-    if (missed.size())
-    {
-        WarningInFunction
-            << nl
-            << "Cannot find "
-            << (loadFromFiles_ ? "field file" : "registered field")
-            << " matching "
-            << UIndirectList<wordRe>(fieldSelection_, missed) << endl;
-    }
-
-
-    // Total number selected
-    forAllConstIters(available, iter)
-    {
-        nFields += iter.val().size();
-    }
-
-    return nFields;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
index 150a0b6dc0bfaba0be406531bafdc60d404856b1..48ff4a204504e4fbdba120d200ca384f439fd988 100644
--- a/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
+++ b/src/sampling/sampledSurface/sampledSurfaces/sampledSurfacesTemplates.C
@@ -28,110 +28,61 @@ License
 #include "sampledSurfaces.H"
 #include "volFields.H"
 #include "surfaceFields.H"
-#include "globalIndex.H"
-#include "stringListOps.H"
+#include "polySurface.H"
+#include "polySurfaceFields.H"
+#include "polySurfacePointFields.H"
+#include "surfMesh.H"
+#include "surfGeoMesh.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
 void Foam::sampledSurfaces::writeSurface
 (
+    surfaceWriter& writer,
     const Field<Type>& values,
-    const label surfi,
-    const word& fieldName,
-    const fileName& outputDir
+    const word& fieldName
 )
 {
-    const sampledSurface& s = operator[](surfi);
-
-    if (changedGeom_[surfi])
-    {
-        // Trigger any changes
-        formatter_->updateMesh(outputDir, s.name());
-        changedGeom_[surfi] = false;
-    }
-
-    if (Pstream::parRun())
-    {
-        // Gather all values into single field
-        Field<Type> allValues;
+    fileName outputName = writer.write(fieldName, values);
 
-        globalIndex::gatherOp(values, allValues);
+    // Case-local file name with "<case>" to make relocatable
 
-        fileName sampleFile;
-        if (Pstream::master())
-        {
-            // Renumber (point data) to correspond to merged points
-            if (mergedList_[surfi].pointsMap().size() == allValues.size())
-            {
-                inplaceReorder(mergedList_[surfi].pointsMap(), allValues);
-                allValues.resize(mergedList_[surfi].points().size());
-            }
-
-            // Write to time directory under outputPath_
-            // skip surface without faces (eg, a failed cut-plane)
-            if (mergedList_[surfi].size())
-            {
-                sampleFile = formatter_->write
-                (
-                    outputDir,
-                    s.name(),
-                    mergedList_[surfi],
-                    fieldName,
-                    allValues,
-                    s.interpolate()
-                );
-            }
-        }
-
-        Pstream::scatter(sampleFile);
-        if (sampleFile.size())
-        {
-            // Case-local file name with "<case>" to make relocatable
-
-            dictionary propsDict;
-            propsDict.add
-            (
-                "file",
-                time_.relativePath(sampleFile, true)
-            );
-            setProperty(fieldName, propsDict);
-        }
-    }
-    else
-    {
-        // Write to time directory under outputPath_
-        // skip surface without faces (eg, a failed cut-plane)
-        if (s.faces().size())
-        {
-            fileName fName = formatter_->write
-            (
-                outputDir,
-                s.name(),
-                s,
-                fieldName,
-                values,
-                s.interpolate()
-            );
+    dictionary propsDict;
+    propsDict.add
+    (
+        "file",
+        time_.relativePath(outputName, true)
+    );
+    setProperty(fieldName, propsDict);
+}
 
-            // Case-local file name with "<case>" to make relocatable
 
-            dictionary propsDict;
-            propsDict.add
-            (
-                "file",
-                time_.relativePath(fName, true)
-            );
-            setProperty(fieldName, propsDict);
-        }
-    }
+template<class Type, class GeoMeshType>
+bool Foam::sampledSurfaces::storeRegistryField
+(
+    const sampledSurface& s,
+    const word& fieldName,
+    const dimensionSet& dims,
+    Field<Type>&& values
+)
+{
+    return s.storeRegistryField<Type, GeoMeshType>
+    (
+        storedObjects(),
+        fieldName,
+        dims,
+        std::move(values),
+        IOobject::groupName(name(), s.name())
+    );
 }
 
 
 template<class Type>
-void Foam::sampledSurfaces::sampleAndWrite
+void Foam::sampledSurfaces::performAction
 (
-    const GeometricField<Type, fvPatchField, volMesh>& vField
+    const GeometricField<Type, fvPatchField, volMesh>& fld,
+    unsigned request
 )
 {
     // The sampler for this field
@@ -140,13 +91,20 @@ void Foam::sampledSurfaces::sampleAndWrite
     // The interpolator for this field
     autoPtr<interpolation<Type>> interpPtr;
 
-    const word& fieldName = vField.name();
-    const fileName outputDir = outputPath_/vField.time().timeName();
+    const word& fieldName = fld.name();
+
+    const dimensionSet& dims = fld.dimensions();
 
     forAll(*this, surfi)
     {
         const sampledSurface& s = operator[](surfi);
 
+        // Skip surface without faces (eg, failed cut-plane)
+        if (!nFaces_[surfi])
+        {
+            continue;
+        }
+
         Field<Type> values;
 
         if (s.interpolate())
@@ -156,7 +114,7 @@ void Foam::sampledSurfaces::sampleAndWrite
                 interpPtr = interpolation<Type>::New
                 (
                     sampleNodeScheme_,
-                    vField
+                    fld
                 );
             }
 
@@ -169,56 +127,117 @@ void Foam::sampledSurfaces::sampleAndWrite
                 samplePtr = interpolation<Type>::New
                 (
                     sampleFaceScheme_,
-                    vField
+                    fld
                 );
             }
 
             values = s.sample(*samplePtr);
         }
 
-        writeSurface<Type>(values, surfi, fieldName, outputDir);
+        if ((request & actions_[surfi]) & ACTION_WRITE)
+        {
+            writeSurface<Type>(writers_[surfi], values, fieldName);
+        }
+
+        if ((request & actions_[surfi]) & ACTION_SURF_MESH)
+        {
+            // Face fields only!
+            s.storeSurfMeshField<Type, surfGeoMesh>
+            (
+                fieldName, dims, values
+            );
+        }
+
+        if ((request & actions_[surfi]) & ACTION_STORE)
+        {
+            if (s.interpolate())
+            {
+                storeRegistryField<Type, polySurfacePointGeoMesh>
+                (
+                    s, fieldName, dims, std::move(values)
+                );
+            }
+            else
+            {
+                storeRegistryField<Type, polySurfaceGeoMesh>
+                (
+                    s, fieldName, dims, std::move(values)
+                );
+            }
+        }
     }
 }
 
 
 template<class Type>
-void Foam::sampledSurfaces::sampleAndWrite
+void Foam::sampledSurfaces::performAction
 (
-    const GeometricField<Type, fvsPatchField, surfaceMesh>& sField
+    const GeometricField<Type, fvsPatchField, surfaceMesh>& fld,
+    unsigned request
 )
 {
-    const word& fieldName = sField.name();
-    const fileName outputDir = outputPath_/sField.time().timeName();
+    const word& fieldName = fld.name();
+
+    const dimensionSet& dims = fld.dimensions();
 
     forAll(*this, surfi)
     {
-        const sampledSurface& s = operator[](surfi);
-        Field<Type> values(s.sample(sField));
-        writeSurface<Type>(values, surfi, fieldName, outputDir);
+        const sampledSurface& s = (*this)[surfi];
+
+        // Skip surface without faces (eg, failed cut-plane)
+        if (!nFaces_[surfi])
+        {
+            continue;
+        }
+
+        Field<Type> values(s.sample(fld));
+
+        if ((request & actions_[surfi]) & ACTION_WRITE)
+        {
+            writeSurface<Type>(writers_[surfi], values, fieldName);
+        }
+
+        if ((request & actions_[surfi]) & ACTION_SURF_MESH)
+        {
+            s.storeSurfMeshField<Type, surfGeoMesh>
+            (
+                fieldName, dims, values
+            );
+        }
+
+        if ((request & actions_[surfi]) & ACTION_STORE)
+        {
+            storeRegistryField<Type, polySurfaceGeoMesh>
+            (
+                s, fieldName, dims, std::move(values)
+            );
+        }
     }
 }
 
 
 template<class GeoField>
-void Foam::sampledSurfaces::sampleAndWrite(const IOobjectList& objects)
+void Foam::sampledSurfaces::performAction
+(
+    const IOobjectList& objects,
+    unsigned request
+)
 {
     wordList fieldNames;
     if (loadFromFiles_)
     {
-        fieldNames = objects.sortedNames(GeoField::typeName, fieldSelection_);
+        fieldNames = objects.sortedNames<GeoField>(fieldSelection_);
     }
     else
     {
         fieldNames = mesh_.thisDb().sortedNames<GeoField>(fieldSelection_);
-
-        writeOriginalIds();
     }
 
     for (const word& fieldName : fieldNames)
     {
         if (verbose_)
         {
-            Info<< "sampleAndWrite: " << fieldName << endl;
+            Info<< "sampleWrite: " << fieldName << endl;
         }
 
         if (loadFromFiles_)
@@ -235,17 +254,37 @@ void Foam::sampledSurfaces::sampleAndWrite(const IOobjectList& objects)
                 mesh_
             );
 
-            sampleAndWrite(fld);
+            performAction(fld, request);
         }
         else
         {
-            sampleAndWrite
+            performAction
             (
-                mesh_.thisDb().lookupObject<GeoField>(fieldName)
+                mesh_.thisDb().lookupObject<GeoField>(fieldName),
+                request
             );
         }
     }
 }
 
 
+template<class Container, class Predicate>
+bool Foam::sampledSurfaces::testAny
+(
+    const Container& items,
+    const Predicate& pred
+)
+{
+    for (const auto& item : items)
+    {
+        if (pred(item))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
 // ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.C b/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.C
deleted file mode 100644
index d1381a6e56e12f3c9132fe14d33be94c461eb14c..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.C
+++ /dev/null
@@ -1,116 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "boundaryDataSurfaceWriter.H"
-#include "makeSurfaceWriterMethods.H"
-#include "argList.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    makeSurfaceWriterType(boundaryDataSurfaceWriter);
-}
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Field writing implementation
-#include "boundaryDataSurfaceWriterImpl.C"
-
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::boundaryDataSurfaceWriter);
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileName Foam::boundaryDataSurfaceWriter::write
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
-{
-    // geometry: rootdir/surfaceName/"points"
-    // field:    rootdir/surfaceName/time/field
-
-    const fileName baseDir(outputDir.path()/surfaceName);
-    const fileName timeName(outputDir.name());
-
-    const pointField& points = surf.points();
-
-    // Dummy time to use as an objectRegistry
-    const fileName caseDir(argList::envGlobalPath());
-
-    Time dummyTime
-    (
-        caseDir.path(), // root-path,
-        caseDir.name(), // case-name,
-        "system",       //
-        "constant",     //
-        false           // no function objects
-    );
-
-
-    // Write points
-    if (verbose)
-    {
-        Info<< "Writing points to " << baseDir/"points" << endl;
-    }
-
-    pointIOField pts
-    (
-        IOobject
-        (
-            baseDir/"points",
-            dummyTime,
-            IOobject::NO_READ,
-            IOobject::NO_WRITE,
-            false
-        ),
-        points
-    );
-
-    {
-        // Do like regIOobject::writeObject but don't do instance() adaptation
-        // since this would write to e.g. 0/ instead of postProcessing/
-
-        // Try opening an OFstream for object
-        mkDir(pts.path());
-        OFstream os(pts.objectPath());
-
-        //pts.writeHeader(os);
-        pts.writeData(os);
-        //pts.writeEndDivider(os);
-    }
-
-    return baseDir;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.H b/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.H
deleted file mode 100644
index 23e9fd3153fb1f1d3f762670a1ec645f6f74fc20..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriter.H
+++ /dev/null
@@ -1,231 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-License
-    This file is part of OpenFOAM.
-
-    OpenFOAM is free software: you can redistribute it and/or modify it
-    under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
-    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-    for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
-
-Class
-    Foam::boundaryDataSurfaceWriter
-
-Description
-    A surfaceWriter for outputting to a form useable for the
-    timeVaryingMapped boundary condition. This reads the data from
-    constant/boundaryData/\<patch\> directory
-
-    Typical way of working:
-    - use a sampledSurface of type 'patch' (to sample a patch):
-    \verbatim
-    surfaces
-    {
-        type            surfaces;
-        surfaceFormat   boundaryData;
-        fields          ( p );
-        surfaces
-        (
-            outlet
-            {
-                type            patch;
-                patches         (outlet);
-                interpolate     false;
-            }
-        );
-    }
-    \endverbatim
-
-    - write using this writer.
-    - move postProcessing/surfaces/outlet to constant/boundaryData/outlet
-      in your destination case.
-    - use a timeVaryingMappedFixedValue condition to read and interpolate
-      the profile:
-        type            timeVaryingMappedFixedValue;
-        setAverage      false;  // do not use read average
-        offset          0;      // do not apply offset to values
-
-    Note:
-    - with 'interpolate false' the data is on the face centres of the
-      patch. Take care that a 2D geometry will only have a single row
-      of face centres so might not provide a valid triangulation
-      (this is what timeVaryingMappedFixedValue uses to do interpolation)
-      (Alternatively use timeVaryingMappedFixedValue with mapMethod 'nearest')
-
-    \heading Output file locations
-
-    The \c rootdir normally corresponds to something like
-    \c postProcessing/\<name\>
-
-    where the geometry is written as:
-    \verbatim
-    rootdir
-    `-- surfaceName
-        `-- "points"
-    \endverbatim
-
-    and field data:
-    \verbatim
-    rootdir
-    `-- surfaceName
-        |-- "points"
-        `-- timeName
-            `-- field
-    \endverbatim
-
-SourceFiles
-    boundaryDataSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef boundaryDataSurfaceWriter_H
-#define boundaryDataSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                  Class boundaryDataSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class boundaryDataSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private Member Functions
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-public:
-
-    //- Runtime type information
-    TypeName("boundaryData");
-
-
-    // Constructors
-
-        //- Construct null
-        boundaryDataSurfaceWriter() = default;
-
-
-    //- Destructor
-    virtual ~boundaryDataSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriterImpl.C b/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriterImpl.C
deleted file mode 100644
index d3445cab762009180c2745ed433820ba962bab3b..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/boundaryData/boundaryDataSurfaceWriterImpl.C
+++ /dev/null
@@ -1,132 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
--------------------------------------------------------------------------------
-License
-    This file is part of OpenFOAM.
-
-    OpenFOAM is free software: you can redistribute it and/or modify i
-    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 "OFstream.H"
-#include "OSspecific.H"
-#include "IOmanip.H"
-#include "Time.H"
-#include "pointIOField.H"
-#include "primitivePatch.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class Type>
-Foam::fileName Foam::boundaryDataSurfaceWriter::writeTemplate
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    // geometry: rootdir/surfaceName/"points"
-    // field:    rootdir/surfaceName/time/field
-
-    const fileName baseDir(outputDir.path()/surfaceName);
-    const fileName timeName(outputDir.name());
-
-    const pointField& points = surf.points();
-    const faceList&    faces = surf.faces();
-
-    // Dummy time to use as an objectRegistry
-    const fileName caseDir(argList::envGlobalPath());
-
-    Time dummyTime
-    (
-        caseDir.path(), // root-path
-        caseDir.name(), // case-name
-        "system",       //
-        "constant",     //
-        false           // no function objects
-    );
-
-
-    // Write points
-
-    pointIOField pts
-    (
-        IOobject
-        (
-            baseDir/"points",
-            dummyTime,
-            IOobject::NO_READ,
-            IOobject::NO_WRITE,
-            false
-        ),
-        label(0)
-    );
-
-    if (isNodeValues)
-    {
-        if (verbose)
-        {
-            Info<< "Writing points to " << baseDir/"points" << endl;
-        }
-        pts = points;
-    }
-    else
-    {
-        if (verbose)
-        {
-            Info<< "Writing face centres to " << baseDir/"points" << endl;
-        }
-
-        primitivePatch pp(SubList<face>(faces, faces.size()), points);
-
-        pts = pp.faceCentres();
-    }
-
-    {
-        // Do like regIOobject::writeObject but don't do instance() adaptation
-        // since this would write to e.g. 0/ instead of postProcessing/
-
-        // Try opening an OFstream for object
-        mkDir(pts.path());
-        OFstream os(pts.objectPath());
-
-        //pts.writeHeader(os);
-        pts.writeData(os);
-        //pts.writeEndDivider(os);
-    }
-
-
-    // Write field
-    {
-        fileName valsDir(baseDir/timeName);
-        mkDir(valsDir);
-        OFstream os(valsDir/fieldName);
-        os  << values;
-    }
-
-    return baseDir;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H
deleted file mode 100644
index b26fd9a0f7b37d66823af70ef7d12b6c6a0ef170..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.H
+++ /dev/null
@@ -1,259 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::ensightSurfaceWriter
-
-Description
-    A surfaceWriter for Ensight format.
-
-    \verbatim
-    formatOptions
-    {
-        ensight
-        {
-            format          ascii;
-            collateTimes    true;
-        }
-    }
-    \endverbatim
-
- Format options:
-    \table
-        Property | Description                             | Required | Default
-        format   | ascii/binary                            | no       | ascii
-        collateTimes | use common geometry for times       | no       | true
-    \endtable
-
-SourceFiles
-    ensightSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef ensightSurfaceWriter_H
-#define ensightSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                    Class ensightSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class ensightSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private data
-
-        //- Write option (default: IOstream::ASCII)
-        IOstream::streamFormat writeFormat_;
-
-        //- Collate times (default: true)
-        bool collateTimes_;
-
-
-    // Private Member Functions
-
-        //- Print time-set for ensight case file
-        static void printTimeset
-        (
-            OSstream& os,
-            const label ts,
-            const scalar& timeValue
-        );
-
-        //- Print time-set for ensight case file
-        static void printTimeset
-        (
-            OSstream& os,
-            const label ts,
-            const UList<scalar>& times
-        );
-
-
-        //- Templated write operation - one file per timestep
-        template<class Type>
-        fileName writeCollated
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-        //- Templated write operation - all time steps in single file
-        template<class Type>
-        fileName writeUncollated
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-public:
-
-    //- Runtime type information
-    TypeName("ensight");
-
-
-    // Constructors
-
-        //- Construct null
-        ensightSurfaceWriter();
-
-        //- Construct with some output options
-        ensightSurfaceWriter(const dictionary& options);
-
-
-    //- Destructor
-    virtual ~ensightSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
-        virtual bool separateGeometry() const;
-
-        //- Trigger for geometry changes.
-        //  \note this is a stop-gap solution
-        virtual void updateMesh
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName     //!< Name of surface
-        ) const; // override
-
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterImpl.C b/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterImpl.C
deleted file mode 100644
index 57d144984519c1a25ea15b7cf97ad39055590628..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriterImpl.C
+++ /dev/null
@@ -1,531 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2014 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "IOmanip.H"
-#include "Fstream.H"
-#include "OSspecific.H"
-
-#include "ensightCase.H"
-#include "ensightPartFaces.H"
-#include "ensightOutput.H"
-#include "ensightPTraits.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class Type>
-Foam::fileName Foam::ensightSurfaceWriter::writeUncollated
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    const ensight::FileName surfName(surfaceName);
-    const ensight::VarName  varName(fieldName);
-
-    // geometry:  rootdir/time/<field>/surfaceName.case
-    // geometry:  rootdir/time/<field>/surfaceName.<index>.mesh
-    // field:     rootdir/time/<field>/surfaceName.<index>.field
-
-    // Variable name as sub-directory for results
-    // eg, something like this:
-    // - VAR1/SURF1.case
-    // - VAR1/SURF1.0000.mesh
-    // - VAR1/SURF1.0001.VAR1
-    // - VAR1/SURF2.case
-    // - VAR1/SURF2.0000.mesh
-    // - VAR1/SURF2.0001.VAR1
-    // and
-    // - VAR2/SURF1.case
-    // - VAR2/SURF1.0000.mesh
-    // - VAR2/SURF1.0001.VAR2
-
-    const fileName baseDir = outputDir/varName;
-    const fileName timeDir = outputDir.name();
-    // Convert timeDir to a value (if possible - use 0.0 otherwise)
-    scalar timeValue = 0.0;
-    readScalar(timeDir, timeValue);
-
-
-    if (!isDir(baseDir))
-    {
-        mkDir(baseDir);
-    }
-
-    OFstream osCase(baseDir/surfName + ".case", IOstream::ASCII);
-
-    // Format options
-    osCase.setf(ios_base::left);
-    osCase.setf(ios_base::scientific, ios_base::floatfield);
-    osCase.precision(5);
-
-    ensightGeoFile osGeom
-    (
-        baseDir,
-        surfName + ".00000000.mesh",
-        writeFormat_
-    );
-    ensightFile osField
-    (
-        baseDir,
-        surfName + ".00000000." + varName,
-        writeFormat_
-    );
-
-    if (verbose)
-    {
-        Info<< "Writing case file to " << osCase.name() << endl;
-    }
-
-    osCase
-        << "FORMAT" << nl
-        << "type: ensight gold" << nl
-        << nl
-        << "GEOMETRY" << nl
-        << "model:  1   " << osGeom.name().name() << nl
-        << nl
-        << "VARIABLE" << nl
-        << ensightPTraits<Type>::typeName
-        <<
-        (
-            isNodeValues
-          ? " per node:    1  "  // time-set 1
-          : " per element: 1  "  // time-set 1
-        )
-        << setw(15) << varName << ' '
-        << surfName.c_str() << ".********." << varName << nl;
-
-    osCase
-        << nl
-        << "TIME" << nl;
-
-    printTimeset(osCase, 1, timeValue);
-    osCase << "# end" << nl;
-
-
-    ensightPartFaces ensPart
-    (
-        0,
-        osGeom.name().name(),
-        surf.points(),
-        surf.faces(),
-        true // contiguous points
-    );
-    osGeom << ensPart;
-
-    // Write field
-    osField.writeKeyword(ensightPTraits<Type>::typeName);
-
-    if (isNodeValues)
-    {
-        ensightOutput::Serial::writePointField
-        (
-            values,
-            ensPart,
-            osField
-            // false // serial
-        );
-    }
-    else
-    {
-        ensightOutput::Detail::writeFaceField
-        (
-            values,
-            ensPart,
-            osField,
-            false // serial
-        );
-    }
-
-    return osCase.name();
-}
-
-
-template<class Type>
-Foam::fileName Foam::ensightSurfaceWriter::writeCollated
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    const ensight::FileName surfName(surfaceName);
-    const ensight::VarName  varName(fieldName);
-
-    // geometry:  rootdir/surfaceName/surfaceName.case
-    // geometry:  rootdir/surfaceName/surfaceName/data/<index>/geometry
-    // field:     rootdir/surfaceName/surfaceName/data/<index>/field
-
-    // Use surface name as sub-directory for results
-    // eg, something like this:
-    // - SURF1/SURF1.case
-    // - SURF1/SURF1/data/00000000/geometry
-    // - SURF1/SURF1/data/00000000/VAR1
-    // - SURF1/SURF1/data/00000000/VAR2
-    // and
-    // - SURF2/SURF2.case
-    // - SURF2/SURF2/data/00000000/geometry
-    // - SURF2/SURF2/data/00000000/VAR1
-    // - SURF2/SURF2/data/00000000/VAR2
-
-    // Names "data" and "geometry" as per ensightCase:
-    const char* fmt  = "%08d";
-    const char* mask = "data/********/";
-
-    const fileName baseDir = outputDir.path()/surfName;
-    const fileName timeDir = outputDir.name();
-    // Convert timeDir to a value (if possible - use 0.0 otherwise)
-    scalar timeValue = 0.0;
-    readScalar(timeDir, timeValue);
-
-    scalar meshValue = 0;
-
-    if (!isDir(baseDir))
-    {
-        mkDir(baseDir);
-    }
-
-    label meshIndex = 0;
-    label timeIndex = 0;
-
-    fileName geometryName;
-
-    // Do case file
-    {
-        dictionary dict;
-        scalarList meshes;
-        scalarList times;
-        bool stateChanged = false;
-
-        if (isFile(baseDir/"fieldsDict"))
-        {
-            IFstream is(baseDir/"fieldsDict");
-            if (is.good() && dict.read(is))
-            {
-                dict.readIfPresent("meshes", meshes);
-                dict.readIfPresent("times", times);
-
-                timeIndex = 1+findLower(times, timeValue);
-
-                if (dict.readIfPresent("updateMesh", meshValue))
-                {
-                    meshIndex = 1+findLower(meshes, meshValue);
-
-                    dict.remove("updateMesh");
-                    stateChanged = true;
-                }
-                else if (meshes.size())
-                {
-                    meshIndex = meshes.size()-1;
-                    meshValue = meshes.last();
-                }
-                else
-                {
-                    meshIndex = 0;
-                }
-            }
-        }
-
-        // Update stored times list
-        meshes.resize(meshIndex+1, -1);
-        times.resize(timeIndex+1, -1);
-
-        if (meshes[meshIndex] != meshValue)
-        {
-            stateChanged = true;
-        }
-        if (times[timeIndex] != timeValue)
-        {
-            stateChanged = true;
-        }
-
-        meshes[meshIndex] = meshValue;
-        times[timeIndex] = timeValue;
-
-        geometryName =
-            "data"/word::printf(fmt, meshIndex)/ensightCase::geometryName;
-
-
-        // Add my information to dictionary
-        {
-            dict.set("meshes", meshes);
-            dict.set("times", times);
-            if (dict.found("fields"))
-            {
-                dictionary& fieldsDict = dict.subDict("fields");
-                if (!fieldsDict.found(fieldName))
-                {
-                    dictionary fieldDict;
-                    fieldDict.set("type", ensightPTraits<Type>::typeName);
-                    fieldDict.set("name", varName); // ensight variable name
-
-                    fieldsDict.set(fieldName, fieldDict);
-
-                    stateChanged = true;
-                }
-            }
-            else
-            {
-                dictionary fieldDict;
-                fieldDict.set("type", ensightPTraits<Type>::typeName);
-                fieldDict.set("name", varName); // ensight variable name
-
-                dictionary fieldsDict;
-                fieldsDict.set(fieldName, fieldDict);
-
-                dict.set("fields", fieldsDict);
-
-                stateChanged = true;
-            }
-        }
-
-
-        if (stateChanged)
-        {
-            if (verbose)
-            {
-                Info<< "Writing state file to fieldsDict" << endl;
-            }
-            {
-                OFstream os(baseDir/"fieldsDict");
-                os << "// Summary of Ensight fields, times" << nl << nl;
-                dict.write(os, false);
-            }
-
-            OFstream osCase(baseDir/surfName + ".case", IOstream::ASCII);
-
-            // Format options
-            osCase.setf(ios_base::left);
-            osCase.setf(ios_base::scientific, ios_base::floatfield);
-            osCase.precision(5);
-
-            if (verbose)
-            {
-                Info<< "Writing case file to " << osCase.name() << endl;
-            }
-
-            // The geometry can be any of the following:
-            // 0: constant/static
-            // 1: moving, with the same frequency as the data
-            // 2: moving, with different frequency as the data
-
-            const label tsGeom =
-                (meshes.size() == 1 ? 0 : meshes == times ? 1 : 2);
-
-            osCase
-                << "FORMAT" << nl
-                << "type: ensight gold" << nl
-                << nl
-                << "GEOMETRY" << nl;
-
-
-            if (tsGeom)
-            {
-                // moving
-                osCase
-                    << "model:  " << tsGeom << "   " // time-set (1|2)
-                    << mask << geometryName.name() << nl;
-            }
-            else
-            {
-                // steady
-                osCase
-                    << "model:  "
-                    << geometryName.c_str() << nl;
-            }
-
-            osCase
-                << nl
-                << "VARIABLE" << nl;
-
-            const dictionary& fieldsDict = dict.subDict("fields");
-            for (const entry& dEntry : fieldsDict)
-            {
-                const dictionary& subDict = dEntry.dict();
-
-                const word fieldType(subDict.get<word>("type"));
-                const word varName = subDict.lookupOrDefault
-                (
-                    "name",
-                    dEntry.keyword() // fieldName as fallback
-                );
-
-                osCase
-                    << fieldType
-                    <<
-                    (
-                        isNodeValues
-                      ? " per node:    1  "  // time-set 1
-                      : " per element: 1  "  // time-set 1
-                    )
-                    << setw(15) << varName << ' '
-                    << mask << varName << nl;
-            }
-
-            osCase
-                << nl
-                << "TIME" << nl;
-
-            printTimeset(osCase, 1, times);
-            if (tsGeom == 2)
-            {
-                printTimeset(osCase, 2, meshes);
-            }
-
-            osCase << "# end" << nl;
-        }
-    }
-
-
-    // Location for data (and possibly the geometry as well)
-    fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
-
-    // As per mkdir -p "data/00000000"
-    mkDir(dataDir);
-
-
-    const fileName meshFile(baseDir/geometryName);
-
-    // Write geometry
-    ensightPartFaces ensPart
-    (
-        0,
-        meshFile.name(),
-        surf.points(),
-        surf.faces(),
-        true // contiguous points
-    );
-    if (!exists(meshFile))
-    {
-        if (verbose)
-        {
-            Info<< "Writing mesh file to " << meshFile.name() << endl;
-        }
-        // Use two-argument form for path-name to avoid validating the base-dir
-        ensightGeoFile osGeom(meshFile.path(), meshFile.name(), writeFormat_);
-        osGeom << ensPart;
-    }
-
-    // Write field
-    ensightFile osField
-    (
-        dataDir,
-        varName,
-        writeFormat_
-    );
-
-    if (verbose)
-    {
-        Info<< "Writing field file to " << osField.name() << endl;
-    }
-
-    // Write field
-    osField.writeKeyword(ensightPTraits<Type>::typeName);
-
-    if (isNodeValues)
-    {
-        ensightOutput::Serial::writePointField
-        (
-            values,
-            ensPart,
-            osField
-            // serial
-        );
-    }
-    else
-    {
-        ensightOutput::Detail::writeFaceField
-        (
-            values,
-            ensPart,
-            osField,
-            false // serial
-        );
-    }
-
-    // Place a timestamp in the directory for future reference
-    {
-        OFstream timeStamp(dataDir/"time");
-        timeStamp
-            << "#   timestep time" << nl
-            << dataDir.name() << " " << timeValue << nl;
-    }
-
-    return baseDir/surfName + ".case";
-}
-
-
-template<class Type>
-Foam::fileName Foam::ensightSurfaceWriter::writeTemplate
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    if (collateTimes_)
-    {
-        return writeCollated
-        (
-            outputDir,
-            surfaceName,
-            surf,
-            fieldName,
-            values,
-            isNodeValues,
-            verbose
-        );
-    }
-    else
-    {
-        return writeUncollated
-        (
-            outputDir,
-            surfaceName,
-            surf,
-            fieldName,
-            values,
-            isNodeValues,
-            verbose
-        );
-    }
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.C b/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.C
deleted file mode 100644
index 8cefc33e9be4df41964b236b7156566b8aca9cf5..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.C
+++ /dev/null
@@ -1,98 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "foamSurfaceWriter.H"
-#include "OFstream.H"
-#include "makeSurfaceWriterMethods.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    makeSurfaceWriterType(foamSurfaceWriter);
-}
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Field writing implementation
-#include "foamSurfaceWriterImpl.C"
-
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::foamSurfaceWriter);
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileName Foam::foamSurfaceWriter::write
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
-{
-    // Output:
-    // - rootdir/time/surfaceName/{points,faces}
-
-    fileName surfaceDir(outputDir/surfaceName);
-
-    if (!isDir(surfaceDir))
-    {
-        mkDir(surfaceDir);
-    }
-
-    if (verbose)
-    {
-        Info<< "Writing geometry to " << surfaceDir << endl;
-    }
-
-    const pointField& points = surf.points();
-    const faceList&    faces = surf.faces();
-
-    // Points
-    OFstream(surfaceDir/"points")() << points;
-
-    // Faces
-    OFstream(surfaceDir/"faces")() << faces;
-
-    // Face centers. Not really necessary but very handy when reusing as inputs
-    // for e.g. timeVaryingMapped bc.
-    pointField faceCentres(faces.size(), Zero);
-
-    forAll(faces, facei)
-    {
-        faceCentres[facei] = faces[facei].centre(points);
-    }
-
-    OFstream(surfaceDir/"faceCentres")() << faceCentres;
-
-    return surfaceDir;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.H b/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.H
deleted file mode 100644
index 04c784a8ccad7adc5cbefbb352dd4d7ef4d8955c..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriter.H
+++ /dev/null
@@ -1,209 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::foamSurfaceWriter
-
-Description
-    A surfaceWriter for OpenFOAM surfaces
-
-    \heading Output file locations
-
-    The \c rootdir normally corresponds to something like
-    \c postProcessing/\<name\>
-
-    \subheading Geometry
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- surfaceName
-            |-- "points"
-            |-- "faceCentres"
-            `-- "faces"
-    \endverbatim
-
-    \subheading Fields
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- surfaceName
-            |-- scalarField
-            |   |-- field
-            |   `-- field
-            |-- vectorField
-                |-- field
-                `-- field
-    \endverbatim
-
-SourceFiles
-    foamSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef foamSurfaceWriter_H
-#define foamSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                      Class foamSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class foamSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private Member Functions
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("foam");
-
-
-    // Constructors
-
-        //- Construct null
-        foamSurfaceWriter() = default;
-
-
-    //- Destructor
-    virtual ~foamSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
-        virtual bool separateGeometry() const
-        {
-            return true;
-        }
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriterImpl.C b/src/sampling/sampledSurface/writers/foam/foamSurfaceWriterImpl.C
deleted file mode 100644
index 002c546a36f9e5c7e4eeff1b88b5ae5f30b8b604..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/foam/foamSurfaceWriterImpl.C
+++ /dev/null
@@ -1,77 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2014 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "OFstream.H"
-#include "OSspecific.H"
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class Type>
-Foam::fileName Foam::foamSurfaceWriter::writeTemplate
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    // Geometry should already have been written
-    // Values to separate directory (e.g. "scalarField/p")
-
-    // field:    rootdir/time/surfaceName/fieldType/field
-
-    const word fieldTypeName
-    (
-        word(pTraits<Type>::typeName) + FieldBase::typeName
-    );
-
-    const fileName base(outputDir/surfaceName);
-    const fileName outputFile(base / fieldTypeName / fieldName);
-
-    if (verbose)
-    {
-        Info<< "Writing field " << fieldName << " to " << base << endl;
-    }
-
-
-    if (!isDir(outputFile.path()))
-    {
-        mkDir(outputFile.path());
-    }
-
-    // Write field
-    OFstream os(outputFile);
-    os << values;
-
-    return os.name();
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.C b/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.C
deleted file mode 100644
index 35ac3ac113787a5cd75656e9b5e8521c7b41510e..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.C
+++ /dev/null
@@ -1,136 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "rawSurfaceWriter.H"
-#include "OFstream.H"
-#include "OSspecific.H"
-#include "makeSurfaceWriterMethods.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    makeSurfaceWriterType(rawSurfaceWriter);
-    addToRunTimeSelectionTable(surfaceWriter, rawSurfaceWriter, wordDict);
-}
-
-
-// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    // Emit x,y,z
-    static inline void writePoint(Ostream& os, const point& p)
-    {
-        os << p.x() << ' ' << p.y() << ' ' << p.z();
-    }
-} // End namespace Foam
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Field writing implementation
-#include "rawSurfaceWriterImpl.C"
-
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::rawSurfaceWriter);
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::rawSurfaceWriter::rawSurfaceWriter()
-:
-    surfaceWriter(),
-    writeCompression_(IOstream::UNCOMPRESSED)
-{}
-
-
-Foam::rawSurfaceWriter::rawSurfaceWriter(const dictionary& options)
-:
-    surfaceWriter(),
-    writeCompression_(IOstream::UNCOMPRESSED)
-{
-    if (options.found("compression"))
-    {
-        writeCompression_ =
-            IOstream::compressionEnum(options.get<word>("compression"));
-    }
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileName Foam::rawSurfaceWriter::write
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
-{
-    // geometry:  rootdir/time/surfaceName.raw
-
-    const pointField& points = surf.points();
-    const faceList&    faces = surf.faces();
-
-
-    if (!isDir(outputDir))
-    {
-        mkDir(outputDir);
-    }
-
-    OFstream os
-    (
-        outputDir/surfaceName + ".raw",
-        IOstream::ASCII,
-        IOstream::currentVersion,
-        writeCompression_
-    );
-
-    if (verbose)
-    {
-        Info<< "Writing geometry to " << os.name() << endl;
-    }
-
-    // Header
-    os  << "# geometry NO_DATA " << faces.size() << nl
-        << "#  x  y  z" << nl;
-
-    // Write faces centres
-    for (const face& f : faces)
-    {
-        writePoint(os, f.centre(points));
-        os << nl;
-    }
-
-    os  << nl;
-
-    return os.name();
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.H b/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.H
deleted file mode 100644
index 7660fa4aa1a09e503e9de4caf661cc984a7a0435..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriter.H
+++ /dev/null
@@ -1,220 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::rawSurfaceWriter
-
-Description
-    A surfaceWriter for raw output.
-
-    The formatOptions for raw:
-    \table
-        Property     | Description                          | Required | Default
-        compression  | on / off                             | no    | off
-    \endtable
-
-    For example,
-    \verbatim
-    formatOptions
-    {
-        raw
-        {
-            compression on;
-        }
-    }
-    \endverbatim
-
-    \heading Output file locations
-
-    The \c rootdir normally corresponds to something like
-    \c postProcessing/\<name\>
-
-    \subheading Geometry
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- surfaceName.{raw}
-    \endverbatim
-
-    \subheading Fields
-    \verbatim
-    rootdir
-    `-- timeName
-        |-- <field0>_surfaceName.{raw}
-        `-- <field1>_surfaceName.{raw}
-    \endverbatim
-
-SourceFiles
-    rawSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef rawSurfaceWriter_H
-#define rawSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                      Class rawSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class rawSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private data
-
-        //- Output compression (default: uncompressed)
-        IOstream::compressionType writeCompression_;
-
-
-    // Private Member Functions
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("raw");
-
-
-    // Constructors
-
-        //- Construct null
-        rawSurfaceWriter();
-
-        //- Construct with some output options
-        rawSurfaceWriter(const dictionary& options);
-
-
-    //- Destructor
-    virtual ~rawSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.H b/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.H
deleted file mode 100644
index 194b66afe3adfa818df02698b00e7ba00883fd42..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.H
+++ /dev/null
@@ -1,215 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::starcdSurfaceWriter
-
-Description
-    A surfaceWriter for STARCD files.
-
-    The geometry is written via the MeshedSurfaceProxy, the fields
-    are written in a trivial ASCII format with ID and VALUE as
-    so-called user data. These \c .usr files can be read into proSTAR
-    with these types of commands. For element data:
-     \verbatim
-        getuser FILENAME.usr cell scalar free
-        getuser FILENAME.usr cell vector free
-    \endverbatim
-    and for vertex data:
-    \verbatim
-        getuser FILENAME.usr vertex scalar free
-        getuser FILENAME.usr vertex vector free
-    \endverbatim
-
-    \heading Output file locations
-
-    The \c rootdir normally corresponds to something like
-    \c postProcessing/\<name\>
-
-    \subheading Geometry
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- surfaceName.{cel,vrt,inp}
-    \endverbatim
-
-    \subheading Fields
-    \verbatim
-    rootdir
-    `-- timeName
-        |-- <field0>_surfaceName.{usr}
-        `-- <field1>_surfaceName.{usr}
-    \endverbatim
-
-SourceFiles
-    starcdSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef starcdSurfaceWriter_H
-#define starcdSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                     Class starcdSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class starcdSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private Member Functions
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("starcd");
-
-
-    // Constructors
-
-        //- Construct null
-        starcdSurfaceWriter() = default;
-
-
-    //- Destructor
-    virtual ~starcdSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
-        virtual bool separateGeometry() const
-        {
-            return true;
-        }
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriterImpl.C b/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriterImpl.C
deleted file mode 100644
index 64fb5d80927d850efb07a45fb699f20ce0bd566c..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriterImpl.C
+++ /dev/null
@@ -1,94 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2014 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "OFstream.H"
-#include "OSspecific.H"
-
-// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    // Emit each component
-    template<class Type>
-    static inline void writeData(Ostream& os, const Type& val)
-    {
-        const direction ncmpt = pTraits<Type>::nComponents;
-        for (direction cmpt=0; cmpt < ncmpt; ++cmpt)
-        {
-            os  << ' ' << component(val, cmpt);
-        }
-        os  << nl;
-    }
-
-} // End namespace Foam
-
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-template<class Type>
-Foam::fileName Foam::starcdSurfaceWriter::writeTemplate
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf&,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    // field:  rootdir/time/<field>_surfaceName.usr
-
-    if (!isDir(outputDir))
-    {
-        mkDir(outputDir);
-    }
-
-    OFstream os(outputDir/fieldName + '_' + surfaceName + ".usr");
-
-    if (verbose)
-    {
-        Info<< "Writing field " << fieldName << " to " << os.name() << endl;
-    }
-
-    // 1-based ids
-    label elemId = 1;
-
-    // No header, just write values
-    for (const Type& val : values)
-    {
-        os  << elemId;
-        writeData(os, val);
-
-        ++elemId;
-    }
-
-    return os.name();
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/surfaceWriter.C b/src/sampling/sampledSurface/writers/surfaceWriter.C
deleted file mode 100644
index c3ef1711f081c83385a37bb3dd409b3d768db4a1..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/surfaceWriter.C
+++ /dev/null
@@ -1,126 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2015 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "surfaceWriter.H"
-
-#include "MeshedSurfaceProxy.H"
-#include "proxySurfaceWriter.H"
-
-#include "HashTable.H"
-#include "word.H"
-#include "addToRunTimeSelectionTable.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    defineTypeNameAndDebug(surfaceWriter, 0);
-    defineRunTimeSelectionTable(surfaceWriter, word);
-    defineRunTimeSelectionTable(surfaceWriter, wordDict);
-    addNamedToRunTimeSelectionTable
-    (
-        surfaceWriter,
-        surfaceWriter,
-        word,
-        null
-    );
-}
-
-
-// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
-
-Foam::autoPtr<Foam::surfaceWriter>
-Foam::surfaceWriter::New(const word& writeType)
-{
-    const auto cstrIter = wordConstructorTablePtr_->cfind(writeType);
-
-    if (cstrIter.found())
-    {
-        return autoPtr<surfaceWriter>(cstrIter()());
-    }
-    else if (MeshedSurfaceProxy<face>::canWriteType(writeType))
-    {
-        // Unknown, but proxy handler can manage it
-        return autoPtr<surfaceWriter>(new proxySurfaceWriter(writeType));
-    }
-
-    FatalErrorInFunction
-        << "Unknown write type \"" << writeType << "\"\n\n"
-        << "Valid types : "
-        << wordConstructorTablePtr_->sortedToc() << nl
-        << "Valid proxy types : "
-        << MeshedSurfaceProxy<face>::writeTypes() << endl
-        << exit(FatalError);
-
-    return nullptr;
-}
-
-
-Foam::autoPtr<Foam::surfaceWriter>
-Foam::surfaceWriter::New(const word& writeType, const dictionary& options)
-{
-    {
-        // Constructor with options (dictionary)
-        const auto cstrIter = wordDictConstructorTablePtr_->cfind(writeType);
-
-        if (cstrIter.found())
-        {
-            return autoPtr<surfaceWriter>(cstrIter()(options));
-        }
-    }
-
-
-    // Drop through to version without options
-
-    const auto cstrIter = wordConstructorTablePtr_->cfind(writeType);
-
-    if (cstrIter.found())
-    {
-        return autoPtr<surfaceWriter>(cstrIter()());
-    }
-    else if (MeshedSurfaceProxy<face>::canWriteType(writeType))
-    {
-        // Unknown, but proxy handler can manage it
-        return autoPtr<surfaceWriter>
-        (
-            new proxySurfaceWriter(writeType, options)
-        );
-    }
-
-    FatalErrorInFunction
-        << "Unknown write type \"" << writeType << "\"\n\n"
-        << "Valid types : "
-        << wordConstructorTablePtr_->sortedToc() << nl
-        << "Valid proxy types : "
-        << MeshedSurfaceProxy<face>::writeTypes() << endl
-        << exit(FatalError);
-
-    return nullptr;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/surfaceWriter.H b/src/sampling/sampledSurface/writers/surfaceWriter.H
deleted file mode 100644
index 3f4b0e8cb741dc105323a7846d2d50ca054ce7a7..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/surfaceWriter.H
+++ /dev/null
@@ -1,230 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2012 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::surfaceWriter
-
-Description
-    Base class for surface writers
-
-SourceFiles
-    surfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef surfaceWriter_H
-#define surfaceWriter_H
-
-#include "typeInfo.H"
-#include "autoPtr.H"
-#include "fileName.H"
-#include "meshedSurfRef.H"
-#include "runTimeSelectionTables.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-/*---------------------------------------------------------------------------*\
-                        Class surfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class surfaceWriter
-{
-public:
-
-    //- Runtime type information
-    TypeName("surfaceWriter");
-
-    // Declare run-time constructor selection table
-
-        declareRunTimeSelectionTable
-        (
-            autoPtr,
-            surfaceWriter,
-            word,
-            (),
-            ()
-        );
-
-        declareRunTimeSelectionTable
-        (
-            autoPtr,
-            surfaceWriter,
-            wordDict,
-            (
-                const dictionary& options
-            ),
-            (options)
-        );
-
-
-    // Selectors
-
-        //- Return a reference to the selected surfaceWriter
-        static autoPtr<surfaceWriter> New(const word& writeType);
-
-        //- Return a reference to the selected surfaceWriter
-        //  Select with extra write option
-        static autoPtr<surfaceWriter> New
-        (
-            const word& writeType,
-            const dictionary& writeOptions
-        );
-
-
-    // Constructors
-
-        //- Construct null
-        surfaceWriter() = default;
-
-
-    //- Destructor
-    virtual ~surfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
-        virtual bool separateGeometry() const
-        {
-            return false;
-        }
-
-        //- Trigger for geometry changes.
-        //  \note this is a stop-gap solution until the writing infrastructure
-        //  is improved.
-        virtual void updateMesh
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName     //!< Name of surface
-        ) const
-        {}
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const
-        {
-            return fileName::null;
-        }
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.C b/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.C
deleted file mode 100644
index b81635082f21fc9dd5c976d30ce15103b98e6737..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.C
+++ /dev/null
@@ -1,192 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "vtkSurfaceWriter.H"
-#include "foamVtkOutputOptions.H"
-#include "OSspecific.H"
-#include <fstream>
-#include "makeSurfaceWriterMethods.H"
-
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    makeSurfaceWriterType(vtkSurfaceWriter);
-    addToRunTimeSelectionTable(surfaceWriter, vtkSurfaceWriter, wordDict);
-}
-
-// Field writing implementation
-#include "vtkSurfaceWriterImpl.C"
-
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::vtkSurfaceWriter);
-
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-void Foam::vtkSurfaceWriter::writeGeometry
-(
-    vtk::formatter& format,
-    const meshedSurf& surf,
-    std::string title
-)
-{
-    const pointField& points = surf.points();
-    const faceList&    faces = surf.faces();
-
-    if (title.empty())
-    {
-        title = "sampleSurface";
-    }
-
-    vtk::legacy::fileHeader
-    (
-        format,
-        title,
-        vtk::fileTag::POLY_DATA
-    );
-
-    vtk::legacy::beginPoints(format.os(), points.size());
-
-    vtk::writeList(format, points);
-    format.flush();
-
-    // Write faces
-    // connectivity count without additional storage (done internally)
-    label nConnectivity = 0;
-    for (const face& f : faces)
-    {
-        nConnectivity += f.size();
-    }
-
-    vtk::legacy::beginPolys
-    (
-        format.os(),
-        faces.size(),
-        nConnectivity
-    );
-
-    for (const face& f : faces)
-    {
-        format.write(f.size());  // The size prefix
-        vtk::writeList(format, f);
-    }
-
-    format.flush();
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::vtkSurfaceWriter::vtkSurfaceWriter()
-:
-    surfaceWriter(),
-    fmtType_(unsigned(vtk::formatType::LEGACY_ASCII)),
-    precision_(IOstream::defaultPrecision())
-{}
-
-
-Foam::vtkSurfaceWriter::vtkSurfaceWriter(const dictionary& options)
-:
-    surfaceWriter(),
-    fmtType_(static_cast<unsigned>(vtk::formatType::LEGACY_ASCII)),
-    precision_(IOstream::defaultPrecision())
-{
-#if 0
-    // Future
-    // format: ascii | binary
-    // legacy  true | false
-
-    vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
-
-    const word formatName = options.lookupOrDefault<word>("format", "");
-    if (formatName.size())
-    {
-        opts.ascii
-        (
-            IOstream::formatEnum(formatName) == IOstream::ASCII
-        );
-    }
-
-    if (options.lookupOrDefault("legacy", false))
-    {
-        opts.legacy(true);
-    }
-
-    // Convert back to raw data type
-    fmtType_ = static_cast<unsigned>(opts.fmt());
-#endif
-
-    // The write precision for ASCII formatters
-    precision_ =
-        options.lookupOrDefaultCompat
-        (
-            "precision", {{"writePrecision", -1806}},
-            IOstream::defaultPrecision()
-        );
-}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileName Foam::vtkSurfaceWriter::write
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
-{
-    // geometry:  rootdir/time/surfaceName.{vtk|vtp}
-
-    fileName outputFile(outputDir/surfaceName + ".vtk");
-
-    if (!isDir(outputDir))
-    {
-        mkDir(outputDir);
-    }
-    if (verbose)
-    {
-        Info<< "Writing geometry to " << outputFile << endl;
-    }
-
-    // As vtk::outputOptions
-    vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
-    opts.legacy(true);
-    opts.precision(precision_);
-
-    std::ofstream os(outputFile);
-
-    auto format = opts.newFormatter(os);
-
-    writeGeometry(*format, surf, surfaceName);
-
-    return outputFile;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.H b/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.H
deleted file mode 100644
index 2ced87cda1c12ec097f5e8b2fd3128d1964c2753..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriter.H
+++ /dev/null
@@ -1,251 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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::vtkSurfaceWriter
-
-Description
-    A surfaceWriter for VTK legacy format.
-
-    The formatOptions for vtk:
-    \table
-        Property    | Description                           | Required | Default
-        format      | [FUTURE] ascii or binary format       | no  | ascii
-        legacy      | [FUTURE] Legacy VTK output            | no  | true
-        precision   | Write precision in ascii         | no | same as IOstream
-    \endtable
-
-    For example,
-    \verbatim
-    formatOptions
-    {
-        vtk
-        {
-            precision  10;
-        }
-    }
-    \endverbatim
-
-    \heading Output file locations
-
-    The \c rootdir normally corresponds to something like
-    \c postProcessing/\<name\>
-
-    \subheading Geometry
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- surfaceName.{vtk}
-    \endverbatim
-
-    \subheading Fields
-    \verbatim
-    rootdir
-    `-- timeName
-        `-- <field>_surfaceName.{vtk}
-    \endverbatim
-
-Note
-    Uses ASCII-only output.
-    All data are written as \c double due to portability issues
-    (paraview on window).
-
-SourceFiles
-    vtkSurfaceWriter.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef vtkSurfaceWriter_H
-#define vtkSurfaceWriter_H
-
-#include "surfaceWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-namespace vtk
-{
-// Forward declarations
-class formatter;
-}
-
-/*---------------------------------------------------------------------------*\
-                      Class vtkSurfaceWriter Declaration
-\*---------------------------------------------------------------------------*/
-
-class vtkSurfaceWriter
-:
-    public surfaceWriter
-{
-    // Private data
-
-        //- The VTK output format type.
-        //  Stored as a raw value to avoid a dependency on fileFormats
-        unsigned fmtType_;
-
-        //- ASCII write precision
-        unsigned precision_;
-
-
-    // Private Member Functions
-
-        static void writeGeometry
-        (
-            vtk::formatter& format,
-            const meshedSurf& surf,
-            std::string title = ""
-        );
-
-        template<class Type>
-        static void writeField
-        (
-            vtk::formatter& format,
-            const Field<Type>& values
-        );
-
-
-        //- Templated write operation
-        template<class Type>
-        fileName writeTemplate
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
-
-
-public:
-
-    //- Runtime type information
-    TypeName("vtk");
-
-
-    // Constructors
-
-        //- Construct null
-        vtkSurfaceWriter();
-
-        //- Construct with some output options
-        explicit vtkSurfaceWriter(const dictionary& options);
-
-
-    //- Destructor
-    virtual ~vtkSurfaceWriter() = default;
-
-
-    // Member Functions
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriterImpl.C b/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriterImpl.C
deleted file mode 100644
index 4c1fde0a542d36ffb2711a418094fb956fc05c7a..0000000000000000000000000000000000000000
--- a/src/sampling/sampledSurface/writers/vtk/vtkSurfaceWriterImpl.C
+++ /dev/null
@@ -1,153 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2014 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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 "foamVtkOutputOptions.H"
-#include "OSspecific.H"
-#include <fstream>
-
-// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    template<class Type>
-    static inline void beginFloatField
-    (
-        vtk::formatter& format,
-        const word& fieldName,
-        const Field<Type>& values
-    )
-    {
-        // Use 'double' instead of 'float' for ASCII output (issue #891)
-        if (format.opts().ascii())
-        {
-            format.os()
-                << fieldName << ' '
-                << int(pTraits<Type>::nComponents) << ' '
-                << values.size() << " double" << nl;
-        }
-        else
-        {
-            format.os()
-                << fieldName << ' '
-                << int(pTraits<Type>::nComponents) << ' '
-                << values.size() << " float" << nl;
-        }
-    }
-}
-
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-// // Unspecialized (unknown) data type - map as zeros
-// void Foam::vtkSurfaceWriter::writeZeros
-// (
-//     vtk::formatter& format,
-//     const label count
-// )
-// {
-//     const float val(0);
-//
-//     for (label i=0; i < count; ++i)
-//     {
-//         format.write(val);
-//     }
-//     format.flush();
-// }
-
-
-template<class Type>
-void Foam::vtkSurfaceWriter::writeField
-(
-    vtk::formatter& format,
-    const Field<Type>& values
-)
-{
-    vtk::writeList(format, values);
-    format.flush();
-}
-
-
-template<class Type>
-Foam::fileName Foam::vtkSurfaceWriter::writeTemplate
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
-{
-    // field:  rootdir/time/<field>_surfaceName.{vtk|vtp}
-
-    fileName outputFile(outputDir/fieldName + '_' + surfaceName + ".vtk");
-
-    if (!isDir(outputDir))
-    {
-        mkDir(outputDir);
-    }
-    if (verbose)
-    {
-        Info<< "Writing field " << fieldName << " to "
-            << outputFile << endl;
-    }
-
-    // As vtk::outputOptions
-    vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
-    opts.legacy(true);
-    opts.precision(precision_);
-
-    std::ofstream os(outputFile);
-
-    auto format = opts.newFormatter(os);
-
-    writeGeometry(*format, surf);
-
-    if (isNodeValues)
-    {
-        format->os()
-            << "POINT_DATA "
-            << values.size() << nl
-            << "FIELD attributes 1" << nl;
-    }
-    else
-    {
-        format->os()
-            << "CELL_DATA "
-            << values.size() << nl
-            << "FIELD attributes 1" << nl;
-    }
-
-    beginFloatField(*format, fieldName, values);
-    writeField(*format, values);
-
-    return outputFile;
-}
-
-
-// ************************************************************************* //
diff --git a/src/sampling/surfMeshSample/surfMeshSamplers/surfMeshSamplers.C b/src/sampling/surfMeshSample/surfMeshSamplers/surfMeshSamplers.C
index eb8b8fde32279ea5c6449f50e7d055f2f6196bfa..a31760524a079e62e67cbac9000dbc74951e1da6 100644
--- a/src/sampling/surfMeshSample/surfMeshSamplers/surfMeshSamplers.C
+++ b/src/sampling/surfMeshSample/surfMeshSamplers/surfMeshSamplers.C
@@ -243,6 +243,7 @@ bool Foam::surfMeshSamplers::read(const dictionary& dict)
         surfaces().transfer(surfs);
     }
 
+
     auto& surfs = surfaces();
     if (surfs.size())
     {
@@ -252,9 +253,6 @@ bool Foam::surfMeshSamplers::read(const dictionary& dict)
         Info<< type() << " fields:  " << flatOutput(fieldSelection_) << nl;
         Info<< nl;
 
-        // Ensure all surfaces and merge information are expired
-        expire();
-
         // Need to initialize corresponding surfMesh for others in the chain.
         // This can simply be a zero-sized placeholder, or the real surface with
         // faces.
@@ -346,18 +344,18 @@ bool Foam::surfMeshSamplers::needsUpdate() const
 
 bool Foam::surfMeshSamplers::expire()
 {
-    bool justExpired = false;
+    label nChanged = 0;
 
     for (surfMeshSample& s : surfaces())
     {
         if (s.expire())
         {
-            justExpired = true;
+            ++nChanged;
         }
     }
 
     // True if any surfaces just expired
-    return justExpired;
+    return nChanged;
 }
 
 
@@ -368,16 +366,17 @@ bool Foam::surfMeshSamplers::update()
         return false;
     }
 
-    bool updated = false;
+    label nUpdated = 0;
+
     for (surfMeshSample& s : surfaces())
     {
         if (s.update())
         {
-            updated = true;
+            ++nUpdated;
         }
     }
 
-    return updated;
+    return nUpdated;
 }
 
 
diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files
index c2ca286e1fe8857f95ecaf9441e3a3ca30b2c08d..4891715e49de82e90f67fa0fd546e7fd6cf10c01 100644
--- a/src/surfMesh/Make/files
+++ b/src/surfMesh/Make/files
@@ -57,4 +57,20 @@ triSurface/fields/triSurfaceFields.C
 triSurface/patches/geometricSurfacePatch.C
 triSurface/patches/surfacePatch.C
 
+
+writers = writers
+
+$(writers)/surfaceWriter.C
+$(writers)/boundaryData/boundaryDataSurfaceWriter.C
+$(writers)/ensight/ensightSurfaceWriter.C
+$(writers)/foam/foamSurfaceWriter.C
+$(writers)/nastran/nastranSurfaceWriter.C
+$(writers)/null/nullSurfaceWriter.C
+$(writers)/proxy/proxySurfaceWriter.C
+$(writers)/raw/rawSurfaceWriter.C
+$(writers)/starcd/starcdSurfaceWriter.C
+$(writers)/vtk/vtkSurfaceWriter.C
+/* $(writers)/x3d/x3dSurfaceWriter.C */
+
+
 LIB = $(FOAM_LIBBIN)/libsurfMesh
diff --git a/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.C b/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..8b3db9f2f4694159bd51ccef32c08c68631f1f4a
--- /dev/null
+++ b/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.C
@@ -0,0 +1,275 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2015 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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 "boundaryDataSurfaceWriter.H"
+#include "argList.H"
+#include "OFstream.H"
+#include "OSspecific.H"
+#include "IOmanip.H"
+#include "Time.H"
+#include "pointIOField.H"
+#include "primitivePatch.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(boundaryDataWriter);
+    addToRunTimeSelectionTable(surfaceWriter, boundaryDataWriter, word);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::boundaryDataWriter::boundaryDataWriter()
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::boundaryDataWriter::boundaryDataWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options)
+{}
+
+
+Foam::surfaceWriters::boundaryDataWriter::boundaryDataWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    boundaryDataWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::boundaryDataWriter::boundaryDataWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    boundaryDataWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::boundaryDataWriter::write()
+{
+    checkOpen();
+
+    // Geometry: rootdir/surfaceName/"points"
+    // Field:    rootdir/surfaceName/<TIME>/field
+
+    fileName surfaceDir = outputPath_;
+
+    // Write points
+    if (verbose_)
+    {
+        Info<< "Writing points to " << surfaceDir/"points" << endl;
+    }
+
+
+    // Dummy time to use as an objectRegistry
+    const fileName caseDir(argList::envGlobalPath());
+
+    Time dummyTime
+    (
+        caseDir.path(), // root-path,
+        caseDir.name(), // case-name,
+        "system",       //
+        "constant",     //
+        false,          // no function objects
+        false           // no libs
+    );
+
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(surfaceDir))
+        {
+            mkDir(surfaceDir);
+        }
+
+        pointIOField pts
+        (
+            IOobject
+            (
+                surfaceDir/"points",
+                dummyTime,
+                IOobject::NO_READ,
+                IOobject::NO_WRITE,
+                false
+            ),
+            surf.points()
+        );
+
+        // Do like regIOobject::writeObject but don't do instance() adaptation
+        // since this would write to e.g. 0/ instead of postProcessing/
+
+        // Try opening an OFstream for object
+        OFstream os(pts.objectPath());
+
+        //pts.writeHeader(os);
+        pts.writeData(os);
+        //pts.writeEndDivider(os);
+    }
+
+    return surfaceDir;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::boundaryDataWriter::writeTemplate
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    // Geometry: rootdir/surfaceName/"points"
+    // Field:    rootdir/surfaceName/<TIME>/field
+
+    fileName surfaceDir = outputPath_;
+
+    const fileName outputFile(surfaceDir/timeName()/fieldName);
+
+
+    // Dummy time to use as an objectRegistry
+    const fileName caseDir(argList::envGlobalPath());
+
+    Time dummyTime
+    (
+        caseDir.path(), // root-path,
+        caseDir.name(), // case-name,
+        "system",       //
+        "constant",     //
+        false,          // no function objects
+        false           // no libs
+    );
+
+
+    // Geometry merge() implicit
+    tmp<Field<Type>> tfield =  mergeField(localValues);
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        const pointField& points = surf.points();
+        const faceList& faces = surf.faces();
+
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        pointIOField pts
+        (
+            IOobject
+            (
+                surfaceDir/"points",
+                dummyTime,
+                IOobject::NO_READ,
+                IOobject::NO_WRITE,
+                false
+            ),
+            label(0)
+        );
+
+        if (this->isPointData())
+        {
+            if (verbose_)
+            {
+                Info<< "Writing points to "
+                    << surfaceDir/"points" << endl;
+            }
+            pts = points;
+        }
+        else
+        {
+            if (verbose_)
+            {
+                Info<< "Writing face centres to "
+                    << surfaceDir/"points" << endl;
+            }
+
+            primitivePatch pp(SubList<face>(faces, faces.size()), points);
+
+            pts = pp.faceCentres();
+        }
+
+        {
+            // Do like regIOobject::writeObject but don't do instance()
+            // adaptation
+            // since this would write to e.g. 0/ instead of postProcessing/
+
+            // Try opening an OFstream for object
+            OFstream os(pts.objectPath());
+
+            //pts.writeHeader(os);
+            pts.writeData(os);
+            //pts.writeEndDivider(os);
+        }
+
+
+        // Write field
+        OFstream(outputFile)() << tfield();
+    }
+
+    return surfaceDir;
+}
+
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::boundaryDataWriter);
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.H b/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..569c07fb5a14e9cf8cdcc3a9d9d71ec628b08763
--- /dev/null
+++ b/src/surfMesh/writers/boundaryData/boundaryDataSurfaceWriter.H
@@ -0,0 +1,187 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::surfaceWriters::boundaryDataWriter
+
+Description
+    A surfaceWriter for outputting to a form useable for the
+    timeVaryingMapped boundary condition. This reads the data from
+    constant/boundaryData/\<patch\> directory
+
+    Typical way of working:
+    - use a sampledSurface of type 'patch' (to sample a patch):
+    \verbatim
+    surfaces
+    {
+        type            surfaces;
+        surfaceFormat   boundaryData;
+        fields          ( p );
+        surfaces
+        (
+            outlet
+            {
+                type            patch;
+                patches         (outlet);
+                interpolate     false;
+            }
+        );
+    }
+    \endverbatim
+
+    - write using this writer.
+    - move postProcessing/surfaces/outlet to constant/boundaryData/outlet
+      in your destination case.
+    - use a timeVaryingMappedFixedValue condition to read and interpolate
+      the profile:
+        type            timeVaryingMappedFixedValue;
+        setAverage      false;  // do not use read average
+        offset          0;      // do not apply offset to values
+
+    Note:
+    - with 'interpolate false' the data is on the face centres of the
+      patch. Take care that a 2D geometry will only have a single row
+      of face centres so might not provide a valid triangulation
+      (this is what timeVaryingMappedFixedValue uses to do interpolation)
+      (Alternatively use timeVaryingMappedFixedValue with mapMethod 'nearest')
+
+    \heading Output file locations
+
+    The \c rootdir normally corresponds to something like
+    \c postProcessing/\<name\>
+
+    where the geometry is written as:
+    \verbatim
+    rootdir
+    `-- surfaceName
+        `-- "points"
+    \endverbatim
+
+    and field data:
+    \verbatim
+    rootdir
+    `-- surfaceName
+        |-- "points"
+        `-- timeName
+            `-- field
+    \endverbatim
+
+SourceFiles
+    boundaryDataSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef boundaryDataSurfaceWriter_H
+#define boundaryDataSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                     Class boundaryDataWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class boundaryDataWriter
+:
+    public surfaceWriter
+{
+    // Private Member Functions
+
+        //- Templated write field operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("boundaryData");
+
+
+    // Constructors
+
+        //- Construct null
+        boundaryDataWriter();
+
+        //- Construct with some output options
+        boundaryDataWriter(const dictionary& options);
+
+        //- Construct from components
+        boundaryDataWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        boundaryDataWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~boundaryDataWriter() = default;
+
+
+    // Member Functions
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+
+    declareSurfaceWriterWriteMethod(label);
+    declareSurfaceWriterWriteMethod(scalar);
+    declareSurfaceWriterWriteMethod(vector);
+    declareSurfaceWriterWriteMethod(sphericalTensor);
+    declareSurfaceWriterWriteMethod(symmTensor);
+    declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C b/src/surfMesh/writers/ensight/ensightSurfaceWriter.C
similarity index 53%
rename from src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C
rename to src/surfMesh/writers/ensight/ensightSurfaceWriter.C
index bcf4f00055ccd29d0db3c7c8a97ff8885e8570b5..69f2d69e8b2f80dd19549368fd0391299964c369 100644
--- a/src/sampling/sampledSurface/writers/ensight/ensightSurfaceWriter.C
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriter.C
@@ -2,10 +2,10 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2013 OpenFOAM Foundation
+                            | Copyright (C) 2011-2014 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,29 +26,32 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "ensightSurfaceWriter.H"
+#include "IOmanip.H"
+#include "Fstream.H"
+#include "OSspecific.H"
+#include "ensightCase.H"
 #include "ensightPartFaces.H"
-#include "makeSurfaceWriterMethods.H"
+#include "ensightOutput.H"
+#include "ensightPTraits.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    makeSurfaceWriterType(ensightSurfaceWriter);
-    addToRunTimeSelectionTable(surfaceWriter, ensightSurfaceWriter, wordDict);
+namespace surfaceWriters
+{
+    defineTypeName(ensightWriter);
+    addToRunTimeSelectionTable(surfaceWriter, ensightWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, ensightWriter, wordDict);
+}
 }
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Field writing implementation
-#include "ensightSurfaceWriterImpl.C"
-
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::ensightSurfaceWriter);
 
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
 
-void Foam::ensightSurfaceWriter::printTimeset
+void Foam::surfaceWriters::ensightWriter::printTimeset
 (
     OSstream& os,
     const label ts,
@@ -69,7 +72,7 @@ void Foam::ensightSurfaceWriter::printTimeset
 }
 
 
-void Foam::ensightSurfaceWriter::printTimeset
+void Foam::surfaceWriters::ensightWriter::printTimeset
 (
     OSstream& os,
     const label ts,
@@ -100,10 +103,9 @@ void Foam::ensightSurfaceWriter::printTimeset
 }
 
 
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::ensightSurfaceWriter::ensightSurfaceWriter()
+Foam::surfaceWriters::ensightWriter::ensightWriter()
 :
     surfaceWriter(),
     writeFormat_(IOstream::ASCII),
@@ -111,9 +113,12 @@ Foam::ensightSurfaceWriter::ensightSurfaceWriter()
 {}
 
 
-Foam::ensightSurfaceWriter::ensightSurfaceWriter(const dictionary& options)
+Foam::surfaceWriters::ensightWriter::ensightWriter
+(
+    const dictionary& options
+)
 :
-    surfaceWriter(),
+    surfaceWriter(options),
     writeFormat_
     (
         IOstreamOption::formatNames.lookupOrDefault
@@ -128,6 +133,35 @@ Foam::ensightSurfaceWriter::ensightSurfaceWriter(const dictionary& options)
 {}
 
 
+Foam::surfaceWriters::ensightWriter::ensightWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    ensightWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::ensightWriter::ensightWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    ensightWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 // Note that ensight does supports geometry in a separate file,
@@ -135,114 +169,53 @@ Foam::ensightSurfaceWriter::ensightSurfaceWriter(const dictionary& options)
 // (when there are fields).
 //
 // Make this false to let the field writers take back control
-bool Foam::ensightSurfaceWriter::separateGeometry() const
+bool Foam::surfaceWriters::ensightWriter::separateGeometry() const
 {
     return false;
 }
 
 
-void Foam::ensightSurfaceWriter::updateMesh
-(
-    const fileName& outputDir,
-    const fileName& surfaceName
-) const
+Foam::fileName Foam::surfaceWriters::ensightWriter::write()
 {
-    if (collateTimes_ && Pstream::master())
-    {
-        const ensight::FileName surfName(surfaceName);
-
-        const fileName baseDir = outputDir.path()/surfName;
-        const fileName timeDir = outputDir.name();
-
-        // Convert timeDir to a value (if possible - use 0.0 otherwise)
-        scalar timeValue = 0.0;
-        readScalar(timeDir, timeValue);
-
-        if (!isDir(baseDir))
-        {
-            mkDir(baseDir);
-        }
+    // if (collateTimes_)
+    // {
+    //     return writeCollated();
+    // }
+    // else
+    // {
+    //     return writeUncollated();
+    // }
+    return writeUncollated();
+}
 
-        dictionary dict;
 
-        if (isFile(baseDir/"fieldsDict"))
-        {
-            IFstream is(baseDir/"fieldsDict");
-            if (is.good() && dict.read(is))
-            {
-                // ... any futher actions
-            }
-        }
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-        dict.set("updateMesh", timeValue);
+#include "ensightSurfaceWriterCollated.C"
+#include "ensightSurfaceWriterUncollated.C"
 
-        {
-            OFstream os(baseDir/"fieldsDict");
-            os << "// Summary of Ensight fields, times" << nl << nl;
-            dict.write(os, false);
-        }
-    }
-}
 
+// Field writing implementations
 
-Foam::fileName Foam::ensightSurfaceWriter::write
+template<class Type>
+Foam::fileName Foam::surfaceWriters::ensightWriter::writeTemplate
 (
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
+    const word& fieldName,
+    const Field<Type>& localValues
+)
 {
-    const ensight::FileName surfName(surfaceName);
-
-    // Uncollated
-    // ==========
-    // geometry:  rootdir/time/surfaceName.case
-    // geometry:  rootdir/time/surfaceName.00000000.mesh
-
-    if (!isDir(outputDir))
+    if (collateTimes_)
     {
-        mkDir(outputDir);
+        return writeCollated(fieldName, localValues);
     }
-
-    OFstream osCase(outputDir/surfName + ".case");
-    ensightGeoFile osGeom
-    (
-        outputDir,
-        surfName + ".00000000.mesh",
-        writeFormat_
-    );
-
-    if (verbose)
+    else
     {
-        Info<< "Writing case file to " << osCase.name() << endl;
+        return writeUncollated(fieldName, localValues);
     }
-
-    const pointField& points = surf.points();
-    const faceList&   faces  = surf.faces();
-
-    osCase
-        << "FORMAT" << nl
-        << "type: ensight gold" << nl
-        << nl
-        << "GEOMETRY" << nl
-        << "model:        1     " << osGeom.name().name() << nl
-        << nl
-        << "TIME" << nl;
-
-    printTimeset(osCase, 1, 0.0);
-
-    ensightPartFaces ensPart(0, osGeom.name().name(), points, faces, true);
-    osGeom << ensPart;
-
-    return osCase.name();
+}
 
 
-    // Collated?
-    // ========
-    // geometry:  rootdir/surfaceName/surfaceName.case
-    // geometry:  rootdir/surfaceName/surfaceName.mesh
-}
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::ensightWriter);
 
 
 // ************************************************************************* //
diff --git a/src/surfMesh/writers/ensight/ensightSurfaceWriter.H b/src/surfMesh/writers/ensight/ensightSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..39a958a20bfbba7534bef3e83364415140668c05
--- /dev/null
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriter.H
@@ -0,0 +1,198 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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::surfaceWriters::ensightWriter
+
+Description
+    A surfaceWriter for Ensight format.
+
+    \verbatim
+    formatOptions
+    {
+        ensight
+        {
+            format          ascii;
+            collateTimes    true;
+        }
+    }
+    \endverbatim
+
+    Format options:
+    \table
+        Property | Description                              | Required | Default
+        format   | ascii/binary                             | no  | ascii
+        collateTimes | use common geometry for times        | no  | true
+    \endtable
+
+SourceFiles
+    ensightSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ensightSurfaceWriter_H
+#define ensightSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class ensightWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class ensightWriter
+:
+    public surfaceWriter
+{
+    // Private Data
+
+        //- Write option (default: IOstream::ASCII)
+        IOstream::streamFormat writeFormat_;
+
+        //- Collate times (default: true)
+        bool collateTimes_;
+
+
+    // Private Member Functions
+
+        //- Print time-set for ensight case file
+        static void printTimeset
+        (
+            OSstream& os,
+            const label ts,
+            const scalar& timeValue
+        );
+
+        //- Print time-set for ensight case file
+        static void printTimeset
+        (
+            OSstream& os,
+            const label ts,
+            const UList<scalar>& times
+        );
+
+
+        //- Write geometry
+        fileName writeCollated();
+
+        //- Write geometry
+        fileName writeUncollated();
+
+        //- Templated write operation - one file per timestep
+        template<class Type>
+        fileName writeCollated
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+        //- Templated write operation - all time steps in single file
+        template<class Type>
+        fileName writeUncollated
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+        //- Templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("ensight");
+
+
+    // Constructors
+
+        //- Construct null
+        ensightWriter();
+
+        //- Construct with some output options
+        explicit ensightWriter(const dictionary& options);
+
+        //- Construct from components
+        ensightWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        ensightWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~ensightWriter() = default;
+
+
+    // Member Functions
+
+        //- True if the surface format supports geometry in a separate file.
+        //  False if geometry and field must be in a single file
+        virtual bool separateGeometry() const;
+
+        //- Write single surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C b/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C
new file mode 100644
index 0000000000000000000000000000000000000000..0b2724ae61618a5d9378c28bedb1a9a5290a5e60
--- /dev/null
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriterCollated.C
@@ -0,0 +1,376 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2014 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated()
+{
+    // Collated?
+    // ========
+    // Geometry:  rootdir/surfaceName/surfaceName.case
+    // Geometry:  rootdir/surfaceName/surfaceName.mesh
+
+    return fileName();
+}
+
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::ensightWriter::writeCollated
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    const ensight::FileName surfName(outputPath_.name());
+    const ensight::VarName  varName(fieldName);
+
+
+    // Collated
+    // ========
+    // Geometry:  rootdir/surfaceName/surfaceName.case
+    // Geometry:  rootdir/surfaceName/data/<index>/geometry
+    // Field:     rootdir/surfaceName/data/<index>/field
+
+    // Use surface name as sub-directory for results. Eg,
+    // - SURF1/SURF1.case
+    // - SURF1/data/00000000/geometry
+    // - SURF1/data/00000000/VAR1
+    // - SURF1/data/00000000/VAR2
+
+    // Names "data" and "geometry" as per ensightCase:
+    const char* fmt  = "%08d";
+    const char* mask = "data/********/";
+
+
+    // Ignore the useTimeDir setting - manage ourselves
+    const fileName baseDir = outputPath_;
+
+    const word   timeDir = timeName();
+    const scalar timeValue = currTime_.value();
+
+    const fileName outputFile = baseDir / surfName + ".case";
+
+    if (verbose_)
+    {
+        Info<< "Writing case file to " << outputFile << endl;
+    }
+
+
+    // Mesh changed since last output? Do before any merging.
+    const bool meshChanged = (!upToDate_);
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        scalar meshValue = 0;
+        label meshIndex = 0;
+        label timeIndex = 0;
+
+        fileName geometryName;
+
+        // Do case file
+        {
+            dictionary dict;
+            scalarList meshes;
+            scalarList times;
+            bool stateChanged = meshChanged;
+
+            if (isFile(baseDir/"fieldsDict"))
+            {
+                IFstream is(baseDir/"fieldsDict");
+                if (is.good() && dict.read(is))
+                {
+                    dict.readIfPresent("meshes", meshes);
+                    dict.readIfPresent("times", times);
+
+                    timeIndex = 1+findLower(times, timeValue);
+
+                    if (meshChanged)
+                    {
+                        meshValue = timeValue;
+                        meshIndex = 1+findLower(meshes, meshValue);
+                    }
+                    else if (meshes.size())
+                    {
+                        meshIndex = meshes.size()-1;
+                        meshValue = meshes.last();
+                    }
+                    else
+                    {
+                        meshIndex = 0;
+                    }
+                }
+            }
+
+            // Update stored times list
+            meshes.resize(meshIndex+1, -1);
+            times.resize(timeIndex+1, -1);
+
+            if (meshes[meshIndex] != meshValue)
+            {
+                stateChanged = true;
+            }
+            if (times[timeIndex] != timeValue)
+            {
+                stateChanged = true;
+            }
+
+            meshes[meshIndex] = meshValue;
+            times[timeIndex] = timeValue;
+
+            geometryName =
+                "data"/word::printf(fmt, meshIndex)/ensightCase::geometryName;
+
+
+            // Add my information to dictionary
+            {
+                dict.set("meshes", meshes);
+                dict.set("times", times);
+                if (dict.found("fields"))
+                {
+                    dictionary& fieldsDict = dict.subDict("fields");
+                    if (!fieldsDict.found(fieldName))
+                    {
+                        dictionary fieldDict;
+                        fieldDict.set("type", ensightPTraits<Type>::typeName);
+                        fieldDict.set("name", varName); // ensight variable name
+
+                        fieldsDict.set(fieldName, fieldDict);
+
+                        stateChanged = true;
+                    }
+                }
+                else
+                {
+                    dictionary fieldDict;
+                    fieldDict.set("type", ensightPTraits<Type>::typeName);
+                    fieldDict.set("name", varName); // ensight variable name
+
+                    dictionary fieldsDict;
+                    fieldsDict.set(fieldName, fieldDict);
+
+                    dict.set("fields", fieldsDict);
+
+                    stateChanged = true;
+                }
+            }
+
+
+            if (stateChanged)
+            {
+                if (verbose_)
+                {
+                    Info<< "Writing state file to fieldsDict" << endl;
+                }
+                {
+                    OFstream os(baseDir/"fieldsDict");
+                    os << "// Summary of Ensight fields, times" << nl << nl;
+                    dict.write(os, false);
+                }
+
+                OFstream osCase(outputFile, IOstream::ASCII);
+
+                // Format options
+                osCase.setf(ios_base::left);
+                osCase.setf(ios_base::scientific, ios_base::floatfield);
+                osCase.precision(5);
+
+                if (verbose_)
+                {
+                    Info<< "Writing case file to " << osCase.name() << endl;
+                }
+
+                // The geometry can be any of the following:
+                // 0: constant/static
+                // 1: moving, with the same frequency as the data
+                // 2: moving, with different frequency as the data
+
+                const label tsGeom =
+                    (meshes.size() == 1 ? 0 : meshes == times ? 1 : 2);
+
+                osCase
+                    << "FORMAT" << nl
+                    << "type: ensight gold" << nl
+                    << nl
+                    << "GEOMETRY" << nl;
+
+
+                if (tsGeom)
+                {
+                    // moving
+                    osCase
+                        << "model:  " << tsGeom << "   " // time-set (1|2)
+                        << mask << geometryName.name() << nl;
+                }
+                else
+                {
+                    // steady
+                    osCase
+                        << "model:  "
+                        << geometryName.c_str() << nl;
+                }
+
+                osCase
+                    << nl
+                    << "VARIABLE" << nl;
+
+                const dictionary& fieldsDict = dict.subDict("fields");
+                for (const entry& dEntry : fieldsDict)
+                {
+                    const dictionary& subDict = dEntry.dict();
+
+                    const word fieldType(subDict.get<word>("type"));
+                    const word varName = subDict.lookupOrDefault
+                    (
+                        "name",
+                        dEntry.keyword() // fieldName as fallback
+                    );
+
+                    osCase
+                        << fieldType
+                        <<
+                        (
+                            this->isPointData()
+                          ? " per node:    1  "  // time-set 1
+                          : " per element: 1  "  // time-set 1
+                        )
+                        << setw(15) << varName << ' '
+                        << mask << varName << nl;
+                }
+
+                osCase
+                    << nl
+                    << "TIME" << nl;
+
+                printTimeset(osCase, 1, times);
+                if (tsGeom == 2)
+                {
+                    printTimeset(osCase, 2, meshes);
+                }
+
+                osCase << "# end" << nl;
+            }
+        }
+
+
+        // Location for data (and possibly the geometry as well)
+        fileName dataDir = baseDir/"data"/word::printf(fmt, timeIndex);
+
+        // As per mkdir -p "data/00000000"
+        mkDir(dataDir);
+
+
+        const fileName meshFile(baseDir/geometryName);
+
+        // Write geometry
+        ensightPartFaces ensPart
+        (
+            0,
+            meshFile.name(),
+            surf.points(),
+            surf.faces(),
+            true // contiguous points
+        );
+        if (!exists(meshFile))
+        {
+            if (verbose_)
+            {
+                Info<< "Writing mesh file to " << meshFile.name() << endl;
+            }
+            // Use two-argument form for path-name to avoid validating base-dir
+            ensightGeoFile osGeom
+            (
+                meshFile.path(),
+                meshFile.name(),
+                writeFormat_
+            );
+            osGeom << ensPart;
+        }
+
+        // Write field
+        ensightFile osField
+        (
+            dataDir,
+            varName,
+            writeFormat_
+        );
+
+        if (verbose_)
+        {
+            Info<< "Writing field file to " << osField.name() << endl;
+        }
+
+        // Write field
+        osField.writeKeyword(ensightPTraits<Type>::typeName);
+
+        if (this->isPointData())
+        {
+            ensightOutput::Serial::writePointField
+            (
+                tfield(),
+                ensPart,
+                osField
+                // serial
+            );
+        }
+        else
+        {
+            ensightOutput::Detail::writeFaceField
+            (
+                tfield(),
+                ensPart,
+                osField,
+                false // serial
+            );
+        }
+
+        // Place a timestamp in the directory for future reference
+        {
+            OFstream timeStamp(dataDir/"time");
+            timeStamp
+                << "#   timestep time" << nl
+                << dataDir.name() << " " << timeValue << nl;
+        }
+    }
+
+
+    return outputFile;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C b/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C
new file mode 100644
index 0000000000000000000000000000000000000000..c3cd3a537891b3d66d3c88d147c85b39564d274f
--- /dev/null
+++ b/src/surfMesh/writers/ensight/ensightSurfaceWriterUncollated.C
@@ -0,0 +1,252 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2014 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated()
+{
+    checkOpen();
+
+    const ensight::FileName surfName(outputPath_.name());
+
+
+    // Uncollated
+    // ==========
+    // Geometry:  rootdir/<TIME>/surfaceName.case
+    // Geometry:  rootdir/<TIME>/surfaceName.00000000.mesh
+
+    fileName outputDir;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputDir = outputPath_.path() / timeName();
+    }
+    else
+    {
+        outputDir = outputPath_.path();
+    }
+
+    const fileName outputFile = outputDir / surfName + ".case";
+
+    if (verbose_)
+    {
+        Info<< "Writing case file to " << outputFile << endl;
+    }
+
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputDir))
+        {
+            mkDir(outputDir);
+        }
+
+        OFstream osCase(outputFile);
+        ensightGeoFile osGeom
+        (
+            outputDir,
+            surfName + ".00000000.mesh",
+            writeFormat_
+        );
+
+        osCase
+            << "FORMAT" << nl
+            << "type: ensight gold" << nl
+            << nl
+            << "GEOMETRY" << nl
+            << "model:        1     " << osGeom.name().name() << nl
+            << nl
+            << "TIME" << nl;
+
+        printTimeset(osCase, 1, 0.0);
+
+        ensightPartFaces ensPart
+        (
+            0,
+            osGeom.name().name(),
+            surf.points(),
+            surf.faces(),
+            true // contiguous points
+        );
+        osGeom << ensPart;
+    }
+
+    return outputFile;
+}
+
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::ensightWriter::writeUncollated
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    const ensight::FileName surfName(outputPath_.name());
+    const ensight::VarName  varName(fieldName);
+
+
+    // Uncollated
+    // ==========
+    // geometry:  rootdir/time/<field>/surfaceName.case
+    // geometry:  rootdir/time/<field>/surfaceName.<index>.mesh
+    // field:     rootdir/time/<field>/surfaceName.<index>.<field>
+
+    // Variable name as sub-directory for results. Eg,
+    // - VAR1/SURF1.case
+    // - VAR1/SURF1.00000000.mesh
+    // - VAR1/SURF1.00000001.VAR1
+    // and
+    // - VAR2/SURF1.case
+    // - VAR2/SURF1.00000000.mesh
+    // - VAR2/SURF1.00000001.VAR2
+
+    fileName outputDir;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputDir = outputPath_.path() / timeName();
+    }
+    else
+    {
+        outputDir = outputPath_.path();
+    }
+
+    const fileName baseDir = outputDir / varName;
+    const word   timeDir = timeName();
+    const scalar timeValue = currTime_.value();
+
+    const fileName outputFile = baseDir / surfName + ".case";
+
+    if (verbose_)
+    {
+        Info<< "Writing case file to " << outputFile << endl;
+    }
+
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        OFstream osCase(outputFile, IOstream::ASCII);
+
+        // Format options
+        osCase.setf(ios_base::left);
+        osCase.setf(ios_base::scientific, ios_base::floatfield);
+        osCase.precision(5);
+
+        ensightGeoFile osGeom
+        (
+            baseDir,
+            surfName + ".00000000.mesh",
+            writeFormat_
+        );
+        ensightFile osField
+        (
+            baseDir,
+            surfName + ".00000000." + varName,
+            writeFormat_
+        );
+
+        osCase
+            << "FORMAT" << nl
+            << "type: ensight gold" << nl
+            << nl
+            << "GEOMETRY" << nl
+            << "model:  1   " << osGeom.name().name() << nl
+            << nl
+            << "VARIABLE" << nl
+            << ensightPTraits<Type>::typeName
+            <<
+            (
+                this->isPointData()
+              ? " per node:    1  "  // time-set 1
+              : " per element: 1  "  // time-set 1
+            )
+            << setw(15) << varName << ' '
+            << surfName.c_str() << ".********." << varName << nl;
+
+        osCase
+            << nl
+            << "TIME" << nl;
+
+        printTimeset(osCase, 1, timeValue);
+        osCase << "# end" << nl;
+
+
+        ensightPartFaces ensPart
+        (
+            0,
+            osGeom.name().name(),
+            surf.points(),
+            surf.faces(),
+            true // contiguous points
+        );
+        osGeom << ensPart;
+
+        // Write field
+        osField.writeKeyword(ensightPTraits<Type>::typeName);
+
+        if (this->isPointData())
+        {
+            ensightOutput::Serial::writePointField
+            (
+                tfield(),
+                ensPart,
+                osField
+                // serial
+            );
+        }
+        else
+        {
+            ensightOutput::Detail::writeFaceField
+            (
+                tfield(),
+                ensPart,
+                osField,
+                false // serial
+            );
+        }
+    }
+
+    return outputFile;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/foam/foamSurfaceWriter.C b/src/surfMesh/writers/foam/foamSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..8d72939d696ce3af7b135841cadbec07e74866ee
--- /dev/null
+++ b/src/surfMesh/writers/foam/foamSurfaceWriter.C
@@ -0,0 +1,208 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2016 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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 "foamSurfaceWriter.H"
+#include "OFstream.H"
+#include "OSspecific.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(foamWriter);
+    addToRunTimeSelectionTable(surfaceWriter, foamWriter, word);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::foamWriter::foamWriter()
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::foamWriter::foamWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options)
+{}
+
+
+Foam::surfaceWriters::foamWriter::foamWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    foamWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::foamWriter::foamWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    foamWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::foamWriter::write()
+{
+    checkOpen();
+
+    // Geometry:
+    // - rootdir/<TIME>/surfaceName/{points,faces}
+
+    fileName surfaceDir = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        surfaceDir = outputPath_.path() / timeName() / outputPath_.name();
+    }
+
+    if (verbose_)
+    {
+        Info<< "Writing geometry to " << surfaceDir << endl;
+    }
+
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        const pointField& points = surf.points();
+        const faceList& faces = surf.faces();
+
+        if (!isDir(surfaceDir))
+        {
+            mkDir(surfaceDir);
+        }
+
+        // Points
+        OFstream(surfaceDir/"points")() << points;
+
+        // Faces
+        OFstream(surfaceDir/"faces")() << faces;
+
+        // Face centers.
+        // Not really necessary but very handy when reusing as inputs
+        // for e.g. timeVaryingMapped bc.
+        pointField faceCentres(faces.size(), Zero);
+
+        forAll(faces, facei)
+        {
+            faceCentres[facei] = faces[facei].centre(points);
+        }
+
+        OFstream(surfaceDir/"faceCentres")() << faceCentres;
+    }
+
+    return surfaceDir;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::foamWriter::writeTemplate
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    // Geometry should already have been written
+    // Values to separate directory (e.g. "scalarField/p")
+
+    // Field:    rootdir/<TIME>/surfaceName/fieldType/field
+    //?? -> or        rootdir/surfaceName/fieldType/<TIME>/field
+
+    fileName surfaceDir = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        surfaceDir = outputPath_.path() / timeName() / outputPath_.name();
+    }
+
+    const fileName outputFile
+    (
+        surfaceDir
+      / (word(pTraits<Type>::typeName) + FieldBase::typeName)
+      / fieldName
+    );
+
+    if (verbose_)
+    {
+        Info<< "Writing field " << fieldName << " to " << surfaceDir << endl;
+    }
+
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield =  mergeField(localValues);
+
+    if (Pstream::master())
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        // Write field
+        OFstream(outputFile)() << tfield();
+    }
+
+    return outputFile;
+}
+
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::foamWriter);
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/foam/foamSurfaceWriter.H b/src/surfMesh/writers/foam/foamSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..893f773ce558c02532062aa620fc2577d4408f47
--- /dev/null
+++ b/src/surfMesh/writers/foam/foamSurfaceWriter.H
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2016 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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::surfaceWriters::foamWriter
+
+Description
+    A surfaceWriter for OpenFOAM surfaces
+
+    \heading Output file locations
+
+    The \c rootdir normally corresponds to something like
+    \c postProcessing/\<name\>
+
+    \subheading Geometry
+    \verbatim
+    rootdir
+    `-- timeName
+        `-- surfaceName
+            |-- "points"
+            |-- "faceCentres"
+            `-- "faces"
+    \endverbatim
+
+    \subheading Fields
+    \verbatim
+    rootdir
+    `-- timeName
+        `-- surfaceName
+            |-- scalarField
+            |   |-- field
+            |   `-- field
+            |-- vectorField
+                |-- field
+                `-- field
+    \endverbatim
+
+SourceFiles
+    foamSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamSurfaceWriter_H
+#define foamSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class foamWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamWriter
+:
+    public surfaceWriter
+{
+    // Private Member Functions
+
+        //- Templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("foam");
+
+
+    // Constructors
+
+        //- Construct null
+        foamWriter();
+
+        //- Construct with some output options
+        explicit foamWriter(const dictionary& options);
+
+        //- Construct from components
+        foamWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        foamWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~foamWriter() = default;
+
+
+    // Member Functions
+
+        //- The surface format has geometry in a separate file.
+        virtual bool separateGeometry() const
+        {
+            return true;
+        }
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.C b/src/surfMesh/writers/nastran/nastranSurfaceWriter.C
similarity index 76%
rename from src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.C
rename to src/surfMesh/writers/nastran/nastranSurfaceWriter.C
index bf3d23d19b5f214777470549d6e27338cf05cf7d..da98e638d4ba97223e1c1ad0b1c5331ffc6d4dc8 100644
--- a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.C
+++ b/src/surfMesh/writers/nastran/nastranSurfaceWriter.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2012-2016 OpenFOAM Foundation
@@ -26,17 +26,22 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "nastranSurfaceWriter.H"
-#include "IOmanip.H"
 #include "Pair.H"
-#include "HashSet.H"
-#include "makeSurfaceWriterMethods.H"
+#include "IOmanip.H"
+#include "OSspecific.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    makeSurfaceWriterType(nastranSurfaceWriter);
-    addToRunTimeSelectionTable(surfaceWriter, nastranSurfaceWriter, wordDict);
+namespace surfaceWriters
+{
+    defineTypeName(nastranWriter);
+    addToRunTimeSelectionTable(surfaceWriter, nastranWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, nastranWriter, wordDict);
+}
 }
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -45,11 +50,12 @@ namespace Foam
 #include "nastranSurfaceWriterImpl.C"
 
 // Field writing methods
-defineSurfaceWriterWriteFields(Foam::nastranSurfaceWriter);
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::nastranWriter);
+
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-Foam::Ostream& Foam::nastranSurfaceWriter::writeKeyword
+Foam::Ostream& Foam::surfaceWriters::nastranWriter::writeKeyword
 (
     Ostream& os,
     const word& keyword
@@ -59,7 +65,7 @@ Foam::Ostream& Foam::nastranSurfaceWriter::writeKeyword
 }
 
 
-void Foam::nastranSurfaceWriter::writeCoord
+void Foam::surfaceWriters::nastranWriter::writeCoord
 (
     Ostream& os,
     const point& pt,
@@ -117,7 +123,7 @@ void Foam::nastranSurfaceWriter::writeCoord
 }
 
 
-void Foam::nastranSurfaceWriter::writeFace
+void Foam::surfaceWriters::nastranWriter::writeFace
 (
     Ostream& os,
     const word& faceType,
@@ -193,7 +199,7 @@ void Foam::nastranSurfaceWriter::writeFace
 }
 
 
-void Foam::nastranSurfaceWriter::writeGeometry
+void Foam::surfaceWriters::nastranWriter::writeGeometry
 (
     Ostream& os,
     const meshedSurf& surf,
@@ -257,13 +263,13 @@ void Foam::nastranSurfaceWriter::writeGeometry
 }
 
 
-Foam::Ostream& Foam::nastranSurfaceWriter::writeFooter
+Foam::Ostream& Foam::surfaceWriters::nastranWriter::writeFooter
 (
     Ostream& os,
     const meshedSurf& surf
 ) const
 {
-    // zone id have been used for the PID. Find unique values.
+    // Zone id have been used for the PID. Find unique values.
 
     labelList pidsUsed = labelHashSet(surf.zoneIds()).sortedToc();
     if (pidsUsed.empty())
@@ -286,7 +292,7 @@ Foam::Ostream& Foam::nastranSurfaceWriter::writeFooter
     }
 
 
-    // use single material ID
+    // Use single material ID
 
     const label MID = 1;
 
@@ -307,7 +313,7 @@ Foam::Ostream& Foam::nastranSurfaceWriter::writeFooter
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::nastranSurfaceWriter::nastranSurfaceWriter()
+Foam::surfaceWriters::nastranWriter::nastranWriter()
 :
     surfaceWriter(),
     writeFormat_(fieldFormat::SHORT),
@@ -317,9 +323,12 @@ Foam::nastranSurfaceWriter::nastranSurfaceWriter()
 {}
 
 
-Foam::nastranSurfaceWriter::nastranSurfaceWriter(const dictionary& options)
+Foam::surfaceWriters::nastranWriter::nastranWriter
+(
+    const dictionary& options
+)
 :
-    surfaceWriter(),
+    surfaceWriter(options),
     writeFormat_
     (
         fileFormats::NASCore::fieldFormatNames.lookupOrDefault
@@ -353,43 +362,82 @@ Foam::nastranSurfaceWriter::nastranSurfaceWriter(const dictionary& options)
 }
 
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-Foam::fileName Foam::nastranSurfaceWriter::write
+Foam::surfaceWriters::nastranWriter::nastranWriter
 (
-    const fileName& outputDir,
-    const fileName& surfaceName,
     const meshedSurf& surf,
-    const bool verbose
-) const
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    nastranWriter(options)
 {
-    // geometry:  rootdir/time/surfaceName.nas
+    open(surf, outputPath, parallel);
+}
+
 
-    if (!isDir(outputDir))
+Foam::surfaceWriters::nastranWriter::nastranWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    nastranWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::nastranWriter::write()
+{
+    checkOpen();
+
+    // Geometry:  rootdir/<TIME>/surfaceName.nas
+
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
     {
-        mkDir(outputDir);
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
     }
+    outputFile.ext("nas");
 
-    OFstream os(outputDir/surfaceName + ".nas");
-    fileFormats::NASCore::setPrecision(os, writeFormat_);
-
-    if (verbose)
+    if (verbose_)
     {
-        Info<< "Writing nastran file to " << os.name() << endl;
+        Info<< "Writing nastran geometry to " << outputFile << endl;
     }
 
-    os  << "TITLE=OpenFOAM " << surfaceName.c_str()
-        << " mesh" << nl
-        << "$" << nl
-        << "BEGIN BULK" << nl;
 
-    List<DynamicList<face>> decomposedFaces;
-    writeGeometry(os, surf, decomposedFaces);
+    const meshedSurf& surf = surface();
 
-    writeFooter(os, surf)
-        << "ENDDATA" << nl;
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        OFstream os(outputFile);
+        fileFormats::NASCore::setPrecision(os, writeFormat_);
+
+        os  << "TITLE=OpenFOAM " << outputPath_.name()
+            << " mesh" << nl
+            << "$" << nl
+            << "BEGIN BULK" << nl;
+
+        List<DynamicList<face>> decomposedFaces;
+        writeGeometry(os, surf, decomposedFaces);
+
+        writeFooter(os, surf)
+            << "ENDDATA" << nl;
+    }
 
-    return os.name();
+    return outputFile;
 }
 
 
diff --git a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.H b/src/surfMesh/writers/nastran/nastranSurfaceWriter.H
similarity index 55%
rename from src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.H
rename to src/surfMesh/writers/nastran/nastranSurfaceWriter.H
index d8140b99e6615ad80b86c915dbf6b1d35f750b54..3993148cada03a2f84e1b1944ad99477a749da3a 100644
--- a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriter.H
+++ b/src/surfMesh/writers/nastran/nastranSurfaceWriter.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2012-2016 OpenFOAM Foundation
@@ -24,7 +24,7 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Class
-    Foam::nastranSurfaceWriter
+    Foam::surfaceWriters::nastranWriter
 
 Description
     A surface writer for the Nastran file format - both surface mesh and fields
@@ -82,7 +82,7 @@ Description
 
 SourceFiles
     nastranSurfaceWriter.C
-    nastranSurfaceWriterTemplates.C
+    nastranSurfaceWriterImpl.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -97,12 +97,14 @@ SourceFiles
 
 namespace Foam
 {
+namespace surfaceWriters
+{
 
 /*---------------------------------------------------------------------------*\
-                    Class nastranSurfaceWriter Declaration
+                        Class nastranWriter Declaration
 \*---------------------------------------------------------------------------*/
 
-class nastranSurfaceWriter
+class nastranWriter
 :
     public surfaceWriter
 {
@@ -117,7 +119,7 @@ public:
 
 private:
 
-    // Private data
+    // Private Data
 
         //- Field format (width and separator)
         fieldFormat writeFormat_;
@@ -189,123 +191,68 @@ private:
         template<class Type>
         fileName writeTemplate
         (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
             const word& fieldName,          //!< Name of field
-            const Field<Type>& values,      //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const;
+            const Field<Type>& localValues  //!< Local field values to write
+        );
 
 
 public:
 
     //- Runtime type information
-    TypeName("nastran");
+    TypeNameNoDebug("nastran");
 
 
     // Constructors
 
         //- Construct null
-        nastranSurfaceWriter();
+        nastranWriter();
 
         //- Construct with some output options
-        nastranSurfaceWriter(const dictionary& options);
+        explicit nastranWriter(const dictionary& options);
+
+        //- Construct from components
+        nastranWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        nastranWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
 
 
     //- Destructor
-    virtual ~nastranSurfaceWriter() = default;
+    virtual ~nastranWriter() = default;
 
 
     // Member Functions
 
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
-        virtual bool separateGeometry() const
-        {
-            return false;
-        }
-
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
+    // Write
 
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
 
-        //- Write scalarField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<scalar>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write vectorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<vector>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write sphericalTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<sphericalTensor>& values, //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write symmTensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<symmTensor>& values,//!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
-
-        //- Write tensorField for a single surface to file.
-        //  One value per face or vertex.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const word& fieldName,          //!< Name of field
-            const Field<tensor>& values,    //!< Field values to write
-            const bool isNodeValues = false,//!< Values are per-vertex
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
 };
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+} // End namespace surfaceWriters
 } // End namespace Foam
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriterImpl.C b/src/surfMesh/writers/nastran/nastranSurfaceWriterImpl.C
similarity index 63%
rename from src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriterImpl.C
rename to src/surfMesh/writers/nastran/nastranSurfaceWriterImpl.C
index fe0aabd52d5b886e087e71a098e6cc041a2b1301..a1809c91e6144810d9c40991fe6f0237c555eb3e 100644
--- a/src/sampling/sampledSurface/writers/nastran/nastranSurfaceWriterImpl.C
+++ b/src/surfMesh/writers/nastran/nastranSurfaceWriterImpl.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2012-2016 OpenFOAM Foundation
@@ -32,7 +32,7 @@ License
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-Foam::Ostream& Foam::nastranSurfaceWriter::writeValue
+Foam::Ostream& Foam::surfaceWriters::nastranWriter::writeValue
 (
     Ostream& os,
     const Type& value
@@ -64,7 +64,7 @@ Foam::Ostream& Foam::nastranSurfaceWriter::writeValue
 
 
 template<class Type>
-Foam::Ostream& Foam::nastranSurfaceWriter::writeFaceValue
+Foam::Ostream& Foam::surfaceWriters::nastranWriter::writeFaceValue
 (
     Ostream& os,
     const loadFormat format,
@@ -144,17 +144,14 @@ Foam::Ostream& Foam::nastranSurfaceWriter::writeFaceValue
 
 
 template<class Type>
-Foam::fileName Foam::nastranSurfaceWriter::writeTemplate
+Foam::fileName Foam::surfaceWriters::nastranWriter::writeTemplate
 (
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
     const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
+    const Field<Type>& localValues
+)
 {
+    checkOpen();
+
     if (!fieldMap_.found(fieldName))
     {
         FatalErrorInFunction
@@ -168,74 +165,97 @@ Foam::fileName Foam::nastranSurfaceWriter::writeTemplate
 
     const loadFormat& format(fieldMap_[fieldName]);
 
-    // field:  rootdir/time/field/surfaceName.nas
+    // Field:  rootdir/<TIME>/field/surfaceName.nas
 
+    fileName outputFile = outputPath_.path();
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile /= timeName();
+    }
+    outputFile /= fieldName / outputPath_.name();
+    outputFile.ext("nas");
 
-    if (!isDir(outputDir/fieldName))
+    if (verbose_)
     {
-        mkDir(outputDir/fieldName);
+        Info<< "Writing field " << fieldName << " to " << outputFile << endl;
     }
 
-    // const scalar timeValue = Foam::name(this->mesh().time().timeValue());
-    const scalar timeValue = 0.0;
 
-    OFstream os(outputDir/fieldName/surfaceName + ".nas");
-    fileFormats::NASCore::setPrecision(os, writeFormat_);
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
 
-    if (verbose)
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
     {
-        Info<< "Writing nastran file to " << os.name() << endl;
-    }
+        const auto& values = tfield();
 
-    os  << "TITLE=OpenFOAM " << surfaceName.c_str()
-        << " " << fieldName << " data" << nl
-        << "$" << nl
-        << "TIME " << timeValue << nl
-        << "$" << nl
-        << "BEGIN BULK" << nl;
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        const scalar timeValue = 0.0;
+
+        OFstream os(outputFile);
+        fileFormats::NASCore::setPrecision(os, writeFormat_);
 
-    List<DynamicList<face>> decomposedFaces;
-    writeGeometry(os, surf, decomposedFaces);
+        if (verbose_)
+        {
+            Info<< "Writing nastran file to " << os.name() << endl;
+        }
 
-    os  << "$" << nl
-        << "$ Field data" << nl
-        << "$" << nl;
+        os  << "TITLE=OpenFOAM " << outputFile.name()
+            << " " << fieldName << " data" << nl
+            << "$" << nl
+            << "TIME " << timeValue << nl
+            << "$" << nl
+            << "BEGIN BULK" << nl;
 
-    label elemId = 0;
+        List<DynamicList<face>> decomposedFaces;
+        writeGeometry(os, surf, decomposedFaces);
 
-    if (isNodeValues)
-    {
-        for (const DynamicList<face>& dFaces : decomposedFaces)
+        os  << "$" << nl
+            << "$ Field data" << nl
+            << "$" << nl;
+
+        label elemId = 0;
+
+        if (this->isPointData())
         {
-            for (const face& f : dFaces)
+            for (const DynamicList<face>& dFaces : decomposedFaces)
             {
-                Type v = Zero;
-
-                for (const label verti : f)
+                for (const face& f : dFaces)
                 {
-                    v += values[verti];
-                }
-                v /= f.size();
+                    Type v = Zero;
+
+                    for (const label verti : f)
+                    {
+                        v += values[verti];
+                    }
+                    v /= f.size();
 
-                writeFaceValue(os, format, v, ++elemId);
+                    writeFaceValue(os, format, v, ++elemId);
+                }
             }
         }
-    }
-    else
-    {
-        for (const DynamicList<face>& dFaces : decomposedFaces)
+        else
         {
-            forAll(dFaces, facei)
+            for (const DynamicList<face>& dFaces : decomposedFaces)
             {
-                writeFaceValue(os, format, values[facei], ++elemId);
+                forAll(dFaces, facei)
+                {
+                    writeFaceValue(os, format, values[facei], ++elemId);
+                }
             }
         }
-    }
 
-    writeFooter(os, surf)
-        << "ENDDATA" << endl;
+        writeFooter(os, surf)
+            << "ENDDATA" << endl;
+    }
 
-    return os.name();
+    return outputFile;
 }
 
 
diff --git a/src/surfMesh/writers/null/nullSurfaceWriter.C b/src/surfMesh/writers/null/nullSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..b9bcbcc1c651ecec7add62d5a281f56789f82585
--- /dev/null
+++ b/src/surfMesh/writers/null/nullSurfaceWriter.C
@@ -0,0 +1,121 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "nullSurfaceWriter.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(nullWriter);
+    addToRunTimeSelectionTable(surfaceWriter, nullWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, nullWriter, wordDict);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::nullWriter::nullWriter()
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::nullWriter::nullWriter(const dictionary& options)
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::nullWriter::nullWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::nullWriter::nullWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    surfaceWriter()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::surfaceWriters::nullWriter::needsUpdate() const
+{
+    return false;
+}
+
+
+bool Foam::surfaceWriters::nullWriter::enabled() const
+{
+    return false;
+}
+
+
+void Foam::surfaceWriters::nullWriter::setSurface
+(
+    const meshedSurf& surf,
+    bool parallel
+)
+{}
+
+
+void Foam::surfaceWriters::nullWriter::setSurface
+(
+    const pointField& points,
+    const faceList& faces,
+    bool parallel
+)
+{}
+
+
+void Foam::surfaceWriters::nullWriter::open(const fileName& outputPath)
+{}
+
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::nullWriter);
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/null/nullSurfaceWriter.H b/src/surfMesh/writers/null/nullSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..2174a566945a2f7500415d74107a944ffc06d935
--- /dev/null
+++ b/src/surfMesh/writers/null/nullSurfaceWriter.H
@@ -0,0 +1,149 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::surfaceWriters::nullWriter
+
+Description
+    Suppresses output of geometry and fields
+
+SourceFiles
+    nullSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef nullSurfaceWriter_H
+#define nullSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class nullWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class nullWriter
+:
+    public surfaceWriter
+{
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("none");
+
+
+    // Constructors
+
+        //- Construct for a given extension
+        nullWriter();
+
+        //- Construct for a given extension
+        explicit nullWriter(const dictionary& options);
+
+        //- Construct from components
+        nullWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        nullWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~nullWriter() = default;
+
+
+    // Member Functions
+
+    // Capability
+
+        //- Never needs an update.
+        virtual bool needsUpdate() const;
+
+        //- The null writer is always disabled, which lets the caller
+        //- skip various (possibly expensive) preparatory operations.
+        virtual bool enabled() const;
+
+
+    // Surface association
+
+        //- Change association with a surface (no-op).
+        virtual void setSurface
+        (
+            const meshedSurf& s,
+            bool parallel = Pstream::parRun()
+        ); // override
+
+        //- Change association with a surface (no-op).
+        virtual void setSurface
+        (
+            const pointField& points,
+            const faceList& faces,
+            bool parallel = Pstream::parRun()
+        ); // override
+
+
+    // Output
+
+        //- Open for output on specified path, using existing surface (no-op)
+        virtual void open(const fileName& outputPath); // override
+
+
+    // Write
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.C b/src/surfMesh/writers/proxy/proxySurfaceWriter.C
similarity index 53%
rename from src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.C
rename to src/surfMesh/writers/proxy/proxySurfaceWriter.C
index 2c83e3614c9e9e8f691ec5943314f02aa7d7b76d..1e8d20856b24dffc9ea658c5e34ef25d34b7adb6 100644
--- a/src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.C
+++ b/src/surfMesh/writers/proxy/proxySurfaceWriter.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011 OpenFOAM Foundation
@@ -28,78 +28,129 @@ License
 #include "proxySurfaceWriter.H"
 #include "MeshedSurfaceProxy.H"
 #include "OSspecific.H"
-#include "makeSurfaceWriterMethods.H"
+#include "surfaceWriterMethods.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    defineTypeNameAndDebug(proxySurfaceWriter, 0);
+namespace surfaceWriters
+{
+    defineTypeName(proxyWriter);
+}
 }
 
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::proxySurfaceWriter::proxySurfaceWriter(const word& fileExt)
+Foam::surfaceWriters::proxyWriter::proxyWriter(const word& fileExt)
 :
     surfaceWriter(),
     fileExtension_(fileExt)
 {}
 
 
-Foam::proxySurfaceWriter::proxySurfaceWriter
+Foam::surfaceWriters::proxyWriter::proxyWriter
 (
     const word& fileExt,
     const dictionary& options
 )
 :
-    surfaceWriter(),
+    surfaceWriter(options),
     fileExtension_(fileExt),
     options_(options)
 {}
 
 
+Foam::surfaceWriters::proxyWriter::proxyWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    proxyWriter(outputPath.ext(), options)
+{
+    surfaceWriter::open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::proxyWriter::proxyWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    proxyWriter(outputPath.ext(), options)
+{
+    surfaceWriter::open(points, faces, outputPath, parallel);
+}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::fileName Foam::proxySurfaceWriter::write
+void Foam::surfaceWriters::proxyWriter::open
 (
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
+    const fileName& outputPath
+)
+{
+    fileExtension_ = outputPath.ext();
+    surfaceWriter::open(outputPath);
+}
+
+
+Foam::fileName Foam::surfaceWriters::proxyWriter::write()
 {
+    checkOpen();
+
     // Avoid bad values
     if (fileExtension_.empty())
     {
         return fileName::null;
     }
 
-    fileName outputFile(outputDir/surfaceName + '.' + fileExtension_);
+    // Geometry:  rootdir/<TIME>/surfaceName.{extension}
 
-    if (!isDir(outputFile.path()))
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
     {
-        mkDir(outputFile.path());
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
     }
+    outputFile.ext(fileExtension_);
 
-    if (verbose)
+    if (verbose_)
     {
         Info<< "Writing geometry to " << outputFile << endl;
     }
 
-    MeshedSurfaceProxy<face>
-    (
-        surf.points(),
-        surf.faces()
-    ).write
-    (
-        outputFile,
-        fileExtension_,
-        options_
-    );
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        MeshedSurfaceProxy<face>(surf.points(), surf.faces()).write
+        (
+            outputFile,
+            fileExtension_,
+            options_
+        );
+    }
 
     return outputFile;
 }
 
 
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::proxyWriter);
+
+
 // ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.H b/src/surfMesh/writers/proxy/proxySurfaceWriter.H
similarity index 61%
rename from src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.H
rename to src/surfMesh/writers/proxy/proxySurfaceWriter.H
index bd3df1ad73b8ab6eef4ea6c2f8628037a8acd40e..383d4b3fc51a77ffc7049efd6c32a58577cbd9f6 100644
--- a/src/sampling/sampledSurface/writers/proxy/proxySurfaceWriter.H
+++ b/src/surfMesh/writers/proxy/proxySurfaceWriter.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011 OpenFOAM Foundation
@@ -24,11 +24,11 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Class
-    Foam::proxySurfaceWriter
+    Foam::surfaceWriters::proxyWriter
 
 Description
-    A surfaceWriter that writes the geometry via the MeshedSurfaceProxy, but
-    which does not support any fields.
+    A surfaceWriter that writes the geometry via the MeshedSurfaceProxy,
+    but which does not support any fields.
 
     \heading Output file locations
 
@@ -59,16 +59,18 @@ SourceFiles
 
 namespace Foam
 {
+namespace surfaceWriters
+{
 
 /*---------------------------------------------------------------------------*\
-                     Class proxySurfaceWriter Declaration
+                         Class proxyWriter Declaration
 \*---------------------------------------------------------------------------*/
 
-class proxySurfaceWriter
+class proxyWriter
 :
     public surfaceWriter
 {
-    // Private data
+    // Private Data
 
         //- The file extension associated with the proxy
         word fileExtension_;
@@ -80,46 +82,75 @@ class proxySurfaceWriter
 public:
 
     //- Runtime type information
-    TypeName("proxy");
+    TypeNameNoDebug("proxy");
 
 
     // Constructors
 
         //- Construct for a given extension
-        explicit proxySurfaceWriter(const word& fileExt);
+        explicit proxyWriter(const word& fileExt);
 
         //- Construct for a given extension, with some output options
-        proxySurfaceWriter(const word& fileExt, const dictionary& options);
+        proxyWriter(const word& fileExt, const dictionary& options);
+
+        //- Construct from components, taking extension from outputPath
+        proxyWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components, taking extension from outputPath
+        proxyWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
 
 
     //- Destructor
-    virtual ~proxySurfaceWriter() = default;
+    virtual ~proxyWriter() = default;
 
 
     // Member Functions
 
+    // Capability
 
-        //- True if the surface format supports geometry in a separate file.
-        //  False if geometry and field must be in a single file
+        //- A separate file is required for geometry.
         virtual bool separateGeometry() const
         {
             return true;
         }
 
 
-        //- Write single surface geometry to file.
-        virtual fileName write
-        (
-            const fileName& outputDir,      //!< output-dir
-            const fileName& surfaceName,    //!< Name of surface
-            const meshedSurf& surf,         //!< Surface geometry
-            const bool verbose = false      //!< Additional verbosity
-        ) const; // override
+    // Output
+
+        //- Open for output on specified path, using existing surface
+        virtual void open(const fileName& outputPath); // override
+
+
+    // Write
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
 };
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+} // End namespace surfaceWriters
 } // End namespace Foam
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/surfMesh/writers/raw/rawSurfaceWriter.C b/src/surfMesh/writers/raw/rawSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..2e79256aaa6a13126e461ce76512ded754cc8369
--- /dev/null
+++ b/src/surfMesh/writers/raw/rawSurfaceWriter.C
@@ -0,0 +1,185 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2016 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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 "rawSurfaceWriter.H"
+#include "OFstream.H"
+#include "OSspecific.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(rawWriter);
+    addToRunTimeSelectionTable(surfaceWriter, rawWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, rawWriter, wordDict);
+}
+}
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    // Emit x,y,z
+    static inline void writePoint(Foam::Ostream& os, const Foam::point& p)
+    {
+        os << p.x() << ' ' << p.y() << ' ' << p.z();
+    }
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Field writing implementation
+#include "rawSurfaceWriterImpl.C"
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::rawWriter);
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::rawWriter::rawWriter()
+:
+    surfaceWriter(),
+    writeCompression_(IOstream::UNCOMPRESSED)
+{}
+
+
+Foam::surfaceWriters::rawWriter::rawWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options),
+    writeCompression_
+    (
+        IOstream::compressionEnum
+        (
+            options.lookupOrDefault<word>("compression", "false")
+        )
+    )
+{}
+
+
+Foam::surfaceWriters::rawWriter::rawWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    rawWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::rawWriter::rawWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    rawWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::rawWriter::write()
+{
+    checkOpen();
+
+    // Geometry:  rootdir/<TIME>/surfaceName.raw
+
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
+    }
+    outputFile.ext("raw");
+
+    if (verbose_)
+    {
+        Info<< "Writing geometry to " << outputFile << endl;
+    }
+
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        const pointField& points = surf.points();
+        const faceList& faces = surf.faces();
+
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        OFstream os
+        (
+            outputFile,
+            IOstream::ASCII,
+            IOstream::currentVersion,
+            writeCompression_
+        );
+
+        // Header
+        {
+            os  << "# geometry NO_DATA " << faces.size() << nl
+                << "#  x  y  z" << nl;
+        }
+
+        // Write faces centres
+        for (const face& f : faces)
+        {
+            writePoint(os, f.centre(points));
+            os << nl;
+        }
+
+        os  << nl;
+    }
+
+    return outputFile;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/raw/rawSurfaceWriter.H b/src/surfMesh/writers/raw/rawSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..bac91063a6660e9fc924f479392b7796485af183
--- /dev/null
+++ b/src/surfMesh/writers/raw/rawSurfaceWriter.H
@@ -0,0 +1,172 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2016 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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::surfaceWriters::rawWriter
+
+Description
+    A surfaceWriter for raw output.
+
+    The formatOptions for raw:
+    \table
+        Property     | Description                          | Required | Default
+        compression  | on / off                             | no    | off
+    \endtable
+
+    For example,
+    \verbatim
+    formatOptions
+    {
+        raw
+        {
+            compression on;
+        }
+    }
+    \endverbatim
+
+    \heading Output file locations
+
+    The \c rootdir normally corresponds to something like
+    \c postProcessing/\<name\>
+
+    \subheading Geometry
+    \verbatim
+    rootdir
+    `-- timeName
+        `-- surfaceName.{raw}
+    \endverbatim
+
+    \subheading Fields
+    \verbatim
+    rootdir
+    `-- timeName
+        |-- <field0>_surfaceName.{raw}
+        `-- <field1>_surfaceName.{raw}
+    \endverbatim
+
+SourceFiles
+    rawSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef rawSurfaceWriter_H
+#define rawSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class rawWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class rawWriter
+:
+    public surfaceWriter
+{
+    // Private data
+
+        //- Output compression (default: uncompressed)
+        IOstream::compressionType writeCompression_;
+
+
+    // Private Member Functions
+
+        //- Templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("raw");
+
+
+    // Constructors
+
+        //- Construct null
+        rawWriter();
+
+        //- Construct with some output options
+        explicit rawWriter(const dictionary& options);
+
+        //- Construct from components
+        rawWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        rawWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~rawWriter() = default;
+
+
+    // Member Functions
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriterImpl.C b/src/surfMesh/writers/raw/rawSurfaceWriterImpl.C
similarity index 61%
rename from src/sampling/sampledSurface/writers/raw/rawSurfaceWriterImpl.C
rename to src/surfMesh/writers/raw/rawSurfaceWriterImpl.C
index 1735941782ac860c8a623cbc77ded16889a4fb84..662f24d7b63ee78239a24106f3449f801a85901f 100644
--- a/src/sampling/sampledSurface/writers/raw/rawSurfaceWriterImpl.C
+++ b/src/surfMesh/writers/raw/rawSurfaceWriterImpl.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2014 OpenFOAM Foundation
@@ -47,6 +47,13 @@ namespace Foam
     template<class Type>
     static inline void writeHeader(Ostream& os, const word& fieldName) {}
 
+    template<>
+    void writeHeader<label>(Ostream& os, const word& fieldName)
+    {
+        os  << "# x  y  z"
+            << "  " << fieldName << nl;
+    }
+
     template<>
     void writeHeader<scalar>(Ostream& os, const word& fieldName)
     {
@@ -101,69 +108,96 @@ namespace Foam
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-Foam::fileName Foam::rawSurfaceWriter::writeTemplate
+Foam::fileName Foam::surfaceWriters::rawWriter::writeTemplate
 (
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
     const word& fieldName,
-    const Field<Type>& values,
-    const bool isNodeValues,
-    const bool verbose
-) const
+    const Field<Type>& localValues
+)
 {
-    // field:  rootdir/time/<field>_surfaceName.raw
+    checkOpen();
 
-    const pointField& points = surf.points();
-    const faceList&    faces = surf.faces();
+    // Field:  rootdir/<TIME>/<field>_surfaceName.raw
 
-    if (!isDir(outputDir))
+    fileName outputFile = outputPath_.path();
+    if (useTimeDir() && !timeName().empty())
     {
-        mkDir(outputDir);
+        // Splice in time-directory
+        outputFile /= timeName();
     }
 
-    OFstream os(outputDir/fieldName + '_' + surfaceName + ".raw");
+    // Append <field>_surfaceName.raw
+    outputFile /= fieldName + '_' + outputPath_.name();
+    outputFile.ext("raw");
 
-    if (verbose)
+    if (verbose_)
     {
-        Info<< "Writing field " << fieldName << " to " << os.name() << endl;
+        Info<< "Writing field " << fieldName << " to " << outputFile << endl;
     }
 
-    // Header
-    os  << "# " << fieldName;
-    if (isNodeValues)
-    {
-        os  << "  POINT_DATA " << values.size() << nl;
-    }
-    else
-    {
-        os  << "  FACE_DATA " << values.size() << nl;
-    }
 
-    // Header
-    // #  x  y  z  field
-    writeHeader<Type>(os, fieldName);
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    const meshedSurf& surf = surface();
 
-    if (isNodeValues)
+    if (Pstream::master() || !parallel_)
     {
-        // Node values
-        forAll(values, elemi)
+        const auto& values = tfield();
+        const pointField& points = surf.points();
+        const faceList& faces = surf.faces();
+
+        if (!isDir(outputFile.path()))
         {
-            writePoint(os, points[elemi]);
-            writeData(os, values[elemi]);
+            mkDir(outputFile.path());
         }
-    }
-    else
-    {
-        // Face values
-        forAll(values, elemi)
+
+        OFstream os
+        (
+            outputFile,
+            IOstream::ASCII,
+            IOstream::currentVersion,
+            writeCompression_
+        );
+
+        // Header
+        {
+            os  << "# " << fieldName;
+            if (this->isPointData())
+            {
+                os  << "  POINT_DATA ";
+            }
+            else
+            {
+                os << "  FACE_DATA ";
+            }
+            os << values.size() << nl;
+
+            // #  x  y  z  field
+            writeHeader<Type>(os, fieldName);
+        }
+
+
+        if (this->isPointData())
+        {
+            // Node values
+            forAll(values, elemi)
+            {
+                writePoint(os, points[elemi]);
+                writeData(os, values[elemi]);
+            }
+        }
+        else
         {
-            writePoint(os, faces[elemi].centre(points));
-            writeData(os, values[elemi]);
+            // Face values
+            forAll(values, elemi)
+            {
+                writePoint(os, faces[elemi].centre(points));
+                writeData(os,  values[elemi]);
+            }
         }
     }
 
-    return os.name();
+    return outputFile;
 }
 
 
diff --git a/src/surfMesh/writers/starcd/starcdSurfaceWriter.C b/src/surfMesh/writers/starcd/starcdSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..75412a20aecb5adeb130cd85f8e0769d280c62a7
--- /dev/null
+++ b/src/surfMesh/writers/starcd/starcdSurfaceWriter.C
@@ -0,0 +1,217 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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 "starcdSurfaceWriter.H"
+#include "OFstream.H"
+#include "OSspecific.H"
+#include "MeshedSurfaceProxy.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(starcdWriter);
+    addToRunTimeSelectionTable(surfaceWriter, starcdWriter, word);
+}
+}
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    // Emit each component
+    template<class Type>
+    static inline void writeData(Ostream& os, const Type& val)
+    {
+        const direction ncmpt = pTraits<Type>::nComponents;
+        for (direction cmpt=0; cmpt < ncmpt; ++cmpt)
+        {
+            os  << ' ' << component(val, cmpt);
+        }
+        os  << nl;
+    }
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::starcdWriter::starcdWriter()
+:
+    surfaceWriter()
+{}
+
+
+Foam::surfaceWriters::starcdWriter::starcdWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options)
+{}
+
+
+Foam::surfaceWriters::starcdWriter::starcdWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    starcdWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::starcdWriter::starcdWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    starcdWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::starcdWriter::write()
+{
+    checkOpen();
+
+    // Geometry:  rootdir/<TIME>/surfaceName.{inp,cel,vrt}
+
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
+    }
+    outputFile.ext("inp");
+
+    if (verbose_)
+    {
+        Info<< "Writing geometry to " << outputFile << endl;
+    }
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        MeshedSurfaceProxy<face>
+        (
+            surf.points(),
+            surf.faces()
+        ).write(outputFile, "inp");
+    }
+
+    return outputFile;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::starcdWriter::writeTemplate
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    // Field:  rootdir/<TIME>/<field>_surfaceName.usr
+
+    fileName outputFile = outputPath_.path();
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile /= timeName();
+    }
+
+    // Append <field>_surfaceName.usr
+    outputFile /= fieldName + '_' + outputPath_.name();
+    outputFile.ext("usr");
+
+    if (verbose_)
+    {
+        Info<< "Writing field " << fieldName << " to " << outputFile << endl;
+    }
+
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    if (Pstream::master() || !parallel_)
+    {
+        const auto& values = tfield();
+
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        OFstream os(outputFile);
+
+        // 1-based ids
+        label elemId = 1;
+
+        // No header, just write values
+        for (const Type& val : values)
+        {
+            os  << elemId;
+            writeData(os, val);
+
+            ++elemId;
+        }
+    }
+
+    return outputFile;
+}
+
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::starcdWriter);
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/starcd/starcdSurfaceWriter.H b/src/surfMesh/writers/starcd/starcdSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..034725d1261b875d790a15b26ac14d6f5d0a3dc5
--- /dev/null
+++ b/src/surfMesh/writers/starcd/starcdSurfaceWriter.H
@@ -0,0 +1,170 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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::surfaceWriters::starcdWriter
+
+Description
+    A surfaceWriter for STARCD files.
+
+    The geometry is written via the MeshedSurfaceProxy, the fields
+    are written in a trivial ASCII format with ID and VALUE as
+    so-called user data. These \c .usr files can be read into proSTAR
+    with these types of commands. For element data:
+     \verbatim
+        getuser FILENAME.usr cell scalar free
+        getuser FILENAME.usr cell vector free
+    \endverbatim
+    and for vertex data:
+    \verbatim
+        getuser FILENAME.usr vertex scalar free
+        getuser FILENAME.usr vertex vector free
+    \endverbatim
+
+    \heading Output file locations
+
+    The \c rootdir normally corresponds to something like
+    \c postProcessing/\<name\>
+
+    \subheading Geometry
+    \verbatim
+    rootdir
+    `-- timeName
+        `-- surfaceName.{cel,vrt,inp}
+    \endverbatim
+
+    \subheading Fields
+    \verbatim
+    rootdir
+    `-- timeName
+        |-- <field0>_surfaceName.{usr}
+        `-- <field1>_surfaceName.{usr}
+    \endverbatim
+
+SourceFiles
+    starcdSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef starcdSurfaceWriter_H
+#define starcdSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class starcdWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class starcdWriter
+:
+    public surfaceWriter
+{
+    // Private Member Functions
+
+        //- Templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("starcd");
+
+
+    // Constructors
+
+        //- Construct null
+        starcdWriter();
+
+        //- Construct with some output options
+        explicit starcdWriter(const dictionary& options);
+
+        //- Construct from components
+        starcdWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        starcdWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~starcdWriter() = default;
+
+
+    // Member Functions
+
+        //- True if the surface format supports geometry in a separate file.
+        //  False if geometry and field must be in a single file
+        virtual bool separateGeometry() const
+        {
+            return true;
+        }
+
+        //- Write single surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/surfaceWriter.C b/src/surfMesh/writers/surfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..625a82505622949497f686dedf4846f828ccf68a
--- /dev/null
+++ b/src/surfMesh/writers/surfaceWriter.C
@@ -0,0 +1,458 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "surfaceWriter.H"
+#include "proxySurfaceWriter.H"
+#include "MeshedSurfaceProxy.H"
+
+#include "Time.H"
+#include "globalIndex.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(surfaceWriter, 0);
+    defineRunTimeSelectionTable(surfaceWriter, word);
+    defineRunTimeSelectionTable(surfaceWriter, wordDict);
+}
+
+Foam::scalar Foam::surfaceWriter::defaultMergeDim = 1e-8;
+
+const Foam::meshedSurf::emptySurface Foam::surfaceWriter::emptySurface_;
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+bool Foam::surfaceWriter::supportedType(const word& writeType)
+{
+    return
+    (
+        wordConstructorTablePtr_->found(writeType)
+     || wordDictConstructorTablePtr_->found(writeType)
+     || MeshedSurfaceProxy<face>::canWriteType(writeType)
+    );
+}
+
+
+Foam::autoPtr<Foam::surfaceWriter>
+Foam::surfaceWriter::New(const word& writeType)
+{
+    // Constructors without dictionary options
+    auto cstrIter = wordConstructorTablePtr_->cfind(writeType);
+
+    if (!cstrIter.found())
+    {
+        if (MeshedSurfaceProxy<face>::canWriteType(writeType))
+        {
+            // Generally unknown, but handle via 'proxy' handler
+            return autoPtr<surfaceWriter>
+            (
+                new surfaceWriters::proxyWriter(writeType)
+            );
+        }
+
+        FatalErrorInFunction
+            << "Unknown write type \"" << writeType << "\"\n\n"
+            << "Valid write types : "
+            << flatOutput(wordConstructorTablePtr_->sortedToc()) << nl
+            << "Valid proxy types : "
+            << MeshedSurfaceProxy<face>::writeTypes() << endl
+            << exit(FatalError);
+    }
+
+    return autoPtr<surfaceWriter>(cstrIter()());
+}
+
+
+Foam::autoPtr<Foam::surfaceWriter>
+Foam::surfaceWriter::New
+(
+    const word& writeType,
+    const dictionary& writeOpts
+)
+{
+    // Constructors with dictionary options
+    auto cstrIter2 = wordDictConstructorTablePtr_->cfind(writeType);
+
+    if (cstrIter2.found())
+    {
+        return autoPtr<surfaceWriter>(cstrIter2()(writeOpts));
+    }
+
+    // Constructors without dictionary options
+    auto cstrIter = wordConstructorTablePtr_->cfind(writeType);
+
+    if (!cstrIter.found())
+    {
+        if (MeshedSurfaceProxy<face>::canWriteType(writeType))
+        {
+            // Generally unknown, but handle via 'proxy' handler
+            return autoPtr<surfaceWriter>
+            (
+                new surfaceWriters::proxyWriter(writeType, writeOpts)
+            );
+        }
+
+        FatalErrorInFunction
+            << "Unknown write type \"" << writeType << "\"\n\n"
+            << "Valid write types : "
+            << wordConstructorTablePtr_->sortedToc() << nl
+            << "Valid proxy types : "
+            << MeshedSurfaceProxy<face>::writeTypes() << endl
+            << exit(FatalError);
+    }
+
+    return autoPtr<surfaceWriter>(cstrIter()());
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriter::surfaceWriter()
+:
+    surf_(std::cref<meshedSurf>(emptySurface_)),
+    upToDate_(false),
+    parallel_(true),
+    useTimeDir_(false),
+    isPointData_(false),
+    verbose_(false),
+    nFields_(0),
+    mergeDim_(defaultMergeDim),
+    merged_(),
+    currTime_(),
+    outputPath_()
+{
+    surfaceWriter::close();
+}
+
+
+Foam::surfaceWriter::surfaceWriter(const dictionary& options)
+:
+    surfaceWriter()
+{
+    options.readIfPresent("verbose", verbose_);
+}
+
+
+Foam::surfaceWriter::surfaceWriter
+(
+    const meshedSurf& surf,
+    bool parallel,
+    const dictionary& options
+)
+:
+    surfaceWriter(options)
+{
+    setSurface(surf, parallel);
+}
+
+
+Foam::surfaceWriter::surfaceWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    bool parallel,
+    const dictionary& options
+)
+:
+    surfaceWriter(options)
+{
+    setSurface(points, faces, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::surfaceWriter::setTime(const instant& inst)
+{
+    currTime_ = inst;
+}
+
+
+void Foam::surfaceWriter::setTime(scalar timeValue)
+{
+    currTime_ = instant(timeValue);
+}
+
+
+void Foam::surfaceWriter::setTime(scalar timeValue, const word& timeName)
+{
+    currTime_.value() = timeValue;
+    currTime_.name() = timeName;
+}
+
+
+void Foam::surfaceWriter::unsetTime()
+{
+    currTime_.value() = 0;
+    currTime_.name().clear();
+}
+
+
+void Foam::surfaceWriter::beginTime(const Time& t)
+{
+    setTime(t.value(), t.timeName());
+}
+
+
+void Foam::surfaceWriter::beginTime(const instant& inst)
+{
+    setTime(inst);
+}
+
+
+void Foam::surfaceWriter::endTime()
+{
+    unsetTime();
+}
+
+
+void Foam::surfaceWriter::open(const fileName& outputPath)
+{
+    outputPath_ = outputPath;
+}
+
+
+void Foam::surfaceWriter::open
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel
+)
+{
+    close();
+    setSurface(surf, parallel);
+    open(outputPath);
+}
+
+
+void Foam::surfaceWriter::open
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel
+)
+{
+    close();
+    setSurface(points, faces, parallel);
+    open(outputPath);
+}
+
+
+void Foam::surfaceWriter::close()
+{
+    outputPath_.clear();
+}
+
+
+void Foam::surfaceWriter::clear()
+{
+    close();
+    expire();
+    surf_ = std::cref<meshedSurf>(emptySurface_);
+}
+
+
+void Foam::surfaceWriter::setSurface
+(
+    const meshedSurf& surf,
+    bool parallel
+)
+{
+    expire();
+    surf_ = std::cref<meshedSurf>(surf);
+}
+
+
+void Foam::surfaceWriter::setSurface
+(
+    const pointField& points,
+    const faceList& faces,
+    bool parallel
+)
+{
+    expire();
+    setSurface(meshedSurfRef(points, faces), parallel);
+}
+
+
+bool Foam::surfaceWriter::needsUpdate() const
+{
+    return !upToDate_;
+}
+
+
+bool Foam::surfaceWriter::expire()
+{
+    const bool changed = upToDate_;
+
+    upToDate_ = false;
+    nFields_ = 0;
+    merged_.clear();
+
+    return changed;
+}
+
+
+bool Foam::surfaceWriter::hasSurface() const
+{
+    return (&emptySurface_ != &(surf_.get()));
+}
+
+
+bool Foam::surfaceWriter::empty() const
+{
+    const bool value = surf_.get().faces().empty();
+
+    return (parallel_ ? returnReduce(value, andOp<bool>()) : value);
+}
+
+
+Foam::label Foam::surfaceWriter::size() const
+{
+    const label value = surf_.get().faces().size();
+
+    return (parallel_ ? returnReduce(value, sumOp<label>()) : value);
+}
+
+
+bool Foam::surfaceWriter::checkOpen() const
+{
+    if (outputPath_.empty())
+    {
+        FatalErrorInFunction
+            << type() << " : Attempted to write without a path" << nl
+            << exit(FatalIOError);
+    }
+
+    return !outputPath_.empty();
+}
+
+
+bool Foam::surfaceWriter::merge() const
+{
+    bool changed = false;
+
+    if (parallel_ && Pstream::parRun() && !upToDate_)
+    {
+        changed = merged_.merge(surf_.get(), mergeDim_);
+    }
+    upToDate_ = true;
+
+    return changed;
+}
+
+
+const Foam::meshedSurf& Foam::surfaceWriter::surface() const
+{
+    merge();
+
+    if (parallel_ && Pstream::parRun())
+    {
+        return merged_;
+    }
+
+    return surf_.get();
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::tmp<Foam::Field<Type>> Foam::surfaceWriter::mergeFieldTemplate
+(
+    const Field<Type>& fld
+) const
+{
+    if (parallel_ && Pstream::parRun())
+    {
+        // Ensure geometry is also merged
+        merge();
+
+        // Gather all values
+        auto tfield = tmp<Field<Type>>::New();
+        auto& allFld = tfield.ref();
+
+        globalIndex::gatherOp(fld, allFld);
+
+        // Renumber (point data) to correspond to merged points
+        if (Pstream::master() && this->isPointData())
+        {
+            inplaceReorder(merged_.pointsMap(), allFld);
+            allFld.resize(merged_.points().size());
+        }
+
+        return tfield;
+    }
+
+    // Mark that any geometry changes have been taken care of
+    upToDate_ = true;
+
+    return fld;
+}
+
+
+#define defineSurfaceWriterMergeMethod(ThisClass, Type)                        \
+    Foam::tmp<Foam::Field<Type>>                                               \
+    ThisClass::mergeField(const Field<Type>& fld) const                        \
+    {                                                                          \
+        return mergeFieldTemplate(fld);                                        \
+    }
+
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::label);
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::scalar);
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::vector);
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::sphericalTensor);
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::symmTensor);
+defineSurfaceWriterMergeMethod(Foam::surfaceWriter, Foam::tensor)
+
+#undef defineSurfaceWriterMergeMethod
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::operator<<
+(
+    Ostream& os,
+    const InfoProxy<surfaceWriter>& ip
+)
+{
+    const surfaceWriter& w = ip.t_;
+
+    os  << "surfaceWriter:"
+        << " upToDate: " << w.upToDate_
+        << " PointData: " << w.isPointData_
+        << " nFields: " << w.nFields_
+        << " time: " << w.currTime_
+        << " path: " << w.outputPath_ << endl;
+
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/surfaceWriter.H b/src/surfMesh/writers/surfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..47ff76137161da8362afb8ff1323f0a14040687c
--- /dev/null
+++ b/src/surfMesh/writers/surfaceWriter.H
@@ -0,0 +1,491 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011-2012 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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/>.
+
+Namespace
+    Foam::surfaceWriters
+
+Description
+    Namespace for surface writers
+
+Class
+    Foam::surfaceWriter
+
+Description
+    Base class for surface writers.
+
+    The surfaceWriter interface is rather large since we need a writer that
+    can either be initially defined without a surface association
+    and have that added at a later stage, or be defined with a surface
+    association.
+
+    \verbatim
+    formatOptions
+    {
+        someFormat // Eg, ensight, vtk, etc
+        {
+            verbose         true;
+        }
+    }
+    \endverbatim
+
+  Format options:
+    \table
+        Property | Description                             | Required | Default
+        verbose  | Additional output verbosity             | no  | no
+    \endtable
+
+SourceFiles
+    sampledWriter.C
+    sampledWriterI.H
+    sampledWriterImpl.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef surfaceWriter_H
+#define surfaceWriter_H
+
+#include <functional>
+#include "typeInfo.H"
+#include "autoPtr.H"
+#include "tmp.H"
+#include "Field.H"
+#include "fileName.H"
+#include "instant.H"
+#include "mergedSurf.H"
+#include "meshedSurfRef.H"
+#include "InfoProxy.H"
+#include "runTimeSelectionTables.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declarations
+class Time;
+class surfaceWriter;
+
+Ostream& operator<<(Ostream& os, const InfoProxy<surfaceWriter>& ip);
+
+
+/*---------------------------------------------------------------------------*\
+                        Class surfaceWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class surfaceWriter
+{
+protected:
+
+   // Static data members
+
+        //- Placeholder
+        static const meshedSurf::emptySurface emptySurface_;
+
+
+    // Protected data
+
+        //- Reference to the surface
+        std::reference_wrapper<const meshedSurf> surf_;
+
+        //- The topology/surface is up-to-date?
+        mutable bool upToDate_;
+
+        //- Writing in parallel (vai master)
+        bool parallel_;
+
+        //- Writer should do something funny
+        bool useTimeDir_;
+
+        //- Is point vs cell data
+        bool isPointData_;
+
+        //- Additional output verbosity
+        bool verbose_;
+
+        //- The number of fields
+        label nFields_;
+
+        //- Dimension for merging
+        scalar mergeDim_;
+
+        //- Merging information and the resulting merged surface (parallel)
+        mutable mergedSurf merged_;
+
+        //- The current time value/name
+        instant currTime_;
+
+        //- The full output directory and file (surface) name
+        fileName outputPath_;
+
+
+    // Protected Member Functions
+
+        //- Verify that the outputPath_ has been set or FatalError
+        bool checkOpen() const;
+
+        //- Merge surfaces if they are not already upToDate (parallel)
+        //- or simply mark the surface as being up-to-date
+        virtual bool merge() const;
+
+        //- Merge surfaces (if not upToDate) and return merged or
+        //- the regular surface
+        const meshedSurf& surface() const;
+
+
+        //- Gather (merge) fields with renumbering and shrinking for point data
+        template<class Type>
+        tmp<Field<Type>> mergeFieldTemplate(const Field<Type>& fld) const;
+
+#undef  declareSurfaceWriterMergeMethod
+#define declareSurfaceWriterMergeMethod(Type)                                  \
+        tmp<Field<Type>> mergeField(const Field<Type>& fld) const;
+
+        declareSurfaceWriterMergeMethod(label);
+        declareSurfaceWriterMergeMethod(scalar);
+        declareSurfaceWriterMergeMethod(vector);
+        declareSurfaceWriterMergeMethod(sphericalTensor);
+        declareSurfaceWriterMergeMethod(symmTensor);
+        declareSurfaceWriterMergeMethod(tensor);
+
+        #undef declareSurfaceWriterMergeMethod
+
+        //- Dummy templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        )
+        {
+            return fileName::null;
+        }
+
+
+public:
+
+    // Public data
+
+        //- The default merge dimension (1e-8)
+        static scalar defaultMergeDim;
+
+
+    //- Runtime type information
+    TypeName("surfaceWriter");
+
+
+        // Declare run-time constructor selection table
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            surfaceWriter,
+            word,
+            (),
+            ()
+        );
+
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            surfaceWriter,
+            wordDict,
+            (
+                const dictionary& writeOpts
+            ),
+            (writeOpts)
+        );
+
+
+    // Selectors
+
+        //- True if New is likely to succeed for this writeType
+        static bool supportedType(const word& writeType);
+
+        //- Return a reference to the selected surfaceWriter
+        static autoPtr<surfaceWriter> New(const word& writeType);
+
+        //- Return a reference to the selected surfaceWriter
+        //  Select with extra write option
+        static autoPtr<surfaceWriter> New
+        (
+            const word& writeType,
+            const dictionary& writeOptions
+        );
+
+
+    // Constructors
+
+        //- Construct null
+        surfaceWriter();
+
+        //- Construct null with specified options
+        explicit surfaceWriter(const dictionary& options);
+
+        //- Construct from components
+        explicit surfaceWriter
+        (
+            const meshedSurf& surf,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        surfaceWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~surfaceWriter() = default;
+
+
+    // Member Functions
+
+    // Capability
+
+        //- The writer is enabled. If the writer is not enabled, it may be
+        //- possible for the caller to skip various preparatory operations.
+        //  This method is primarily useful for the null writer
+        virtual bool enabled() const
+        {
+            return true;
+        }
+
+        //- True if the surface format requires geometry in a separate file.
+        virtual bool separateGeometry() const
+        {
+            return false;
+        }
+
+
+    // Bookkeeping
+
+        //- Does the writer need an update (eg, lagging behind surface changes)
+        virtual bool needsUpdate() const;
+
+        //- Mark that surface changed and the writer will need an update,
+        //- and set nFields = 0.
+        //  May also free up unneeded data.
+        //  Return false if it was previously already expired.
+        virtual bool expire();
+
+        //- Close any open output, remove association with a surface and
+        //- expire the writer. The parallel flag remains untouched.
+        virtual void clear();
+
+
+    // Surface association
+
+        //- Change association with a surface and expire the writer
+        virtual void setSurface
+        (
+            const meshedSurf& surf,
+            bool parallel = Pstream::parRun()
+        );
+
+        //- Change association with a surface and expire the writer
+        virtual void setSurface
+        (
+            const pointField& points,
+            const faceList& faces,
+            bool parallel = Pstream::parRun()
+        );
+
+
+    // Queries, Access
+
+        //- Writer is associated with a surface
+        bool hasSurface() const;
+
+        //- The surface to write is empty if the global number of faces is zero
+        bool empty() const;
+
+        //- The global number of faces for the associated surface
+        label size() const;
+
+        //- The number of expected output fields.
+        //  Currently only used by the legacy VTK format.
+        inline label nFields() const;
+
+        //- The number of expected output fields.
+        //  Currently only used by the legacy VTK format.
+        inline label& nFields();
+
+        //- Are the field data to be treated as point data?
+        inline bool isPointData() const;
+
+        //- Set handling of field data to face/point data.
+        inline bool& isPointData();
+
+        //- Should a time directory be spliced into the output path?
+        inline bool useTimeDir() const;
+
+        //- Change handling of spliced output path.
+        inline bool& useTimeDir();
+
+        //- Get output verbosity
+        inline bool verbose() const;
+
+        //- Set output verbosity
+        inline bool& verbose();
+
+        //- The current value of the point merge dimension (metre)
+        inline scalar mergeDim() const;
+
+        //- The current value of the point merge dimension (metre)
+        inline scalar& mergeDim();
+
+
+    // Time
+
+        //- True if there is a known time
+        inline bool hasTime() const;
+
+        //- The current time value/name
+        inline const word& timeName() const;
+
+        //- The current time value/name
+        inline scalar timeValue() const;
+
+
+        //- Set the current time
+        void setTime(const instant& inst);
+
+        //- Set current time from timeValue, auto generating the name
+        void setTime(scalar timeValue);
+
+        //- Set current time from timeValue and timeName
+        void setTime(scalar timeValue, const word& timeName);
+
+        //- Clear the current time
+        void unsetTime();
+
+
+        //- Begin a time-step
+        virtual void beginTime(const Time& t);
+
+        //- Begin a time-step
+        virtual void beginTime(const instant& inst);
+
+        //- End a time-step
+        virtual void endTime();
+
+
+    // Output
+
+        //- Open for output on specified path, using existing surface
+        virtual void open(const fileName& outputPath);
+
+        //- Open from components
+        virtual void open
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun()
+        );
+
+        //- Open from components
+        virtual void open
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun()
+        );
+
+        //- Finish output, performing any necessary cleanup
+        virtual void close();
+
+
+    // Write
+
+        //- Write single surface geometry to file.
+        virtual fileName writeGeometry() const
+        {
+            return fileName::null;
+        }
+
+
+#undef  declareSurfaceWriterWriteMethod
+#define declareSurfaceWriterWriteMethod(Type)                                  \
+        /**! Write field of Type (per face or vertex) */                       \
+        virtual fileName write                                                 \
+        (                                                                      \
+            const word& fieldName,          /*!< Name of field */              \
+            const Field<Type>& values       /*!< Field values to write */      \
+        ) = 0
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+
+
+#undef  declareSurfaceWriterWriteMethod
+#define declareSurfaceWriterWriteMethod(Type)                                  \
+                                                                               \
+        /**! Write field of Type (per face or vertex) */                       \
+        virtual fileName write                                                 \
+        (                                                                      \
+            const word& fieldName,          /*!< Name of field */              \
+            const Field<Type>& values       /*!< Field values to write */      \
+        )   // override
+
+
+    // Other IO
+
+        //- Return info proxy.
+        virtual InfoProxy<surfaceWriter> info() const
+        {
+            return *this;
+        }
+
+        //- Output info proxy
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const InfoProxy<surfaceWriter>& ip
+        );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "surfaceWriterI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.C b/src/surfMesh/writers/surfaceWriterI.H
similarity index 50%
rename from src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.C
rename to src/surfMesh/writers/surfaceWriterI.H
index 1d7c37eab64ed5776d9ff4f41a88b194517c240d..0b62b6e04f2ac02867bb6cf95059b6deb57039cc 100644
--- a/src/sampling/sampledSurface/writers/starcd/starcdSurfaceWriter.C
+++ b/src/surfMesh/writers/surfaceWriterI.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -25,56 +23,83 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "starcdSurfaceWriter.H"
-#include "MeshedSurfaceProxy.H"
-#include "OFstream.H"
-#include "OSspecific.H"
-#include "makeSurfaceWriterMethods.H"
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+inline Foam::label Foam::surfaceWriter::nFields() const
+{
+    return nFields_;
+}
 
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-namespace Foam
+inline Foam::label& Foam::surfaceWriter::nFields()
 {
-    makeSurfaceWriterType(starcdSurfaceWriter);
+    return nFields_;
 }
 
-// Field writing implementation
-#include "starcdSurfaceWriterImpl.C"
 
-// Field writing methods
-defineSurfaceWriterWriteFields(Foam::starcdSurfaceWriter);
+inline bool Foam::surfaceWriter::isPointData() const
+{
+    return isPointData_;
+}
 
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+inline bool& Foam::surfaceWriter::isPointData()
+{
+    return isPointData_;
+}
+
+
+inline bool Foam::surfaceWriter::useTimeDir() const
+{
+    return useTimeDir_;
+}
+
+
+inline bool& Foam::surfaceWriter::useTimeDir()
+{
+    return useTimeDir_;
+}
+
 
-Foam::fileName Foam::starcdSurfaceWriter::write
-(
-    const fileName& outputDir,
-    const fileName& surfaceName,
-    const meshedSurf& surf,
-    const bool verbose
-) const
+inline bool Foam::surfaceWriter::verbose() const
 {
-    // geometry:  rootdir/time/surfaceName.{raw,vrt,inp}
+    return verbose_;
+}
+
+
+inline bool& Foam::surfaceWriter::verbose()
+{
+    return verbose_;
+}
 
-    fileName outputFile(outputDir/surfaceName + ".inp");
 
-    if (verbose)
-    {
-        Info<< "Writing geometry to " << outputFile << endl;
-    }
+inline Foam::scalar Foam::surfaceWriter::mergeDim() const
+{
+    return mergeDim_;
+}
+
 
-    if (!isDir(outputFile.path()))
-    {
-        mkDir(outputFile.path());
-    }
+inline Foam::scalar& Foam::surfaceWriter::mergeDim()
+{
+    return mergeDim_;
+}
 
-    MeshedSurfaceProxy<face>(surf.points(), surf.faces()).write
-    (
-        outputFile
-    );
 
-    return outputFile;
+inline bool Foam::surfaceWriter::hasTime() const
+{
+    return currTime_.name().size();
+}
+
+
+inline const Foam::word& Foam::surfaceWriter::timeName() const
+{
+    return currTime_.name();
+}
+
+
+inline Foam::scalar Foam::surfaceWriter::timeValue() const
+{
+    return currTime_.name().empty() ? 0 : currTime_.value();
 }
 
 
diff --git a/src/sampling/sampledSurface/writers/makeSurfaceWriterMethods.H b/src/surfMesh/writers/surfaceWriterMethods.H
similarity index 54%
rename from src/sampling/sampledSurface/writers/makeSurfaceWriterMethods.H
rename to src/surfMesh/writers/surfaceWriterMethods.H
index 5a0569b1b9b390d6e0cf28922993dccb28800185..b4672fafa86c2b11bcea52c416c6d743aba13de4 100644
--- a/src/sampling/sampledSurface/writers/makeSurfaceWriterMethods.H
+++ b/src/surfMesh/writers/surfaceWriterMethods.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2011-2016 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -24,56 +22,35 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 InClass
-    Foam::makeSurfaceWriterMethods
+    Foam::surfaceWriterMethods
 
 Description
-    Convenience macros for instantiating writer methods for surfaceWriter
-    classes.
+    Convenience macros for instantiating surfaceWriter methods.
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef makeSurfaceWriterMethods_H
-#define makeSurfaceWriterMethods_H
-
-#include "surfaceWriter.H"
-#include "addToRunTimeSelectionTable.H"
+#ifndef surfaceWriterMethods_H
+#define surfaceWriterMethods_H
 
 namespace Foam
 {
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-#define makeSurfaceWriterType(ThisClass)                                       \
-    defineTypeNameAndDebug(ThisClass, 0);                                      \
-    addToRunTimeSelectionTable(surfaceWriter, ThisClass, word)
-
-
+// Instantiate templated method for standard types
 #define defineSurfaceWriterWriteField(ThisClass, FieldType)                    \
     Foam::fileName ThisClass::write                                            \
     (                                                                          \
-        const fileName& outputDir,                                             \
-        const fileName& surfaceName,                                           \
-        const meshedSurf& surf,                                                \
         const word& fieldName,                                                 \
-        const Field<FieldType>& values,                                        \
-        const bool isNodeValues,                                               \
-        const bool verbose                                                     \
-    ) const                                                                    \
+        const Field<FieldType>& values                                         \
+    )                                                                          \
     {                                                                          \
-        return writeTemplate                                                   \
-        (                                                                      \
-            outputDir,                                                         \
-            surfaceName,                                                       \
-            surf,                                                              \
-            fieldName,                                                         \
-            values,                                                            \
-            isNodeValues,                                                      \
-            verbose                                                            \
-        );                                                                     \
+        return writeTemplate(fieldName, values);                               \
     }
 
 
 #define defineSurfaceWriterWriteFields(ThisClass)                              \
+    defineSurfaceWriterWriteField(ThisClass, label);                           \
     defineSurfaceWriterWriteField(ThisClass, scalar);                          \
     defineSurfaceWriterWriteField(ThisClass, vector);                          \
     defineSurfaceWriterWriteField(ThisClass, sphericalTensor);                 \
diff --git a/src/surfMesh/writers/vtk/vtkSurfaceWriter.C b/src/surfMesh/writers/vtk/vtkSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..65fd1b122e5d71e037bb542f8acc42e0f4709636
--- /dev/null
+++ b/src/surfMesh/writers/vtk/vtkSurfaceWriter.C
@@ -0,0 +1,291 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "vtkSurfaceWriter.H"
+#include "foamVtkSurfaceWriter.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(vtkWriter);
+    addToRunTimeSelectionTable(surfaceWriter, vtkWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, vtkWriter, wordDict);
+
+    // Accept vtp ending as well
+    addNamedToRunTimeSelectionTable
+    (
+        surfaceWriter,
+        vtkWriter,
+        word,
+        vtp
+    );
+    addNamedToRunTimeSelectionTable
+    (
+        surfaceWriter,
+        vtkWriter,
+        wordDict,
+        vtp
+    );
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::vtkWriter::vtkWriter()
+:
+    surfaceWriter(),
+    fmtType_(static_cast<unsigned>(vtk::formatType::INLINE_BASE64)),
+    precision_(IOstream::defaultPrecision()),
+    writer_(nullptr)
+{}
+
+
+Foam::surfaceWriters::vtkWriter::vtkWriter
+(
+    const vtk::outputOptions& opts
+)
+:
+    surfaceWriter(),
+    fmtType_(static_cast<unsigned>(opts.fmt())),
+    precision_(opts.precision()),
+    writer_(nullptr)
+{}
+
+
+Foam::surfaceWriters::vtkWriter::vtkWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options),
+    fmtType_(static_cast<unsigned>(vtk::formatType::INLINE_BASE64)),
+    precision_
+    (
+        options.lookupOrDefaultCompat
+        (
+            "precision", {{"writePrecision", 1806}},
+            IOstream::defaultPrecision()
+        )
+    ),
+    writer_(nullptr)
+{
+    // format: ascii | binary
+    // legacy: true | false
+
+    vtk::outputOptions opts(vtk::formatType::INLINE_BASE64);
+
+    const word formatName = options.lookupOrDefault<word>("format", "");
+    if (formatName.size())
+    {
+        opts.ascii
+        (
+            IOstream::formatEnum(formatName) == IOstream::ASCII
+        );
+    }
+
+    if (options.lookupOrDefault("legacy", false))
+    {
+        opts.legacy(true);
+    }
+
+    // Convert back to raw data type
+    fmtType_ = static_cast<unsigned>(opts.fmt());
+}
+
+
+Foam::surfaceWriters::vtkWriter::vtkWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    vtkWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::vtkWriter::vtkWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    vtkWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::surfaceWriters::vtkWriter::close()
+{
+    writer_.clear();
+    surfaceWriter::close();
+}
+
+
+void Foam::surfaceWriters::vtkWriter::beginTime(const Time& t)
+{
+    writer_.clear();
+    surfaceWriter::beginTime(t);
+}
+
+
+void Foam::surfaceWriters::vtkWriter::beginTime(const instant& inst)
+{
+    writer_.clear();
+    surfaceWriter::beginTime(inst);
+}
+
+
+void Foam::surfaceWriters::vtkWriter::endTime()
+{
+    writer_.clear();
+    surfaceWriter::endTime();
+}
+
+
+Foam::fileName Foam::surfaceWriters::vtkWriter::write()
+{
+    checkOpen();
+
+    if (needsUpdate())
+    {
+        writer_.clear();
+    }
+    merge();
+
+    // From raw unsigned values to vtk::outputOptions
+    vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_), precision_);
+
+
+    // Geometry:  rootdir/<TIME>/surfaceName.{vtk|vtp}
+
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
+    }
+    outputFile.ext(vtk::surfaceWriter::ext(opts));
+
+    if (verbose_)
+    {
+        Info<< "Writing geometry to " << outputFile << endl;
+    }
+
+    const meshedSurf& surf = surface();
+
+    if (writer_.empty() && (Pstream::master() || !parallel_))
+    {
+        writer_.reset
+        (
+            new vtk::surfaceWriter
+            (
+                surf.points(),
+                surf.faces(),
+                opts,
+                outputFile,
+                false  // serial!
+            )
+        );
+
+        if (this->hasTime())
+        {
+            // Time name in title
+            writer_->setTime(currTime_);
+            writer_->writeTimeValue();
+        }
+        else
+        {
+            // Surface name in title
+            writer_->beginFile(outputPath_.nameLessExt());
+        }
+
+        writer_->writeGeometry();
+    }
+
+    return outputFile;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::vtkWriter::writeTemplate
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    // Field:  rootdir/<TIME>/surfaceName.{vtk|vtp}
+
+    // Open file, writing geometry (if required)
+    fileName outputFile = this->write();
+
+    if (verbose_)
+    {
+        Info<< "Writing field " << fieldName << " to " << outputFile << endl;
+    }
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (this->isPointData())
+        {
+            writer_->beginPointData(nFields_);
+        }
+        else
+        {
+            writer_->beginCellData(nFields_);
+        }
+
+        writer_->write(fieldName, tfield());
+    }
+
+    return outputFile;
+}
+
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::vtkWriter);
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/vtk/vtkSurfaceWriter.H b/src/surfMesh/writers/vtk/vtkSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..d9872b35cfbaffd1fc048063cd69f158d0f9cc3d
--- /dev/null
+++ b/src/surfMesh/writers/vtk/vtkSurfaceWriter.H
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2011, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+                            | Copyright (C) 2011 OpenFOAM Foundation
+-------------------------------------------------------------------------------
+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::surfaceWriters::vtkWriter
+
+Description
+    A surfaceWriter for VTK legacy (.vtk) or XML (.vtp) format.
+
+    The formatOptions for vtk:
+    \table
+        Property    | Description                           | Required | Default
+        format      | ascii or binary format                | no  | binary
+        legacy      | Legacy VTK output                     | no  | false
+        precision   | Write precision in ascii         | no | same as IOstream
+    \endtable
+
+    For example,
+    \verbatim
+    formatOptions
+    {
+        vtk
+        {
+            format      binary;
+            legacy      false;
+            precision   10;
+        }
+    }
+    \endverbatim
+
+    \heading Output file locations
+
+    The \c rootdir normally corresponds to something like
+    \c postProcessing/\<name\>
+
+    \subheading Geometry and Fields
+    \verbatim
+    rootdir
+    `-- timeName
+        `-- surfaceName.{vtk,vtp}
+    \endverbatim
+
+SourceFiles
+    vtkSurfaceWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef vtkSurfaceWriter_H
+#define vtkSurfaceWriter_H
+
+#include "surfaceWriter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace vtk
+{
+// Forward declarations
+class outputOptions;
+class surfaceWriter;
+}
+
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class vtkWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class vtkWriter
+:
+    public surfaceWriter
+{
+    // Private Data
+
+        //- The VTK output format type.
+        //  Stored as a raw value to avoid a header dependency on fileFormats
+        unsigned fmtType_;
+
+        //- ASCII write precision
+        unsigned precision_;
+
+        //- Backend writer - master only
+        autoPtr<Foam::vtk::surfaceWriter> writer_;
+
+
+    // Private Member Functions
+
+        //- Templated write field operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("vtk");
+
+
+    // Constructors
+
+        //- Construct null
+        vtkWriter();
+
+        //- Construct with some output options
+        explicit vtkWriter(const vtk::outputOptions& opts);
+
+        //- Construct with some output options
+        explicit vtkWriter(const dictionary& options);
+
+        //- Construct from components
+        //  The file name is with/without an extension.
+        vtkWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components with specified output path.
+        //  The file name is with/without an extension.
+        vtkWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~vtkWriter() = default;
+
+
+    // Member Functions
+
+        //- Finish output, clears backend.
+        virtual void close(); // override
+
+        //- Begin time step. Clears existing backend.
+        virtual void beginTime(const Time& t); // override
+
+        //- Begin time step. Clears existing backend.
+        virtual void beginTime(const instant& inst); // override
+
+        //- End time step. Clears existing backend.
+        virtual void endTime(); // override
+
+
+    // Write
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //