diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriter.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriter.C
index 605110940435ea50d4a5f85ca7cbb11678dd76ba..cbe2d515bc7d73d301146692382ea10800b7fd40 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriter.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriter.C
@@ -226,20 +226,27 @@ void Foam::vtk::lagrangianWriter::beginParcelData(label nFields)
 {
     if (!nParcels_) return;   // Skip if there are no parcels
 
-    const vtk::fileTag dataType =
-    (
-        useVerts_
-      ? vtk::fileTag::CELL_DATA
-      : vtk::fileTag::POINT_DATA
-    );
-
-    if (legacy_)
+    if (useVerts_)
     {
-        legacy::dataHeader(os_, dataType, nParcels_, nFields);
+        if (legacy_)
+        {
+            legacy::beginCellData(format(), nParcels_, nFields);
+        }
+        else
+        {
+            format().beginCellData();
+        }
     }
     else
     {
-        format().tag(dataType);
+        if (legacy_)
+        {
+            legacy::beginPointData(format(), nParcels_, nFields);
+        }
+        else
+        {
+            format().beginPointData();
+        }
     }
 }
 
@@ -248,16 +255,17 @@ void Foam::vtk::lagrangianWriter::endParcelData()
 {
     if (!nParcels_) return;   // Skip if there are no parcels
 
-    const vtk::fileTag dataType =
-    (
-        useVerts_
-      ? vtk::fileTag::CELL_DATA
-      : vtk::fileTag::POINT_DATA
-    );
-
     if (!legacy_)
     {
-        format().endTag(dataType);
+
+        if (useVerts_)
+        {
+            format().endCellData();
+        }
+        else
+        {
+            format().endPointData();
+        }
     }
 }
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriterTemplates.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriterTemplates.C
index 5b4181f55f751692545d54c5d6c3eaa18a636241..bd0e7202eade1a428d22febd3d462828fefbd9bf 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriterTemplates.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamVtkLagrangianWriterTemplates.C
@@ -78,7 +78,7 @@ void Foam::vtk::lagrangianWriter::writeIOField(const wordList& fieldNames)
 
             if (legacy_)
             {
-                legacy::intField(os(), fldName, nCmpt, fld.size());
+                legacy::intField<nCmpt>(format(), fldName, fld.size());
             }
             else
             {
@@ -103,7 +103,7 @@ void Foam::vtk::lagrangianWriter::writeIOField(const wordList& fieldNames)
 
             if (legacy_)
             {
-                legacy::floatField(os(), fldName, nCmpt, fld.size());
+                legacy::floatField<nCmpt>(format(), fldName, fld.size());
             }
             else
             {
diff --git a/src/conversion/vtk/output/foamVtkInternalWriter.C b/src/conversion/vtk/output/foamVtkInternalWriter.C
index 49bf062e8273aea0e407a27d7df3b77ccf93590f..4972f98ca12eb27e158d9e9ed8859f186841cc4d 100644
--- a/src/conversion/vtk/output/foamVtkInternalWriter.C
+++ b/src/conversion/vtk/output/foamVtkInternalWriter.C
@@ -284,17 +284,11 @@ void Foam::vtk::internalWriter::beginCellData(label nFields)
 {
     if (legacy_)
     {
-        legacy::dataHeader
-        (
-            os(),
-            vtk::fileTag::CELL_DATA,
-            vtuCells_.nFieldCells(),
-            nFields
-        );
+        legacy::beginCellData(format(), vtuCells_.nFieldCells(), nFields);
     }
     else
     {
-        format().tag(vtk::fileTag::CELL_DATA);
+        format().beginCellData();
     }
 }
 
@@ -303,7 +297,7 @@ void Foam::vtk::internalWriter::endCellData()
 {
     if (!legacy_)
     {
-        format().endTag(vtk::fileTag::CELL_DATA);
+        format().endCellData();
     }
 }
 
@@ -312,17 +306,16 @@ void Foam::vtk::internalWriter::beginPointData(label nFields)
 {
     if (legacy_)
     {
-        legacy::dataHeader
+        legacy::beginPointData
         (
-            os(),
-            vtk::fileTag::POINT_DATA,
+            format(),
             vtuCells_.nFieldPoints(),
             nFields
         );
     }
     else
     {
-        format().tag(vtk::fileTag::POINT_DATA);
+        format().beginPointData();
     }
 }
 
@@ -331,7 +324,7 @@ void Foam::vtk::internalWriter::endPointData()
 {
     if (!legacy_)
     {
-        format().endTag(vtk::fileTag::POINT_DATA);
+        format().endPointData();
     }
 }
 
diff --git a/src/conversion/vtk/output/foamVtkInternalWriterTemplates.C b/src/conversion/vtk/output/foamVtkInternalWriterTemplates.C
index 77689e5ec7b8d37391168db0ae145d2e91955e88..b0177ade71fb1c825d924b177ce1157720f1c584 100644
--- a/src/conversion/vtk/output/foamVtkInternalWriterTemplates.C
+++ b/src/conversion/vtk/output/foamVtkInternalWriterTemplates.C
@@ -43,7 +43,7 @@ void Foam::vtk::internalWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), field.name(), nCmpt, cellMap.size());
+        legacy::floatField<nCmpt>(format(), field.name(), cellMap.size());
     }
     else
     {
@@ -87,7 +87,7 @@ void Foam::vtk::internalWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), field.name(), nCmpt, nVals);
+        legacy::floatField<nCmpt>(format(), field.name(), nVals);
     }
     else
     {
@@ -136,7 +136,7 @@ void Foam::vtk::internalWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), vfield.name(), nCmpt, nVals);
+        legacy::floatField<nCmpt>(format(), vfield.name(), nVals);
     }
     else
     {
@@ -179,7 +179,7 @@ void Foam::vtk::internalWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), vfield.name(), nCmpt, nVals);
+        legacy::floatField<nCmpt>(format(), vfield.name(), nVals);
     }
     else
     {
diff --git a/src/conversion/vtk/output/foamVtkOutputFieldsTemplates.C b/src/conversion/vtk/output/foamVtkOutputFieldsTemplates.C
index 74cb846a1b4db8d29465a5129d1e90e80d7d90c9..5136eb0bf51f21d6f35bac440cd2d6d24b1c96cf 100644
--- a/src/conversion/vtk/output/foamVtkOutputFieldsTemplates.C
+++ b/src/conversion/vtk/output/foamVtkOutputFieldsTemplates.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -39,7 +39,7 @@ void Foam::vtk::writeField
     );
 
     fmt.writeSize(payLoad);
-    writeList(fmt, fld);
+    vtk::writeList(fmt, fld);
 
     fmt.flush();
 }
@@ -59,7 +59,7 @@ void Foam::vtk::writeField
     );
 
     fmt.writeSize(payLoad);
-    writeList(fmt, fld, cellMap);
+    vtk::writeList(fmt, fld, cellMap);
 
     fmt.flush();
 }
@@ -78,7 +78,7 @@ void Foam::vtk::writeField
     );
 
     fmt.writeSize(payLoad);
-    writeList(fmt, fld.internalField());
+    vtk::writeList(fmt, fld.internalField());
 
     fmt.flush();
 }
@@ -98,7 +98,7 @@ void Foam::vtk::writeField
     );
 
     fmt.writeSize(payLoad);
-    writeList(fmt, fld.internalField(), cellMap);
+    vtk::writeList(fmt, fld.internalField(), cellMap);
 
     fmt.flush();
 }
diff --git a/src/conversion/vtk/output/foamVtkPatchWriter.C b/src/conversion/vtk/output/foamVtkPatchWriter.C
index 18764148cd6cc6b351bb13ed71fd2450e5487fa3..3ca9500a84f87b11fb87fe35dd51cb3988526b38 100644
--- a/src/conversion/vtk/output/foamVtkPatchWriter.C
+++ b/src/conversion/vtk/output/foamVtkPatchWriter.C
@@ -301,17 +301,11 @@ void Foam::vtk::patchWriter::beginCellData(label nFields)
 {
     if (legacy_)
     {
-        legacy::dataHeader
-        (
-            os(),
-            vtk::fileTag::CELL_DATA,
-            nFaces_,
-            nFields
-        );
+        legacy::beginCellData(format(), nFaces_, nFields);
     }
     else
     {
-        format().tag(vtk::fileTag::CELL_DATA);
+        format().beginCellData();
     }
 }
 
@@ -320,7 +314,7 @@ void Foam::vtk::patchWriter::endCellData()
 {
     if (!legacy_)
     {
-        format().endTag(vtk::fileTag::CELL_DATA);
+        format().endCellData();
     }
 }
 
@@ -329,17 +323,11 @@ void Foam::vtk::patchWriter::beginPointData(label nFields)
 {
     if (legacy_)
     {
-        legacy::dataHeader
-        (
-            os(),
-            vtk::fileTag::POINT_DATA,
-            nPoints_,
-            nFields
-        );
+        legacy::beginPointData(format(), nPoints_, nFields);
     }
     else
     {
-        format().tag(vtk::fileTag::POINT_DATA);
+        format().beginPointData();
     }
 }
 
@@ -348,7 +336,7 @@ void Foam::vtk::patchWriter::endPointData()
 {
     if (!legacy_)
     {
-        format().endTag(vtk::fileTag::POINT_DATA);
+        format().endPointData();
     }
 }
 
@@ -373,7 +361,7 @@ void Foam::vtk::patchWriter::writePatchIDs()
 
     if (legacy_)
     {
-        legacy::intField(os_, "patchID", 1, nFaces_);
+        legacy::intField<1>(format(), "patchID", nFaces_);  // 1 component
     }
     else
     {
diff --git a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C
index 9c77912499fce907fe916f401ee10ae7b1fe4ab7..0bc9602390497a4c501260c1c12e0891be1a5889 100644
--- a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C
+++ b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C
@@ -39,7 +39,7 @@ void Foam::vtk::patchWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os_, field.name(), nCmpt, nFaces_);
+        legacy::floatField<nCmpt>(format(), field.name(), nFaces_);
     }
     else
     {
@@ -83,7 +83,7 @@ void Foam::vtk::patchWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os_, field.name(), nCmpt, nPoints_);
+        legacy::floatField<nCmpt>(format(), field.name(), nPoints_);
     }
     else
     {
@@ -121,7 +121,7 @@ void Foam::vtk::patchWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os_, field.name(), nCmpt, nPoints_);
+        legacy::floatField<nCmpt>(format(), field.name(), nPoints_);
     }
     else
     {
diff --git a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C b/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C
index f5ba90efc171b7cb0a2e2946fae57e95429700d9..96b62ed9c78382e80afd84a014f1e47fe111f193 100644
--- a/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C
+++ b/src/conversion/vtk/output/foamVtkSurfaceMeshWriter.C
@@ -230,11 +230,11 @@ void Foam::vtk::surfaceMeshWriter::beginCellData(label nFields)
 {
     if (legacy_)
     {
-        legacy::dataHeader(os(), vtk::fileTag::CELL_DATA, pp_.size(), nFields);
+        legacy::beginCellData(format(), pp_.size(), nFields);
     }
     else
     {
-        format().tag(vtk::fileTag::CELL_DATA);
+        format().beginCellData();
     }
 }
 
@@ -243,7 +243,7 @@ void Foam::vtk::surfaceMeshWriter::endCellData()
 {
     if (!legacy_)
     {
-        format().endTag(vtk::fileTag::CELL_DATA);
+        format().endCellData();
     }
 }
 
diff --git a/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C b/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
index 8b52dfc02f43eabaa09101f78522fb8cd6b4b862..c54d53f3dd0226479fdc9330b85ef680b2c983e8 100644
--- a/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
+++ b/src/conversion/vtk/output/foamVtkSurfaceMeshWriterTemplates.C
@@ -70,7 +70,7 @@ void Foam::vtk::surfaceMeshWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), field.name(), nCmpt, pp_.size());
+        legacy::floatField<nCmpt>(format(), field.name(), pp_.size());
     }
     else
     {
@@ -100,7 +100,7 @@ void Foam::vtk::surfaceMeshWriter::write
 
     if (legacy_)
     {
-        legacy::floatField(os(), field.name(), nCmpt, pp_.size());
+        legacy::floatField<nCmpt>(format(), field.name(), pp_.size());
     }
     else
     {
diff --git a/src/conversion/vtk/output/foamVtkWriteSurfFields.C b/src/conversion/vtk/output/foamVtkWriteSurfFields.C
index bfe1f35038153a731c738266c23fe83a473bd6f7..1742470dbf89c8a36351feafb47a6efc3d829602 100644
--- a/src/conversion/vtk/output/foamVtkWriteSurfFields.C
+++ b/src/conversion/vtk/output/foamVtkWriteSurfFields.C
@@ -94,10 +94,9 @@ void Foam::vtk::writeSurfFields
     // Fields
     if (legacy_)
     {
-        legacy::dataHeader
+        legacy::beginPointData
         (
-            os,
-            vtk::fileTag::POINT_DATA,
+            format(),
             mesh.nFaces(),
             surfVectorFields.size()
         );
@@ -114,7 +113,7 @@ void Foam::vtk::writeSurfFields
 
         if (legacy_)
         {
-            legacy::floatField(os, fld.name(), nCmpt, mesh.nFaces());
+            legacy::floatField<nCmpt>(format(), fld.name(), mesh.nFaces());
         }
         else
         {
diff --git a/src/fileFormats/vtk/core/foamVtkCore.C b/src/fileFormats/vtk/core/foamVtkCore.C
index 7a61ccb0345d2744588811d211e5ac534935a49b..c8b41b924c2de40b8e4925ab266918286e057976 100644
--- a/src/fileFormats/vtk/core/foamVtkCore.C
+++ b/src/fileFormats/vtk/core/foamVtkCore.C
@@ -27,6 +27,32 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+const Foam::Enum
+<
+    Foam::vtk::fileTag
+>
+Foam::vtk::fileExtension
+{
+    { fileTag::POLY_DATA, "vtp" },
+    { fileTag::UNSTRUCTURED_GRID, "vtu" },
+    { fileTag::MULTI_BLOCK, "vtm" },
+    // { fileTag::COLLECTION, "pvd" },
+};
+
+
+const Foam::Enum
+<
+    Foam::vtk::fileTag
+>
+Foam::vtk::fileContentVersions
+{
+    { fileTag::POLY_DATA, "0.1" },
+    { fileTag::UNSTRUCTURED_GRID, "0.1" },
+    { fileTag::MULTI_BLOCK, "1.0" },
+    // { fileTag::COLLECTION, "0.1" },
+};
+
+
 const Foam::Enum
 <
     Foam::vtk::fileTag
@@ -35,6 +61,7 @@ Foam::vtk::fileTagNames
 ({
     { fileTag::VTK_FILE, "VTKFile" },
     { fileTag::DATA_ARRAY, "DataArray" },
+    { fileTag::BLOCK, "Block" },
     { fileTag::PIECE, "Piece" },
     { fileTag::DATA_SET, "DataSet" },
     { fileTag::POINTS, "Points" },
@@ -44,9 +71,11 @@ Foam::vtk::fileTagNames
     { fileTag::LINES, "Lines" },
     { fileTag::CELL_DATA, "CellData" },
     { fileTag::POINT_DATA, "PointData" },
+    { fileTag::FIELD_DATA, "FieldData" },
     { fileTag::POLY_DATA, "PolyData" },
     { fileTag::UNSTRUCTURED_GRID, "UnstructuredGrid" },
     { fileTag::MULTI_BLOCK, "vtkMultiBlockDataSet" },
+    // { fileTag::COLLECTION, "Collection" },
 });
 
 
@@ -58,6 +87,7 @@ Foam::vtk::fileAttrNames
 ({
     { fileAttr::OFFSET, "offset" },
     { fileAttr::NUMBER_OF_COMPONENTS, "NumberOfComponents" },
+    { fileAttr::NUMBER_OF_TUPLES, "NumberOfTuples" },
     { fileAttr::NUMBER_OF_POINTS, "NumberOfPoints" },
     { fileAttr::NUMBER_OF_CELLS, "NumberOfCells" },
     { fileAttr::NUMBER_OF_POLYS, "NumberOfPolys" },
@@ -81,4 +111,30 @@ Foam::vtk::dataArrayAttrNames
 });
 
 
+// Legacy
+
+const Foam::word Foam::vtk::legacy::fileExtension("vtk");
+
+const Foam::Enum
+<
+    Foam::vtk::fileTag
+>
+Foam::vtk::legacy::contentNames
+({
+    { vtk::fileTag::POLY_DATA, "POLYDATA" },
+    { vtk::fileTag::UNSTRUCTURED_GRID, "UNSTRUCTURED_GRID" },
+});
+
+
+const Foam::Enum
+<
+    Foam::vtk::fileTag
+>
+Foam::vtk::legacy::dataTypeNames
+({
+    { vtk::fileTag::CELL_DATA,  "CELL_DATA" },
+    { vtk::fileTag::POINT_DATA, "POINT_DATA" },
+});
+
+
 // ************************************************************************* //
diff --git a/src/fileFormats/vtk/core/foamVtkCore.H b/src/fileFormats/vtk/core/foamVtkCore.H
index 8584c9672ceebb8655b6c6403ffcb5ccb85b3d81..88620ebc5c3c57a3e94a899fcfe8b53ee82a339d 100644
--- a/src/fileFormats/vtk/core/foamVtkCore.H
+++ b/src/fileFormats/vtk/core/foamVtkCore.H
@@ -35,6 +35,7 @@ SourceFiles
 #ifndef foamVtkCore_H
 #define foamVtkCore_H
 
+#include <cstdint>
 #include "Enum.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -59,22 +60,29 @@ namespace vtk
 
     //- The output format type for file contents.
     //  Upper bits for output type, lower bits for the format itself.
-    enum class formatType
+    enum class formatType : uint8_t
     {
-        /** XML inline ASCII, using the asciiFormatter */
-        INLINE_ASCII  = 0,
-        /** XML inline base64, using the base64Formatter */
-        INLINE_BASE64 = 0x01,
-        /** XML append base64, using the appendBase64Formatter */
-        APPEND_BASE64 = 0x11,
-        /** XML append raw binary, using the appendRawFormatter */
-        APPEND_BINARY = 0x12,
-        /** Legacy ASCII, using the legacyAsciiFormatter */
-        LEGACY_ASCII  = 0x20,
-        /** Legacy raw binary, using the legacyRawFormatter */
-        LEGACY_BINARY = 0x22,
+        INLINE_ASCII  = 0,    //!< XML inline ASCII, asciiFormatter
+        INLINE_BASE64 = 0x01, //!< XML inline base64, base64Formatter
+        APPEND_BASE64 = 0x11, //!< XML append base64, appendBase64Formatter
+        APPEND_BINARY = 0x12, //!< XML append raw binary, appendRawFormatter
+        LEGACY_ASCII  = 0x20, //!< Legacy ASCII, legacyAsciiFormatter
+        LEGACY_BINARY = 0x22, //!< Legacy raw binary, legacyRawFormatter
     };
 
+    //- Test for XML append format
+    inline bool isAppend(enum formatType fmt)
+    {
+        return (uint8_t(fmt) & 0x10);
+    }
+
+    //- Test for vtk legacy format
+    inline bool isLegacy(enum formatType fmt)
+    {
+        return (uint8_t(fmt) & 0x20);
+    }
+
+
     //- Equivalent to enumeration in "vtkCellType.h"
     enum cellType
     {
@@ -104,6 +112,7 @@ namespace vtk
     {
         VTK_FILE,               //!< "VTKFile"
         DATA_ARRAY,             //!< "DataArray"
+        BLOCK,                  //!< "Block"
         PIECE,                  //!< "Piece"
         DATA_SET,               //!< "DataSet"
         POINTS,                 //!< "Points"
@@ -113,12 +122,19 @@ namespace vtk
         LINES,                  //!< "Lines"
         CELL_DATA,              //!< "CellData"
         POINT_DATA,             //!< "PointData"
+        FIELD_DATA,             //!< "FieldData"
         POLY_DATA,              //!< "PolyData"
         UNSTRUCTURED_GRID,      //!< "UnstructuredGrid"
         MULTI_BLOCK,            //!< "vtkMultiBlockDataSet"
     };
 
-    //- Strings corresponding to the vtk xml tags
+    //- File extension (without ".") for some vtk XML file content types
+    extern const Foam::Enum<fileTag> fileExtension;
+
+    //- Version string for some vtk XML file content types
+    extern const Foam::Enum<fileTag> fileContentVersions;
+
+    //- Strings corresponding to the vtk XML tags
     extern const Foam::Enum<fileTag> fileTagNames;
 
     //- Some common XML attributes for vtk files
@@ -126,6 +142,7 @@ namespace vtk
     {
         OFFSET,                 //!< "offset"
         NUMBER_OF_COMPONENTS,   //!< "NumberOfComponents"
+        NUMBER_OF_TUPLES,       //!< "NumberOfTuples"
         NUMBER_OF_POINTS,       //!< "NumberOfPoints"
         NUMBER_OF_CELLS,        //!< "NumberOfCells"
         NUMBER_OF_POLYS,        //!< "NumberOfPolys"
@@ -133,10 +150,10 @@ namespace vtk
         NUMBER_OF_LINES,        //!< "NumberOfLines"
     };
 
-    //- Strings corresponding to the vtk xml attributes
+    //- Strings corresponding to the vtk XML attributes
     extern const Foam::Enum<fileAttr> fileAttrNames;
 
-    //- Some common names for XML data arrays
+    //- Some common names for XML DataArray entries
     enum class dataArrayAttr
     {
         POINTS,                 //!< "Points"
@@ -147,10 +164,29 @@ namespace vtk
         FACEOFFSETS,            //!< "faceoffsets"
     };
 
-    //- Strings corresponding to the vtk xml attributes
+    //- Strings corresponding to the vtk XML DataArray attributes
     extern const Foam::Enum<dataArrayAttr> dataArrayAttrNames;
 
 
+/*---------------------------------------------------------------------------*\
+                               Namespace legacy
+\*---------------------------------------------------------------------------*/
+
+namespace legacy
+{
+
+    //- Legacy file extension ("vtk")
+    extern const word fileExtension;
+
+    //- Legacy content names (POLYDATA, UNSTRUCTURED_GRID)
+    extern const Foam::Enum<vtk::fileTag> contentNames;
+
+    //- Legacy data type names (CELL_DATA, POINT_DATA)
+    extern const Foam::Enum<vtk::fileTag> dataTypeNames;
+
+} // End namespace legacy
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace vtk
diff --git a/src/fileFormats/vtk/core/foamVtkPTraits.C b/src/fileFormats/vtk/core/foamVtkPTraits.C
index 0cc5ca04e49720ba33bc279637be951e5801f9ba..7a542257c8b6867617214ca5b48fe0e5684d1bf6 100644
--- a/src/fileFormats/vtk/core/foamVtkPTraits.C
+++ b/src/fileFormats/vtk/core/foamVtkPTraits.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-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -66,5 +66,9 @@ const char* const
 Foam::vtkPTraits<Foam::endian>::typeName = "BigEndian";
 #endif
 
+template<>
+const char* const
+Foam::vtkPTraits<std::string>::typeName = "String";
+
 
 // ************************************************************************* //
diff --git a/src/fileFormats/vtk/core/foamVtkPTraits.H b/src/fileFormats/vtk/core/foamVtkPTraits.H
index ac8df0664659fe746c0c1b32e286aca89f2975e1..b40375f1fb277886b88771d3e6581e89fa7dd2dd 100644
--- a/src/fileFormats/vtk/core/foamVtkPTraits.H
+++ b/src/fileFormats/vtk/core/foamVtkPTraits.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -33,6 +33,7 @@ Description
 #define foamVtkPTraits_H
 
 #include <cstdint>
+#include <string>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -47,13 +48,10 @@ class endian;
 \*---------------------------------------------------------------------------*/
 
 template<class PrimitiveType>
-class vtkPTraits
+struct vtkPTraits
 {
-public:
-
     // Static data members
-
-        static const char* const typeName;
+    static const char* const typeName;
 };
 
 
@@ -81,6 +79,9 @@ const char* const vtkPTraits<double>::typeName;   // Float64
 template<>
 const char* const vtkPTraits<Foam::endian>::typeName; // (Big|Little)Endian
 
+template<>
+const char* const vtkPTraits<std::string>::typeName; // String
+
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
diff --git a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C
index 5527e5c35aefc28cc4ce9d9bbc34fae72e434002..334241b2f1842b4ca70628510fefbb248eff7d24 100644
--- a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C
+++ b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -41,7 +41,8 @@ Foam::vtk::appendBase64Formatter::appendBase64Formatter
     std::ostream& os
 )
 :
-    foamVtkBase64Layer(os)
+    foamVtkBase64Layer(os),
+    offset_(0)
 {}
 
 
@@ -68,4 +69,16 @@ const char* Foam::vtk::appendBase64Formatter::name() const
 }
 
 
+uint64_t Foam::vtk::appendBase64Formatter::offset(const uint64_t numbytes)
+{
+    uint64_t prev = offset_;
+
+    if (formatter::npos != numbytes)
+    {
+        offset_ += this->encodedLength(sizeof(uint64_t) + numbytes);
+    }
+    return prev;
+}
+
+
 // ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H
index 2384289f679b37e2f702606f71b1fdae1782c8c9..6f3814a22ac6abc5ec01b42db69627752fd47dbb 100644
--- a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H
+++ b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -46,7 +46,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                    Class appendBase64Formatter Declaration
+                 Class vtk::appendBase64Formatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class appendBase64Formatter
@@ -58,6 +58,9 @@ class appendBase64Formatter
         static const char* name_;
         static const outputOptions opts_;
 
+        //- The current offset for append data.
+        uint64_t offset_;
+
 
     // Private Member Functions
 
@@ -88,6 +91,12 @@ public:
         //- Output name for XML type ("append")
         virtual const char* name() const;
 
+        //- Increase the append data offset by numbytes and sizeof(uint64_t).
+        //  The additional (uint64_t) size information is consistent with
+        //  writeSize().
+        //
+        //  \return The previous data offset
+        virtual uint64_t offset(const uint64_t numbytes);
 };
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C
index f8b7bcf29fd6c660ce1396628b9b68680a911039..c7e2783c5e69e62dae2875897ca5615846cf7f7b 100644
--- a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C
+++ b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C
@@ -52,7 +52,8 @@ void Foam::vtk::appendRawFormatter::write
 
 Foam::vtk::appendRawFormatter::appendRawFormatter(std::ostream& os)
 :
-    formatter(os)
+    formatter(os),
+    offset_(0)
 {}
 
 
@@ -77,9 +78,22 @@ const char* Foam::vtk::appendRawFormatter::encoding() const
 }
 
 
-void Foam::vtk::appendRawFormatter::writeSize(const uint64_t nBytes)
+uint64_t Foam::vtk::appendRawFormatter::offset(const uint64_t numbytes)
 {
-    write(reinterpret_cast<const char*>(&nBytes), sizeof(uint64_t));
+    uint64_t prev = offset_;
+
+    if (formatter::npos != numbytes)
+    {
+        offset_ += this->encodedLength(sizeof(uint64_t) + numbytes);
+    }
+    return prev;
+}
+
+
+bool Foam::vtk::appendRawFormatter::writeSize(const uint64_t numbytes)
+{
+    write(reinterpret_cast<const char*>(&numbytes), sizeof(uint64_t));
+    return true;
 }
 
 
diff --git a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H
index dbbcf0788ae8f008a22871ba2d530306bac990e7..fc05f9249c446c4e1a8577f78cf2f8df2a89c683 100644
--- a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H
+++ b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -45,7 +45,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                     Class appendRawFormatter Declaration
+                   Class vtk::appendRawFormatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class appendRawFormatter
@@ -58,6 +58,9 @@ class appendRawFormatter
         static const char* encoding_;
         static const outputOptions opts_;
 
+        //- The current offset for append data.
+        uint64_t offset_;
+
 
     // Private Member Functions
 
@@ -99,9 +102,16 @@ public:
         //- Output name for append encoding type ("raw")
         virtual const char* encoding() const;
 
+        //- Increase the append data offset by numbytes and sizeof(uint64_t).
+        //  The additional (uint64_t) size information is consistent with
+        //  writeSize()
+        //
+        //  \return The previous data offset
+        virtual uint64_t offset(const uint64_t numbytes);
 
         //- Write leading size for binary output
-        virtual void writeSize(const uint64_t nBytes);
+        //  \return True - format uses this information
+        virtual bool writeSize(const uint64_t numbytes);
 
         virtual void write(const uint8_t val);
         virtual void write(const label val);
diff --git a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C
index d220627ab7182a8b2e659add37f0ee34c1ae4b24..fa549ff24a2b7999b6ed2845a061b910b13a6940 100644
--- a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C
+++ b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C
@@ -113,8 +113,10 @@ const char* Foam::vtk::asciiFormatter::encoding() const
 }
 
 
-void Foam::vtk::asciiFormatter::writeSize(const uint64_t ignored)
-{/*nop*/}
+bool Foam::vtk::asciiFormatter::writeSize(const uint64_t)
+{
+    return false;
+}
 
 
 void Foam::vtk::asciiFormatter::write(const uint8_t val)
@@ -163,8 +165,7 @@ void Foam::vtk::asciiFormatter::flush()
 }
 
 
-std::size_t
-Foam::vtk::asciiFormatter::encodedLength(std::size_t ignored) const
+std::size_t Foam::vtk::asciiFormatter::encodedLength(std::size_t) const
 {
     return 0;
 }
diff --git a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H
index b050d6573c31ba01f348e0bfe99b3b6d1c4fac18..25d7fc7f380122046ed3a4fe6c89b9476b3cc154 100644
--- a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H
+++ b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -47,7 +47,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                       Class asciiFormatter Declaration
+                     Class vtk::asciiFormatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class asciiFormatter
@@ -108,7 +108,8 @@ public:
 
 
         //- Write leading size - this is a no-op for ascii output
-        virtual void writeSize(const uint64_t ignored);
+        //  \return False - never used by this format
+        virtual bool writeSize(const uint64_t ignored);
 
         virtual void write(const uint8_t val);
         virtual void write(const label val);
@@ -119,6 +120,7 @@ public:
         virtual void flush();
 
         //- The encoded length for ascii output is not applicable.
+        //  \return 0
         virtual std::size_t encodedLength(std::size_t ignored) const;
 
 };
diff --git a/src/fileFormats/vtk/format/foamVtkBase64Formatter.H b/src/fileFormats/vtk/format/foamVtkBase64Formatter.H
index cd733e643e5d6388ae385fce3bed1936cef50532..9b96c1ac719891379aca65695fbef4e00f37e18e 100644
--- a/src/fileFormats/vtk/format/foamVtkBase64Formatter.H
+++ b/src/fileFormats/vtk/format/foamVtkBase64Formatter.H
@@ -44,7 +44,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                       Class base64Formatter Declaration
+                    Class vtk::base64Formatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class base64Formatter
diff --git a/src/fileFormats/vtk/format/foamVtkBase64Layer.C b/src/fileFormats/vtk/format/foamVtkBase64Layer.C
index fbdc7ad5257203ae75a0ead3919a00e4cb21789e..8cbfbc2c01ffcaf7de691b984265ab5b3bc01375 100644
--- a/src/fileFormats/vtk/format/foamVtkBase64Layer.C
+++ b/src/fileFormats/vtk/format/foamVtkBase64Layer.C
@@ -68,9 +68,10 @@ const char* Foam::vtk::foamVtkBase64Layer::encoding() const
 }
 
 
-void Foam::vtk::foamVtkBase64Layer::writeSize(const uint64_t nBytes)
+bool Foam::vtk::foamVtkBase64Layer::writeSize(const uint64_t numbytes)
 {
-    write(reinterpret_cast<const char*>(&nBytes), sizeof(uint64_t));
+    write(reinterpret_cast<const char*>(&numbytes), sizeof(uint64_t));
+    return true;
 }
 
 
@@ -121,10 +122,7 @@ void Foam::vtk::foamVtkBase64Layer::flush()
 }
 
 
-std::size_t Foam::vtk::foamVtkBase64Layer::encodedLength
-(
-    std::size_t n
-) const
+std::size_t Foam::vtk::foamVtkBase64Layer::encodedLength(std::size_t n) const
 {
     return base64Layer::encodedLength(n);
 }
diff --git a/src/fileFormats/vtk/format/foamVtkBase64Layer.H b/src/fileFormats/vtk/format/foamVtkBase64Layer.H
index d965c6348ddb176ba3c2328b9036857c48cac301..3805a57747af2b4b4c458ad354ed0c68c4382954 100644
--- a/src/fileFormats/vtk/format/foamVtkBase64Layer.H
+++ b/src/fileFormats/vtk/format/foamVtkBase64Layer.H
@@ -43,7 +43,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                     Class foamVtkBase64Layer Declaration
+                   Class vtk::foamVtkBase64Layer Declaration
 \*---------------------------------------------------------------------------*/
 
 class foamVtkBase64Layer
@@ -89,9 +89,9 @@ public:
         //- Name for the XML append encoding ("base64").
         virtual const char* encoding() const;
 
-
         //- Write leading size for binary output
-        virtual void writeSize(const uint64_t nBytes);
+        //  \return True - format uses this information
+        virtual bool writeSize(const uint64_t numbytes);
 
         virtual void write(const uint8_t val);
         virtual void write(const label val);
diff --git a/src/fileFormats/vtk/format/foamVtkFormatter.C b/src/fileFormats/vtk/format/foamVtkFormatter.C
index 711bcdecd248278378b5df3a9b9b1c4d32f26b5f..c6b2a4e56f70604b6f6246eabce354ffffa46d26 100644
--- a/src/fileFormats/vtk/format/foamVtkFormatter.C
+++ b/src/fileFormats/vtk/format/foamVtkFormatter.C
@@ -24,104 +24,103 @@ License
 
 #include "foamVtkFormatter.H"
 
-// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
-
-std::size_t Foam::vtk::formatter::encodedLength(std::size_t n) const
-{
-    return n;
-}
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
-
-void Foam::vtk::formatter::indent()
+bool Foam::vtk::formatter::canWriteAttr(const word& k) const
 {
-    label n = xmlTags_.size() * 2;
-    while (n--)
+    if (!inTag_)
     {
-        os_ << ' ';
+        WarningInFunction
+            << "xml attribute '" << k << "' but not inside a tag!" << endl;
     }
+
+    return inTag_;
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::xmlHeader()
+bool Foam::vtk::formatter::canWriteToplevel(const char* what) const
 {
     if (inTag_)
     {
         WarningInFunction
-            << "xml header, but already within a tag!"
+            << "Cannot add " << what << " inside a tag!"
             << endl;
     }
 
-    os_ << "<?xml version='1.0'?>" << nl;
+    return !inTag_;
+}
 
-    return *this;
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+void Foam::vtk::formatter::quoting(const quoteChar quote)
+{
+    quote_ = quote;
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::xmlComment(const std::string& comment)
+uint64_t Foam::vtk::formatter::offset(const uint64_t)
 {
-    if (inTag_)
-    {
-        WarningInFunction
-            << "adding xml comment inside a tag??"
-            << endl;
-    }
+    return formatter::npos;
+}
 
-    indent();
-    os_ << "<!-- " << comment << " -->" << nl;
 
-    return *this;
+std::size_t Foam::vtk::formatter::encodedLength(std::size_t n) const
+{
+    return n;
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::openTag(const word& tagName)
+bool Foam::vtk::formatter::openTagImpl(const word& tagName)
 {
     if (inTag_)
     {
         WarningInFunction
-            << "open XML tag '" << tagName
-            << "', but already within a tag!"
+            << "open xml tag '" << tagName << "', but already within a tag!"
             << endl;
+
+        return false;
     }
 
+    // Emit, before changing the stack or the state.
     indent();
     os_ << '<' << tagName;
 
+    // Add to the stack and change the state.
     xmlTags_.append(tagName);
     inTag_ = true;
 
-    return *this;
+    return true;
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::closeTag(const bool isEmpty)
+Foam::vtk::formatter& Foam::vtk::formatter::closeTag(const bool isEmpty)
 {
     if (!inTag_)
     {
         WarningInFunction
-            << "close XML tag, but not within a tag!"
+            << "attempt to close xml tag, but not within a tag!"
             << endl;
     }
-
-    if (isEmpty)
+    else
     {
-        // eg, <tag ... />
-        xmlTags_.remove();
-        os_ << " /";
+        // Change the state
+        inTag_ = false;
+
+        if (isEmpty)
+        {
+            // Eg, <tag ... />
+            xmlTags_.remove();
+            os_ << " /";
+        }
+        os_ << '>' << nl;
     }
-    os_ << '>' << nl;
-
-    inTag_ = false;
 
     return *this;
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::endTag(const word& tagName)
+Foam::vtk::formatter& Foam::vtk::formatter::endTag(const word& tagName)
 {
     const word curr(xmlTags_.remove());
     indent();
@@ -129,16 +128,18 @@ Foam::vtk::formatter::endTag(const word& tagName)
     if (inTag_)
     {
         WarningInFunction
-            << "adding XML endTag '" << curr
+            << "adding xml endTag '" << curr
             << "' but already in another tag!"
             << endl;
+
+        // Also suppress further output, or not?
     }
 
-    // verify expected end tag
+    // Verify expected end tag
     if (!tagName.empty() && tagName != curr)
     {
         WarningInFunction
-            << "expecting to end xml-tag '" << tagName
+            << "expecting to end xml tag '" << tagName
             << "' but found '" << curr << "' instead"
             << endl;
     }
@@ -151,8 +152,7 @@ Foam::vtk::formatter::endTag(const word& tagName)
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::beginVTKFile
+Foam::vtk::formatter& Foam::vtk::formatter::beginVTKFile
 (
     const word& contentType,
     const word& contentVersion,
@@ -176,15 +176,7 @@ Foam::vtk::formatter::beginVTKFile
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::endVTKFile()
-{
-    return endTag(vtk::fileTag::VTK_FILE);
-}
-
-
-Foam::vtk::formatter&
-Foam::vtk::formatter::beginAppendedData()
+Foam::vtk::formatter& Foam::vtk::formatter::beginAppendedData()
 {
     openTag("AppendedData");
     xmlAttr("encoding", encoding());
@@ -195,31 +187,124 @@ Foam::vtk::formatter::beginAppendedData()
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::endAppendedData()
+Foam::vtk::formatter& Foam::vtk::formatter::endAppendedData()
 {
-    flush();     // flush any pending encoded content
-    os_ << nl;   // ensure clear separation from content.
+    flush();     // Flush any pending encoded content
+    os_ << nl;   // Ensure clear separation from content
     return endTag("AppendedData");
 }
 
 
-Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+Foam::vtk::formatter& Foam::vtk::formatter::beginBlock
 (
-    const word& k,
-    const std::string& v,
-    const char quote
+    label index,
+    std::string name
 )
 {
-    if (!inTag_)
+    openTag(vtk::fileTag::BLOCK);
+    if (index >= 0)
     {
-        WarningInFunction
-            << "xml attribute '" << k << "' but not within a tag!"
-            << endl;
+        xmlAttr("index", index);
+    }
+    if (name.size())
+    {
+        xmlAttr("name", name);
     }
+    closeTag();
+
+    return *this;
+}
+
+
+Foam::vtk::formatter& Foam::vtk::formatter::beginPiece
+(
+    label index,
+    std::string name
+)
+{
+    openTag(vtk::fileTag::PIECE);
+    if (index >= 0)
+    {
+        xmlAttr("index", index);
+    }
+    if (name.size())
+    {
+        xmlAttr("name", name);
+    }
+    closeTag();
+
+    return *this;
+}
+
+
+Foam::vtk::formatter& Foam::vtk::formatter::DataSet
+(
+    label index,
+    std::string file,
+    bool autoName
+)
+{
+    openTag(vtk::fileTag::DATA_SET);
+
+    if (index >= 0)
+    {
+        xmlAttr("index", index);
+    }
+    if (file.size())
+    {
+        if (autoName)
+        {
+            xmlAttr("name", fileName::nameLessExt(file));
+        }
+        xmlAttr("file", file);
+    }
+    closeTag(true);  // Empty tag. ie, <DataSet ... />
+
+    return *this;
+}
+
+
+Foam::vtk::formatter& Foam::vtk::formatter::DataSet
+(
+    label index,
+    std::string file,
+    std::string name
+)
+{
+    openTag(vtk::fileTag::DATA_SET);
+
+    if (index >= 0)
+    {
+        xmlAttr("index", index);
+    }
+    if (name.size())
+    {
+        xmlAttr("name", name);
+    }
+    if (file.size())
+    {
+        xmlAttr("file", file);
+    }
+    closeTag(true);  // Empty tag. ie, <DataSet ... />
+
+    return *this;
+}
+
+
+Foam::vtk::formatter& Foam::vtk::formatter::writeTimeValue(scalar timeValue)
+{
+    // Emit "TimeValue" as FieldData
+    // NumberOfTuples="1" (required!)
+
+    uint64_t payLoad = vtk::sizeofData<float>(1);
+
+    beginDataArray<float,1,1>("TimeValue");
+    writeSize(payLoad);
+
+    write(timeValue);
+    flush();
 
-    os_ << ' ' << k << '=' << quote << v.c_str() << quote;
+    endDataArray();
 
     return *this;
 }
diff --git a/src/fileFormats/vtk/format/foamVtkFormatter.H b/src/fileFormats/vtk/format/foamVtkFormatter.H
index c3e9c916386ff62e8c0a0df92917760706d71e19..ef423c10cc26a18ec80ab790e0f2d68496ecca51 100644
--- a/src/fileFormats/vtk/format/foamVtkFormatter.H
+++ b/src/fileFormats/vtk/format/foamVtkFormatter.H
@@ -27,7 +27,8 @@ Class
 Description
     Abstract class for a VTK output stream formatter.
 
-    Includes very simple support for writing XML elements.
+    Includes simple support for writing XML elements.
+    By default uses single-quoting for XML attributes.
 
 SourceFiles
     foamVtkFormatter.C
@@ -39,8 +40,9 @@ SourceFiles
 #define foamVtkFormatter_H
 
 #include "int.H"
-#include "uint64.H"
 #include "label.H"
+#include "uint64.H"
+#include "direction.H"
 #include "word.H"
 #include "UList.H"
 #include "DynamicList.H"
@@ -59,11 +61,23 @@ namespace vtk
 class outputOptions;
 
 /*---------------------------------------------------------------------------*\
-                          Class formatter Declaration
+                       Class vtk::formatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class formatter
 {
+public:
+
+    //- Quoting for XML attributes
+    enum quoteChar : char
+    {
+        DOUBLE_QUOTE = '\"',  //!< Double-quote XML attributes
+        SINGLE_QUOTE = '\'',  //!< Single-quote XML attributes
+    };
+
+
+protected:
+
     // Private Data
 
         //- The output stream for the formatter
@@ -75,33 +89,60 @@ class formatter
         //- Tag open/closed/ended state
         mutable bool inTag_;
 
+        //- Quoting character for XML attributes
+        char quote_;
+
 
-    // Private Member Functions
+protected:
+
+    // Protected Member Functions
 
-        //- Write XML attribute key/value pair
+        //- Can write XML key/value attribute pair when inside a tag.
+        //- Emit warning and return false if this condition is not met.
+        bool canWriteAttr(const word& k) const;
+
+        //- Can write tag-like top-level content (eg, comment, ...)
+        //- when not already inside a tag.
+        //- Emit warning and return false if this condition is not met.
+        bool canWriteToplevel(const char* what) const;
+
+        //- Write XML key/value attribute pair (implementation).
         template<class Type>
-        formatter& writeAttribute
-        (
-            const word& k,
-            const Type& v,
-            const char quote = '\''
-        );
+        inline void writeAttr(const word& k, const Type& v);
 
+        //- No-op write XML attribute (for templating code).
+        //  \return formatter for chaining
+        inline formatter& xmlAttr();
 
-protected:
+        //- No-op XML comment loop (for templating code).
+        inline void xmlCommentLoop();
 
-    // Protected Member Functions
+        //- Loop/output XML comments
+        template<class... Args>
+        inline void xmlCommentLoop(const std::string& text, Args&&... args);
+
+        //- Open XML tag (implementation), checking if not already inside
+        //- another tag.
+        //- Emit warning and return false if this condition is not met.
+        bool openTagImpl(const word& tagName);
+
+
+    // Constructors
 
         //- Construct and attach to an output stream
         inline formatter(std::ostream& os);
 
+
 public:
 
     // Public typedefs
 
-        //- Use UInt64 for header data
+        //- The header data is vtk UInt64
         typedef uint64_t headerType;
 
+        //- Out of range position or size.
+        static constexpr uint64_t npos = uint64_t(-1);
+
 
     //- Destructor
     virtual ~formatter() = default;
@@ -123,9 +164,23 @@ public:
         //- Name for the XML append encoding
         virtual const char* encoding() const = 0;
 
+        //- Change quoting char for XML attributes (default: SINGLE_QUOTE)
+        void quoting(const quoteChar quote);
+
+        //- Increase the append data offset by numbytes and sizeof(uint64_t).
+        //  The additional (uint64_t) size information is consistent with
+        //  writeSize()
+        //
+        //  \return The previous data offset or formatter::npos for formats
+        //     that do not support appending data.
+        virtual uint64_t offset(const uint64_t numbytes);
+
+        //- The encoded length for binary output is pass-through.
+        virtual std::size_t encodedLength(std::size_t n) const;
 
         //- Write leading size for binary output
-        virtual void writeSize(const uint64_t nBytes) = 0;
+        //  \return True if used by the formatter.
+        virtual bool writeSize(const uint64_t numbytes) = 0;
 
         virtual void write(const uint8_t val) = 0;
         virtual void write(const label val)   = 0;
@@ -135,61 +190,65 @@ public:
         //- Flush encoding, write newline etc.
         virtual void flush() = 0;
 
-        //- The encoded length for binary output.
-        //  The default is pass-through.
-        virtual std::size_t encodedLength(std::size_t n) const;
-
 
-    // Member Functions
+    // General Output
 
-    // Output
+        //- Add indenting according to the current XML tag depth
+        //  Two spaces per depth.
+        inline void indent();
 
-        //- Indent according to the currently nested XML tags
-        void indent();
+        //- Add indenting of n spaces.
+        inline void indent(label n);
 
         //- Write XML header
         //  \return formatter for chaining
-        formatter& xmlHeader();
+        inline formatter& xmlHeader();
 
         //- Write XML comment (at the current indentation level)
         //  \return formatter for chaining
-        formatter& xmlComment(const std::string& comment);
+        template<class... Args>
+        inline formatter& xmlComment(const std::string& text, Args&&... args);
 
 
-        //- Open XML tag
+        //- Start an XML tag, optionally with attributes
         //  \return formatter for chaining
-        formatter& openTag(const word& tagName);
+        template<class... Args>
+        inline formatter& openTag(const word& tagName, Args&&... args);
 
-        //- Open XML tag
+        //- Start an XML tag, optionally with attributes
         //  \return formatter for chaining
-        inline formatter& openTag(const vtk::fileTag& tagEnum);
+        template<class... Args>
+        inline formatter& openTag(vtk::fileTag t, Args&&... args);
 
-        //- Close XML tag, optional as an empty container.
+        //- Finish an XML tag, optional as an empty container.
         //  Always adds a trailing newline.
         //  \return formatter for chaining
         formatter& closeTag(const bool isEmpty = false);
 
-        //- End XML tag, optional with sanity check
+        //- An end XML tag, optional with sanity check
         //  Always adds a trailing newline.
         //  \return formatter for chaining
         formatter& endTag(const word& tagName = word::null);
 
-        //- End XML tag with sanity check
+        //- An end XML tag with sanity check
         //  Always adds a trailing newline.
         //  \return formatter for chaining
-        inline formatter& endTag(const vtk::fileTag& tagEnum);
+        inline virtual formatter& endTag(vtk::fileTag t);
 
         //- Write XML tag without any attributes. Combines openTag/closeTag.
         //  \return formatter for chaining
-        inline formatter& tag(const word& tagName);
+        template<class... Args>
+        inline formatter& tag(const word& t, Args&&... args);
 
         //- Write XML tag without any attributes. Combines openTag/closeTag.
         //  \return formatter for chaining
-        inline formatter& tag(const vtk::fileTag& tagEnum);
+        template<class... Args>
+        inline formatter& tag(vtk::fileTag t, Args&&... args);
+
 
         //- Add a "VTKFile" XML tag for contentType, followed by a tag for
-        //  the contentType itself. Optionally leave the contentType tag
-        //  open for adding additional attributes.
+        //- the contentType itself.
+        //  \param leaveOpen Leave tag open for additional attributes.
         //  \return formatter for chaining
         formatter& beginVTKFile
         (
@@ -199,127 +258,300 @@ public:
         );
 
         //- Add a "VTKFile" XML tag for contentType, followed by a tag for
-        //  the contentType itself. Optionally leave the contentType tag
-        //  open for adding additional attributes.
+        //- the contentType itself.
+        //  \param leaveOpen Leave tag open for additional attributes.
         //  \return formatter for chaining
         inline formatter& beginVTKFile
         (
-            const vtk::fileTag& contentType,
+            vtk::fileTag contentType,
             const word& contentVersion,
             const bool leaveOpen = false
         );
 
+        //- Add a "VTKFile" XML tag for contentType, followed by a tag for
+        //- the contentType itself.
+        //  \param leaveOpen Leave tag open for additional attributes.
+        //  \return formatter for chaining
+        inline formatter& beginVTKFile
+        (
+            vtk::fileTag contentType,
+            const bool leaveOpen = false
+        );
+
+        //- Add a "VTKFile" XML tag for contentType, followed by a tag for
+        //- the contentType itself.
+        //  \param leaveOpen Leave tag open for additional attributes.
+        //  \return formatter for chaining
+        template<vtk::fileTag ContentType>
+        inline formatter& beginVTKFile(bool leaveOpen = false);
+
         //- Add a "AppendedData" XML tag with the current encoding and output
-        //  the requisite '_' prefix.
+        //- the requisite '_' prefix.
         //  \return formatter for chaining
         formatter& beginAppendedData();
 
+        //- Begin "Block" XML section.
+        //  \param index The index of the block
+        //  \param name The name of the block (ignored if empty)
+        //  \return formatter for chaining
+        formatter& beginBlock(label index, std::string name = "");
+
+        //- End "Block" XML section.
+        //  \return formatter for chaining
+        inline formatter& endBlock();
+
+        //- Begin "Piece" XML section.
+        //  \param index The index of the piece
+        //  \param name The name of the piece (ignored if empty)
+        //  \return formatter for chaining
+        formatter& beginPiece(label index, std::string name = "");
+
+        //- End "Piece" XML section.
+        //  \return formatter for chaining
+        inline virtual formatter& endPiece();
 
-        //- Open "DataArray" XML tag
+        //- Insert a single "DataSet" XML entry tag.
+        //  \param index The index of the DataSet
+        //  \param file The file name for the data (ignored if empty)
+        //  \param autoName The name for the data extracted from the file name
+        //      (without extension)
         //  \return formatter for chaining
-        template<class Type, int nComp=0>
-        formatter& openDataArray(const word& dataName);
+        formatter& DataSet
+        (
+            label index,
+            std::string file = "",
+            bool autoName = true
+        );
 
-        //- Open "DataArray" XML tag
+        //- Insert a single "DataSet" XML entry tag.
+        //  \param index The index of the DataSet
+        //  \param file The file name for the data (ignored if empty)
+        //  \param name The name for the dataset
         //  \return formatter for chaining
-        template<class Type, int nComp=0>
-        formatter& openDataArray(const vtk::dataArrayAttr& attrEnum);
+        formatter& DataSet
+        (
+            label index,
+            std::string file,
+            std::string name
+        );
 
+        //- Begin "DataArray" XML section.
+        //
+        //  \param dataName The name of the DataArray
+        //  \param payLoad  Additional payLoad information to increment
+        //     the offset for an append formatter and add the "offset"
+        //     attribute accordingly.
+        //  \param leaveOpen Leave tag open for additional attributes.
+        //
+        //  \return formatter for chaining
+        template<class Type, direction nComp=1, int nTuple=0>
+        formatter& beginDataArray
+        (
+            const word& dataName,
+            uint64_t payLoad = npos,
+            bool leaveOpen = false
+        );
+
+        //- Begin "DataArray" XML section.
+        //
+        //  \param dataName The name of the DataArray as an enumeration
+        //  \param payLoad  Additional payLoad information to increment
+        //     the offset for an append formatter and add the "offset"
+        //     attribute accordingly.
+        //  \param leaveOpen Leave tag open for additional attributes.
+        //
+        //  \return formatter for chaining
+        template<class Type, direction nComp=1, int nTuple=0>
+        inline formatter& beginDataArray
+        (
+            const vtk::dataArrayAttr& dataName,
+            uint64_t payLoad = npos,
+            bool leaveOpen = false
+        );
+
+        //- End "DataArray" XML section
+        //  \return formatter for chaining
+        inline virtual formatter& endDataArray();
 
         //- Insert a single "PDataArray" XML entry tag.
         //  For some entries, the name is optional.
         //  \return formatter for chaining
-        template<class Type, int nComp=0>
+        template<class Type, direction nComp=1, int nTuple=0>
         formatter& PDataArray(const word& dataName);
 
 
-        //- End "DataArray" XML tag
-        //  \return formatter for chaining
-        inline formatter& endDataArray();
+        //- Begin "FieldData" XML section.
+        inline formatter& beginFieldData();
+
+        //- Begin "CellData" XML section.
+        inline formatter& beginCellData();
+
+        //- Begin "PointData" XML section.
+        inline formatter& beginPointData();
+
+        //- End "FieldData" XML section.
+        inline virtual formatter& endFieldData();
+
+        //- End "CellData" XML section.
+        inline virtual formatter& endCellData();
+
+        //- End "PointData" XML section.
+        inline virtual formatter& endPointData();
+
 
-        //- End "AppendedData" XML tag
+        //- End "AppendedData" XML section
         //  \return formatter for chaining
         formatter& endAppendedData();
 
-        //- End "VTKFile" XML tag
+        //- End "VTKFile" XML section.
         //  \return formatter for chaining
-        formatter& endVTKFile();
+        inline virtual formatter& endVTKFile();
 
 
-        //- Write XML attribute
+        //- Emit "TimeValue" for FieldData (name as per Catalyst output)
+        formatter& writeTimeValue(scalar timeValue);
+
+
+    // XML Attributes
+
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
-        formatter& xmlAttr
+        template<class... Args>
+        inline formatter& xmlAttr
         (
             const word& k,
             const std::string& v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
             const word& k,
             const int32_t v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
             const word& k,
             const int64_t v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
             const word& k,
             const uint64_t v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
             const word& k,
             const scalar v,
-            const char quote = '\''
+            Args&&... args
+        );
+
+        //- Pair-wise write of XML key/value attributes
+        //  \return formatter for chaining
+        template<class... Args>
+        inline formatter& xmlAttr
+        (
+            const vtk::fileAttr& k,
+            const std::string& v,
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
-            const vtk::fileAttr& attrEnum,
+            const vtk::fileAttr& k,
             const int32_t v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
-            const vtk::fileAttr& attrEnum,
+            const vtk::fileAttr& k,
             const int64_t v,
-            const char quote = '\''
+            Args&&... args
         );
 
-        //- Write XML attribute
+        //- Pair-wise write of XML key/value attributes
         //  \return formatter for chaining
+        template<class... Args>
         inline formatter& xmlAttr
         (
-            const vtk::fileAttr& attrEnum,
+            const vtk::fileAttr& k,
             const uint64_t v,
-            const char quote = '\''
+            Args&&... args
         );
+
+        //- Pair-wise write of XML key/value attributes
+        //  \return formatter for chaining
+        template<class... Args>
+        inline formatter& xmlAttr
+        (
+            const vtk::fileAttr& k,
+            const scalar v,
+            Args&&... args
+        );
+
+
+    // Housekeeping
+
+        //- Open "DataArray" XML tag and leave open (requires a closeTag).
+        //  \deprecated Use beginDataArray instead (SEPT-2018)
+        template<class Type, direction nComp=1, int nTuple=0>
+        formatter& openDataArray(const word& dataName)
+        {
+            return beginDataArray<Type, nComp, nTuple>
+            (
+                dataName, formatter::npos, true
+            );
+        }
+
+        //- Open "DataArray" XML tag and leave open (requires a closeTag).
+        //  \deprecated Use beginDataArray instead (SEPT-2018)
+        template<class Type, direction nComp=1, int nTuple=0>
+        formatter& openDataArray(const vtk::dataArrayAttr& dataName)
+        {
+            return beginDataArray<Type, nComp, nTuple>
+            (
+                dataName, formatter::npos, true
+            );
+        }
+
 };
 
 
+// Global Functions
+
+//- Commonly used calculation for header and payload sizes
+template<class Type, direction nComp=1>
+inline uint64_t sizeofData(label count)
+{
+    return (count * nComp * sizeof(Type));
+}
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace vtk
diff --git a/src/fileFormats/vtk/format/foamVtkFormatterI.H b/src/fileFormats/vtk/format/foamVtkFormatterI.H
index 58e050bf54223b03609bab01eff9afe9f385e85a..bf3204c388a505f28a5a4432bede3c026ffa0fd1 100644
--- a/src/fileFormats/vtk/format/foamVtkFormatterI.H
+++ b/src/fileFormats/vtk/format/foamVtkFormatterI.H
@@ -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-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -23,13 +23,43 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr()
+{
+    return *this;
+}
+
+
+inline void Foam::vtk::formatter::xmlCommentLoop()
+{}
+
+
+template<class... Args>
+inline void Foam::vtk::formatter::xmlCommentLoop
+(
+    const std::string& text,
+    Args&&... args
+)
+{
+    if (text.length())
+    {
+        indent(); indent(4);
+        os_ << text << nl;
+    }
+
+    xmlCommentLoop(std::forward<Args>(args)...);
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline Foam::vtk::formatter::formatter(std::ostream& os)
 :
     os_(os),
     xmlTags_(),
-    inTag_(false)
+    inTag_(false),
+    quote_(SINGLE_QUOTE)
 {}
 
 
@@ -41,43 +71,124 @@ inline std::ostream& Foam::vtk::formatter::os()
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::openTag(const vtk::fileTag& tagEnum)
+inline void Foam::vtk::formatter::indent()
 {
-    return openTag(vtk::fileTagNames[tagEnum]);
+    indent(2*xmlTags_.size());
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::endTag(const vtk::fileTag& tagEnum)
+inline void Foam::vtk::formatter::indent(label n)
 {
-    return endTag(vtk::fileTagNames[tagEnum]);
+    while (n-- > 0)
+    {
+        os_ << ' ';
+    }
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::tag(const word& tagName)
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlHeader()
 {
-    openTag(tagName);
+    if (canWriteToplevel("xml header"))
+    {
+        os_ << "<?xml version='1.0'?>" << nl;
+    }
+
+    return *this;
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlComment
+(
+    const std::string& text,
+    Args&&... args
+)
+{
+    if (canWriteToplevel("xml comment"))
+    {
+        indent();
+        os_ << "<!--";
+
+        if (sizeof...(Args))
+        {
+            os_ << nl;
+
+            xmlCommentLoop(text, std::forward<Args>(args)...);
+
+            indent(); indent(2);
+        }
+        else
+        {
+            os_ << ' ' << text << ' ';
+        }
+
+        os_ << "-->" << nl;
+    }
+
+    return *this;
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::openTag
+(
+    const word& tagName,
+    Args&&... args
+)
+{
+    if (openTagImpl(tagName))
+    {
+        xmlAttr(std::forward<Args>(args)...);
+    }
+
+    return *this;
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::openTag
+(
+    vtk::fileTag t,
+    Args&&... args
+)
+{
+    return openTag(vtk::fileTagNames[t], std::forward<Args>(args)...);
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::tag
+(
+    const word& t,
+    Args&&... args
+)
+{
+    openTagImpl(t);
+    xmlAttr(std::forward<Args>(args)...);
     closeTag();
 
     return *this;
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::tag(const vtk::fileTag& tagEnum)
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::tag
+(
+    vtk::fileTag t,
+    Args&&... args
+)
 {
-    return tag(vtk::fileTagNames[tagEnum]);
+    return tag(vtk::fileTagNames[t], std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::beginVTKFile
+// Begin tags
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginVTKFile
 (
-    const vtk::fileTag& contentType,
+    vtk::fileTag contentType,
     const word& contentVersion,
-    const bool leaveOpen
+    bool leaveOpen
 )
 {
     return beginVTKFile
@@ -89,94 +200,275 @@ Foam::vtk::formatter::beginVTKFile
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::endDataArray()
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginVTKFile
+(
+    vtk::fileTag contentType,
+    bool leaveOpen
+)
+{
+    return beginVTKFile
+    (
+        vtk::fileTagNames[contentType],
+        vtk::fileContentVersions[contentType],
+        leaveOpen
+    );
+}
+
+
+template<Foam::vtk::fileTag ContentType>
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginVTKFile(bool leaveOpen)
+{
+    return beginVTKFile
+    (
+        vtk::fileTagNames[ContentType],
+        vtk::fileContentVersions[ContentType],
+        leaveOpen
+    );
+}
+
+
+template<class Type, Foam::direction nComp, int nTuple>
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginDataArray
+(
+    const vtk::dataArrayAttr& dataName,
+    uint64_t payLoad,
+    bool leaveOpen
+)
+{
+    return
+        beginDataArray<Type, nComp, nTuple>
+        (
+            vtk::dataArrayAttrNames[dataName],
+            payLoad,
+            leaveOpen
+        );
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginCellData()
+{
+    return tag(vtk::fileTag::CELL_DATA);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginPointData()
+{
+    return tag(vtk::fileTag::POINT_DATA);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::beginFieldData()
+{
+    return tag(vtk::fileTag::FIELD_DATA);
+}
+
+
+// End tags
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endTag(vtk::fileTag t)
+{
+    return endTag(vtk::fileTagNames[t]);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endCellData()
 {
-    return endTag("DataArray");
+    return endTag(vtk::fileTag::CELL_DATA);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+inline Foam::vtk::formatter& Foam::vtk::formatter::endPointData()
+{
+    return endTag(vtk::fileTag::POINT_DATA);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endFieldData()
+{
+    return endTag(vtk::fileTag::FIELD_DATA);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endDataArray()
+{
+    return endTag(vtk::fileTag::DATA_ARRAY);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endBlock()
+{
+    return endTag(vtk::fileTag::BLOCK);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endPiece()
+{
+    return endTag(vtk::fileTag::PIECE);
+}
+
+
+inline Foam::vtk::formatter& Foam::vtk::formatter::endVTKFile()
+{
+    return endTag(vtk::fileTag::VTK_FILE);
+}
+
+
+// Attributes
+
+template<class Type>
+inline void Foam::vtk::formatter::writeAttr(const word& k, const Type& v)
+{
+    os_ << ' ' << k << '=' << quote_ << v << quote_;
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
+(
+    const word& k,
+    const std::string& v,
+    Args&&... args
+)
+{
+    if (!canWriteAttr(k)) return *this;
+
+    writeAttr(k, v.c_str());
+    return xmlAttr(std::forward<Args>(args)...);
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
     const word& k,
     const int32_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return writeAttribute(k, v, quote);
+    if (!canWriteAttr(k)) return *this;
+
+    writeAttr(k, v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
     const word& k,
     const int64_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return writeAttribute(k, v, quote);
+    if (!canWriteAttr(k)) return *this;
+
+    writeAttr(k, v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
     const word& k,
     const uint64_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return writeAttribute(k, v, quote);
+    if (!canWriteAttr(k)) return *this;
+
+    writeAttr(k, v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
     const word& k,
     const scalar v,
-    const char quote
+    Args&&... args
 )
 {
-    return writeAttribute(k, v, quote);
+    if (!canWriteAttr(k)) return *this;
+
+    writeAttr(k, v);
+    return xmlAttr(std::forward<Args>(args)...);
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
+(
+    const vtk::fileAttr& k,
+    const std::string& v,
+    Args&&... args
+)
+{
+    if (!canWriteAttr(vtk::fileAttrNames[k])) return *this;
+
+    writeAttr(vtk::fileAttrNames[k], v.c_str());
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
-    const vtk::fileAttr& attrEnum,
+    const vtk::fileAttr& k,
     const int32_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return xmlAttr(vtk::fileAttrNames[attrEnum], v, quote);
+    if (!canWriteAttr(vtk::fileAttrNames[k])) return *this;
+
+    writeAttr(vtk::fileAttrNames[k], v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
-    const vtk::fileAttr& attrEnum,
+    const vtk::fileAttr& k,
     const int64_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return xmlAttr(vtk::fileAttrNames[attrEnum], v, quote);
+    if (!canWriteAttr(vtk::fileAttrNames[k])) return *this;
+
+    writeAttr(vtk::fileAttrNames[k], v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
-inline Foam::vtk::formatter&
-Foam::vtk::formatter::xmlAttr
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
 (
-    const vtk::fileAttr& attrEnum,
+    const vtk::fileAttr& k,
     const uint64_t v,
-    const char quote
+    Args&&... args
 )
 {
-    return xmlAttr(vtk::fileAttrNames[attrEnum], v, quote);
+    if (!canWriteAttr(vtk::fileAttrNames[k])) return *this;
+
+    writeAttr(vtk::fileAttrNames[k], v);
+    return xmlAttr(std::forward<Args>(args)...);
+}
+
+
+template<class... Args>
+inline Foam::vtk::formatter& Foam::vtk::formatter::xmlAttr
+(
+    const vtk::fileAttr& k,
+    const scalar v,
+    Args&&... args
+)
+{
+    if (!canWriteAttr(vtk::fileAttrNames[k])) return *this;
+
+    writeAttr(vtk::fileAttrNames[k], v);
+    return xmlAttr(std::forward<Args>(args)...);
 }
 
 
diff --git a/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C b/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C
index 87f4f9131b50ccfdc3fb37088d95c240e57a147e..215d8bcb7d9d7fb0c33d586d8d291e2dc6b59717 100644
--- a/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C
+++ b/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -26,75 +26,66 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-template<class Type>
-Foam::vtk::formatter&
-Foam::vtk::formatter::writeAttribute
+template<class Type, Foam::direction nComp, int nTuple>
+Foam::vtk::formatter& Foam::vtk::formatter::beginDataArray
 (
-    const word& k,
-    const Type& v,
-    const char quote
+    const word& dataName,
+    uint64_t payLoad,
+    bool leaveOpen
 )
 {
-    if (!inTag_)
-    {
-        WarningInFunction
-            << "xml attribute '" << k << "' but not within a tag!"
-            << endl;
-    }
-
-    os_ << ' ' << k << '=' << quote << v << quote;
-
-    return *this;
-}
-
-
-template<class Type, int nComp>
-Foam::vtk::formatter&
-Foam::vtk::formatter::openDataArray
-(
-    const word& dataName
-)
-{
-    openTag("DataArray");
+    openTag(vtk::fileTag::DATA_ARRAY);
     xmlAttr("type", vtkPTraits<Type>::typeName);
     xmlAttr("Name", dataName);
+
     if (nComp > 1)
     {
-        xmlAttr(fileAttr::NUMBER_OF_COMPONENTS, nComp);
+        xmlAttr(fileAttr::NUMBER_OF_COMPONENTS, int(nComp));
+    }
+    if (nTuple > 0)
+    {
+        xmlAttr(fileAttr::NUMBER_OF_TUPLES, nTuple);
     }
     xmlAttr("format", name());
 
-    return *this;
-}
+    if (formatter::npos != payLoad)
+    {
+        uint64_t off = offset(payLoad);
+        if (formatter::npos != off)
+        {
+            xmlAttr("offset", off);
+        }
+    }
 
+    if (!leaveOpen)
+    {
+        closeTag();
+    }
 
-template<class Type, int nComp>
-Foam::vtk::formatter&
-Foam::vtk::formatter::openDataArray
-(
-    const vtk::dataArrayAttr& attrEnum
-)
-{
-    return openDataArray<Type, nComp>(vtk::dataArrayAttrNames[attrEnum]);
+    return *this;
 }
 
 
-template<class Type, int nComp>
-Foam::vtk::formatter&
-Foam::vtk::formatter::PDataArray
+template<class Type, Foam::direction nComp, int nTuple>
+Foam::vtk::formatter& Foam::vtk::formatter::PDataArray
 (
     const word& dataName
 )
 {
     openTag("PDataArray");
     xmlAttr("type", vtkPTraits<Type>::typeName);
+
     if (dataName.size())
     {
         xmlAttr("Name", dataName);
     }
     if (nComp > 1)
     {
-        xmlAttr(fileAttr::NUMBER_OF_COMPONENTS, nComp);
+        xmlAttr(fileAttr::NUMBER_OF_COMPONENTS, int(nComp));
+    }
+    if (nTuple > 0)
+    {
+        xmlAttr(fileAttr::NUMBER_OF_TUPLES, nTuple);
     }
 
     closeTag(true);
diff --git a/src/fileFormats/vtk/format/foamVtkLegacyAsciiFormatter.H b/src/fileFormats/vtk/format/foamVtkLegacyAsciiFormatter.H
index e191d1eb937561ae4fe7c055f5cb0c6c177347f3..9bfec2594cba3045b6747d6690fbeba71f69192c 100644
--- a/src/fileFormats/vtk/format/foamVtkLegacyAsciiFormatter.H
+++ b/src/fileFormats/vtk/format/foamVtkLegacyAsciiFormatter.H
@@ -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-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -46,7 +46,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                    Class legacyAsciiFormatter Declaration
+                  Class vtk::legacyAsciiFormatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class legacyAsciiFormatter
@@ -95,6 +95,17 @@ public:
         //  Currently identical to name(), but do not rely on this.
         virtual const char* encoding() const;
 
+
+    // Disable some XML-only methods
+
+        inline virtual formatter& endTag(vtk::fileTag) { return *this; }
+        inline virtual formatter& endDataArray() { return *this; }
+        inline virtual formatter& endFieldData() { return *this; }
+        inline virtual formatter& endCellData() { return *this; }
+        inline virtual formatter& endPointData() { return *this; }
+        inline virtual formatter& endPiece() { return *this; }
+        inline virtual formatter& endVTKFile() { return *this; }
+
 };
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.C b/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.C
index 18f6890404c779af14494c5d490eb0a9360ef221..85a78e91c8a815a0477078e126db9d633b5cd97d 100644
--- a/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.C
+++ b/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.C
@@ -80,11 +80,10 @@ const char* Foam::vtk::legacyRawFormatter::encoding() const
 }
 
 
-void Foam::vtk::legacyRawFormatter::writeSize
-(
-    const uint64_t ignored
-)
-{/*nop*/}
+bool Foam::vtk::legacyRawFormatter::writeSize(const uint64_t)
+{
+    return false;
+}
 
 
 void Foam::vtk::legacyRawFormatter::write
diff --git a/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.H b/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.H
index ae88684b052675aa32a56fbc2a0c8bed2a6938ff..d37624c581febe7891a4e7206b297462f691ced1 100644
--- a/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.H
+++ b/src/fileFormats/vtk/format/foamVtkLegacyRawFormatter.H
@@ -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-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -48,7 +48,7 @@ namespace vtk
 {
 
 /*---------------------------------------------------------------------------*\
-                       Class legacyRawFormatter Declaration
+                   Class vtk::legacyRawFormatter Declaration
 \*---------------------------------------------------------------------------*/
 
 class legacyRawFormatter
@@ -104,7 +104,8 @@ public:
 
 
         //- Write leading size - a no-op for legacy binary output
-        virtual void writeSize(const uint64_t ignored);
+        //  \return False - never used by this format
+        virtual bool writeSize(const uint64_t ignored);
 
         virtual void write(const uint8_t val);
         virtual void write(const label val);
@@ -114,6 +115,17 @@ public:
         //- Write a newline to the output
         virtual void flush();
 
+
+    // Disable some XML-only methods
+
+        inline virtual formatter& endTag(vtk::fileTag) { return *this; }
+        inline virtual formatter& endDataArray() { return *this; }
+        inline virtual formatter& endFieldData() { return *this; }
+        inline virtual formatter& endCellData() { return *this; }
+        inline virtual formatter& endPointData() { return *this; }
+        inline virtual formatter& endPiece() { return *this; }
+        inline virtual formatter& endVTKFile() { return *this; }
+
 };
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/fileFormats/vtk/output/foamVtkOutput.C b/src/fileFormats/vtk/output/foamVtkOutput.C
index 589537fd286b0cca87398a032492cf4c2494eb5c..ea5f533cb6771cc439ee10cbeae762dd5d4f7e0a 100644
--- a/src/fileFormats/vtk/output/foamVtkOutput.C
+++ b/src/fileFormats/vtk/output/foamVtkOutput.C
@@ -33,33 +33,20 @@ License
 #include "foamVtkLegacyAsciiFormatter.H"
 #include "foamVtkLegacyRawFormatter.H"
 #include "typeInfo.H"
+#include "globalIndex.H"
 #include "instant.H"
+#include "Fstream.H"
+#include "Pstream.H"
+#include "OSspecific.H"
 
-// * * * * * * * * * * * * * * * Static Data * * * * * * * * * * * * * * * * //
-
-const Foam::Enum
-<
-    Foam::vtk::fileTag
->
-Foam::vtk::legacy::contentNames
-({
-    { vtk::fileTag::POLY_DATA, "POLYDATA" },
-    { vtk::fileTag::UNSTRUCTURED_GRID, "UNSTRUCTURED_GRID" },
-});
-
-
-const Foam::Enum
-<
-    Foam::vtk::fileTag
->
-Foam::vtk::legacy::dataTypeNames
-({
-    { vtk::fileTag::CELL_DATA, "CELL_DATA" },
-    { vtk::fileTag::POINT_DATA, "POINT_DATA" }
-});
+// * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
+Foam::autoPtr<Foam::vtk::formatter>
+Foam::vtk::newFormatter(std::ostream& os, unsigned prec)
+{
+    return autoPtr<vtk::formatter>::NewFrom<vtk::asciiFormatter>(os, prec);
+}
 
-// * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
 Foam::autoPtr<Foam::vtk::formatter>
 Foam::vtk::newFormatter
@@ -102,14 +89,24 @@ Foam::vtk::newFormatter
 }
 
 
-void Foam::vtk::writeSeries
+void Foam::vtk::seriesInfo
 (
     Ostream& os,
-    const word& prefix,
-    const word& suffix,
-    const UList<instant>& series
+    const fileName& base,
+    const UList<instant>& series,
+    const char sep
 )
 {
+    // Split the base into (stem, ext) components
+    //
+    // base = "path/file.vtm"
+    //
+    // stem = "file"
+    // ext = ".vtm"
+
+    const word stem = base.nameLessExt();
+    const word ext = "." + base.ext();
+
     // Begin file-series (JSON)
     os  << "{\n  \"file-series-version\" : \"1.0\",\n  \"files\" : [\n";
 
@@ -118,12 +115,12 @@ void Foam::vtk::writeSeries
     label nremain = series.size();
 
     // Each entry
-    //   { "name" : "<prefix>name<suffix>", "time" : value }
+    //   { "name" : "<stem><sep>name<ext>",  "time" : value }
 
     for (const instant& inst : series)
     {
         os  << "    { \"name\" : \""
-            << prefix << inst.name() << suffix
+            << stem << sep << inst.name() << ext
             << "\", \"time\" : " << inst.value() << " }";
 
         if (--nremain)
@@ -137,34 +134,207 @@ void Foam::vtk::writeSeries
 }
 
 
-Foam::label Foam::vtk::writeVtmFile
+void Foam::vtk::seriesWrite
 (
-    std::ostream& os,
-    const UList<fileName>& files
+    const fileName& base,
+    const UList<instant>& series,
+    const char sep
 )
 {
-    asciiFormatter vtmFile(os);
+    mkDir(base.path());
 
-    vtmFile
-        .xmlHeader()
-        .beginVTKFile(fileTagNames[vtk::fileTag::MULTI_BLOCK], "1.0");
+    autoPtr<OFstream> osPtr =
+    (
+        base.hasExt("series")
+      ? autoPtr<OFstream>::New(base)
+      : autoPtr<OFstream>::New(base + ".series")
+    );
 
-    forAll(files, i)
+    seriesInfo(*osPtr, base, series, sep);
+}
+
+
+Foam::fileName Foam::vtk::seriesBase
+(
+    const fileName& outputName,
+    const char sep
+)
+{
+    const auto dash = outputName.rfind(sep);
+
+    // No separator? Or separator in path() instead of name()?
+    if
+    (
+        std::string::npos == dash
+     || std::string::npos != outputName.find('/', dash)
+    )
     {
-        vtmFile
-            .openTag(vtk::fileTag::DATA_SET)
-            .xmlAttr("index", i)
-            .xmlAttr("file", files[i])
-            .closeTag(true);
+        // Warn?
+        return outputName;
     }
 
-    vtmFile.endTag(fileTagNames[vtk::fileTag::MULTI_BLOCK]).endVTKFile();
+    const auto dot = outputName.find('.', dash);
 
-    return files.size();
+    if (std::string::npos == dot)
+    {
+        return outputName.substr(0, dash);
+    }
+
+    return outputName.substr(0, dash) + outputName.substr(dot);
 }
 
 
-std::ostream& Foam::vtk::legacy::fileHeader
+void Foam::vtk::writeList
+(
+    vtk::formatter& fmt,
+    const UList<uint8_t>& values
+)
+{
+    // No nComponents for char, so use fmt.write() directly
+    for (const uint8_t val : values)
+    {
+        fmt.write(val);
+    }
+}
+
+
+void Foam::vtk::writeListParallel
+(
+    vtk::formatter& fmt,
+    const UList<uint8_t>& values
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values);
+
+        List<uint8_t> recv;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv;
+
+            vtk::writeList(fmt, recv);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << values;
+    }
+}
+
+
+void Foam::vtk::writeListParallel
+(
+    vtk::formatter& fmt,
+    const labelUList& values,
+    const globalIndex& procOffset
+)
+{
+    if (Pstream::master())
+    {
+        // Write with offset
+        const label offsetId = procOffset.offset(0);
+
+        for (const label val : values)
+        {
+            vtk::write(fmt, val + offsetId);
+        }
+
+        labelList recv;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv;
+
+            const label offsetId = procOffset.offset(slave);
+
+            // Write with offset
+            for (const label val : recv)
+            {
+                vtk::write(fmt, val + offsetId);
+            }
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << values;
+    }
+}
+
+
+// * * * * * * * * * * * * * * Legacy Functions  * * * * * * * * * * * * * * //
+
+
+void Foam::vtk::legacy::fileHeader
+(
+    std::ostream& os,
+    const std::string& title,
+    bool binary
+)
+{
+    // Line 1:
+    os  << "# vtk DataFile Version 2.0" << nl;
+
+    // Line 2: title
+
+    const auto truncate = title.find('\n');
+
+    if (title.empty() || 0 == truncate)
+    {
+        // Avoid an empty title
+
+        os << "File generated by OpenFOAM";
+        #if OPENFOAM
+        os << ' ' << OPENFOAM;
+        #endif
+        os << nl;
+    }
+    else if (std::string::npos == truncate)
+    {
+        os << title << nl;
+    }
+    else
+    {
+        os << title.substr(0, truncate) << nl;
+    }
+
+    // Line 3: format
+    os  << (binary ? "BINARY" : "ASCII") << nl;
+}
+
+
+void Foam::vtk::legacy::fileHeader
 (
     vtk::formatter& fmt,
     const std::string& title,
@@ -173,13 +343,11 @@ std::ostream& Foam::vtk::legacy::fileHeader
 {
     std::ostream& os = fmt.os();
 
-    fileHeader(os, title, isType<legacyRawFormatter>(fmt));
-    if (!contentType.empty())
+    legacy::fileHeader(os, title, isType<legacyRawFormatter>(fmt));
+    if (contentType.size())
     {
         os << "DATASET " << contentType.c_str() << nl;
     }
-
-    return os;
 }
 
 
diff --git a/src/fileFormats/vtk/output/foamVtkOutput.H b/src/fileFormats/vtk/output/foamVtkOutput.H
index 2f4a0d2f7af03c1dfc5d9a2c85184ccede0b9c1d..9cd5434847a8aedefe6397973890d839fe90a601 100644
--- a/src/fileFormats/vtk/output/foamVtkOutput.H
+++ b/src/fileFormats/vtk/output/foamVtkOutput.H
@@ -44,6 +44,7 @@ SourceFiles
 #define foamVtkOutput_H
 
 #include "autoPtr.H"
+#include "bitSet.H"
 #include "Enum.H"
 #include "foamVtkCore.H"
 #include "foamVtkFormatter.H"
@@ -55,8 +56,10 @@ SourceFiles
 
 namespace Foam
 {
+
 // Forward declarations
 class instant;
+class globalIndex;
 
 namespace vtk
 {
@@ -66,7 +69,11 @@ namespace vtk
 // General Functions
 
     //- Return a default asciiFormatter
-    autoPtr<vtk::formatter> newFormatter(std::ostream& os);
+    autoPtr<vtk::formatter> newFormatter
+    (
+        std::ostream& os,
+        unsigned prec = IOstream::defaultPrecision()
+    );
 
     //- Return a new formatter based on the specified format type
     autoPtr<vtk::formatter> newFormatter
@@ -77,23 +84,56 @@ namespace vtk
     );
 
 
-    //- Write file series (JSON format) for specified time instances
+    //- Print file series (JSON format) for specified time instances
     //
-    //  \param prefix before the \c instant.name()
-    //  \param suffix after the \c instant.name()
-    //  \param series the list of name/value entries
-    void writeSeries
+    //  \param os     The output stream
+    //  \param base   The name for the series (eg, "path/file.vtm")
+    //  \param series The list of suffix/value entries
+    //  \param sep    The separator used between file stem and suffix.
+    void seriesInfo
     (
         Ostream& os,
-        const word& prefix,
-        const word& suffix,
-        const UList<instant>& series
+        const fileName& base,
+        const UList<instant>& series,
+        const char sep = '_'
     );
 
-    //- Write vtm datasets for specified files
-    label writeVtmFile(std::ostream& os, const UList<fileName>& files);
+    //- Write file series (JSON format) to disk, for specified time instances
+    //
+    //  \param base   The name for the series (eg, "path/file.vtm")
+    //  \param series The list of suffix/value entries
+    //  \param sep    The separator used between file stem and suffix.
+    void seriesWrite
+    (
+        const fileName& base,
+        const UList<instant>& series,
+        const char sep = '_'
+    );
+
+    //- Extract the base name for a file series
+    //
+    //  \param outputName   The name of the data output file
+    //      Eg, "somefile_0001.vtm" would extract to "somefile.vtm"
+    //  \param sep    The separator used between file stem and suffix.
+    fileName seriesBase(const fileName& outputName, const char sep = '_');
 
 
+    //- Write a list of uint8_t values.
+    //  The output does not include the payload size.
+    void writeList
+    (
+        vtk::formatter& fmt,
+        const UList<uint8_t>& values
+    );
+
+    //- Write a list of uint8_t values.
+    //  The output does not include the payload size.
+    void writeListParallel
+    (
+        vtk::formatter& fmt,
+        const UList<uint8_t>& values
+    );
+
     //- Write a value component-wise.
     template<class Type>
     inline void write
@@ -109,7 +149,7 @@ namespace vtk
     void writeList
     (
         vtk::formatter& fmt,
-        const UList<Type>& list
+        const UList<Type>& values
     );
 
     //- Write a list of values.
@@ -118,7 +158,7 @@ namespace vtk
     void writeList
     (
         vtk::formatter& fmt,
-        const FixedList<Type, Size>& list
+        const FixedList<Type, Size>& values
     );
 
 
@@ -128,7 +168,88 @@ namespace vtk
     void writeList
     (
         vtk::formatter& fmt,
-        const UList<Type>& list,
+        const UList<Type>& values,
+        const labelUList& addressing
+    );
+
+    //- Write a list of values via indirect addressing.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeList
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values,
+        const bitSet& selected
+    );
+
+    //- Write a list of values and a list of values via indirect addressing.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeLists
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values1,
+        const UList<Type>& values2,
+        const labelUList& addressing
+    );
+
+
+    //- Write a list of values.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeListParallel
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values
+    );
+
+    //- Write a list of values, with constant per-processor offset
+    //  The output does not include the payload size.
+    void writeListParallel
+    (
+        vtk::formatter& fmt,
+        const UList<label>& values,
+        const globalIndex& procOffset
+    );
+
+    //- Write a list of values via indirect addressing.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeListParallel
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values,
+        const labelUList& addressing
+    );
+
+    //- Write a list of values via indirect addressing.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeListParallel
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values,
+        const bitSet& selected
+    );
+
+    //- Write a list of values and another list of values.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeListsParallel
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values1,
+        const UList<Type>& values2
+    );
+
+    //- Write a list of values and a list of values via indirect addressing.
+    //  The output does not include the payload size.
+    template<class Type>
+    void writeListsParallel
+    (
+        vtk::formatter& fmt,
+        const UList<Type>& values1,
+        const UList<Type>& values2,
         const labelUList& addressing
     );
 
@@ -137,93 +258,101 @@ namespace vtk
                                Namespace legacy
 \*---------------------------------------------------------------------------*/
 
-//- Some minimal additional support for writing legacy files
 namespace legacy
 {
 
-// Constants
-
-    //- Strings corresponding to the (POLYDATA, UNSTRUCTURED_GRID) elements
-    extern const Foam::Enum<vtk::fileTag> contentNames;
-
-    //- Strings corresponding to the (CELL_DATA, POINT_DATA) elements
-    extern const Foam::Enum<vtk::fileTag> dataTypeNames;
-
-
 // Functions
 
     //- Emit header for legacy file.
     //  Writes "ASCII" or "BINARY" depending on specified type.
-    inline std::ostream& fileHeader
-    (
-        std::ostream& os,
-        const std::string& title,
-        const bool binary
-    );
+    void fileHeader(std::ostream& os, const std::string& title, bool binary);
 
     //- Emit header for legacy file, with "ASCII" or "BINARY" depending on
-    //  the formatter type.
-    //  Includes "DATASET" with the specified dataset type.
-    inline void fileHeader
+    //- the formatter type.
+    //  If the contentType is non-empty, it is used for "DATASET" line.
+    void fileHeader
     (
         vtk::formatter& fmt,
         const std::string& title,
-        const vtk::fileTag& contentTypeTag
+        const std::string& contentType
     );
 
     //- Emit header for legacy file, with "ASCII" or "BINARY" depending on
-    //  the formatter type.
-    //  If the contentType is non-empty, it is used for "DATASET" line.
-    std::ostream& fileHeader
+    //- the formatter type.
+    //  Includes "DATASET" with the specified dataset type.
+    inline void fileHeader
     (
         vtk::formatter& fmt,
         const std::string& title,
-        const std::string& contentType
+        vtk::fileTag contentType
     );
 
+    //- Emit header for legacy file, with "ASCII" or "BINARY" depending on
+    //- the formatter type.
+    //  Includes "DATASET" of the templated dataset type.
+    template<vtk::fileTag ContentType>
+    inline void fileHeader(vtk::formatter& fmt, const std::string& title);
 
     //- Emit header for POINTS (with trailing newline).
-    inline void beginPoints(std::ostream& os, const label nPoints);
+    inline void beginPoints(std::ostream& os, label nPoints);
 
     //- Emit header for POLYGONS (with trailing newline).
     //  The nConnectivity is the sum of all connectivity points used,
     //  but \b without additional space for the size prefixes.
     //  The additional prefix sizes are added internally.
-    inline void beginPolys
+    inline void beginPolys(std::ostream& os, label nPolys, label nConnectivity);
+
+
+    //- Emit "FIELD FieldData <n>"
+    inline void fieldData(vtk::formatter& fmt, label nFields);
+
+    //- Emit legacy FIELD FieldData nFields.
+    inline void beginFieldData(vtk::formatter& fmt, label nFields);
+
+    //- Emit legacy CELL_DATA nCells, FIELD FieldData nFields.
+    inline void beginCellData
     (
-        std::ostream& os,
-        const label nPolys,
-        const label nConnectivity
+        vtk::formatter& fmt,
+        label nCells,
+        label nFields
     );
 
-
-    //- Use the enumerations vtk::fileTag::CELL_DATA, vtk::fileTag::POINT_DATA,
-    //  to emit a legacy CELL_DATA, POINT_DATA element.
-    //  The nEntries corresponds similarly to the number of cells or points,
-    //  respectively.
-    inline void dataHeader
+    //- Emit legacy POINT_DATA nPoints, FIELD FieldData nFields.
+    inline void beginPointData
     (
-        std::ostream& os,
-        const vtk::fileTag& dataTypeTag,
-        const label nEntries,
-        const label nFields
+        vtk::formatter& fmt,
+        label nPoints,
+        label nFields
     );
 
+
+    //- Emit "TimeValue" for a FIELD entry (name as per Catalyst output)
+    inline void writeTimeValue(vtk::formatter& fmt, scalar timeValue);
+
     //- Start output of float field with the specified name.
+    template<direction nComp>
     inline void floatField
     (
-        std::ostream& os,
+        vtk::formatter& fmt,
+        const word& name,
+        const label nEntries
+    );
+
+    //- Start output of double field with the specified name.
+    template<direction nComp>
+    inline void doubleField
+    (
+        vtk::formatter& fmt,
         const word& name,
-        const int nCmpt,
         const label nEntries
     );
 
     //- Start output of int field with the specified name.
+    template<direction nComp>
     inline void intField
     (
-        std::ostream& os,
+        vtk::formatter& fmt,
         const word& name,
-        const int nCmpt,
         const label nEntries
     );
 
diff --git a/src/fileFormats/vtk/output/foamVtkOutputI.H b/src/fileFormats/vtk/output/foamVtkOutputI.H
index b7717ea66d34faaa88930bde4fb797b724d16a68..40b5275737a809d406c51cba3abf1f59a5ec03a8 100644
--- a/src/fileFormats/vtk/output/foamVtkOutputI.H
+++ b/src/fileFormats/vtk/output/foamVtkOutputI.H
@@ -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-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -59,87 +59,149 @@ inline void write<double>(vtk::formatter& fmt, const double& val)
 
 // * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
-inline std::ostream& Foam::vtk::legacy::fileHeader
+inline void Foam::vtk::legacy::fileHeader
 (
-    std::ostream& os,
+    vtk::formatter& fmt,
     const std::string& title,
-    const bool binary
+    vtk::fileTag contentType
 )
 {
-    os  << "# vtk DataFile Version 2.0" << nl
-        << title << nl
-        << (binary ? "BINARY" : "ASCII") << nl;
-
-    return os;
+    legacy::fileHeader(fmt, title, legacy::contentNames[contentType]);
 }
 
 
+template<Foam::vtk::fileTag ContentType>
 inline void Foam::vtk::legacy::fileHeader
 (
     vtk::formatter& fmt,
-    const std::string& title,
-    const vtk::fileTag& contentTypeTag
+    const std::string& title
 )
 {
-    fileHeader(fmt, title, contentNames[contentTypeTag]);
+    legacy::fileHeader(fmt, title, legacy::contentNames[ContentType]);
+}
+
+
+inline void Foam::vtk::legacy::beginPoints(std::ostream& os, label nPoints)
+{
+    os  << nl
+        << "POINTS " << nPoints << " float" << nl;
 }
 
 
-inline void Foam::vtk::legacy::beginPoints
+inline void Foam::vtk::legacy::beginPolys
 (
     std::ostream& os,
-    const label nPoints
+    label nPolys,
+    label nConnectivity
 )
 {
-    os  << "POINTS " << nPoints << " float" << nl;
+    os  << nl
+        << "POLYGONS " << nPolys << ' ' << (nPolys + nConnectivity) << nl;
 }
 
 
-inline void Foam::vtk::legacy::beginPolys
+inline void Foam::vtk::legacy::fieldData
 (
-    std::ostream& os,
-    const label nPolys,
-    const label nConnectivity
+    vtk::formatter& fmt,
+    label nFields
 )
 {
-    os  << "POLYGONS " << nPolys << ' ' << (nPolys + nConnectivity) << nl;
+    fmt.os()
+        << "FIELD FieldData " << nFields << nl;
 }
 
 
-inline void Foam::vtk::legacy::dataHeader
+inline void Foam::vtk::legacy::beginFieldData
 (
-    std::ostream& os,
-    const vtk::fileTag& dataTypeTag,
-    const label nEntries,
-    const label nFields
+    vtk::formatter& fmt,
+    label nFields
+)
+{
+    legacy::fieldData(fmt, nFields);
+}
+
+
+inline void Foam::vtk::legacy::beginCellData
+(
+    vtk::formatter& fmt,
+    label nCells,
+    label nFields
+)
+{
+    fmt.os()
+        << nl
+        << legacy::dataTypeNames[vtk::fileTag::CELL_DATA]
+        << ' ' << nCells << nl;
+    legacy::fieldData(fmt, nFields);
+}
+
+
+inline void Foam::vtk::legacy::beginPointData
+(
+    vtk::formatter& fmt,
+    label nPoints,
+    label nFields
+)
+{
+    fmt.os()
+        << nl
+        << legacy::dataTypeNames[vtk::fileTag::POINT_DATA]
+        << ' ' << nPoints << nl;
+    legacy::fieldData(fmt, nFields);
+}
+
+
+inline void Foam::vtk::legacy::writeTimeValue
+(
+    vtk::formatter& fmt,
+    scalar timeValue
 )
 {
-    os  << dataTypeNames[dataTypeTag] << ' ' << nEntries << nl
-        << "FIELD attributes " << nFields << nl;
+    legacy::floatField<1>(fmt, "TimeValue", 1);
+    fmt.write(timeValue);
+    fmt.flush();
 }
 
 
+template<Foam::direction nComp>
+inline void Foam::vtk::legacy::doubleField
+(
+    vtk::formatter& fmt,
+    const word& fieldName,
+    label nEntries
+)
+{
+    fmt.os()
+        << fieldName << ' '
+        << int(nComp) << ' ' << nEntries << " double" << nl;
+}
+
+
+template<Foam::direction nComp>
 inline void Foam::vtk::legacy::floatField
 (
-    std::ostream& os,
+    vtk::formatter& fmt,
     const word& fieldName,
-    const int nCmpt,
-    const label nEntries
+    label nEntries
 )
 {
-    os  << fieldName << ' ' << nCmpt << ' ' << nEntries << " float" << nl;
+    fmt.os()
+        << fieldName << ' '
+        << int(nComp) << ' ' << nEntries << " float" << nl;
 }
 
 
+template<Foam::direction nComp>
 inline void Foam::vtk::legacy::intField
 (
-    std::ostream& os,
+    vtk::formatter& fmt,
     const word& fieldName,
-    const int nCmpt,
-    const label nEntries
+    label nEntries
 )
 {
-    os  << fieldName << ' ' << nCmpt << ' ' << nEntries << " int" << nl;
+    fmt.os()
+        << fieldName << ' '
+        << int(nComp) << ' ' << nEntries << " int" << nl;
 }
 
 
diff --git a/src/fileFormats/vtk/output/foamVtkOutputOptions.H b/src/fileFormats/vtk/output/foamVtkOutputOptions.H
index fb15f9dd00dc786923d3e9ecb30e7b5cfa07ab5f..976c5413b711dda11fa24419e5ae042bfc88ec61 100644
--- a/src/fileFormats/vtk/output/foamVtkOutputOptions.H
+++ b/src/fileFormats/vtk/output/foamVtkOutputOptions.H
@@ -97,6 +97,9 @@ public:
         //- The output format type
         inline formatType fmt() const;
 
+        //- The file extension (legacy or xml) for the given content-type
+        inline word ext(vtk::fileTag contentType) const;
+
         //- True if writer uses legacy file format
         inline bool legacy() const;
 
diff --git a/src/fileFormats/vtk/output/foamVtkOutputOptionsI.H b/src/fileFormats/vtk/output/foamVtkOutputOptionsI.H
index c8802c3da8b5744fac2e80190ad9185b6656ce57..36a0a3c7b211142104cb10f70b95a095d5578bfb 100644
--- a/src/fileFormats/vtk/output/foamVtkOutputOptionsI.H
+++ b/src/fileFormats/vtk/output/foamVtkOutputOptionsI.H
@@ -25,7 +25,6 @@ License
 
 #include "foamVtkOutput.H"
 
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline Foam::vtk::outputOptions::outputOptions()
@@ -56,13 +55,23 @@ Foam::vtk::outputOptions::newFormatter(std::ostream& os) const
 
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
-inline Foam::vtk::formatType
-Foam::vtk::outputOptions::fmt() const
+inline Foam::vtk::formatType Foam::vtk::outputOptions::fmt() const
 {
     return fmtType_;
 }
 
 
+inline Foam::word Foam::vtk::outputOptions::ext(vtk::fileTag contentType) const
+{
+    return
+    (
+        legacy()
+      ? vtk::legacy::fileExtension
+      : vtk::fileExtension[contentType]
+    );
+}
+
+
 inline bool Foam::vtk::outputOptions::legacy() const
 {
     return
diff --git a/src/fileFormats/vtk/output/foamVtkOutputTemplates.C b/src/fileFormats/vtk/output/foamVtkOutputTemplates.C
index 7a398f8d9d0c6199ecb3fd2e4d40e20156251c86..917aaa75d66e90af0f14978ea33f55e9300586c6 100644
--- a/src/fileFormats/vtk/output/foamVtkOutputTemplates.C
+++ b/src/fileFormats/vtk/output/foamVtkOutputTemplates.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -23,6 +23,9 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+#include "Pstream.H"
+#include "ListOps.H"
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 template<class Type>
@@ -44,12 +47,12 @@ template<class Type>
 void Foam::vtk::writeList
 (
     vtk::formatter& fmt,
-    const UList<Type>& list
+    const UList<Type>& values
 )
 {
-    for (const Type& val : list)
+    for (const Type& val : values)
     {
-        write(fmt, val);
+        vtk::write(fmt, val);
     }
 }
 
@@ -58,12 +61,12 @@ template<class Type, unsigned Size>
 void Foam::vtk::writeList
 (
     vtk::formatter& fmt,
-    const FixedList<Type, Size>& list
+    const FixedList<Type, Size>& values
 )
 {
-    for (const Type& val : list)
+    for (const Type& val : values)
     {
-        write(fmt, val);
+        vtk::write(fmt, val);
     }
 }
 
@@ -72,13 +75,261 @@ template<class Type>
 void Foam::vtk::writeList
 (
     vtk::formatter& fmt,
-    const UList<Type>& list,
+    const UList<Type>& values,
     const labelUList& addressing
 )
 {
     for (const label idx : addressing)
     {
-        write(fmt, list[idx]);
+        vtk::write(fmt, values[idx]);
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeList
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values,
+    const bitSet& selected
+)
+{
+    for (const label idx : selected)
+    {
+        vtk::write(fmt, values[idx]);
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeLists
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values,
+    const UList<Type>& indirect,
+    const labelUList& addressing
+)
+{
+    vtk::writeList(fmt, values);
+    vtk::writeList(fmt, indirect, addressing);
+}
+
+
+template<class Type>
+void Foam::vtk::writeListParallel
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values);
+
+        List<Type> recv;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv;
+
+            vtk::writeList(fmt, recv);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << values;
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeListParallel
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values,
+    const labelUList& addressing
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values, addressing);
+
+        List<Type> recv;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv;
+
+            vtk::writeList(fmt, recv);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << List<Type>(values, addressing);
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeListParallel
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values,
+    const bitSet& selected
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values, selected);
+
+        List<Type> recv;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv;
+
+            vtk::writeList(fmt, recv);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << subset(selected, values);
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeListsParallel
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values1,
+    const UList<Type>& values2
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values1);
+        vtk::writeList(fmt, values2);
+
+        List<Type> recv1, recv2;
+
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv1 >> recv2;
+
+            vtk::writeList(fmt, recv1);
+            vtk::writeList(fmt, recv2);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << values1 << values2;
+    }
+}
+
+
+template<class Type>
+void Foam::vtk::writeListsParallel
+(
+    vtk::formatter& fmt,
+    const UList<Type>& values1,
+    const UList<Type>& values2,
+    const labelUList& addressing
+)
+{
+    if (Pstream::master())
+    {
+        vtk::writeList(fmt, values1);
+        vtk::writeList(fmt, values2, addressing);
+
+        List<Type> recv1, recv2;
+;
+        // Receive and write
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+
+            fromSlave >> recv1 >> recv2;
+
+            vtk::writeList(fmt, recv1);
+            vtk::writeList(fmt, recv2);
+        }
+    }
+    else
+    {
+        // Send to master
+        OPstream toMaster
+        (
+            Pstream::commsTypes::blocking,
+            Pstream::masterNo()
+        );
+
+        toMaster << values1 << List<Type>(values2, addressing);
     }
 }
 
diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
index 07500761acd37e1fe1dbb9986ffa703cb951a70f..c99faa34ce0d1aba1df4051386ae958f5a86a435 100644
--- a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
+++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
@@ -466,10 +466,12 @@ bool Foam::functionObjects::vtkCloud::write()
     // Each cloud separately
     for (const word& cloudName : cloudNames)
     {
-        const word prefix(cloudName + "_");
-        const word suffix(".vtp");    // No legacy supported
+        // Legacy is not to be supported
 
-        const fileName outputName(vtkDir/prefix + timeDesc + suffix);
+        const fileName outputName
+        (
+            vtkDir/cloudName + "_" + timeDesc + ".vtp"
+        );
 
         if (writeCloud(outputName, cloudName))
         {
@@ -483,9 +485,11 @@ bool Foam::functionObjects::vtkCloud::write()
 
                 series_(cloudName).append({time_.value(), timeDesc});
 
-                OFstream os(vtkDir/cloudName + ".vtp.series", IOstream::ASCII);
-
-                vtk::writeSeries(os, prefix, suffix, series_[cloudName]);
+                vtk::seriesWrite
+                (
+                    vtkDir/cloudName + ".vtp",
+                    series_[cloudName]
+                );
             }
         }
     }
diff --git a/src/meshTools/output/foamVtkWriteCellSetFaces.C b/src/meshTools/output/foamVtkWriteCellSetFaces.C
index 9a10c37e72d9fce91dc0c3b093991380bf6ae034..38bc7d107d0a6e127637bbfd5036f842d1265a1e 100644
--- a/src/meshTools/output/foamVtkWriteCellSetFaces.C
+++ b/src/meshTools/output/foamVtkWriteCellSetFaces.C
@@ -132,7 +132,7 @@ void Foam::vtk::writeCellSetFaces
 
 
     // Write data - faceId/cellId
-    legacy::dataHeader(os, vtk::fileTag::CELL_DATA, pp.size(), 1);
+    legacy::beginCellData(format(), pp.size(), 1);
 
     os << "cellID 1 " << pp.size() << " int" << nl;
 
diff --git a/src/meshTools/output/foamVtkWriteFaceSet.C b/src/meshTools/output/foamVtkWriteFaceSet.C
index f0f2a215211dffa010b5867c37733e2bc06d6c0f..cfe8a5e895dd3744d32a88f8d3db6569538b86ba 100644
--- a/src/meshTools/output/foamVtkWriteFaceSet.C
+++ b/src/meshTools/output/foamVtkWriteFaceSet.C
@@ -96,7 +96,7 @@ void Foam::vtk::writeFaceSet
 
     // Write data - faceId/cellId
 
-    legacy::dataHeader(os, vtk::fileTag::CELL_DATA, pp.size(), 1);
+    legacy::beginCellData(format(), pp.size(), 1);
 
     os << "faceID 1 " << pp.size() << " int" << nl;
 
diff --git a/src/meshTools/output/foamVtkWritePointSet.C b/src/meshTools/output/foamVtkWritePointSet.C
index 64c88f0350a281660fd4fdf852bd79ec43a110f4..e228359946280463e44af830e7d077cb74eeb1b2 100644
--- a/src/meshTools/output/foamVtkWritePointSet.C
+++ b/src/meshTools/output/foamVtkWritePointSet.C
@@ -64,7 +64,7 @@ void Foam::vtk::writePointSet
     format().flush();
 
     // Write data - pointID
-    legacy::dataHeader(os, vtk::fileTag::POINT_DATA, pointLabels.size(), 1);
+    legacy::beginPointData(format(), pointLabels.size(), 1);
 
     os << "pointID 1 " << pointLabels.size() << " int" << nl;
 
diff --git a/src/surfMesh/surfaceFormats/vtk/VTKsurfaceFormatCore.C b/src/surfMesh/surfaceFormats/vtk/VTKsurfaceFormatCore.C
index 9de55aae6fad67d514f9e142bd058f33bd770876..9d921f48ff677aa636d245104250b8c8eccdf0cf 100644
--- a/src/surfMesh/surfaceFormats/vtk/VTKsurfaceFormatCore.C
+++ b/src/surfMesh/surfaceFormats/vtk/VTKsurfaceFormatCore.C
@@ -92,21 +92,8 @@ void Foam::fileFormats::VTKsurfaceFormatCore::writeCellData
         nFaces += z.size();
     }
 
-    vtk::legacy::dataHeader
-    (
-        format.os(),
-        vtk::fileTag::CELL_DATA,
-        nFaces,
-        1  // Only one field
-    );
-
-    vtk::legacy::intField
-    (
-        format.os(),
-        "region",
-        1, // nComponent
-        nFaces
-    );
+    vtk::legacy::beginCellData(format, nFaces, 1);      // 1 field
+    vtk::legacy::intField<1>(format, "region", nFaces); // 1 component
 
     label zoneId = 0;
     for (const surfZone& zone : zones)
@@ -132,21 +119,8 @@ void Foam::fileFormats::VTKsurfaceFormatCore::writeCellData
     // Number of faces
     const label nFaces = zoneIds.size();
 
-    vtk::legacy::dataHeader
-    (
-        format.os(),
-        vtk::fileTag::CELL_DATA,
-        nFaces,
-        1  // Only one field
-    );
-
-    vtk::legacy::intField
-    (
-        format.os(),
-        "region",
-        1, // nComponent
-        nFaces
-    );
+    vtk::legacy::beginCellData(format, nFaces, 1);      // 1 field
+    vtk::legacy::intField<1>(format, "region", nFaces); // 1 component
 
     vtk::writeList(format, zoneIds);
     format.flush();