diff --git a/src/conversion/Make/files b/src/conversion/Make/files
index ed8b65e68f4b60008615844e60c3b0ed699651e6..5d6233a1c6171e55a7dcc68a7ab09c2ee312ab0c 100644
--- a/src/conversion/Make/files
+++ b/src/conversion/Make/files
@@ -26,7 +26,6 @@ polyDualMesh/polyDualMesh.C
 
 vtk/output/foamVtkInternalWriter.H
 vtk/output/foamVtkPatchWriter.H
-vtk/output/foamVtkSurfaceMeshWriter.C
 vtk/output/foamVtkWriteSurfFields.C
 
 LIB = $(FOAM_LIBBIN)/libconversion
diff --git a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C b/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C
deleted file mode 100644
index 96b62ed9c78382e80afd84a014f1e47fe111f193..0000000000000000000000000000000000000000
--- a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C
+++ /dev/null
@@ -1,264 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2017 OpenCFD Ltd.
--------------------------------------------------------------------------------
-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 "foamVtkSurfaceMeshWriter.H"
-#include "foamVtkOutput.H"
-
-
-// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
-
-void Foam::vtk::surfaceMeshWriter::beginPiece()
-{
-    if (!legacy_)
-    {
-        format()
-            .openTag(vtk::fileTag::PIECE)
-            .xmlAttr(vtk::fileAttr::NUMBER_OF_POINTS, pp_.nPoints())
-            .xmlAttr(vtk::fileAttr::NUMBER_OF_POLYS,  pp_.size())
-            .closeTag();
-    }
-}
-
-
-void Foam::vtk::surfaceMeshWriter::writePoints()
-{
-    const uint64_t payLoad = (pp_.nPoints()*3*sizeof(float));
-
-    if (legacy_)
-    {
-        legacy::beginPoints(os_, pp_.nPoints());
-    }
-    else
-    {
-        format().tag(vtk::fileTag::POINTS)
-            .openDataArray<float, 3>(vtk::dataArrayAttr::POINTS)
-            .closeTag();
-    }
-
-    format().writeSize(payLoad);
-
-    vtk::writeList(format(), pp_.localPoints());
-    format().flush();
-
-    if (!legacy_)
-    {
-        format()
-            .endDataArray()
-            .endTag(vtk::fileTag::POINTS);
-    }
-}
-
-
-void Foam::vtk::surfaceMeshWriter::writePolysLegacy()
-{
-    // connectivity count without additional storage (done internally)
-    uint64_t nConnectivity = 0;
-    forAll(pp_, facei)
-    {
-        nConnectivity += pp_[facei].size();
-    }
-
-    legacy::beginPolys(os_, pp_.size(), nConnectivity);
-
-
-    // legacy: size + connectivity together
-    // [nPts, id1, id2, ..., nPts, id1, id2, ...]
-
-    forAll(pp_, facei)
-    {
-        const face& f = pp_.localFaces()[facei];
-
-        format().write(f.size());  // The size prefix
-        vtk::writeList(format(), f);
-    }
-
-    format().flush();
-}
-
-
-void Foam::vtk::surfaceMeshWriter::writePolys()
-{
-    //
-    // 'connectivity'
-    //
-
-    format().tag(vtk::fileTag::POLYS);
-
-    //
-    // 'connectivity'
-    //
-    {
-        // payload count
-        uint64_t payLoad = 0;
-        forAll(pp_, facei)
-        {
-            payLoad += pp_[facei].size();
-        }
-
-        format().openDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY)
-            .closeTag();
-
-        // payload size
-        format().writeSize(payLoad * sizeof(label));
-
-        forAll(pp_, facei)
-        {
-            const face& f = pp_.localFaces()[facei];
-            vtk::writeList(format(), f);
-        }
-
-        format().flush();
-
-        format()
-            .endDataArray();
-    }
-
-
-    //
-    // 'offsets'  (connectivity offsets)
-    //
-    {
-        format()
-            .openDataArray<label>(vtk::dataArrayAttr::OFFSETS)
-            .closeTag();
-
-        // payload size
-        format().writeSize(pp_.size() * sizeof(label));
-
-        label off = 0;
-        forAll(pp_, facei)
-        {
-            off += pp_[facei].size();
-
-            format().write(off);
-        }
-
-        format().flush();
-        format().endDataArray();
-    }
-
-    format().endTag(vtk::fileTag::POLYS);
-}
-
-
-void Foam::vtk::surfaceMeshWriter::writeMesh()
-{
-    writePoints();
-    if (legacy_)
-    {
-        writePolysLegacy();
-    }
-    else
-    {
-        writePolys();
-    }
-}
-
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::vtk::surfaceMeshWriter::surfaceMeshWriter
-(
-    const indirectPrimitivePatch& pp,
-    const word& name,
-    const fileName& baseName,
-    const vtk::outputOptions outOpts
-)
-:
-    pp_(pp),
-    legacy_(outOpts.legacy()),
-    format_(),
-    os_()
-{
-    outputOptions opts(outOpts);
-    opts.append(false);  // No append supported
-
-    os_.open((baseName + (legacy_ ? ".vtk" : ".vtp")).c_str());
-    format_ = opts.newFormatter(os_);
-
-    if (legacy_)
-    {
-        legacy::fileHeader(format(), name, vtk::fileTag::POLY_DATA);
-    }
-    else
-    {
-        // XML (inline)
-
-        format()
-            .xmlHeader()
-            .xmlComment(name)
-            .beginVTKFile(vtk::fileTag::POLY_DATA, "0.1");
-
-    }
-
-    beginPiece();
-    writeMesh();
-}
-
-
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::vtk::surfaceMeshWriter::~surfaceMeshWriter()
-{}
-
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-void Foam::vtk::surfaceMeshWriter::beginCellData(label nFields)
-{
-    if (legacy_)
-    {
-        legacy::beginCellData(format(), pp_.size(), nFields);
-    }
-    else
-    {
-        format().beginCellData();
-    }
-}
-
-
-void Foam::vtk::surfaceMeshWriter::endCellData()
-{
-    if (!legacy_)
-    {
-        format().endCellData();
-    }
-}
-
-
-void Foam::vtk::surfaceMeshWriter::writeFooter()
-{
-    if (!legacy_)
-    {
-        // slight cheat. </Piece> too
-        format().endTag(vtk::fileTag::PIECE);
-
-        format().endTag(vtk::fileTag::POLY_DATA)
-            .endVTKFile();
-    }
-}
-
-
-// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.H b/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.H
index 2963e3c665f4e110b584fef59bd8bb6a6c8c0e0d..d6d89bb164791f293a7ca682fce144224a16570c 100644
--- a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.H
+++ b/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.H
@@ -25,10 +25,19 @@ Class
     Foam::vtk::surfaceMeshWriter
 
 Description
-    Write faces with fields
+    Write faces (eg face-zones or face-sets) with fields.
+
+    The file output states are managed by the Foam::vtk::fileWriter class.
+    FieldData (eg, TimeValue) must appear before any geometry pieces.
+
+Note
+    Parallel output is combined into a single Piece without point merging,
+    which is similar to using multi-piece data sets, but allows more
+    convenient creation as a streaming process.
+    In the future, the duplicate points at processor connections
+    may be addressed using ghost points.
 
 SourceFiles
-    foamVtkSurfaceMeshWriter.C
     foamVtkSurfaceMeshWriterTemplates.C
 
 \*---------------------------------------------------------------------------*/
@@ -36,57 +45,34 @@ SourceFiles
 #ifndef foamVtkSurfaceMeshWriter_H
 #define foamVtkSurfaceMeshWriter_H
 
-#include "pointMesh.H"
-#include "OFstream.H"
-#include "volFields.H"
-#include "surfaceFields.H"
+#include <fstream>
 #include "areaFields.H"
-#include "indirectPrimitivePatch.H"
-#include "foamVtkOutputOptions.H"
+#include "surfaceFields.H"
+#include "foamVtkIndPatchWriter.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
-class volPointInterpolation;
-
 namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                      Class surfaceMeshWriter Declaration
+                   Class vtk::surfaceMeshWriter Declaration
 \*---------------------------------------------------------------------------*/
 
 class surfaceMeshWriter
+:
+    public vtk::indirectPatchWriter
 {
-    // Private Member Data
-
-        const indirectPrimitivePatch& pp_;
-
-        //- Commonly used query
-        const bool legacy_;
-
-        autoPtr<vtk::formatter> format_;
-
-        std::ofstream os_;
-
-
     // Private Member Functions
 
-        //- Begin piece
-        void beginPiece();
-
-        //- Write patch points
-        void writePoints();
-
-        //- Write patch faces
-        void writePolysLegacy();
-
-        //- Write patch faces
-        void writePolys();
-
-        //- Write mesh topology
-        void writeMesh();
+        //- Get face field (internal face or boundary face)
+        template<class Type>
+        tmp<Field<Type>> getFaceField
+        (
+            const GeometricField<Type, fvsPatchField, surfaceMesh>& sfld
+        ) const;
 
 
         //- No copy construct
@@ -95,93 +81,67 @@ class surfaceMeshWriter
         //- No copy assignment
         void operator=(const surfaceMeshWriter&) = delete;
 
+
 public:
 
     // Constructors
 
-        //- Construct from components
+        //- Construct from patch (default format INLINE_BASE64)
         surfaceMeshWriter
         (
             const indirectPrimitivePatch& pp,
-            const word& name,
-            const fileName& baseName,
-            const vtk::outputOptions outOpts
-        );
+            const vtk::outputOptions opts = vtk::formatType::INLINE_BASE64
+        )
+        :
+            vtk::indirectPatchWriter(pp, opts)
+        {}
+
+        //- Construct from patch (default format INLINE_BASE64),
+        //- and open the file for writing.
+        //  The file name is with/without an extension.
+        surfaceMeshWriter
+        (
+            const indirectPrimitivePatch& pp,
+            const fileName& file,
+            bool parallel = Pstream::parRun()
+        )
+        :
+            vtk::indirectPatchWriter(pp, file, parallel)
+        {}
+
+        //- Construct from patch and open the file for writing.
+        //  The file name is with/without an extension.
+        surfaceMeshWriter
+        (
+            const indirectPrimitivePatch& pp,
+            const vtk::outputOptions opts,
+            const fileName& file,
+            bool parallel = Pstream::parRun()
+        )
+        :
+            vtk::indirectPatchWriter(pp, opts, file, parallel)
+        {}
 
 
     //- Destructor
-    ~surfaceMeshWriter();
+    virtual ~surfaceMeshWriter() = default;
 
 
     // Member Functions
 
-        inline std::ofstream& os()
-        {
-            return os_;
-        }
-
-        inline vtk::formatter& format()
-        {
-            return *format_;
-        }
-
-        //- Open write for CellData of count fields.
-        //  The parameters are only used for the legacy format.
-        void beginCellData(label nFields);
-
-        //- Close write for CellData
-        void endCellData();
-
-        //- Write file footer
-        void writeFooter();
-
-
-        //- Get face field (internal face or boundary face)
-        template<class Type>
-        tmp<Field<Type>> getFaceField
-        (
-            const GeometricField<Type, fvsPatchField, surfaceMesh>& sfld
-        ) const;
-
-
-      // Write fields (individually)
-
-        //- Write surface field
+        //- Write surface field (CellData)
         template<class Type>
         void write
         (
             const GeometricField<Type, fvsPatchField, surfaceMesh>& field
         );
 
-        //- Write surface field
+        //- Write surface field (CellData)
         template<class Type>
         void write
         (
             const GeometricField<Type, faPatchField, areaMesh>& field
         );
-
-
-      // Write fields (collectively)
-
-        //- Write surface fields
-        template<class Type>
-        void write
-        (
-            const UPtrList
-            <
-                const GeometricField<Type, fvsPatchField, surfaceMesh>
-            >& sflds
-        );
-
-        //- Write surface fields
-        template<class Type>
-        void write
-        (
-            const UPtrList
-            <
-                const GeometricField<Type, faPatchField, areaMesh>
-            >& sflds
-        );
 };
 
 
diff --git a/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C b/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
index c54d53f3dd0226479fdc9330b85ef680b2c983e8..ef111e12c749587169b9e9f59ab497e5f367d00d 100644
--- a/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
+++ b/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2016-2017 OpenCFD Ltd.
+     \\/     M anipulation  | Copyright (C) 2016-2018 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -23,9 +23,7 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "foamVtkSurfaceMeshWriter.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
 Foam::tmp<Foam::Field<Type>>
@@ -36,56 +34,50 @@ Foam::vtk::surfaceMeshWriter::getFaceField
 {
     const polyBoundaryMesh& patches = sfld.mesh().boundaryMesh();
 
-    tmp<Field<Type>> tfld(new Field<Type>(pp_.size()));
-    Field<Type>& fld = tfld.ref();
+    const labelList& faceAddr = this->patch().addressing();
+
+    auto tfld = tmp<Field<Type>>::New(faceAddr.size());
+    auto iter = tfld.ref().begin();
 
-    forAll(pp_.addressing(), i)
+    for (const label facei : faceAddr)
     {
-        const label facei = pp_.addressing()[i];
         const label patchi = patches.whichPatch(facei);
 
         if (patchi == -1)
         {
-            fld[i] = sfld[facei];
+            *iter = sfld[facei];
         }
         else
         {
             const label localFacei = facei - patches[patchi].start();
-            fld[i] = sfld.boundaryField()[patchi][localFacei];
+            *iter = sfld.boundaryField()[patchi][localFacei];
         }
+
+        ++iter;
     }
 
     return tfld;
 }
 
 
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
 template<class Type>
 void Foam::vtk::surfaceMeshWriter::write
 (
     const GeometricField<Type, fvsPatchField, surfaceMesh>& field
 )
 {
-    const int nCmpt(pTraits<Type>::nComponents);
-    const uint64_t payLoad(pp_.size() * nCmpt * sizeof(float));
-
-    if (legacy_)
+    if (notState(outputState::CELL_DATA))
     {
-        legacy::floatField<nCmpt>(format(), field.name(), pp_.size());
+        FatalErrorInFunction
+            << "Bad writer state (" << stateNames[state_]
+            << ") - should be (" << stateNames[outputState::CELL_DATA]
+            << ") for field " << field.name() << nl << endl
+            << exit(FatalError);
     }
-    else
-    {
-        format().openDataArray<float, nCmpt>(field.name()).closeTag();
-    }
-
-    format().writeSize(payLoad);
-    vtk::writeList(format(), getFaceField(field)());
 
-    format().flush();
-
-    if (!legacy_)
-    {
-        format().endDataArray();
-    }
+    this->indirectPatchWriter::write(field.name(), getFaceField(field)());
 }
 
 
@@ -95,59 +87,16 @@ void Foam::vtk::surfaceMeshWriter::write
     const GeometricField<Type, faPatchField, areaMesh>& field
 )
 {
-    const int nCmpt(pTraits<Type>::nComponents);
-    const uint64_t payLoad(pp_.size() * nCmpt * sizeof(float));
-
-    if (legacy_)
-    {
-        legacy::floatField<nCmpt>(format(), field.name(), pp_.size());
-    }
-    else
-    {
-        format().openDataArray<float, nCmpt>(field.name()).closeTag();
-    }
-
-    format().writeSize(payLoad);
-    vtk::writeList(format(), field.primitiveField());
-
-    format().flush();
-
-    if (!legacy_)
-    {
-        format().endDataArray();
-    }
-}
-
-
-template<class Type>
-void Foam::vtk::surfaceMeshWriter::write
-(
-    const UPtrList
-    <
-        const GeometricField<Type, fvsPatchField, surfaceMesh>
-    >& sflds
-)
-{
-    for (const auto& field : sflds)
+    if (notState(outputState::CELL_DATA))
     {
-        write(field);
+        FatalErrorInFunction
+            << "Bad writer state (" << stateNames[state_]
+            << ") - should be (" << stateNames[outputState::CELL_DATA]
+            << ") for field " << field.name() << nl << endl
+            << exit(FatalError);
     }
-}
-
 
-template<class Type>
-void Foam::vtk::surfaceMeshWriter::write
-(
-    const UPtrList
-    <
-        const GeometricField<Type, faPatchField, areaMesh>
-    >& sflds
-)
-{
-    for (const auto& field : sflds)
-    {
-        write(field);
-    }
+    this->indirectPatchWriter::write(field.name(), field.primitiveField());
 }
 
 
diff --git a/src/functionObjects/utilities/Make/options b/src/functionObjects/utilities/Make/options
index 9cccdbc1a446f565c9c6c70be62b2ef71afe289b..67eeb1ac213d684ab8bf74b3de222e478853d50c 100644
--- a/src/functionObjects/utilities/Make/options
+++ b/src/functionObjects/utilities/Make/options
@@ -2,6 +2,7 @@ EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/finiteArea/lnInclude \
     -I$(LIB_SRC)/fileFormats/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/conversion/lnInclude \
     -I$(LIB_SRC)/sampling/lnInclude \
     -I$(LIB_SRC)/dynamicMesh/lnInclude \
@@ -12,6 +13,7 @@ EXE_INC = \
 LIB_LIBS = \
     -lfiniteVolume \
     -lfiniteArea \
+    -lmeshTools \
     -lconversion \
     -lsampling \
     -ldynamicMesh \