diff --git a/etc/caseDicts/annotated/runTimePostProcessingDict b/etc/caseDicts/annotated/runTimePostProcessingDict
new file mode 100644
index 0000000000000000000000000000000000000000..cbe82ec81f0a0b74bdf9bb3f4cc0fca3433b7020
--- /dev/null
+++ b/etc/caseDicts/annotated/runTimePostProcessingDict
@@ -0,0 +1,281 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      runTimePostProcessingDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+// Type of functionObject
+type            runTimePostProcessing;
+
+// Where to load it from
+libs            ("librunTimePostProcessing.so");
+
+// Function object enabled flag
+enabled         true;
+
+// When to output the average fields
+writeControl    writeTime;
+
+//- Optionally disable parallel VTK rendering, default = true
+// parallel        true;
+
+//- The output characteristics
+output
+{
+    //- The name stem for output images
+    name        image;
+
+    //- The image dimensions
+    width       1400;
+    height      1050;
+}
+
+
+//- The camera settings
+camera
+{
+    // If camera is moving, optionally provide start and end times
+    // startPosition    0.2;
+    // endPosition      0.75;
+
+    // Total number of frames to generate
+    nFrameTotal     1;
+
+    // Parallel projection flag
+    parallelProjection  yes;
+
+    // clipBox is optional
+
+    position        (385 -560 650);
+    focalPoint      (160 90 60);
+    up              (0.06 0.7 0.7);
+
+    position        ( -41.95 -247.55 426.87 );
+    focalPoint      ( 146 76 40 );
+    up              ( 0.3646 0.6194 0.6953 );
+
+    zoom    1.5;
+    // clipBox (-10 18 0)(280 160 76);
+    clipBox (-30 0 0)(300 200 80);
+}
+
+
+// Default colours
+// - If select to colourBy colour, these values are used unless
+// they are locally overridden
+colours
+{
+    background  (0.317647 0.341176 0.431373);
+    background2 (0.317647 0.341176 0.431373);
+    text        (0.75 0.75 0.75);
+    edge        (1 0 0);
+    surface     (0.5 0.5 0.5);
+    line        (1 0 0);
+    point       (0.5 0.5 0.5);
+}
+
+
+// Line data
+lines
+{
+    streamline
+    {
+        type            functionObjectLine;
+        functionObject  streamLines;
+        colourMap       rainbow;
+        representation  tube;
+        visible         true;
+        tubeRadius      0.5;
+        colourBy        field;
+        field           U;
+        range           (0 20);
+        opacity         1;
+        scalarBar
+        {
+            visible         no;
+        }
+    }
+}
+
+
+// Surface data
+surfaces
+{
+    geom
+    {
+        type            geometry;
+        files           ("<case>/myGeometry.vtp");
+        renderMode      phong;
+        representation  surface;
+        edgeColour      (0.5 0.5 0.5);
+        visible         yes;
+        featureEdges    none;
+        opacity         1.0;
+    }
+
+    surf1
+    {
+        type            functionObjectSurface;
+        functionObject  planes.plane0;
+        liveObject      true;
+        colourMap       coolToWarm;
+
+        representation  surface;
+        maxGlyphLength  0.1;
+        smooth          true;
+        visible         yes;
+
+        featureEdges    none;
+        colourBy        field;
+        field           U;
+        range           (0 20);
+        opacity         1;
+
+        scalarBar
+        {
+            visible     no;
+        }
+    }
+
+    patches
+    {
+        type            patches;
+        patches         ( buildings ground );
+        nearCellValue   true;
+        smooth          true;
+        colourMap       coolToWarm;
+        representation  surface;
+        representation  glyph;
+
+        // maxGlyphLength  5;
+        maxGlyphLength  0;
+        visible         yes;
+        featureEdges    none;
+        colourBy        field;
+
+        field           U;
+        range           (0 20);
+
+        opacity         1;
+        scalarBar
+        {
+            visible         no;
+            position        (0.8 0.1);
+            vertical        yes;
+            fontSize        16;
+            titleSize       18;
+            title           "velocity [m/s]";
+            labelFormat     "%6.2f";
+            numberOfLabels  5;
+
+            bold        yes;
+            italic      yes;
+            shadow      yes;
+        }
+    }
+
+
+    cutting
+    {
+        type            plane;
+        planeType       pointAndNormal;
+
+        pointAndNormalDict
+        {
+            point   (100 100 50);
+            normal  (1 0 0);
+        }
+
+        offsets         (0 100 200);
+
+        smooth          true;
+        colourMap       coolToWarm;
+        representation  surface;
+
+        representation  glyph;
+
+        // maxGlyphLength  5;
+        maxGlyphLength  0;
+        visible         yes;
+        featureEdges    none;
+
+        colourBy        field;
+        colourField     U;
+        field           U;
+        range           (0 20);
+
+        opacity         1;
+        scalarBar
+        {
+            visible         no;
+        }
+    }
+
+    iso
+    {
+        type            iso;
+        values          (0 4 8);
+
+        smooth          true;
+        colourMap       coolToWarm;
+        representation  surface;
+
+        representation  glyph;
+
+        // maxGlyphLength  5;
+        maxGlyphLength  0;
+        visible         yes;
+        featureEdges    none;
+
+        colourBy        field;
+        colourField     U;
+        field           U;
+        range           (0 20);
+
+        colourField     k;
+        field           k;
+        range           (0 20);
+
+        colourBy        field;
+        colourField     U;
+        field           U;
+        range           (0 20);
+
+        opacity         1;
+        scalarBar
+        {
+            visible         no;
+        }
+    }
+}
+
+// Text data
+text
+{
+    text1
+    {
+        string      "buildings";
+        position    (0.5 0.15);
+        halign      centre;
+        size        18;
+        opacity     0.4;
+        bold        yes;
+        italic      yes;
+        shadow      yes;
+        visible     yes;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/etc/caseDicts/postProcessing/visualization/runTimePostPro.cfg b/etc/caseDicts/postProcessing/visualization/runTimePostPro.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..cf5ab38a1cb54e889c98f1bcfea697d3f9bc0576
--- /dev/null
+++ b/etc/caseDicts/postProcessing/visualization/runTimePostPro.cfg
@@ -0,0 +1,107 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+type            runTimePostProcessing;
+
+libs            ("librunTimePostProcessing.so");
+
+writeControl    writeTime;
+
+// Output characteristics
+output
+{
+    //- The name stem for output images
+    name        image;
+
+    //- The image dimensions
+    width       1400;
+    height      1050;
+}
+
+
+// Some convenient colour schemes
+colourScheme
+{
+    black
+    {
+        background  (0 0 0);
+        text        (0.75 0.75 0.75);
+        edge        (1 0 0);
+        surface     (0.5 0.5 0.5);
+        line        (1 0 0);
+        point       (0.5 0.5 0.5);
+    }
+
+    blueGradient
+    {
+        background  (1 1 1);
+        background2 (0 0 1);
+        text        (0 0 0);
+        edge        (1 0 0);
+        surface     (0.5 0.5 0.5);
+        line        (1 0 0);
+        point       (0.5 0.5 0.5);
+    }
+
+    greyGradient
+    {
+        background  (0.5 0.5 0.5);
+        background2 (0.7 0.7 0.7);
+        text        (1 1 1);
+        edge        (1 0 0);
+        surface     (0.5 0.5 0.5);
+        line        (1 0 0);
+        point       (0.5 0.5 0.5);
+    }
+
+    paraview
+    {
+        background  (0.317647 0.341176 0.431373);
+        text        (0.75 0.75 0.75);
+        edge        (1 0 0);
+        surface     (0.5 0.5 0.5);
+        line        (1 0 0);
+        point       (0.5 0.5 0.5);
+    }
+}
+
+
+// Some typical scalarBar settings
+scalarBar
+{
+    right
+    {
+        visible         true;
+        vertical        true;
+        position        (0.8 0.1);
+        size            (0.1 0.75);
+        fontSize        16;
+        labelFormat     "%f";
+        numberOfLabels  5;
+        bold            yes;
+        italic          yes;
+        shadow          yes;
+    }
+
+    bottom
+    {
+        visible         true;
+        vertical        false;
+        position        (0.2 0.1);
+        size            (0.6 0.1);
+        fontSize        16;
+        labelFormat     "%f";
+        numberOfLabels  5;
+        bold            yes;
+        italic          yes;
+        shadow          yes;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/etc/controlDict b/etc/controlDict
index f6a1120cfc879ec856f6af3eb48ae143db0d1453..3d6c94dd8003e42d21c61b21f81244a474c89b6d 100644
--- a/etc/controlDict
+++ b/etc/controlDict
@@ -820,6 +820,7 @@ DebugSwitches
     rotatedBoxToCell    0;
     rotatingPressureInletOutletVelocity 0;
     rotatingTotalPressure 0;
+    runTimePostPro::geometryBase        0;
     sampledPatch        0;
     sampledPlane        0;
     sampledSet          0;
diff --git a/src/functionObjects/graphics/runTimePostProcessing/CMakeLists-Project.txt b/src/functionObjects/graphics/runTimePostProcessing/CMakeLists-Project.txt
index d64168acbde2fd0de130c189bbef424ba4e72d64..3db266e911ed24d7465da6ace0e073b557c10479 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/CMakeLists-Project.txt
+++ b/src/functionObjects/graphics/runTimePostProcessing/CMakeLists-Project.txt
@@ -13,13 +13,40 @@ else()
     message(FATAL_ERROR " VTK version is too old - requires VTK6 or newer")
 endif()
 
+#-----------------------------------------------------------------------------
+# Test some characteristics
+set(test_file ${CMAKE_CURRENT_BINARY_DIR}/check_mpi.cxx)
+file(WRITE ${test_file}
+    "#include <vtkMPICommunicator.h>\n"
+    "int main() {\n"
+    "  vtkMPICommunicator* p = vtkMPICommunicator::New();\n"
+    "  p->Delete();\n"
+    "  return 0;\n"
+    "}"
+)
+try_compile(FOAM_USING_VTK_MPI
+    ${CMAKE_CURRENT_BINARY_DIR} ${test_file}
+    LINK_LIBRARIES vtkParallelMPI
+    CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PARAVIEW_INCLUDE_DIRS}"
+)
+if (FOAM_USING_VTK_MPI)
+    add_definitions(-DFOAM_USING_VTK_MPI)
+    message("Building with VTK MPI")
+    include(vtkMPI)
+else()
+    message(WARNING "==== Building without VTK MPI ====")
+endif()
+
+#-----------------------------------------------------------------------------
+
 include_directories(
     ${LIB_SRC}/OpenFOAM/include
     ${LIB_SRC}/OpenFOAM/lnInclude
     ${LIB_SRC}/OSspecific/${WM_OSTYPE}/lnInclude
     ${LIB_SRC}/finiteVolume/lnInclude
-    ${LIB_SRC}/surfMesh/lnInclude
+    ${LIB_SRC}/fileFormats/lnInclude
     ${LIB_SRC}/conversion/lnInclude
+    ${LIB_SRC}/surfMesh/lnInclude
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${CMAKE_CURRENT_BINARY_DIR}
 )
@@ -45,12 +72,16 @@ set(LIBRARY_OUTPUT_PATH $ENV{FOAM_LIBBIN}
 
 file(GLOB SOURCE_FILES
     fieldVisualisationBase.C
+    scalarBar.C
     functionObjectBase.C
     functionObjectCloud.C
     functionObjectLine.C
     functionObjectSurface.C
     geometryBase.C
+    geometryCloud.C
+    geometryCloudGather.C
     geometryPatches.C
+    geometryPatchesGather.C
     geometrySurface.C
     pathline.C
     pointData.C
@@ -58,16 +89,27 @@ file(GLOB SOURCE_FILES
     runTimePostProcessingFunctionObject.C
     scene.C
     surface.C
+    surfaceGather.C
     text.C
+    contourFilter.C
+    cuttingPlaneFilter.C
+    volumeFilter.C
 )
 
 set(OPENFOAM_LIBRARIES
     OpenFOAM
     finiteVolume
     surfMesh
+    fileFormats
     conversion
 )
 
+if (FOAM_USING_VTK_MPI)
+    set(LINK_LIBRARIES vtkParallelMPI)
+else()
+    set(LINK_LIBRARIES)
+endif()
+
 add_library(
     runTimePostProcessing
     SHARED
@@ -84,6 +126,7 @@ set_target_properties(
 target_link_libraries(
     runTimePostProcessing
     ${VTK_LIBRARIES}
+    ${LINK_LIBRARIES}
     ${OPENFOAM_LIBRARIES}
 )
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/contourFilter.C b/src/functionObjects/graphics/runTimePostProcessing/contourFilter.C
new file mode 100644
index 0000000000000000000000000000000000000000..2a6eb421a343e6fdd57bbe8b73a825927cfdb311
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/contourFilter.C
@@ -0,0 +1,303 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "contourFilter.H"
+#include "runTimePostProcessing.H"
+#include "addToRunTimeSelectionTable.H"
+
+// VTK includes
+#include "vtkActor.h"
+#include "vtkCellDataToPointData.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkContourFilter.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkRenderer.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+    defineTypeName(contourFilter);
+    addToRunTimeSelectionTable(surface, contourFilter, dictionary);
+}
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::contourFilter::contourFilter
+(
+    const runTimePostProcessing& parent,
+    const dictionary& dict,
+    const HashPtrTable<Function1<vector>>& colours
+)
+:
+    volumeFilter(parent, dict, colours),
+    fieldVisualisationBase(dict, colours),
+    colourFieldName_(dict.get<word>("colourField")),
+    values_()
+{
+    dict.readEntry("values", values_);
+
+    // Extra safety
+    if (values_.empty())
+    {
+        values_.resize(1);
+        values_.first() = Zero;
+    }
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::runTimePostPro::contourFilter::
+addGeometry
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_)
+    {
+        return false;
+    }
+
+    if (needsCollective())
+    {
+        Info<< type() << " : Not available for collective operation" << endl;
+        return false;
+    }
+
+    DebugInfo << "    Adding iso-surface" << endl;
+
+    // Bookkeeping for vtkUnstructuredGrid
+    vtk::vtuAdaptor adaptor;
+    vtkSmartPointer<vtkMultiPieceDataSet> multiPiece = mesh(adaptor);
+
+
+    // Add (scalar/vector) field.
+    // - always need field(s) for glyphs or colourByField:
+
+    int nCmpt = 0;
+    {
+        const auto* ioptr =
+            parent().mesh().cfindObject<regIOobject>(fieldName_);
+
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<scalar>
+            (
+                multiPiece, adaptor, ioptr, fieldName_
+            );
+        }
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<vector>
+            (
+                multiPiece, adaptor, ioptr, fieldName_
+            );
+        }
+    }
+
+
+    // If the input is vector, need magnitude
+
+    word magFieldName = fieldName_;
+
+    if (nCmpt == 3)
+    {
+        addMagField(fieldName_, multiPiece);
+        magFieldName = "mag(" + fieldName_ + ")";
+    }
+
+    // Colouring
+    nCmpt = 0;
+    if (colourBy_ == cbField && fieldName_ != colourFieldName_)
+    {
+        const auto* ioptr =
+            parent().mesh().cfindObject<regIOobject>(fieldName_);
+
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<scalar>
+            (
+                multiPiece, adaptor, ioptr, colourFieldName_
+            );
+        }
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<vector>
+            (
+                multiPiece, adaptor, ioptr, colourFieldName_
+            );
+        }
+    }
+
+
+    // Now have a multi-piece dataset that is one of the following:
+    //
+    // - one-piece per processor (OpenFOAM = parallel, VTK=parallel)
+
+
+    // Re-query field information - we may have stored it differently
+    // than the original source.
+
+    fieldSummary fieldInfo = queryFieldSummary(magFieldName, multiPiece);
+    fieldInfo.reduce();
+
+    fieldSummary colourFieldInfo =
+        queryFieldSummary(colourFieldName_, multiPiece);
+    colourFieldInfo.reduce();
+
+
+    // Not rendered on this processor?
+    // This is where we stop, but could also have an MPI barrier
+    if (!renderer)
+    {
+        return true;
+    }
+
+
+    // Rendering
+    {
+        auto contour = vtkSmartPointer<vtkContourFilter>::New();
+
+        vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
+
+        // CellData - Need a cell->point filter
+        if (!fieldInfo.hasPointData() || !colourFieldInfo.hasPointData())
+        {
+            cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
+            cellToPoint->SetInputData(multiPiece);
+
+            contour->SetInputConnection(cellToPoint->GetOutputPort());
+        }
+        else
+        {
+            contour->SetInputData(multiPiece);
+        }
+
+        contour->SetNumberOfContours(values_.size());
+        forAll(values_, valuei)
+        {
+            contour->SetValue(valuei, values_[valuei]);
+        }
+
+        contour->SetInputArrayToProcess
+        (
+            0,  // index: scalars(0)
+            0,  // port
+            0,  // connection
+            vtkDataObject::FIELD_ASSOCIATION_POINTS,
+            magFieldName.c_str()
+        );
+
+        contour->Modified();
+        contour->Update();
+
+        auto polyData = vtkSmartPointer<vtkCompositeDataGeometryFilter>::New();
+
+        polyData->SetInputConnection(contour->GetOutputPort());
+        polyData->Update();
+
+        auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        mapper->SetInputConnection(polyData->GetOutputPort());
+
+        if (representation_ == rtGlyph)
+        {
+            addGlyphs
+            (
+                position,
+                colourFieldName_, colourFieldInfo,  // scaling
+                colourFieldName_, colourFieldInfo,  // colouring
+                maxGlyphLength_,
+                polyData->GetOutput(),
+                surfaceActor_,
+                renderer
+            );
+        }
+        else
+        {
+            setField
+            (
+                position,
+                colourFieldName_,
+                FieldAssociation::POINT_DATA,
+                mapper,
+                renderer
+            );
+
+            surfaceActor_->SetMapper(mapper);
+
+            setRepresentation(surfaceActor_);
+
+            renderer->AddActor(surfaceActor_);
+        }
+    }
+
+    return true;
+}
+
+
+void Foam::functionObjects::runTimePostPro::contourFilter::
+addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (visible_)
+    {
+        // Live source
+        if (addGeometry(position, renderer))
+        {
+            return;
+        }
+
+        WarningInFunction
+            << "Unsupported for OpenFOAM parallel and VTK serial"
+            << endl;
+    }
+}
+
+
+bool Foam::functionObjects::runTimePostPro::contourFilter::clear()
+{
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/contourFilter.H b/src/functionObjects/graphics/runTimePostProcessing/contourFilter.H
new file mode 100644
index 0000000000000000000000000000000000000000..403052d43dc0c4383fc7872c47b9306c4a4bfd61
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/contourFilter.H
@@ -0,0 +1,140 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::contourFilter
+
+Description
+    Iso-surface contours of OpenFOAM volume fields.
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The surface type: isoSurface          | yes |
+        field       | The field defining the surface        | yes |
+        colourField | The field to display on the surface   | yes |
+        values      | List of iso-values to define the surface(s) | yes |
+    \endtable
+
+SourceFiles
+    contourFilter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_contourFilter_H
+#define functionObjects_runTimePostPro_contourFilter_H
+
+#include "volumeFilter.H"
+#include "fieldVisualisationBase.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class contourFilter Declaration
+\*---------------------------------------------------------------------------*/
+
+class contourFilter
+:
+    public volumeFilter,
+    public fieldVisualisationBase
+{
+protected:
+
+    // Protected Data
+
+        //- Name of field to colour by
+        word colourFieldName_;
+
+        //- The iso values
+        List<scalar> values_;
+
+
+    // Protected Member Functions
+
+        //- No copy construct
+        contourFilter(const contourFilter&) = delete;
+
+        //- No copy assignment
+        void operator=(const contourFilter&) = delete;
+
+
+public:
+
+    //- Run-time type information
+    TypeNameNoDebug("isoSurface");
+
+
+    // Constructors
+
+        //- Construct from dictionary
+        contourFilter
+        (
+            const runTimePostProcessing& parent,
+            const dictionary& dict,
+            const HashPtrTable<Function1<vector>>& colours
+        );
+
+
+    //- Destructor
+    virtual ~contourFilter() = default;
+
+
+    // Member Functions
+
+        //- Add cutting planes to scene (using simulation source)
+        bool addGeometry
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add cutting planes to scene (using simulation source)
+        virtual void addGeometryToScene
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add cutting planes to scene (using simulation source)
+        virtual bool clear();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.C b/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.C
new file mode 100644
index 0000000000000000000000000000000000000000..929d5858acfef046db7dfe000849b08d78273579
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.C
@@ -0,0 +1,292 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "cuttingPlaneFilter.H"
+#include "runTimePostProcessing.H"
+#include "addToRunTimeSelectionTable.H"
+
+// VTK includes
+#include "vtkActor.h"
+#include "vtkCellDataToPointData.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkCutter.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPlane.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkRenderer.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+    defineTypeName(cuttingPlaneFilter);
+    addToRunTimeSelectionTable(surface, cuttingPlaneFilter, dictionary);
+}
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::cuttingPlaneFilter
+(
+    const runTimePostProcessing& parent,
+    const dictionary& dict,
+    const HashPtrTable<Function1<vector>>& colours
+)
+:
+    volumeFilter(parent, dict, colours),
+    fieldVisualisationBase(dict, colours),
+    plane_(dict),
+    values_()
+{
+    dict.readIfPresent("offsets", values_);
+
+    if (values_.empty())
+    {
+        values_.resize(1);
+        values_.first() = Zero;
+    }
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::
+addGeometry
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_)
+    {
+        return false;
+    }
+
+    if (needsCollective())
+    {
+        Info<< type() << " : Not available for collective operation" << endl;
+        return false;
+    }
+
+    DebugInfo << "    Adding cutting plane" << endl;
+
+
+    // Bookkeeping for vtkUnstructuredGrid
+    vtk::vtuAdaptor adaptor;
+    vtkSmartPointer<vtkMultiPieceDataSet> multiPiece = mesh(adaptor);
+
+
+    // Add (scalar/vector) field.
+    // - Need field(s) for glyphs or colourByField:
+
+    int nCmpt = 0;
+    if (representation_ == rtGlyph || colourBy_ == cbField)
+    {
+        const auto* ioptr =
+            parent().mesh().cfindObject<regIOobject>(fieldName_);
+
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<scalar>
+            (
+                multiPiece, adaptor, ioptr, fieldName_
+            );
+        }
+        if (!nCmpt)
+        {
+            nCmpt = addDimField<vector>
+            (
+                multiPiece, adaptor, ioptr, fieldName_
+            );
+        }
+    }
+
+
+    // Now have a multi-piece dataset that is one of the following:
+    //
+    // - one-piece per processor (OpenFOAM = parallel, VTK=parallel)
+
+
+    // Re-query field information - we may have stored it differently
+    // than the original source.
+
+    fieldSummary fieldInfo = queryFieldSummary(fieldName_, multiPiece);
+    fieldInfo.reduce();
+
+
+    // Not rendered on this processor?
+    // This is where we stop, but could also have an MPI barrier
+    if (!renderer)
+    {
+        return true;
+    }
+
+
+    // Rendering
+    {
+        // OpenFOAM plane -> vtkPlane definition
+
+        auto pln = vtkSmartPointer<vtkPlane>::New();
+
+        pln->SetNormal
+        (
+            plane_.normal().x(),
+            plane_.normal().y(),
+            plane_.normal().z()
+        );
+        pln->SetOrigin
+        (
+            plane_.origin().x(),
+            plane_.origin().y(),
+            plane_.origin().z()
+        );
+
+
+        // Plane cutting algorithm
+
+        auto cutter = vtkSmartPointer<vtkCutter>::New();
+
+        cutter->SetInputData(multiPiece);
+        cutter->SetCutFunction(pln);
+
+        cutter->SetNumberOfContours(values_.size());
+
+        forAll(values_, pointi)
+        {
+            cutter->SetValue(pointi, values_[pointi]);
+        }
+
+        cutter->SetInputArrayToProcess
+        (
+            (nCmpt == 3 ? 1 : 0), // index: scalars(0), vectors(1)
+            0, // port
+            0, // connection
+            vtkDataObject::FIELD_ASSOCIATION_CELLS,
+            fieldName_.c_str()
+        );
+
+        cutter->Modified();
+        cutter->Update();
+
+        auto polyData = vtkSmartPointer<vtkCompositeDataGeometryFilter>::New();
+
+        polyData->SetInputConnection(cutter->GetOutputPort());
+        polyData->Update();
+
+        auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        mapper->SetInputConnection(polyData->GetOutputPort());
+
+        if (representation_ == rtGlyph)
+        {
+            addGlyphs
+            (
+                position,
+                fieldName_, fieldInfo,  // scaling
+                fieldName_, fieldInfo,  // colouring
+                maxGlyphLength_,
+                polyData->GetOutput(),
+                surfaceActor_,
+                renderer
+            );
+        }
+        else
+        {
+            vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
+
+            // CellData - Need a cell->point filter
+            if (smooth_ && !fieldInfo.hasPointData())
+            {
+                cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPoint->SetInputConnection(cutter->GetOutputPort());
+
+                polyData->SetInputConnection(cellToPoint->GetOutputPort());
+                polyData->Update();
+            }
+
+            setField
+            (
+                position,
+                fieldName_,
+                (
+                    smooth_
+                  ? FieldAssociation::POINT_DATA
+                  : FieldAssociation(fieldInfo.association_)
+                ),
+                mapper,
+                renderer
+            );
+
+            surfaceActor_->SetMapper(mapper);
+
+            setRepresentation(surfaceActor_);
+
+            renderer->AddActor(surfaceActor_);
+        }
+    }
+
+    return true;
+}
+
+
+void Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::
+addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (visible_)
+    {
+        // Live source
+        if (addGeometry(position, renderer))
+        {
+            return;
+        }
+
+        WarningInFunction
+            << "Unsupported for OpenFOAM parallel and VTK serial"
+            << endl;
+    }
+}
+
+
+bool Foam::functionObjects::runTimePostPro::cuttingPlaneFilter::clear()
+{
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.H b/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.H
new file mode 100644
index 0000000000000000000000000000000000000000..1c89db28e00ad1e457a59fb83c407a0ce38da993
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/cuttingPlaneFilter.H
@@ -0,0 +1,160 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::cuttingPlaneFilter
+
+Description
+    Cutting planes of OpenFOAM volume fields.
+
+    Example of text object specification:
+    \verbatim
+    planes
+    {
+        type            plane;
+        planeType       pointAndNormal;
+
+        pointAndNormalDict
+        {
+                point   (0 0 0);
+                normal  (1 0 0);
+        }
+
+        offsets         (0 10 20);
+
+        field    T;
+    }
+    \endverbatim
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The surface type: plane               | yes |
+        planeType   | Selector for plane description        | yes |
+        offsets     | Offets of the origin in the normal direction | no | (0)
+        field       | The field to display                  | yes |
+    \endtable
+
+SourceFiles
+    cuttingPlaneFilter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_cuttingPlaneFilter_H
+#define functionObjects_runTimePostPro_cuttingPlaneFilter_H
+
+#include "plane.H"
+#include "volumeFilter.H"
+#include "fieldVisualisationBase.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class cuttingPlaneFilter Declaration
+\*---------------------------------------------------------------------------*/
+
+class cuttingPlaneFilter
+:
+    public volumeFilter,
+    public fieldVisualisationBase
+{
+protected:
+
+    // Protected Data
+
+        //- The definition of the plane
+        plane plane_;
+
+        //- The offsets to the plane - defaults to (0).
+        List<scalar> values_;
+
+
+    // Protected Member Functions
+
+        //- No copy construct
+        cuttingPlaneFilter(const cuttingPlaneFilter&) = delete;
+
+        //- No copy assignment
+        void operator=(const cuttingPlaneFilter&) = delete;
+
+
+public:
+
+    //- Run-time type information
+    TypeNameNoDebug("plane");
+
+
+    // Constructors
+
+        //- Construct from dictionary
+        cuttingPlaneFilter
+        (
+            const runTimePostProcessing& parent,
+            const dictionary& dict,
+            const HashPtrTable<Function1<vector>>& colours
+        );
+
+
+    //- Destructor
+    virtual ~cuttingPlaneFilter() = default;
+
+
+    // Member Functions
+
+        //- Add cutting planes to scene (using simulation source)
+        bool addGeometry
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add cutting planes to scene (using simulation source)
+        virtual void addGeometryToScene
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Cleanup files etc.
+        virtual bool clear();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.C b/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.C
index 5eba57decd9ae85b3f055653ebe1831de666838f..a8ee5dd4551f1675bcdd38ad68bc2e588bac1ae6 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,25 +27,27 @@ License
 #include "fieldVisualisationBase.H"
 #include "runTimePostProcessing.H"
 
+#include "doubleVector.H"
+#include "foamVtkTools.H"
+
 // VTK includes
 #include "vtkArrowSource.h"
+#include "vtkCellDataToPointData.h"
 #include "vtkCellData.h"
 #include "vtkColorTransferFunction.h"
-#include "vtkFloatArray.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkDataObjectTreeIterator.h"
+#include "vtkFieldData.h"
 #include "vtkGlyph3D.h"
 #include "vtkLookupTable.h"
 #include "vtkPointData.h"
 #include "vtkPolyData.h"
 #include "vtkPolyDataMapper.h"
 #include "vtkRenderer.h"
-#include "vtkScalarBarActor.h"
 #include "vtkSmartPointer.h"
 #include "vtkSphereSource.h"
-#include "vtkTextActor.h"
-#include "vtkTextProperty.h"
-#include "vtkCellDataToPointData.h"
 
-// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 const Foam::Enum
 <
@@ -69,13 +69,323 @@ const Foam::Enum
 Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
 colourMapTypeNames
 ({
-    { colourMapType::cmRainbow, "rainbow" },
-    { colourMapType::cmBlueWhiteRed, "blueWhiteRed" },
+    { colourMapType::cmCoolToWarm, "coolToWarm" },
+    { colourMapType::cmCoolToWarm, "blueWhiteRed" },
+    { colourMapType::cmColdAndHot, "coldAndHot" },
     { colourMapType::cmFire, "fire" },
+    { colourMapType::cmRainbow, "rainbow" },
     { colourMapType::cmGreyscale, "greyscale" },
+    { colourMapType::cmGreyscale, "grayscale" },
+    { colourMapType::cmXray, "xray" },
 });
 
 
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
+queryFieldSummary
+(
+    const word& fieldName,
+    vtkDataSet* dataset
+)
+{
+    fieldSummary queried;
+
+    if (dataset)
+    {
+        vtkDataArray* array;
+
+        array = vtkDataArray::SafeDownCast
+        (
+            dataset->GetCellData()->GetAbstractArray(fieldName.c_str())
+        );
+
+        if (array)
+        {
+            queried.nComponents_ = array->GetNumberOfComponents();
+            queried.association_ |= FieldAssociation::CELL_DATA;
+            queried.range_ += vtk::Tools::rangeOf(array);
+        }
+
+        array = vtkDataArray::SafeDownCast
+        (
+            dataset->GetPointData()->GetAbstractArray(fieldName.c_str())
+        );
+
+        if (array)
+        {
+            queried.nComponents_ = array->GetNumberOfComponents();
+            queried.association_ |= FieldAssociation::POINT_DATA;
+            queried.range_ += vtk::Tools::rangeOf(array);
+        }
+    }
+
+    return queried;
+}
+
+
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
+queryFieldSummary
+(
+    const word& fieldName,
+    vtkCompositeDataSet* data
+)
+{
+    fieldSummary queried;
+
+    auto iter = vtkSmartPointer<vtkDataObjectTreeIterator>::New();
+
+    iter->SetDataSet(data);
+    iter->VisitOnlyLeavesOn();
+    iter->SkipEmptyNodesOn();
+
+    for
+    (
+        iter->InitTraversal();
+        !iter->IsDoneWithTraversal();
+        iter->GoToNextItem()
+    )
+    {
+        vtkDataSet* dataset = vtkDataSet::SafeDownCast
+        (
+            iter->GetCurrentDataObject()
+        );
+
+        if (dataset)
+        {
+            fieldSummary local(queryFieldSummary(fieldName, dataset));
+
+            if (!queried.nComponents_)
+            {
+                queried.nComponents_ = local.nComponents_;
+            }
+
+            queried.association_ |= local.association_;
+            queried.range_ += local.range_;
+        }
+    }
+
+    return queried;
+}
+
+
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::FieldAssociation
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
+queryFieldAssociation
+(
+    const word& fieldName,
+    vtkDataSet* dataset
+)
+{
+    unsigned where(FieldAssociation::NO_DATA);
+
+    if (dataset)
+    {
+        if (dataset->GetCellData()->HasArray(fieldName.c_str()))
+        {
+            where |= FieldAssociation::CELL_DATA;
+        }
+        if (dataset->GetPointData()->HasArray(fieldName.c_str()))
+        {
+            where |= FieldAssociation::POINT_DATA;
+        }
+    }
+
+    return FieldAssociation(where);
+}
+
+
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::FieldAssociation
+Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
+queryFieldAssociation
+(
+    const word& fieldName,
+    vtkCompositeDataSet* data
+)
+{
+    unsigned where(FieldAssociation::NO_DATA);
+
+    auto iter = vtkSmartPointer<vtkDataObjectTreeIterator>::New();
+
+    iter->SetDataSet(data);
+    iter->VisitOnlyLeavesOn();
+    iter->SkipEmptyNodesOn();
+
+    for
+    (
+        iter->InitTraversal();
+        !iter->IsDoneWithTraversal();
+        iter->GoToNextItem()
+    )
+    {
+        vtkDataSet* dataset = vtkDataSet::SafeDownCast
+        (
+            iter->GetCurrentDataObject()
+        );
+
+        where |= queryFieldAssociation(fieldName, dataset);
+    }
+
+    return FieldAssociation(where);
+}
+
+
+void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField
+(
+    const word& fieldName,
+    vtkFieldData* fieldData
+)
+{
+    if (!fieldData)
+    {
+        return;
+    }
+
+    vtkDataArray* input = vtkDataArray::SafeDownCast
+    (
+        fieldData->GetAbstractArray(fieldName.c_str())
+    );
+
+    if (!input)
+    {
+        return;
+    }
+
+    const word magFieldName = "mag(" + fieldName + ")";
+
+    vtkDataArray* output = vtkDataArray::SafeDownCast
+    (
+        fieldData->GetAbstractArray(magFieldName.c_str())
+    );
+
+    if (output)
+    {
+        return;
+    }
+
+
+    // Simplfy and only handle scalar/vector input
+
+    const int nCmpt = input->GetNumberOfComponents();
+    const vtkIdType len = input->GetNumberOfTuples();
+
+    if (nCmpt == 1)
+    {
+        auto data = vtkSmartPointer<vtkFloatArray>::New();
+
+        data->SetName(magFieldName.c_str());
+        data->SetNumberOfComponents(1);
+        data->SetNumberOfTuples(len);
+
+        double scratch;
+        for (vtkIdType i=0; i < len; ++i)
+        {
+            input->GetTuple(i, &scratch);
+
+            scratch = Foam::mag(scratch);
+            data->SetTuple(i, &scratch);
+        }
+
+        fieldData->AddArray(data);
+    }
+    else if (nCmpt == 3)
+    {
+        auto data = vtkSmartPointer<vtkFloatArray>::New();
+
+        data->SetName(magFieldName.c_str());
+        data->SetNumberOfComponents(1);
+        data->SetNumberOfTuples(len);
+
+        doubleVector scratch;
+        for (vtkIdType i=0; i < len; ++i)
+        {
+            input->GetTuple(i, scratch.v_);
+
+            scratch.x() = Foam::mag(scratch);
+
+            data->SetTuple(i, scratch.v_);
+        }
+
+        fieldData->AddArray(data);
+    }
+}
+
+
+void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField
+(
+    const word& fieldName,
+    vtkDataSet* dataset
+)
+{
+    if (dataset)
+    {
+        addMagField(fieldName, dataset->GetCellData());
+        addMagField(fieldName, dataset->GetPointData());
+    }
+}
+
+
+void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::addMagField
+(
+    const word& fieldName,
+    vtkCompositeDataSet* data
+)
+{
+    auto iter = vtkSmartPointer<vtkDataObjectTreeIterator>::New();
+
+    iter->SetDataSet(data);
+    iter->VisitOnlyLeavesOn();
+    iter->SkipEmptyNodesOn();
+
+    for
+    (
+        iter->InitTraversal();
+        !iter->IsDoneWithTraversal();
+        iter->GoToNextItem()
+    )
+    {
+        vtkDataSet* dataset = vtkDataSet::SafeDownCast
+        (
+            iter->GetCurrentDataObject()
+        );
+        addMagField(fieldName, dataset);
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
+fieldSummary::reduce()
+{
+    if (Pstream::parRun())
+    {
+        Foam::reduce(nComponents_, maxOp<int>());
+        Foam::reduce(association_, bitOrOp<unsigned>());
+        Foam::reduce(range_, minMaxOp<scalar>());
+    }
+}
+
+
+Foam::Ostream& Foam::operator<<
+(
+    Ostream& os,
+    const InfoProxy
+    <
+        functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary
+    >& proxy
+)
+{
+    os  << "nComponents:" << proxy.t_.nComponents_
+        << " association:" << label(proxy.t_.association_)
+        << " min/max:" << proxy.t_.range_;
+
+    return os;
+}
+
+
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
 void Foam::functionObjects::runTimePostPro::fieldVisualisationBase::
@@ -84,7 +394,7 @@ setColourMap
     vtkLookupTable* lut
 ) const
 {
-    label nColours = 256;
+    constexpr label nColours = 256;
 
     lut->SetNumberOfColors(nColours);
 
@@ -92,47 +402,73 @@ setColourMap
 
     switch (colourMap_)
     {
-        case cmRainbow:
-        {
-            ctf->SetColorSpaceToHSV();
-            ctf->AddRGBPoint(0, 0, 0, 1);
-            ctf->AddRGBPoint(0.5, 0, 1, 0);
-            ctf->AddRGBPoint(1, 1, 0, 0);
-            break;
-        }
-        case cmBlueWhiteRed:
+        case cmCoolToWarm:  // ParaView: "Cool To Warm"
         {
-            // Values taken from ParaView settings
             ctf->SetColorSpaceToDiverging();
-            ctf->AddRGBPoint(0.0, 0.231373, 0.298039, 0.752941);
+            ctf->AddRGBPoint(0.0, 0.231372, 0.298039, 0.752941);
             ctf->AddRGBPoint(0.5, 0.865003, 0.865003, 0.865003);
             ctf->AddRGBPoint(1.0, 0.705882, 0.0156863, 0.14902);
+            // ctf->SetNanColor(1, 1, 0);
+            break;
+        }
+
+        case cmColdAndHot:  // ParaView : "Cold and Hot"
+        {
+            ctf->SetColorSpaceToRGB();
+            ctf->AddRGBPoint(0, 0, 1, 1);
+            ctf->AddRGBPoint(0.45, 0, 0, 1);
+            ctf->AddRGBPoint(0.5, 0, 0, 0.5019608);
+            ctf->AddRGBPoint(0.55, 1, 0, 0);
+            ctf->AddRGBPoint(1, 1, 1, 0);
             break;
         }
-        case cmFire:
+
+        case cmFire:  // ParaView: Black-Body Radiation
         {
-            // Values taken from ParaView settings
             ctf->SetColorSpaceToRGB();
             ctf->AddRGBPoint(0, 0, 0, 0);
             ctf->AddRGBPoint(0.4, 0.901961, 0, 0);
             ctf->AddRGBPoint(0.8, 0.901961, 0.901961, 0);
             ctf->AddRGBPoint(1, 1, 1, 1);
+            // ctf->SetNanColor(0, 0.49804, 1);
+            break;
+        }
+
+        case cmRainbow:
+        {
+            ctf->SetColorSpaceToHSV();
+            ctf->AddRGBPoint(0, 0, 0, 1);
+            ctf->AddRGBPoint(0.5, 0, 1, 0);
+            ctf->AddRGBPoint(1, 1, 0, 0);
+            // ctf->SetNanColor(0.498039, 0.498039, 0.498039);
             break;
         }
-        case cmGreyscale:
+
+        case cmGreyscale: // ParaView: grayscale
         {
             ctf->SetColorSpaceToRGB();
             ctf->AddRGBPoint(0, 0, 0, 0);
             ctf->AddRGBPoint(1, 1, 1, 1);
+            // ctf->SetNanColor(1, 0, 0);
+            break;
+        }
+
+        case cmXray: // ParaView: "X ray"
+        {
+            ctf->SetColorSpaceToRGB();
+            ctf->AddRGBPoint(0, 1, 1, 1);
+            ctf->AddRGBPoint(1, 0, 0, 0);
+            // ctf->SetNanColor(1, 0, 0);
             break;
         }
     }
 
 
-    for (label i = 0; i < nColours; i++)
+    double rgba[4] = { 0, 0, 0, 1 };
+    for (label i = 0; i < nColours; ++i)
     {
-        double* c = ctf->GetColor(scalar(i)/scalar(nColours));
-        lut->SetTableValue(i, c[0], c[1], c[2], 1.0);
+        ctf->GetColor(scalar(i)/scalar(nColours), rgba);
+        lut->SetTableValue(i, rgba);
     }
 }
 
@@ -145,105 +481,11 @@ addScalarBar
     vtkLookupTable* lut
 ) const
 {
-    // Add scalar bar legend
-    if (!scalarBar_.visible_)
+    // Add the scalar bar - only once!
+    if (renderer && Pstream::master())
     {
-        return;
+        scalarBar_.add(colours_["text"]->value(position), renderer, lut);
     }
-
-    auto sbar = vtkSmartPointer<vtkScalarBarActor>::New();
-    sbar->SetLookupTable(lut);
-    sbar->SetNumberOfLabels(scalarBar_.numberOfLabels_);
-
-    const vector textColour = colours_["text"]->value(position);
-
-    // Work-around to supply our own scalarbar title
-    // - Default scalar bar title text is scales by the scalar bar box
-    //   dimensions so if the title is a long string, the text is shrunk to fit
-    //   Instead, suppress title and set the title using a vtkTextActor
-    auto titleActor = vtkSmartPointer<vtkTextActor>::New();
-    sbar->SetTitle(" ");
-    titleActor->SetInput(scalarBar_.title_.c_str());
-    titleActor->GetTextProperty()->SetFontFamilyToArial();
-    titleActor->GetTextProperty()->SetFontSize(3*scalarBar_.fontSize_);
-    titleActor->GetTextProperty()->SetJustificationToCentered();
-    titleActor->GetTextProperty()->SetVerticalJustificationToBottom();
-    titleActor->GetTextProperty()->BoldOn();
-    titleActor->GetTextProperty()->ItalicOff();
-    titleActor->GetTextProperty()->SetColor
-    (
-        textColour[0],
-        textColour[1],
-        textColour[2]
-    );
-    titleActor->GetPositionCoordinate()->
-        SetCoordinateSystemToNormalizedViewport();
-
-    // How to use the standard scalar bar text
-    // sbar->SetTitle(scalarBar_.title_.c_str());
-    // sbar->GetTitleTextProperty()->SetColor
-    // (
-    //     textColour[0],
-    //     textColour[1],
-    //     textColour[2]
-    // );
-    // sbar->GetTitleTextProperty()->SetFontSize(scalarBar_.fontSize_);
-    // sbar->GetTitleTextProperty()->ShadowOff();
-    // sbar->GetTitleTextProperty()->BoldOn();
-    // sbar->GetTitleTextProperty()->ItalicOff();
-
-    sbar->GetLabelTextProperty()->SetColor
-    (
-        textColour[0],
-        textColour[1],
-        textColour[2]
-    );
-    sbar->GetLabelTextProperty()->SetFontSize(scalarBar_.fontSize_);
-    sbar->GetLabelTextProperty()->ShadowOff();
-    sbar->GetLabelTextProperty()->BoldOff();
-    sbar->GetLabelTextProperty()->ItalicOff();
-    sbar->SetLabelFormat(scalarBar_.labelFormat_.c_str());
-
-    sbar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
-    sbar->GetPositionCoordinate()->SetValue
-    (
-        scalarBar_.position_.first(),
-        scalarBar_.position_.second()
-    );
-    if (scalarBar_.vertical_)
-    {
-        sbar->SetOrientationToVertical();
-        sbar->SetWidth(0.1);
-        sbar->SetHeight(0.75);
-        sbar->SetTextPositionToSucceedScalarBar();
-    }
-    else
-    {
-        sbar->SetOrientationToHorizontal();
-
-        // Adjustments since not using scalarbar title property
-        sbar->SetWidth(0.75);
-        sbar->SetHeight(0.07);
-        sbar->SetBarRatio(0.5);
-        // sbar->SetHeight(0.1);
-        // sbar->SetTitleRatio(0.01);
-        sbar->SetTextPositionToPrecedeScalarBar();
-    }
-
-    titleActor->GetPositionCoordinate()->SetValue
-    (
-        scalarBar_.position_.first() + 0.5*sbar->GetWidth(),
-        scalarBar_.position_.second() + sbar->GetHeight()
-    );
-
-    // sbar->DrawFrameOn();
-    // sbar->DrawBackgroundOn();
-    // sbar->UseOpacityOff();
-    // sbar->VisibilityOff();
-    sbar->VisibilityOn();
-
-    renderer->AddActor(sbar);
-    renderer->AddActor2D(titleActor);
 }
 
 
@@ -252,9 +494,9 @@ setField
 (
     const scalar position,
     const word& colourFieldName,
-    vtkPolyDataMapper* mapper,
-    vtkRenderer* renderer,
-    vtkPolyData* pData
+    const FieldAssociation fieldAssociation,
+    vtkMapper* mapper,
+    vtkRenderer* renderer
 ) const
 {
     mapper->InterpolateScalarsBeforeMappingOn();
@@ -266,6 +508,7 @@ setField
             mapper->ScalarVisibilityOff();
             break;
         }
+
         case cbField:
         {
             // Create look-up table for colours
@@ -277,15 +520,15 @@ setField
             const char* fieldName = colourFieldName.c_str();
             mapper->SelectColorArray(fieldName);
 
-            // Set to use either point or cell data
-            // Note: if both point and cell data exists, preferentially
-            //       choosing point data.  This is often the case when using
-            //       glyphs
-            if (pData->GetPointData()->HasArray(fieldName))
+            // Use either point or cell data
+            // - if both point and cell data exists, preferentially choose
+            //   point data.  This is often the case when using glyphs.
+
+            if (fieldAssociation & FieldAssociation::POINT_DATA)
             {
                 mapper->SetScalarModeToUsePointFieldData();
             }
-            else if (pData->GetCellData()->HasArray(fieldName))
+            else if (fieldAssociation & FieldAssociation::CELL_DATA)
             {
                 mapper->SetScalarModeToUseCellFieldData();
             }
@@ -301,7 +544,7 @@ setField
             mapper->SetLookupTable(lut);
             mapper->ScalarVisibilityOn();
 
-            // Add the bar
+            // Add the scalar bar
             addScalarBar(position, renderer, lut);
             break;
         }
@@ -316,43 +559,20 @@ addGlyphs
 (
     const scalar position,
     const word& scaleFieldName,
+    const fieldSummary& scaleFieldInfo,
     const word& colourFieldName,
+    const fieldSummary& colourFieldInfo,
     const scalar maxGlyphLength,
+
     vtkPolyData* data,
     vtkActor* actor,
     vtkRenderer* renderer
 ) const
 {
-    auto glyph = vtkSmartPointer<vtkGlyph3D>::New();
-    auto glyphMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
-    glyphMapper->SetInputConnection(glyph->GetOutputPort());
-
-    glyph->SetInputData(data);
-    glyph->ScalingOn();
-
-    bool needPointData = false;
+    // Determine whether we have CellData/PointData and (scalar/vector)
+    // or if we need to a cell->point data filter.
 
-    // Determine whether we have scalar or vector data
-    // and if we need to convert CellData -> PointData
-
-    label nComponents = -1;
-    const char* scaleFieldNameChar = scaleFieldName.c_str();
-    if (data->GetPointData()->HasArray(scaleFieldNameChar))
-    {
-        nComponents =
-            data->GetPointData()->GetArray(scaleFieldNameChar)
-                ->GetNumberOfComponents();
-    }
-    else if (data->GetCellData()->HasArray(scaleFieldNameChar))
-    {
-        // Need to convert CellData to PointData
-        needPointData = true;
-
-        nComponents =
-            data->GetCellData()->GetArray(scaleFieldNameChar)
-                ->GetNumberOfComponents();
-    }
-    else
+    if (!scaleFieldInfo.exists())
     {
         WarningInFunction
             << "Cannot add glyphs. No such cell or point field: "
@@ -360,30 +580,46 @@ addGlyphs
         return;
     }
 
-
-    const bool ok = (nComponents == 1 || nComponents == 3);
-
-    if (!ok)
+    if (!scaleFieldInfo.isScalar() && !scaleFieldInfo.isVector())
     {
         WarningInFunction
             << "Glyphs can only be added to scalar or vector data. "
             << "Unable to process field " << scaleFieldName << endl;
         return;
     }
-    else if (needPointData)
+
+
+    // Setup glyphs
+
+    // The min/max data range for the input data (cell or point),
+    // which will be slightly less after using a cell->point filter
+    // (since it averages), but is still essentially OK.
+
+
+    auto glyph = vtkSmartPointer<vtkGlyph3D>::New();
+    glyph->ScalingOn();
+
+    auto glyphMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    glyphMapper->SetInputConnection(glyph->GetOutputPort());
+
+    vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
+
+    // The data source is filtered or original (PointData)
+    if (!scaleFieldInfo.hasPointData() || !colourFieldInfo.hasPointData())
     {
-        auto cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
+        // CellData - Need a cell->point filter
+        cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
         cellToPoint->SetInputData(data);
-        cellToPoint->Update();
-        vtkDataSet* pds = cellToPoint->GetOutput();
-        vtkDataArray* pData = pds->GetPointData()->GetArray(scaleFieldNameChar);
 
-        // Store in main vtkPolyData
-        data->GetPointData()->AddArray(pData);
+        glyph->SetInputConnection(cellToPoint->GetOutputPort());
+    }
+    else
+    {
+        glyph->SetInputData(data);
     }
 
 
-    if (nComponents == 1)
+    if (scaleFieldInfo.nComponents_ == 1)
     {
         auto sphere = vtkSmartPointer<vtkSphereSource>::New();
         sphere->SetCenter(0, 0, 0);
@@ -397,12 +633,12 @@ addGlyphs
 
         if (maxGlyphLength > 0)
         {
-            // Can get range from point data:
-
-            // double range[2];
-            // vtkDataArray* values =
-            //     data->GetPointData()->GetScalars(scaleFieldNameChar);
-            // values->GetRange(range);
+            // Using range from the data:
+            // glyph->SetRange
+            // (
+            //     scaleFieldInfo.range_.first(),
+            //     scaleFieldInfo.range_.second()
+            // );
 
             // Set range according to user-supplied limits
             glyph->ClampingOn();
@@ -421,14 +657,14 @@ addGlyphs
         glyph->SetColorModeToColorByScalar();
         glyph->SetInputArrayToProcess
         (
-            0, // scalars
-            0,
-            0,
+            0, // index (0) = scalars
+            0, // port
+            0, // connection
             vtkDataObject::FIELD_ASSOCIATION_POINTS,
-            scaleFieldNameChar
+            scaleFieldName.c_str()
         );
     }
-    else if (nComponents == 3)
+    else if (scaleFieldInfo.nComponents_ == 3)
     {
         auto arrow = vtkSmartPointer<vtkArrowSource>::New();
         arrow->SetTipResolution(10);
@@ -441,24 +677,13 @@ addGlyphs
 
         if (maxGlyphLength > 0)
         {
-            vtkDataArray* values =
-                data->GetPointData()->GetVectors(scaleFieldNameChar);
-
-            double range[6];
-            values->GetRange(range);
-
-            // Attempt to set range for vectors...
-            // scalar x0 = sqrt(sqr(range_.first())/3.0);
-            // scalar x1 = sqrt(sqr(range_.second())/3.0);
-            // range[0] = x0;
-            // range[1] = x0;
-            // range[2] = x0;
-            // range[3] = x1;
-            // range[4] = x1;
-            // range[5] = x1;
-
+            // Set range according data limits
             glyph->ClampingOn();
-            glyph->SetRange(range);
+            glyph->SetRange
+            (
+                scaleFieldInfo.range_.first(),
+                scaleFieldInfo.range_.second()
+            );
             glyph->SetScaleFactor(maxGlyphLength);
         }
         else
@@ -471,20 +696,30 @@ addGlyphs
         glyph->SetColorModeToColorByVector();
         glyph->SetInputArrayToProcess
         (
-            1, // vectors
-            0,
-            0,
+            1, // index (1) = vectors
+            0, // port
+            0, // connection
             vtkDataObject::FIELD_ASSOCIATION_POINTS,
-            scaleFieldNameChar
+            scaleFieldName.c_str()
         );
     }
 
 
-    if (ok)
+    // Apply colouring etc.
+    // We already established PointData, which as either in the original,
+    // or generated with vtkCellDataToPointData filter.
+
     {
         glyph->Update();
 
-        setField(position, colourFieldName, glyphMapper, renderer, data);
+        setField
+        (
+            position,
+            colourFieldName,
+            FieldAssociation::POINT_DATA,  // Original or after filter
+            glyphMapper,
+            renderer
+        );
 
         glyphMapper->Update();
 
@@ -506,9 +741,11 @@ fieldVisualisationBase
 :
     colours_(colours),
     fieldName_(dict.get<word>("field")),
+    smooth_(dict.lookupOrDefault("smooth", false)),
     colourBy_(cbColour),
     colourMap_(cmRainbow),
-    range_()
+    range_(),
+    scalarBar_()
 {
     colourByTypeNames.readEntry("colourBy", dict, colourBy_);
 
@@ -516,26 +753,24 @@ fieldVisualisationBase
     {
         case cbColour:
         {
-            scalarBar_.visible_ = false;
+            scalarBar_.hide();
             break;
         }
+
         case cbField:
         {
             dict.readEntry("range", range_);
-
             colourMapTypeNames.readIfPresent("colourMap", dict, colourMap_);
 
-            const dictionary& sbDict = dict.subDict("scalarBar");
-            sbDict.readEntry("visible", scalarBar_.visible_);
+            const dictionary* sbar = dict.findDict("scalarBar");
 
-            if (scalarBar_.visible_)
+            if (sbar)
+            {
+                scalarBar_.read(*sbar);
+            }
+            else
             {
-                sbDict.readEntry("vertical", scalarBar_.vertical_);
-                sbDict.readEntry("position", scalarBar_.position_);
-                sbDict.readEntry("title", scalarBar_.title_);
-                sbDict.readEntry("fontSize", scalarBar_.fontSize_);
-                sbDict.readEntry("labelFormat", scalarBar_.labelFormat_);
-                sbDict.readEntry("numberOfLabels", scalarBar_.numberOfLabels_);
+                scalarBar_.hide();
             }
             break;
         }
diff --git a/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.H b/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.H
index 6120a90a027f6f628e919887b8d7044f331386cd..b1a56af9eee67069d28a499e0995423852fe6f2b 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,6 +27,20 @@ Class
 Description
     Base class for scene objects
 
+    Dictionary controls - colour by field
+    \table
+        Property    | Description                           | Required | Default
+        colourBy    | Colouring type (color / field)        | yes |
+        range       | Lower/upper range to display          | yes |
+        smooth      | Request smoother output               | no  | false
+        colourMap   | Colour map for rendering              | no  | rainbow
+        scalarBar   | Scalar-bar sub-dictionary             | yes |
+    \endtable
+
+Colour maps include "coolToWarm" ("blueWhiteRed"), "coldAndHot",
+"fire", "rainbow", "greyscale" ("grayscale"), "xray". For historical
+reasons, the default is still "rainbow".
+
 SourceFiles
     fieldVisualisationBase.C
 
@@ -41,27 +53,35 @@ SourceFiles
 #include "Tuple2.H"
 #include "Enum.H"
 #include "vector.H"
+#include "MinMax.H"
 #include "HashPtrTable.H"
+#include "scalarBar.H"
 #include "Function1.H"
 
-// VTK includes
 #include "vtkSmartPointer.h"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations
 class vtkActor;
+class vtkCompositeDataSet;
+class vtkCompositeDataGeometryFilter;
+class vtkCompositePolyDataMapper;
+class vtkDataSet;
+class vtkDataSetAlgorithm;
+class vtkFieldData;
 class vtkLookupTable;
 class vtkMapper;
 class vtkPolyData;
 class vtkPolyDataMapper;
 class vtkRenderer;
 
+
 namespace Foam
 {
 namespace functionObjects
 {
-// Forward declarations
+// Forward Declarations
 class runTimePostProcessing;
 
 namespace runTimePostPro
@@ -75,48 +95,110 @@ class fieldVisualisationBase
 {
 public:
 
-    // Public enumerations
+    // Public Enumerations
 
+        //- Colouring type
         enum colourByType
         {
-            cbColour,
-            cbField
+            cbColour,           //!< "colour" : Use specified colour
+            cbField             //!< "field" : Use named field
         };
 
+        //- Enumeration names for colourByType
         static const Enum<colourByType> colourByTypeNames;
 
+        //- Colour map enumerations
         enum colourMapType
         {
-            cmRainbow,
-            cmBlueWhiteRed,
-            cmFire,
-            cmGreyscale
+            cmCoolToWarm,       //!< ParaView "Cool To Warm" blue-white-read
+            cmBlueWhiteRed = cmCoolToWarm,
+            cmColdAndHot,       //!< ParaView "Cold and Hot"
+            cmFire,             //!< ParaView "Black-Body Radiation"
+            cmRainbow,          //!< "rainbow"
+            cmGreyscale,        //!< ParaView "Grayscale"
+            cmXray              //!< ParaView "X Ray"
         };
 
+        //- Enumeration names for colourMapType
         static const Enum<colourMapType> colourMapTypeNames;
 
 
-protected:
+        //- Enumeration of the data field associations
+        //  These values are used internally and do NOT correspond to the
+        //  vtkDataObject::FieldAssociations enumeration.
+        enum FieldAssociation
+        {
+            NO_DATA  = 0,           //!< No associated data
+            CELL_DATA = 0x1,        //!< Associated with cells (faces)
+            POINT_DATA = 0x2,       //!< Associated with points
+            CELL_POINT_DATA = 0x3   //!< Associated with cells and/or points
+        };
 
-    // Protected Data
 
-        struct scalarBar
+        //- General field characteristics.
+        //  For convenience, the interface is exposed but external use is
+        //  highly discouraged.
+        struct fieldSummary
         {
-            bool visible_;
-            bool vertical_;
-            Tuple2<scalar, scalar> position_;
-            string title_;
-            label fontSize_;
-            string labelFormat_;
-            label numberOfLabels_;
+            int nComponents_;
+            unsigned association_;
+            scalarMinMax range_;
+
+            //- Construct null
+            fieldSummary()
+            :
+                nComponents_(0),
+                association_(0u),
+                range_()
+            {}
+
+            //- Parallel reduction. A no-op if Pstream::parRun() is false
+            void reduce();
+
+            //- True if nComponents_ == 1
+            bool isScalar() const
+            {
+                return nComponents_ == 1;
+            }
+
+            //- True if nComponents_ == 3
+            bool isVector() const
+            {
+                return nComponents_ == 3;
+            }
+
+            //- True if association_ is non-zero
+            bool exists() const
+            {
+                return association_;
+            }
+
+            //- True if there is a POINT_DATA association
+            bool hasPointData() const
+            {
+                return (association_ & FieldAssociation::POINT_DATA);
+            }
+
+            InfoProxy<fieldSummary> info() const
+            {
+                return InfoProxy<fieldSummary>(*this);
+            }
         };
 
+
+protected:
+
+    // Protected Data
+
         //- Colours
         const HashPtrTable<Function1<vector>>& colours_;
 
         //- Field name
         word fieldName_;
 
+        //- Requested smoother fields (eg, interpolate cell -> point values)
+        bool smooth_;
+
         //- Colour by type
         colourByType colourBy_;
 
@@ -126,16 +208,67 @@ protected:
         //- Range of values
         Tuple2<scalar, scalar> range_;
 
-        //- Scalar bar
+        //- Scalar bar characteristics
         scalarBar scalarBar_;
 
 
     // Protected Member Functions
 
+        //- Query DataSet for field name and its field association
+        static fieldSummary queryFieldSummary
+        (
+            const word& fieldName,
+            vtkDataSet* dataset
+        );
+
+        //- Query composite DataSet for field name and its FieldAssociation
+        static fieldSummary queryFieldSummary
+        (
+            const word& fieldName,
+            vtkCompositeDataSet* data
+        );
+
+        //- Query DataSet for field name and its field association
+        static FieldAssociation queryFieldAssociation
+        (
+            const word& fieldName,
+            vtkDataSet* dataset
+        );
+
+        //- Query composite DataSet for field name and its FieldAssociation
+        static FieldAssociation queryFieldAssociation
+        (
+            const word& fieldName,
+            vtkCompositeDataSet* data
+        );
+
+
+        //- Add "mag(..)" field for filters that only accept scalars
+        static void addMagField
+        (
+            const word& fieldName,
+            vtkFieldData* fieldData
+        );
+
+        //- Add "mag(..)" field for filters that only accept scalars
+        static void addMagField
+        (
+            const word& fieldName,
+            vtkDataSet* dataset
+        );
+
+        //- Add "mag(..)" field for filters that only accept scalars
+        static void addMagField
+        (
+            const word& fieldName,
+            vtkCompositeDataSet* data
+        );
+
+
         //- Set the colour map
         void setColourMap(vtkLookupTable* lut) const;
 
-        //- Add scalar bar to renderer
+        //- Add scalar bar (if visible) to renderer
         void addScalarBar
         (
             const scalar position,
@@ -148,9 +281,9 @@ protected:
         (
             const scalar position,
             const word& colourFieldName,
-            vtkPolyDataMapper* mapper,
-            vtkRenderer* renderer,
-            vtkPolyData* pData
+            const FieldAssociation fieldAssociation,
+            vtkMapper* mapper,
+            vtkRenderer* renderer
         ) const;
 
         //- Add glyphs
@@ -158,13 +291,17 @@ protected:
         (
             const scalar position,
             const word& scaleFieldName,
+            const fieldSummary& scaleFieldInfo,
             const word& colourFieldName,
+            const fieldSummary& colourFieldInfo,
             const scalar maxGlyphLength,
+
             vtkPolyData* data,
             vtkActor* actor,
             vtkRenderer* renderer
         ) const;
 
+
         //- No copy construct
         fieldVisualisationBase(const fieldVisualisationBase&) = delete;
 
@@ -204,6 +341,19 @@ public:
 
 } // End namespace runTimePostPro
 } // End namespace functionObjects
+
+
+// Ostream
+Ostream& operator<<
+(
+    Ostream& os,
+    const InfoProxy
+    <
+        functionObjects::runTimePostPro::fieldVisualisationBase::fieldSummary
+    >& proxy
+);
+
+
 } // End namespace Foam
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.C b/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.C
index 28d4fbffb3f432803048e8af85d5a1adcb2438f4..c44fecc56fee28aa6148cdfe6b40bd42a13fad6a 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -25,20 +25,6 @@ License
 
 #include "functionObjectBase.H"
 
-// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
-
-namespace Foam
-{
-namespace functionObjects
-{
-namespace runTimePostPro
-{
-    defineTypeNameAndDebug(functionObjectBase, 0);
-}
-}
-}
-
-
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
 Foam::fileName
@@ -69,7 +55,12 @@ bool Foam::functionObjects::runTimePostPro::functionObjectBase::removeFile
 {
     // Foam::rm() ignores empty names etc.
 
-    return Foam::rm(getFileName(keyword, subDictName));
+    if (Pstream::master())
+    {
+        return Foam::rm(getFileName(keyword, subDictName));
+    }
+
+    return false;
 }
 
 
@@ -85,6 +76,7 @@ Foam::functionObjects::runTimePostPro::functionObjectBase::functionObjectBase
     fieldVisualisationBase(dict, colours),
     state_(state),
     functionObjectName_(dict.get<word>("functionObject")),
+    liveObject_(dict.lookupOrDefault("liveObject", true)),
     clearObjects_(dict.lookupOrDefault("clearObjects", false))
 {}
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.H b/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.H
index 47647fc5e736b7393438cbfbb7046b03363d6659..abab1a26074c290d82de3cca9af2b062e1d44a2e 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectBase.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -29,11 +29,15 @@ Description
 
     Dictionary controls
     \table
-        Property     | Description                      | Required    | Default
-        functionObject | The data source                | yes         |
-        clearObjects  | Remove file after use           | no          | no
+        Property     | Description                          | Required | Default
+        functionObject | The data source                    | yes |
+        clearObjects | Remove file after use                | no  | no
+        liveObject   | Prefer simulation data source        | no  | true
     \endtable
 
+    The "live" keyword indiates that from within the simulation (in memory)
+    is preferred over data from disk (for example).
+
 SourceFiles
     functionObjectBase.C
 
@@ -64,14 +68,17 @@ class functionObjectBase
 {
 protected:
 
-    // Protected data
+    // Protected Data
 
         //- Reference to the state
         const stateFunctionObject& state_;
 
-        //- The function object name which provides the source data
+        //- The function object name that provides the source data
         word functionObjectName_;
 
+        //- Flag to indicate "live" (simulation) data source should be used
+        bool liveObject_;
+
         //- Flag to indicate that source data should be cleared after use
         bool clearObjects_;
 
@@ -109,6 +116,7 @@ protected:
         //  \note does not change the stateFunctionObject
         bool removeFile(const word& keyword, const word& subDictName);
 
+
         //- No copy construct
         functionObjectBase(const functionObjectBase&) = delete;
 
@@ -118,10 +126,6 @@ protected:
 
 public:
 
-    //- Run-time type information
-    TypeName("functionObjectBase");
-
-
     // Constructors
 
         //- Construct from dictionary
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.C b/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.C
index a429d7fffd3189ee60a3d56b412ee35273732d77..54e71f0444cff104e7945e1f76f54075532e8ca0 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -33,11 +31,11 @@ License
 
 // VTK includes
 #include "vtkActor.h"
-#include "vtkRenderer.h"
-#include "vtkSmartPointer.h"
 #include "vtkPolyData.h"
 #include "vtkPolyDataMapper.h"
 #include "vtkProperty.h"
+#include "vtkRenderer.h"
+#include "vtkSmartPointer.h"
 
 // VTK Readers
 #include "vtkPolyDataReader.h"
@@ -51,13 +49,42 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(functionObjectCloud, 0);
+    defineTypeName(functionObjectCloud);
     addToRunTimeSelectionTable(pointData, functionObjectCloud, dictionary);
 }
 }
 }
 
 
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace
+{
+
+static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
+{
+    // Very simple - we only support vtp files, which are expected to have
+    // the scaling and colouring fields.
+
+    vtkSmartPointer<vtkPolyData> dataset;
+
+    if (fName.ext() == "vtp")
+    {
+        auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
+
+        reader->SetFileName(fName.c_str());
+        reader->Update();
+        dataset = reader->GetOutput();
+
+        return dataset;
+    }
+
+    return dataset;
+}
+
+} // End anonymous namespace
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::functionObjects::runTimePostPro::functionObjectCloud::functionObjectCloud
@@ -73,9 +100,7 @@ Foam::functionObjects::runTimePostPro::functionObjectCloud::functionObjectCloud
     inputFileName_(),
     colourFieldName_(dict.get<word>("colourField")),
     actor_()
-{
-    actor_ = vtkSmartPointer<vtkActor>::New();
-}
+{}
 
 
 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
@@ -87,8 +112,8 @@ Foam::functionObjects::runTimePostPro::functionObjectCloud::
 
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
-void Foam::functionObjects::runTimePostPro::functionObjectCloud::
-addGeometryToScene
+bool Foam::functionObjects::runTimePostPro::functionObjectCloud::
+addGeometryFromFile
 (
     const scalar position,
     vtkRenderer* renderer
@@ -96,72 +121,118 @@ addGeometryToScene
 {
     if (!visible_)
     {
-        return;
+        return false;
     }
 
+    vtkSmartPointer<vtkPolyData> polyData;
+
+    bool good = true;
+
     // The vtkCloud stores 'file' via the stateFunctionObject
     // (lookup by cloudName).
     // It only generates VTP format, which means there is a single file
     // containing all fields.
 
-    inputFileName_ = getFileName("file", cloudName_);
+    if (Pstream::master())
+    {
+        inputFileName_ = getFileName("file", cloudName_);
+
+        if (inputFileName_.size())
+        {
+            polyData = getPolyDataFile(inputFileName_);
+
+            if (!polyData || polyData->GetNumberOfPoints() == 0)
+            {
+                good = false;
+
+                WarningInFunction
+                    << "Could not read "<< inputFileName_ << nl
+                    << "Only VTK (.vtp) files are supported"
+                    << endl;
+            }
+            else
+            {
+                DebugInfo
+                    << "    Resolved cloud file "
+                    << inputFileName_ << endl;
+            }
+        }
+        else
+        {
+            good = false;
 
-    if (inputFileName_.empty())
+            WarningInFunction
+                << "Unable to find function object " << functionObjectName_
+                << " output for field " << fieldName_
+                << ". Cloud will not be processed"
+                << endl;
+        }
+    }
+    else
     {
-        WarningInFunction
-            << "Unable to find function object " << functionObjectName_
-            << " output for field " << fieldName_
-            << ". Cloud will not be processed"
-            << endl;
-        return;
+        inputFileName_.clear();
     }
 
+    reduce(good, andOp<bool>());
 
-    vtkSmartPointer<vtkPolyData> dataset;
-
-    if (inputFileName_.hasExt("vtp"))
+    if (!good)
     {
-        auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
-        reader->SetFileName(inputFileName_.c_str());
-        reader->Update();
-
-        dataset = reader->GetOutput();
+        return false;
     }
-    else
+
+    // Only render on master
+    if (!renderer || !Pstream::master())
     {
-        // Invalid name - ignore.
-        // Don't support VTK legacy format at all - it is too wasteful
-        // and cumbersome.
+        return true;
+    }
 
-        WarningInFunction
-            << "Could not read "<< inputFileName_ << nl
-            << "Only VTK (.vtp) files are supported"
-            << ". Cloud will not be processed"
-            << endl;
 
-        inputFileName_.clear();
-    }
+    // Rendering
 
+    actor_ = vtkSmartPointer<vtkActor>::New();
 
-    if (dataset)
     {
+        fieldSummary scaleFieldInfo =
+            queryFieldSummary(fieldName_, polyData);
+
+        fieldSummary colourFieldInfo =
+            queryFieldSummary(colourFieldName_, polyData);
+
+        // No reduction
+
         auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
 
         actor_->SetMapper(mapper);
 
+        /// dataset->Print(std::cout);
+
         addGlyphs
         (
             position,
-            fieldName_,
-            colourFieldName_,
+            fieldName_, scaleFieldInfo,         // scaling
+            colourFieldName_, colourFieldInfo,  // colour
             maxGlyphLength_,
-            dataset,
+            polyData,
             actor_,
             renderer
         );
 
         renderer->AddActor(actor_);
     }
+
+    return true;
+}
+
+
+void Foam::functionObjects::runTimePostPro::functionObjectCloud::
+addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    // File source
+    addGeometryFromFile(position, renderer);
 }
 
 
@@ -170,10 +241,16 @@ void Foam::functionObjects::runTimePostPro::functionObjectCloud::updateActors
     const scalar position
 )
 {
-    actor_->GetProperty()->SetOpacity(opacity(position));
+    if (actor_)
+    {
+        const vector colour = pointColour_->value(position);
+
+        vtkProperty* prop = actor_->GetProperty();
 
-    vector pc = pointColour_->value(position);
-    actor_->GetProperty()->SetColor(pc[0], pc[1], pc[2]);
+        prop->SetOpacity(opacity(position));
+
+        prop->SetColor(colour[0], colour[1], colour[2]);
+    }
 }
 
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.H b/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.H
index 9cf9e2dd6691a9921a9493453d4584943ed8f6cc..ba8d43a1689b30726bb033cea914ba136d000278 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectCloud.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,7 +25,17 @@ Class
     Foam::functionObjects::runTimePostPro::functionObjectCloud
 
 Description
-    Visualisation of cloud data from function object output
+    Visualisation of cloud data from function object output (file-based only).
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The point type: functionObjectCloud   | yes |
+        functionObject | The data source                    | yes |
+        cloud         | The cloud name                      | no  |
+        field         | The field for glyphs scaling        | no  |
+        colourField   | The field to display                | no  |
+    \endtable
 
 SourceFiles
     functionObjectCloud.C
@@ -87,7 +95,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("functionObjectCloud");
+    TypeNameNoDebug("functionObjectCloud");
 
 
     // Constructors
@@ -107,7 +115,14 @@ public:
 
     // Member Functions
 
-        //- Add tube(s) to scene
+        //- Add cloud to scene (using file source)
+        bool addGeometryFromFile
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add cloud to scene
         virtual void addGeometryToScene
         (
             const scalar position,
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.C b/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.C
index 3db0705aab98f9dcec9fed97ffbc0c702842c479..e27dabcc972c71887bcdd7e57a0934a321266c8a 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -50,7 +48,7 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(functionObjectLine, 0);
+    defineTypeName(functionObjectLine);
     addToRunTimeSelectionTable(pathline, functionObjectLine, dictionary);
 }
 }
@@ -67,7 +65,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
     // Not extremely elegant...
     vtkSmartPointer<vtkPolyData> dataset;
 
-    if (fName.ext() == "vtk")
+    if ("vtk" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkPolyDataReader>::New();
 
@@ -78,7 +76,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
         return dataset;
     }
 
-    if (fName.ext() == "vtp")
+    if ("vtp" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
 
@@ -127,7 +125,8 @@ addGeometryToScene
     vtkRenderer* renderer
 )
 {
-    if (!visible_)
+    // Currently master-only
+    if (!visible_ || !renderer || !Pstream::master())
     {
         return;
     }
@@ -155,10 +154,19 @@ addGeometryToScene
         return;
     }
 
+    DebugInfo << "    Resolved lines " << fName << endl;
+
 
     auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
 
-    setField(position, fieldName_, mapper, renderer, polyData);
+    setField
+    (
+        position,
+        fieldName_,
+        queryFieldAssociation(fieldName_, polyData),
+        mapper,
+        renderer
+    );
 
     actor_->SetMapper(mapper);
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.H b/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.H
index f5b88bc9c6b1f98c7c902f27388419b6910c4806..63cc9619727fac28f6b342ba597409b4aab75194 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectLine.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,7 +25,13 @@ Class
     Foam::functionObjects::runTimePostPro::functionObjectLine
 
 Description
-    Visualisation of line data from function object output
+    Visualisation of line data from function object output (file-based only).
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The line type: functionObjectLine     | yes |
+    \endtable
 
 SourceFiles
     functionObjectLine.C
@@ -78,7 +82,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("functionObjectLine");
+    TypeNameNoDebug("functionObjectLine");
 
 
     // Constructors
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.C b/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.C
index ce841148051de3fb9603a21da963067c96723bfa..801305f8c788f8e7fd86cbe55a8e70173ddfea3b 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,6 +30,13 @@ License
 
 // VTK includes
 #include "vtkActor.h"
+#include "vtkCellData.h"
+#include "vtkCellDataToPointData.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
 #include "vtkPolyData.h"
 #include "vtkPolyDataMapper.h"
 #include "vtkProperty.h"
@@ -50,7 +55,7 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(functionObjectSurface, 0);
+    defineTypeName(functionObjectSurface);
     addToRunTimeSelectionTable(surface, functionObjectSurface, dictionary);
 }
 }
@@ -67,7 +72,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
     // Not extremely elegant...
     vtkSmartPointer<vtkPolyData> dataset;
 
-    if (fName.ext() == "vtk")
+    if ("vtk" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkPolyDataReader>::New();
 
@@ -78,7 +83,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
         return dataset;
     }
 
-    if (fName.ext() == "vtp")
+    if ("vtp" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
 
@@ -110,17 +115,10 @@ functionObjectSurface
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::functionObjects::runTimePostPro::functionObjectSurface::
-~functionObjectSurface()
-{}
-
-
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
-void Foam::functionObjects::runTimePostPro::functionObjectSurface::
-addGeometryToScene
+bool Foam::functionObjects::runTimePostPro::functionObjectSurface::
+addGeometry
 (
     const scalar position,
     vtkRenderer* renderer
@@ -128,39 +126,291 @@ addGeometryToScene
 {
     if (!visible_)
     {
-        return;
+        return false;
+    }
+
+    DebugInfo << "    Resolve surface " << functionObjectName_ << endl;
+
+    const polySurface* surf =
+    (
+        geometryBase::parent_.storedObjects()
+       .cfindObject<polySurface>(functionObjectName_)
+    );
+
+    // Treat surface with no faces/points like a missing surface
+    surf = ((surf && surf->nPoints()) ? surf : nullptr);
+
+    bool hasSurface = surf;
+
+
+    // Retrieve the field association (CELL, POINT) for the given field
+
+    unsigned fieldAssociation(0u);
+    if (surf)
+    {
+        unsigned queried = surf->queryFieldAssociation(fieldName_);
+
+        if (queried & polySurface::FACE_DATA)
+        {
+            fieldAssociation |= FieldAssociation::CELL_DATA;
+        }
+        if (queried & polySurface::POINT_DATA)
+        {
+            fieldAssociation |= FieldAssociation::POINT_DATA;
+        }
+    }
+
+    // Reduce the information
+    if (Pstream::parRun())
+    {
+        if (!hasSurface)
+        {
+            // No geometry - set all field association bits ON to ensure
+            // it does not affect bitwise reduction.
+            fieldAssociation = (~0u);
+        }
+
+        reduce(hasSurface, orOp<bool>());
+        reduce(fieldAssociation, bitAndOp<unsigned>());
     }
 
-    fileName fName = getFileName("file", fieldName_);
-    if (fName.empty())
+    if (!hasSurface)
     {
         WarningInFunction
-            << "Unable to read file name from function object "
-            << functionObjectName_ << " for field " << fieldName_
-            << ". Surface will not be processed"
+            << "No functionObject surface, or has no faces: "
+            << functionObjectName_
             << endl;
-        return;
+
+        if (debug)
+        {
+            Info<< "    Available surfaces:" << nl
+                << geometryBase::parent_.storedObjects()
+                    .sortedNames<polySurface>() << endl;
+        }
+        return false;
     }
 
+    //// Pout<< "local surface = " << (surf ? surf->nFaces() : 0) << nl;
+
+
+    // Create a vtkMultiPieceDataSet with vtkPolyData on the leaves
+    vtkSmartPointer<vtkMultiPieceDataSet> multiPiece;
 
-    auto polyData = getPolyDataFile(fName);
+    // Requesting glyphs on the surface AND only have face data?
+    // - just use the faceCentres directly and attach fields as CellData
+    //   (not PointData).
 
-    if (!polyData || polyData->GetNumberOfPoints() == 0)
+    if
+    (
+        representation_ == rtGlyph
+     && (fieldAssociation == FieldAssociation::CELL_DATA)
+    )
     {
-        WarningInFunction
-            << "Could not read "<< fName << nl
-            << "Only VTK (.vtp, .vtk) files are supported"
-            << endl;
-        return;
+        multiPiece = gatherFaceCentres(surf);
     }
+    else
+    {
+        multiPiece = gatherSurfacePieces(surf);
+    }
+
+
+    // Add the field (the information is consistent after last reduction).
+
+    // Need field(s) for glyphs or colourByField:
+
+    if (representation_ == rtGlyph || colourBy_ == cbField)
+    {
+        if (fieldAssociation == FieldAssociation::CELL_DATA)
+        {
+            addDimField<polySurfaceGeoMesh>
+            (
+                multiPiece,
+                surf,
+                fieldName_
+            );
+        }
+        else if (fieldAssociation & FieldAssociation::POINT_DATA)
+        {
+            addDimField<polySurfacePointGeoMesh>
+            (
+                multiPiece,
+                surf,
+                fieldName_
+            );
+        }
+    }
+
+
+    // Now have a multi-piece dataset that is one of the following:
+    //
+    // - one-piece per processor (OpenFOAM = parallel, VTK=parallel)
+    // - all pieces on master only (OpenFOAM = parallel, VTK=serial)
+
+    // Re-query field information - we may have stored it differently
+    // than the original source.
+
+    fieldSummary fieldInfo = queryFieldSummary(fieldName_, multiPiece);
+    fieldInfo.reduce();
+
+
+    // Not rendered on this processor?
+    // This is where we stop, but could also have an MPI barrier
+    if (!renderer)
+    {
+        return true;
+    }
+
+
+    // Rendering
+
+    {
+        auto polyData = vtkSmartPointer<vtkCompositeDataGeometryFilter>::New();
+
+        polyData->SetInputData(multiPiece);
+        polyData->Update();
+
+        if (representation_ == rtGlyph)
+        {
+            addGlyphs
+            (
+                position,
+                fieldName_, fieldInfo,  // scaling
+                fieldName_, fieldInfo,  // colouring
+                maxGlyphLength_,
+                polyData->GetOutput(),
+                surfaceActor_,
+                renderer
+            );
+        }
+        else
+        {
+            vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
+
+            // CellData - Need a cell->point filter
+            if (smooth_ && !fieldInfo.hasPointData())
+            {
+                cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPoint->SetInputData(multiPiece);
+
+                polyData->SetInputConnection(cellToPoint->GetOutputPort());
+            }
+            else
+            {
+                polyData->SetInputData(multiPiece);
+            }
+            polyData->Update();
+
+
+            if (!smooth_)
+            {
+                addFeatureEdges(renderer, polyData);
+            }
+
+            auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+            mapper->SetInputConnection(polyData->GetOutputPort());
+
+            setField
+            (
+                position,
+                fieldName_,
+                (
+                    smooth_
+                  ? FieldAssociation::POINT_DATA
+                  : FieldAssociation(fieldInfo.association_)
+                ),
+                mapper,
+                renderer
+            );
+
+            surfaceActor_->SetMapper(mapper);
+
+            setRepresentation(surfaceActor_);
+
+            renderer->AddActor(surfaceActor_);
+        }
+    }
+
+    return true;
+}
+
+
+bool Foam::functionObjects::runTimePostPro::functionObjectSurface::
+addGeometryFromFile
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_)
+    {
+        return false;
+    }
+
+    vtkSmartPointer<vtkPolyData> polyData;
+
+    bool good = true;
+
+    // File reading is serial (master only)
+    if (Pstream::master())
+    {
+        fileName fName = getFileName("file", fieldName_);
+
+        if (fName.size())
+        {
+            polyData = getPolyDataFile(fName);
+
+            if (!polyData || polyData->GetNumberOfPoints() == 0)
+            {
+                good = false;
+
+                WarningInFunction
+                    << "Could not read "<< fName << nl
+                    << "Only VTK (.vtp, .vtk) files are supported"
+                    << endl;
+            }
+            else
+            {
+                DebugInfo << "    Resolved surface " << fName << endl;
+            }
+        }
+        else
+        {
+            good = false;
+
+            WarningInFunction
+                << "Unable to read file name from function object "
+                << functionObjectName_ << " for field " << fieldName_
+                << ". Surface will not be processed"
+                << endl;
+        }
+    }
+
+    reduce(good, andOp<bool>());
+
+    if (!good)
+    {
+        return false;
+    }
+
+    // Only render on master
+    if (!renderer || !Pstream::master())
+    {
+        return true;
+    }
+
+    fieldSummary fieldInfo = queryFieldSummary(fieldName_, polyData);
+    // No reduction (serial)
+
+
+    // Render
 
     if (representation_ == rtGlyph)
     {
         addGlyphs
         (
             position,
-            fieldName_,
-            fieldName_,
+            fieldName_, fieldInfo,  // scaling
+            fieldName_, fieldInfo,  // colouring
             maxGlyphLength_,
             polyData,
             surfaceActor_,
@@ -174,7 +424,14 @@ addGeometryToScene
         auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
         mapper->SetInputData(polyData);
 
-        setField(position, fieldName_, mapper, renderer, polyData);
+        setField
+        (
+            position,
+            fieldName_,
+            queryFieldAssociation(fieldName_, polyData),
+            mapper,
+            renderer
+        );
 
         surfaceActor_->SetMapper(mapper);
 
@@ -182,6 +439,44 @@ addGeometryToScene
 
         renderer->AddActor(surfaceActor_);
     }
+
+    return true;
+}
+
+
+void Foam::functionObjects::runTimePostPro::functionObjectSurface::
+addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_)
+    {
+        return;
+    }
+
+    if (liveObject_)
+    {
+        // Live source
+        if (addGeometry(position, renderer))
+        {
+            return;
+        }
+
+        WarningInFunction
+            << "No functionObject live source, or is empty: "
+            << functionObjectName_
+            << " ... attempting with file source"
+            << endl;
+    }
+    else
+    {
+        DebugInfo << "Using file source only" << nl;
+    }
+
+    // File source
+    addGeometryFromFile(position, renderer);
 }
 
 
@@ -189,6 +484,8 @@ bool Foam::functionObjects::runTimePostPro::functionObjectSurface::clear()
 {
     if (functionObjectBase::clear())
     {
+        // Even for a "live" data source we allow file cleanup
+        // (eg, from a previous run, etc)
         return removeFile("file", fieldName_);
     }
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.H b/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.H
index 719ba19e6b7de06f7b785cf5884335125bd87395..eb1288a624f753266841aca8ae398af32876b3fa 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/functionObjectSurface.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,7 +25,22 @@ Class
     Foam::functionObjects::runTimePostPro::functionObjectSurface
 
 Description
-    Visualisation of surface data from function object output
+    Visualisation of surface data from function object output,
+    typically the result of a prior sampled surfaces operation.
+
+    Operates in a "live" mode, in which the previously sampled surfaces
+    are retrieved from the functionObject registry.
+    Or in the traditional file-based mode, in which the sampling is used
+    to generate a file and its name is retrieved from the functionObject
+    properties.
+
+    File-based import is restricted to "vtk" and "vtp" formats.
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The type: functionObjectSurface       | yes |
+    \endtable
 
 SourceFiles
     functionObjectfunctionObjectSurface.C
@@ -72,7 +85,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("functionObjectSurface");
+    TypeNameNoDebug("functionObjectSurface");
 
 
     // Constructors
@@ -87,11 +100,25 @@ public:
 
 
     //- Destructor
-    virtual ~functionObjectSurface();
+    virtual ~functionObjectSurface() = default;
 
 
     // Member Functions
 
+        //- Add functionObjectSurface to scene (using simulation source)
+        bool addGeometry
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add functionObjectSurface to scene (using file source)
+        bool addGeometryFromFile
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
         //- Add functionObjectSurface(s) to scene
         virtual void addGeometryToScene
         (
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryBase.C b/src/functionObjects/graphics/runTimePostProcessing/geometryBase.C
index c75179dbd93e8a9437737356d002f1c888454fb1..bac6e4fb06ec0ee7a7b93117d043068f89289e61 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometryBase.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryBase.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,6 +33,18 @@ License
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
 
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+    defineDebugSwitchWithName(geometryBase, "runTimePostPro::geometryBase", 0);
+}
+}
+}
+
 const Foam::Enum
 <
     Foam::functionObjects::runTimePostPro::geometryBase::renderModeType
@@ -89,7 +99,7 @@ Foam::functionObjects::runTimePostPro::geometryBase::geometryBase
 :
     parent_(parent),
     name_(dict.dictName()),
-    visible_(dict.get<bool>("visible")),
+    visible_(dict.lookupOrDefault("visible", true)),
     renderMode_
     (
         renderModeTypeNames.lookupOrDefault("renderMode", dict, rmGouraud)
@@ -123,6 +133,13 @@ Foam::functionObjects::runTimePostPro::geometryBase::parent() const
 }
 
 
+bool Foam::functionObjects::runTimePostPro::geometryBase::
+needsCollective() const
+{
+    return parent_.needsCollective();
+}
+
+
 const Foam::word&
 Foam::functionObjects::runTimePostPro::geometryBase::name() const
 {
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryBase.H b/src/functionObjects/graphics/runTimePostProcessing/geometryBase.H
index 96f7cc99e57afcb01620e4e3a0d9904861037997..6fdb2c5ba5129ce7d55a46c23fde05c3c265acd0 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometryBase.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryBase.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,12 +25,12 @@ Class
     Foam::functionObjects::runTimePostPro::geometryBase
 
 Description
-    Base class for surface, text handling
+    Base class for surface, text handling etc.
 
     Dictionary controls
     \table
         Property    | Description                          | Required | Default
-        visible     | Display the object                   | yes |
+        visible     | Display the object                   | no  | yes
         renderMode  | Shading (flat/gouraud/phong)         | no  | gouraud
         opacity     | Object opacity                       | no  | 1.0
     \endtable
@@ -53,7 +51,7 @@ SourceFiles
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations (VTK)
 class vtkRenderer;
 class vtkActor;
 
@@ -62,7 +60,7 @@ namespace Foam
 namespace functionObjects
 {
 
-// Forward declarations
+// Forward Declarations
 class runTimePostProcessing;
 
 namespace runTimePostPro
@@ -75,11 +73,11 @@ namespace runTimePostPro
 
 class geometryBase
 {
-
 public:
 
-    // Public enumerations
+    // Public Enumerations
 
+        //- Surface shading types
         enum renderModeType
         {
             rmFlat,             //!< Flat shading
@@ -87,6 +85,7 @@ public:
             rmPhong             //!< Phong shading
         };
 
+        //- Names for surface shading types
         static const Enum<renderModeType> renderModeTypeNames;
 
 
@@ -97,7 +96,7 @@ protected:
         //- Reference to the parent function object
         const runTimePostProcessing& parent_;
 
-        //- Name
+        //- The surface name
         word name_;
 
         //- Visible flag
@@ -113,7 +112,7 @@ protected:
         const HashPtrTable<Function1<vector>>& colours_;
 
 
-    // Protected functions
+    // Protected Functions
 
         //- Initialise actor
         void initialiseActor(vtkActor* actor) const;
@@ -127,6 +126,9 @@ protected:
 
 public:
 
+    //- Debug switch
+    static int debug;
+
     // Constructors
 
         //- Construct from dictionary
@@ -149,6 +151,10 @@ public:
         //- Return the reference to the parent function object
         const runTimePostProcessing& parent() const;
 
+        //- May need to gather geometry parts to render on single-processor
+        //  True when OpenFOAM is running in parallel but VTK is not.
+        bool needsCollective() const;
+
         //- Return the name
         const word& name() const;
 
@@ -162,7 +168,7 @@ public:
         const HashPtrTable<Function1<vector>>& colours() const;
 
 
-    // Scene interaction
+    // Scene Interaction
 
         //- Add geometry to scene
         virtual void addGeometryToScene
@@ -174,7 +180,7 @@ public:
         //- Update the actors
         virtual void updateActors(const scalar position) = 0;
 
-        //- Clear files used to create the object(s)
+        //- Clear any files used to create the object(s)
         virtual bool clear() = 0;
 };
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C
new file mode 100644
index 0000000000000000000000000000000000000000..350c714f8bf5ac217048e866948dfacd2ab5918c
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C
@@ -0,0 +1,263 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "geometryCloud.H"
+#include "cloud.H"
+#include "fvMesh.H"
+#include "runTimePostProcessing.H"
+#include "addToRunTimeSelectionTable.H"
+
+// VTK includes
+#include "vtkActor.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkRenderer.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+    defineTypeName(geometryCloud);
+    addToRunTimeSelectionTable(pointData, geometryCloud, dictionary);
+}
+}
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+bool Foam::functionObjects::runTimePostPro::geometryCloud::addCloudField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const objectRegistry& obrTmp,
+    const word& fieldName
+) const
+{
+    const regIOobject* ioptr = obrTmp.cfindObject<regIOobject>(fieldName);
+
+    return (multiPiece) &&
+    (
+        addCloudField<label>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addCloudField<scalar>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addCloudField<vector>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addCloudField<sphericalTensor>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addCloudField<symmTensor>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addCloudField<tensor>
+        (
+            multiPiece, ioptr, fieldName
+        )
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::geometryCloud::geometryCloud
+(
+    const runTimePostProcessing& parent,
+    const dictionary& dict,
+    const HashPtrTable<Function1<vector>>& colours
+)
+:
+    pointData(parent, dict, colours),
+    fieldVisualisationBase(dict, colours),
+    cloudName_(dict.get<word>("cloud")),
+    colourFieldName_(dict.get<word>("colourField")),
+    actor_()
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::geometryCloud::
+~geometryCloud()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::runTimePostPro::geometryCloud::
+addGeometry
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_)
+    {
+        return false;
+    }
+
+
+    // This is similar (almost identical) to vtkCloud
+
+    const auto* objPtr = parent().mesh().cfindObject<cloud>(cloudName_);
+    if (!objPtr)
+    {
+        return false;
+    }
+
+    objectRegistry obrTmp
+    (
+        IOobject
+        (
+            "runTimePostPro::cloud::" + cloudName_,
+            parent().mesh().time().constant(),
+            parent().mesh(),
+            IOobject::NO_READ,
+            IOobject::NO_WRITE,
+            false
+        )
+    );
+
+    objPtr->writeObjects(obrTmp);
+
+    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+
+    if (!pointsPtr)
+    {
+        // This should be impossible
+        return false;
+    }
+
+
+    // Create a vtkMultiPieceDataSet with vtkPolyData on the leaves
+    auto multiPiece = gatherCloud(obrTmp);
+
+    // Add in scaleField and colourField
+    addCloudField(multiPiece, obrTmp, fieldName_);
+    addCloudField(multiPiece, obrTmp, colourFieldName_);
+
+
+    // Not rendered on this processor?
+    // This is where we stop, but could also have an MPI barrier
+    if (!renderer)
+    {
+        return true;
+    }
+
+
+    // Rendering
+
+    actor_ = vtkSmartPointer<vtkActor>::New();
+
+    {
+        fieldSummary scaleFieldInfo =
+            queryFieldSummary(fieldName_, multiPiece);
+
+        fieldSummary colourFieldInfo =
+            queryFieldSummary(colourFieldName_, multiPiece);
+
+        auto polyData = vtkSmartPointer<vtkCompositeDataGeometryFilter>::New();
+
+        polyData->SetInputData(multiPiece);
+        polyData->Update();
+
+        auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+
+        actor_->SetMapper(mapper);
+
+        /// dataset->Print(std::cout);
+
+        addGlyphs
+        (
+            position,
+            fieldName_, scaleFieldInfo,         // scaling
+            colourFieldName_, colourFieldInfo,  // colour
+            maxGlyphLength_,
+            polyData->GetOutput(),
+            actor_,
+            renderer
+        );
+
+        renderer->AddActor(actor_);
+    }
+
+    return true;
+}
+
+
+void Foam::functionObjects::runTimePostPro::geometryCloud::
+addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    // Live source
+    addGeometry(position, renderer);
+}
+
+
+void Foam::functionObjects::runTimePostPro::geometryCloud::updateActors
+(
+    const scalar position
+)
+{
+    if (actor_)
+    {
+        const vector colour = pointColour_->value(position);
+
+        vtkProperty* prop = actor_->GetProperty();
+
+        prop->SetOpacity(opacity(position));
+
+        prop->SetColor(colour[0], colour[1], colour[2]);
+    }
+}
+
+
+bool Foam::functionObjects::runTimePostPro::geometryCloud::clear()
+{
+    return false;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.H b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.H
new file mode 100644
index 0000000000000000000000000000000000000000..a420307fe3c1aa2c243622af9ad0591f12a0219b
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.H
@@ -0,0 +1,201 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::geometryCloud
+
+Description
+    Visualisation of cloud data from function object output (file-based only).
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The point type: geometryCloud         | yes |
+        cloud         | The cloud name                      | no  |
+        field         | The field for glyphs scaling        | no  |
+        colourField   | The field to display                | no  |
+    \endtable
+
+SourceFiles
+    geometryCloud.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_geometryCloud_H
+#define functionObjects_runTimePostPro_geometryCloud_H
+
+#include "pointData.H"
+#include "functionObjectBase.H"
+#include "IOField.H"
+
+#include "vtkSmartPointer.h"
+#include "vtkMultiPieceDataSet.h"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                     Class geometryCloud Declaration
+\*---------------------------------------------------------------------------*/
+
+class geometryCloud
+:
+    public pointData,
+    public fieldVisualisationBase
+{
+protected:
+
+    // Protected Data
+
+        //- Name of geometryCloud
+        word cloudName_;
+
+        //- Name of field to colour by
+        word colourFieldName_;
+
+        //- Actor
+        vtkSmartPointer<vtkActor> actor_;
+
+
+    // Protected Member Functions
+
+        //- Gather and convert cloud positions with vtkPolyData for the leaves.
+        //  If VTK is also running in parallel, each cloud is left
+        //  as a processor-local piece. Otherwise all processor-local
+        //  parts are gathered onto the master in their correponding
+        //  slots.
+        vtkSmartPointer<vtkMultiPieceDataSet>
+        gatherCloud(const objectRegistry& obrTmp) const;
+
+
+        //- Add field
+        template<class Type>
+        bool addField
+        (
+            vtkDataSet* piece,
+            const Field<Type>& fld,
+            const word& fieldName
+        ) const;
+
+        //- Add field
+        template<class Type>
+        bool addCloudField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const IOField<Type>* fldptr,
+            const word& fieldName
+        ) const;
+
+        //- Add field
+        template<class Type>
+        bool addCloudField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const regIOobject* fieldPtr,
+            const word& fieldName
+        ) const;
+
+        //- Add field
+        bool addCloudField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const objectRegistry& obrTmp,
+            const word& fieldName
+        ) const;
+
+
+        //- No copy construct
+        geometryCloud(const geometryCloud&) = delete;
+
+        //- No copy assignment
+        void operator=(const geometryCloud&) = delete;
+
+
+public:
+
+    //- Run-time type information
+    TypeNameNoDebug("geometryCloud");
+
+
+    // Constructors
+
+        //- Construct from dictionary
+        geometryCloud
+        (
+            const runTimePostProcessing& parent,
+            const dictionary& dict,
+            const HashPtrTable<Function1<vector>>& colours
+        );
+
+
+    //- Destructor
+    virtual ~geometryCloud();
+
+
+    // Member Functions
+
+        //- Add cloud to scene (from simulation)
+        bool addGeometry
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Add cloud to scene
+        virtual void addGeometryToScene
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Update actors
+        virtual void updateActors(const scalar position);
+
+        //- No-op
+        virtual bool clear();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "geometryCloudTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C
new file mode 100644
index 0000000000000000000000000000000000000000..c8eb4e0d47530add3c6c5900e484ed59ecba47da
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "geometryCloud.H"
+#include "cloud.H"
+#include "runTimePostProcessing.H"
+
+#include "foamVtkTools.H"
+
+// VTK includes
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPolyData.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::geometryCloud::gatherCloud
+(
+    const objectRegistry& obrTmp
+) const
+{
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+    multiPiece->SetNumberOfPieces(Pstream::nProcs());
+
+    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        if (pointsPtr && pointsPtr->size())
+        {
+            multiPiece->SetPiece
+            (
+                Pstream::myProcNo(),
+                Foam::vtk::Tools::Vertices(*pointsPtr)
+            );
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces on master
+
+        if (pointsPtr && pointsPtr->size())
+        {
+            // Add myself
+
+            multiPiece->SetPiece
+            (
+                Pstream::myProcNo(),
+                Foam::vtk::Tools::Vertices(*pointsPtr)
+            );
+        }
+
+        // Receive points
+        pointField points;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            points.clear();
+
+            fromSlave >> points;
+
+            if (points.size())
+            {
+                multiPiece->SetPiece
+                (
+                    slave,
+                    Foam::vtk::Tools::Vertices(points)
+                );
+            }
+        }
+    }
+    else
+    {
+        // Slave - send points
+
+        OPstream toMaster
+        (
+            Pstream::commsTypes::scheduled,
+            Pstream::masterNo()
+        );
+
+        if (pointsPtr)
+        {
+            toMaster << *pointsPtr;
+        }
+        else
+        {
+            toMaster << pointField();
+        }
+    }
+
+    return multiPiece;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloudTemplates.C b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..b45cb40246df81adb328a9ed8dbcab1eadc3eea3
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudTemplates.C
@@ -0,0 +1,168 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "foamVtkTools.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type>
+bool Foam::functionObjects::runTimePostPro::geometryCloud::addField
+(
+    vtkDataSet* piece,
+    const Field<Type>& fld,
+    const word& fieldName
+) const
+{
+    if (!piece) return false;
+
+    auto vtkfield = Foam::vtk::Tools::convertFieldToVTK<Type>(fieldName, fld);
+
+    // Only has verts
+    piece->GetPointData()->AddArray(vtkfield);
+
+    return true;
+}
+
+
+template<class Type>
+bool Foam::functionObjects::runTimePostPro::geometryCloud::addCloudField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const IOField<Type>* fldptr,
+    const word& fieldName
+) const
+{
+    if (!multiPiece)
+    {
+        return false;
+    }
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        return fldptr &&
+            addField<Type>
+            (
+                multiPiece->GetPiece(Pstream::myProcNo()),
+                *fldptr,
+                fieldName
+            );
+    }
+
+
+    // Gather fields
+    const bool ok = returnReduce((fldptr != nullptr), orOp<bool>());
+
+    if (!ok)
+    {
+        return false;
+    }
+
+    if (Pstream::master())
+    {
+        if (fldptr)
+        {
+            // My field data
+            addField<Type>
+            (
+                multiPiece->GetPiece(Pstream::myProcNo()),
+                *fldptr,
+                fieldName
+            );
+        }
+
+        // Receive field data
+        Field<Type> recv;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            recv.clear();
+
+            fromSlave
+                >> recv;
+
+            if (recv.size())
+            {
+                addField<Type>
+                (
+                    multiPiece->GetPiece(slave),
+                    recv,
+                    fieldName
+                );
+            }
+        }
+    }
+    else
+    {
+        // Slave - send field data
+
+        OPstream toMaster
+        (
+            Pstream::commsTypes::scheduled,
+            Pstream::masterNo()
+        );
+
+        if (fldptr)
+        {
+            toMaster
+                << *fldptr;
+        }
+        else
+        {
+            toMaster
+                << List<Type>();
+        }
+    }
+
+    return ok;
+}
+
+
+template<class Type>
+bool Foam::functionObjects::runTimePostPro::geometryCloud::addCloudField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return addCloudField<Type>
+    (
+        multiPiece,
+        dynamic_cast<const IOField<Type>*>(ioptr),
+        fieldName
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.C b/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.C
new file mode 100644
index 0000000000000000000000000000000000000000..c3e711b5afac86f7093d5fcc0e6ed1115723afa8
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.C
@@ -0,0 +1,315 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "geometryPatches.H"
+#include "fvMesh.H"
+#include "volFields.H"
+#include "emptyPolyPatch.H"
+#include "processorPolyPatch.H"
+#include "foamVtkTools.H"
+#include "runTimePostProcessing.H"
+#include "addToRunTimeSelectionTable.H"
+
+// VTK includes
+#include "vtkActor.h"
+#include "vtkCellArray.h"
+#include "vtkCellData.h"
+#include "vtkCellDataToPointData.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkRenderer.h"
+#include "vtkSmartPointer.h"
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+    defineTypeName(geometryPatches);
+    addToRunTimeSelectionTable(surface, geometryPatches, dictionary);
+}
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::geometryPatches::geometryPatches
+(
+    const runTimePostProcessing& parent,
+    const dictionary& dict,
+    const HashPtrTable<Function1<vector>>& colours
+)
+:
+    geometrySurface(parent, dict, colours, List<fileName>()),
+    fieldVisualisationBase(dict, colours),
+    selectPatches_(),
+    nearCellValue_(dict.lookupOrDefault("nearCellValue", false))
+{
+    dict.readEntry("patches", selectPatches_);
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+void Foam::functionObjects::runTimePostPro::geometryPatches::addGeometryToScene
+(
+    const scalar position,
+    vtkRenderer* renderer
+)
+{
+    if (!visible_ || selectPatches_.empty())
+    {
+        return;
+    }
+
+    const polyBoundaryMesh& patches = parent().mesh().boundaryMesh();
+
+    bitSet selectedPatchIds(patches.size());
+
+    for (const polyPatch& pp : patches)
+    {
+        if (isType<emptyPolyPatch>(pp) || pp.empty())
+        {
+            continue;
+        }
+        else if (isA<processorPolyPatch>(pp))
+        {
+            break; // No processor patches
+        }
+
+        if (selectPatches_.match(pp.name()))
+        {
+            selectedPatchIds.set(pp.index());
+        }
+    }
+
+
+    labelListList patchIds(Pstream::nProcs());
+    patchIds[Pstream::myProcNo()] = selectedPatchIds.sortedToc();
+
+    Pstream::gatherList(patchIds);
+    Pstream::scatterList(patchIds);
+
+    label nPieces = 0;
+    for (const labelList& ids : patchIds)
+    {
+        nPieces += ids.size();
+    }
+
+    /// Pout<< "add patches: " << selectPatches_ << nl
+    ///     << " = " << patchIds << " == " << nPieces << " total" << nl;
+
+    if (!nPieces)
+    {
+        WarningInFunction
+            << "No patches selected: " << flatOutput(selectPatches_)
+            << endl;
+        return;
+    }
+
+    DebugInfo << "    Add geometry patches" << nl;
+
+
+    // Create a vtkMultiPieceDataSet with vtkPolyData on the leaves
+
+    // When adding fields, only map scalar/vector fields.
+    // - this is easier and all we mostly ever need
+
+    vtkSmartPointer<vtkMultiPieceDataSet> multiPiece;
+
+    // Requesting glyphs on the surface - just use the faceCentres directly
+    // and attach fields as CellData (not PointData).
+
+    if (representation_ == rtGlyph)
+    {
+        multiPiece = gatherPatchFaceCentres(patchIds);
+    }
+    else
+    {
+        multiPiece = gatherPatchPieces(patchIds);
+    }
+
+
+    // Add (scalar/vector) field.
+    // - Need field(s) for glyphs or colourByField:
+
+    int nCmpt = 0;
+    if (representation_ == rtGlyph || colourBy_ == cbField)
+    {
+        if (!nCmpt)
+        {
+            nCmpt = addPatchField<scalar>
+            (
+                multiPiece,
+                patchIds,
+                parent().mesh().cfindObject<volScalarField>(fieldName_),
+                fieldName_
+            );
+        }
+        if (!nCmpt)
+        {
+            nCmpt = addPatchField<vector>
+            (
+                multiPiece,
+                patchIds,
+                parent().mesh().cfindObject<volVectorField>(fieldName_),
+                fieldName_
+            );
+        }
+    }
+
+    // Now have a multi-piece dataset with
+    // one piece per patch and processor.
+    //
+    // For VTK=parallel, these pieces reside on their original processors.
+    // For VTK=serial, they are master only
+
+    // Re-query actually field information, since we may have stored it
+    // somewhere slightly different than the original source.
+
+    fieldSummary fieldInfo = queryFieldSummary(fieldName_, multiPiece);
+    fieldInfo.reduce();
+
+
+    // Not rendering on this processor?
+    // This is where we stop, but could also have a MPI barrier
+    if (!renderer)
+    {
+        return;
+    }
+
+
+    // Rendering
+
+    {
+        auto polyData = vtkSmartPointer<vtkCompositeDataGeometryFilter>::New();
+
+        polyData->SetInputData(multiPiece);
+        polyData->Update();
+
+        if (representation_ == rtGlyph)
+        {
+            addGlyphs
+            (
+                position,
+                fieldName_, fieldInfo,  // scaling
+                fieldName_, fieldInfo,  // colouring
+                maxGlyphLength_,
+                polyData->GetOutput(),
+                surfaceActor_,
+                renderer
+            );
+        }
+        else
+        {
+            vtkSmartPointer<vtkCellDataToPointData> cellToPoint;
+
+            // CellData - Need a cell->point filter
+            if (smooth_ && !fieldInfo.hasPointData())
+            {
+                cellToPoint = vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPoint->SetInputData(multiPiece);
+
+                polyData->SetInputConnection(cellToPoint->GetOutputPort());
+            }
+            else
+            {
+                polyData->SetInputData(multiPiece);
+            }
+            polyData->Update();
+
+
+            if (!smooth_)
+            {
+                addFeatureEdges(renderer, polyData);
+            }
+
+            auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+            mapper->SetInputConnection(polyData->GetOutputPort());
+
+            setField
+            (
+                position,
+                fieldName_,
+                (
+                    smooth_
+                  ? FieldAssociation::POINT_DATA
+                  : FieldAssociation::CELL_DATA
+                ),
+                mapper,
+                renderer
+            );
+
+            surfaceActor_->SetMapper(mapper);
+
+            setRepresentation(surfaceActor_);
+
+            renderer->AddActor(surfaceActor_);
+        }
+    }
+}
+
+
+void Foam::functionObjects::runTimePostPro::geometryPatches::updateActors
+(
+    const scalar position
+)
+{
+    if (!visible_)
+    {
+        return;
+    }
+
+    surface::updateActors(position);
+
+    surfaceActor_->GetProperty()->SetOpacity(opacity(position));
+
+    vector sc = surfaceColour_->value(position);
+    surfaceActor_->GetProperty()->SetColor(sc[0], sc[1], sc[2]);
+
+    vector ec = edgeColour_->value(position);
+    surfaceActor_->GetProperty()->SetEdgeColor(ec[0], ec[1], ec[2]);
+}
+
+
+bool Foam::functionObjects::runTimePostPro::geometryPatches::clear()
+{
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.H b/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.H
new file mode 100644
index 0000000000000000000000000000000000000000..cb7a8de79c2c1f552d8a30e12bfcf35a0efc12ba
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryPatches.H
@@ -0,0 +1,171 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::geometryPatches
+
+Description
+    Visualisation of OpenFOAM patches and fields.
+
+    \heading Basic Usage
+    \table
+        Property    | Description                           | Required | Default
+        type        | The surface type: patches             | yes |
+        patches     | Patches to display (wordRe list)      | yes |
+    \endtable
+
+SourceFiles
+    geometryPatches.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_geometryPatches_H
+#define functionObjects_runTimePostPro_geometryPatches_H
+
+#include "surface.H"
+#include "geometrySurface.H"
+#include "fieldVisualisationBase.H"
+#include "volFieldsFwd.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                       Class geometryPatches Declaration
+\*---------------------------------------------------------------------------*/
+
+class geometryPatches
+:
+    public geometrySurface,
+    public fieldVisualisationBase
+{
+protected:
+
+    // Protected Data
+
+        //- Requested names of patches to process
+        wordRes selectPatches_;
+
+        //- Use cell value on patches instead of patch value itself
+        bool nearCellValue_;
+
+
+    // Protected Member Functions
+
+        //- Gather and convert patches to multi-piece dataset with
+        //- vtkPolyData for each patch/processor.
+        //  For serial, the pieces are gathered to the master.
+        vtkSmartPointer<vtkMultiPieceDataSet>
+        gatherPatchPieces(const labelListList& patchIds) const;
+
+        //- Gather and convert patch face centres to multi-piece dataset with
+        //- vtkPolyData for each patch/processor.
+        //  For serial, the pieces are gathered to the master.
+        vtkSmartPointer<vtkMultiPieceDataSet>
+        gatherPatchFaceCentres(const labelListList& patchIds) const;
+
+
+    // Adding Fields - multi-piece
+
+        //- Add patch values.
+        //  For nCells == nPoints (eg, only has face centres) add as PointData.
+        //  \return 0 on failure to map and nCmpt (eg, 1=scalar, 3=vector)
+        //  on success.
+        template<class Type>
+        int addPatchField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const labelListList& patchIds,
+            const GeometricField<Type, fvPatchField, volMesh>* fldptr,
+            const word& fieldName
+        ) const;
+
+
+        //- No copy construct
+        geometryPatches(const geometryPatches&) = delete;
+
+        //- No copy assignment
+        void operator=(const geometryPatches&) = delete;
+
+
+public:
+
+    //- Run-time type information
+    TypeNameNoDebug("patches");
+
+
+    // Constructors
+
+        //- Construct from dictionary
+        geometryPatches
+        (
+            const runTimePostProcessing& parent,
+            const dictionary& dict,
+            const HashPtrTable<Function1<vector>>& colours
+        );
+
+
+    //- Destructor
+    virtual ~geometryPatches() = default;
+
+
+    // Member Functions
+
+        //- Add geometry surface(s) to scene
+        virtual void addGeometryToScene
+        (
+            const scalar position,
+            vtkRenderer* renderer
+        );
+
+        //- Update actors
+        virtual void updateActors(const scalar position);
+
+        //- Clear files used to create the object(s)
+        virtual bool clear();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "geometryPatchesTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesGather.C b/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesGather.C
new file mode 100644
index 0000000000000000000000000000000000000000..ce03fa02bbda36d5a40ff9eaf10323f5de08b0ad
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesGather.C
@@ -0,0 +1,293 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "geometryPatches.H"
+#include "fvMesh.H"
+#include "volFields.H"
+#include "emptyPolyPatch.H"
+#include "processorPolyPatch.H"
+#include "foamVtkTools.H"
+#include "runTimePostProcessing.H"
+
+// VTK includes
+#include "vtkCellArray.h"
+#include "vtkCellData.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
+#include "vtkPolyData.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::geometryPatches::gatherPatchPieces
+(
+    const labelListList& patchIds
+) const
+{
+    const polyBoundaryMesh& patches = parent().mesh().boundaryMesh();
+
+    label nPieces = 0;
+    for (const labelList& ids : patchIds)
+    {
+        nPieces += ids.size();
+    }
+
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+    multiPiece->SetNumberOfPieces(nPieces);
+
+    label pieceId = 0;
+
+    if (!needsCollective())
+    {
+        // Simple case
+
+        for (int proci=0; proci < Pstream::myProcNo(); ++proci)
+        {
+            pieceId += patchIds[proci].size();
+        }
+
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const polyPatch& pp = patches[patchId];
+
+            multiPiece->SetPiece
+            (
+                pieceId,
+                Foam::vtk::Tools::Patch::mesh(pp)
+            );
+
+            ++pieceId;
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces on master
+
+        // Add myself
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const polyPatch& pp = patches[patchId];
+
+            multiPiece->SetPiece
+            (
+                pieceId,
+                Foam::vtk::Tools::Patch::mesh(pp)
+            );
+
+            ++pieceId;
+        }
+
+        // Receive surfaces
+        pointField points;
+        faceList faces;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            const label nSlavePatches = patchIds[slave].size();
+
+            if (!nSlavePatches)
+            {
+                continue;
+            }
+
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            for (label recvi=0; recvi < nSlavePatches; ++recvi)
+            {
+                points.clear();
+                faces.clear();
+
+                fromSlave
+                    >> points >> faces;
+
+                multiPiece->SetPiece
+                (
+                    pieceId,
+                    Foam::vtk::Tools::Patch::mesh(points, faces)
+                );
+
+                ++pieceId;
+            }
+        }
+    }
+    else
+    {
+        // Slave - send surfaces
+        const labelList& slavePatchIds = patchIds[Pstream::myProcNo()];
+
+        if (slavePatchIds.size())
+        {
+            OPstream toMaster
+            (
+                Pstream::commsTypes::scheduled,
+                Pstream::masterNo()
+            );
+
+            for (const label patchId : patchIds[Pstream::myProcNo()])
+            {
+                const polyPatch& pp = patches[patchId];
+
+                toMaster
+                    << pp.localPoints() << pp.localFaces();
+            }
+        }
+    }
+
+    return multiPiece;
+}
+
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::geometryPatches::gatherPatchFaceCentres
+(
+    const labelListList& patchIds
+) const
+{
+    const polyBoundaryMesh& patches = parent().mesh().boundaryMesh();
+
+    label nPieces = 0;
+    for (const labelList& ids : patchIds)
+    {
+        nPieces += ids.size();
+    }
+
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+    multiPiece->SetNumberOfPieces(nPieces);
+
+    label pieceId = 0;
+
+    if (!needsCollective())
+    {
+        // Simple case
+
+        for (int proci=0; proci < Pstream::myProcNo(); ++proci)
+        {
+            pieceId += patchIds[proci].size();
+        }
+
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const polyPatch& pp = patches[patchId];
+
+            auto geom = vtkSmartPointer<vtkPolyData>::New();
+
+            geom->SetPoints(Foam::vtk::Tools::Patch::faceCentres(pp));
+            geom->SetVerts(Foam::vtk::Tools::identityVertices(pp.size()));
+
+            multiPiece->SetPiece(pieceId, geom);
+
+            ++pieceId;
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces (face centres) on master
+
+        // Add myself
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const polyPatch& pp = patches[patchId];
+
+            auto geom = vtkSmartPointer<vtkPolyData>::New();
+
+            geom->SetPoints(Foam::vtk::Tools::Patch::faceCentres(pp));
+            geom->SetVerts(Foam::vtk::Tools::identityVertices(pp.size()));
+
+            multiPiece->SetPiece(pieceId, geom);
+
+            ++pieceId;
+        }
+
+        // Receive points
+        pointField points;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            const label nSlavePatches = patchIds[slave].size();
+
+            if (!nSlavePatches)
+            {
+                continue;
+            }
+
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            for (label recvi=0; recvi < nSlavePatches; ++recvi)
+            {
+                points.clear();
+
+                fromSlave >> points;
+
+                multiPiece->SetPiece
+                (
+                    pieceId,
+                    Foam::vtk::Tools::Vertices(points)
+                );
+
+                ++pieceId;
+            }
+        }
+    }
+    else
+    {
+        // Slave - send face centres
+
+        const labelList& slavePatchIds = patchIds[Pstream::myProcNo()];
+
+        if (slavePatchIds.size())
+        {
+            OPstream toMaster
+            (
+                Pstream::commsTypes::scheduled,
+                Pstream::masterNo()
+            );
+
+            for (const label patchId : patchIds[Pstream::myProcNo()])
+            {
+                const polyPatch& pp = patches[patchId];
+
+                toMaster << pp.faceCentres();
+            }
+        }
+    }
+
+    return multiPiece;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesTemplates.C b/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..ec8edea73f085941bd68013c44634c2860b3a070
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryPatchesTemplates.C
@@ -0,0 +1,221 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "geometryPatches.H"
+#include "fvMesh.H"
+#include "volFields.H"
+#include "foamVtkTools.H"
+
+// VTK includes
+#include "vtkCellData.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+int Foam::functionObjects::runTimePostPro::geometryPatches::addPatchField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const labelListList& patchIds,
+    const GeometricField<Type, fvPatchField, volMesh>* fldptr,
+    const word& fieldName
+) const
+{
+    if (!multiPiece || !fldptr)
+    {
+        return 0;
+    }
+
+    const int nCmpt(pTraits<Type>::nComponents);
+
+    const auto& bf = fldptr->boundaryField();
+
+    label pieceId = 0;
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        for (int proci=0; proci < Pstream::myProcNo(); ++proci)
+        {
+            pieceId += patchIds[proci].size();
+        }
+
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const auto& pf = bf[patchId];
+
+            auto vtkfield =
+            (
+                nearCellValue_
+              ? Foam::vtk::Tools::convertFieldToVTK<Type>
+                (
+                    fieldName,
+                    pf.patchInternalField()()
+                )
+              : Foam::vtk::Tools::convertFieldToVTK<Type>
+                (
+                    fieldName,
+                    pf
+                )
+            );
+
+            auto piece = multiPiece->GetPiece(pieceId);
+
+            if (piece->GetNumberOfCells() == piece->GetNumberOfPoints())
+            {
+                // Only has verts
+                piece->GetPointData()->AddArray(vtkfield);
+            }
+            else
+            {
+                piece->GetCellData()->AddArray(vtkfield);
+            }
+
+            ++pieceId;
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces on master
+
+        // Add myself
+        for (const label patchId : patchIds[Pstream::myProcNo()])
+        {
+            const auto& pf = bf[patchId];
+
+            auto vtkfield =
+            (
+                nearCellValue_
+              ? Foam::vtk::Tools::convertFieldToVTK<Type>
+                (
+                    fieldName,
+                    pf.patchInternalField()()
+                )
+              : Foam::vtk::Tools::convertFieldToVTK<Type>
+                (
+                    fieldName,
+                    pf
+                )
+            );
+
+            auto piece = multiPiece->GetPiece(pieceId);
+
+            if (piece->GetNumberOfCells() == piece->GetNumberOfPoints())
+            {
+                // Only has verts
+                piece->GetPointData()->AddArray(vtkfield);
+            }
+            else
+            {
+                piece->GetCellData()->AddArray(vtkfield);
+            }
+
+            ++pieceId;
+        }
+
+        // Receive field
+        Field<Type> recv;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            const label nSlavePatches = patchIds[slave].size();
+
+            if (!nSlavePatches)
+            {
+                continue;
+            }
+
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            for (label recvi=0; recvi < nSlavePatches; ++recvi)
+            {
+                recv.clear();
+
+                fromSlave >> recv;
+
+                auto vtkfield = Foam::vtk::Tools::convertFieldToVTK<Type>
+                (
+                    fieldName,
+                    recv
+                );
+
+                auto piece = multiPiece->GetPiece(pieceId);
+
+                if (piece->GetNumberOfCells() == piece->GetNumberOfPoints())
+                {
+                    // Only has verts
+                    piece->GetPointData()->AddArray(vtkfield);
+                }
+                else
+                {
+                    piece->GetCellData()->AddArray(vtkfield);
+                }
+
+                ++pieceId;
+            }
+        }
+    }
+    else
+    {
+        // Slave - send fields
+        const labelList& slavePatchIds = patchIds[Pstream::myProcNo()];
+
+        if (slavePatchIds.size())
+        {
+            OPstream toMaster
+            (
+                Pstream::commsTypes::scheduled,
+                Pstream::masterNo()
+            );
+
+            for (const label patchId : patchIds[Pstream::myProcNo()])
+            {
+                const auto& pf = bf[patchId];
+
+                if (nearCellValue_)
+                {
+                    toMaster << pf.patchInternalField()();
+                }
+                else
+                {
+                    toMaster << pf;
+                }
+            }
+        }
+    }
+
+    return nCmpt;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.C b/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.C
index f3af0ab14f6054530fd38cb88477f71a157cab0d..ee6fc254d0171207cd73c2c96c19b43bb5f2ae99 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -77,7 +75,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
     // Not extremely elegant...
     vtkSmartPointer<vtkPolyData> dataset;
 
-    if (fName.ext() == "vtk")
+    if ("vtk" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkPolyDataReader>::New();
 
@@ -88,7 +86,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
         return dataset;
     }
 
-    if (fName.ext() == "vtp")
+    if ("vtp" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkXMLPolyDataReader>::New();
 
@@ -99,7 +97,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
         return dataset;
     }
 
-    if (fName.ext() == "obj")
+    if ("obj" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkOBJReader>::New();
 
@@ -110,7 +108,7 @@ static vtkSmartPointer<vtkPolyData> getPolyDataFile(const Foam::fileName& fName)
         return dataset;
     }
 
-    if (fName.ext() == "stl" || fName.ext() == "stlb")
+    if ("stl" == fName.ext() || "stlb" == fName.ext())
     {
         auto reader = vtkSmartPointer<vtkSTLReader>::New();
 
@@ -147,6 +145,12 @@ void Foam::functionObjects::runTimePostPro::geometrySurface::addGeometryToScene
     const fileName& fName
 ) const
 {
+    // Master-only, since that is where the files are.
+    if (!visible_ || !renderer || !Pstream::master())
+    {
+        return;
+    }
+
     if (representation_ == rtGlyph)
     {
         FatalErrorInFunction
@@ -154,9 +158,11 @@ void Foam::functionObjects::runTimePostPro::geometrySurface::addGeometryToScene
             << " object" << exit(FatalError);
     }
 
+    DebugInfo << "    Add geometry surface: " << fName << nl;
+
     auto surf = getPolyDataFile(fName);
 
-    if (!surf || surf->GetNumberOfPoints() == 0)
+    if (!surf || !surf->GetNumberOfPoints())
     {
         FatalErrorInFunction
             << "Could not read "<< fName << nl
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.H b/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.H
index 4e9e41e98195a4c0646c0111c527183fcb572185..65f5556790a4492da8428aab93defb81df7086ef 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometrySurface.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,7 +25,18 @@ Class
     Foam::functionObjects::runTimePostPro::geometrySurface
 
 Description
-    Visualisation of surface geometry data
+    Read and visualize surface geometry files.
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        type        | The type: geometry                    | yes |
+        files       | The files to read                     | yes |
+    \endtable
+
+    Standard file types (vtk, vtp, obj, stl, stlb) are read with the
+    VTK-native readers. Other file types use the OpenFOAM surfMesh
+    readers and convert to VTK.
 
 SourceFiles
     geometrySurface.C
@@ -41,7 +50,7 @@ SourceFiles
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations
 class vtkPolyData;
 
 namespace Foam
@@ -61,7 +70,7 @@ class geometrySurface
 {
 protected:
 
-    // Protected data
+    // Protected Data
 
         //- File names
         List<fileName> fileNames_;
@@ -126,7 +135,7 @@ public:
         //- Update actors
         virtual void updateActors(const scalar position);
 
-        //- Clear files used to create the object(s)
+        //- Clear files used to create the object(s) - no-op
         virtual bool clear();
 };
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/pathline.C b/src/functionObjects/graphics/runTimePostProcessing/pathline.C
index 7ab781a99cfd74b2101e58c8672bdc6e10c9faa9..1642b4496b4e74bb38319825ccc0925e32345baa 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/pathline.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/pathline.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,7 +44,7 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(pathline, 0);
+    defineTypeName(pathline);
     defineRunTimeSelectionTable(pathline, dictionary);
 }
 }
@@ -94,7 +92,6 @@ void Foam::functionObjects::runTimePostPro::pathline::addLines
             mapper->SetInputData(data);
             mapper->Update();
             break;
-
         }
         case rtTube:
         {
@@ -107,9 +104,7 @@ void Foam::functionObjects::runTimePostPro::pathline::addLines
 
             mapper->SetInputConnection(tubes->GetOutputPort());
             mapper->Update();
-
             break;
-
         }
         case rtVector:
         {
@@ -133,7 +128,7 @@ Foam::functionObjects::runTimePostPro::pathline::pathline
     (
         representationTypeNames.get("representation", dict)
     ),
-    tubeRadius_(0.0),
+    tubeRadius_(0.001),
     lineColour_(nullptr)
 {
     if (dict.found("lineColour"))
@@ -165,7 +160,6 @@ Foam::functionObjects::runTimePostPro::pathline::pathline
             break;
         }
     }
-
 }
 
 
@@ -180,10 +174,7 @@ Foam::functionObjects::runTimePostPro::pathline::New
     const word& pathlineType
 )
 {
-    if (debug)
-    {
-        Info<< "Selecting pathline " << pathlineType << endl;
-    }
+    DebugInfo << "Selecting pathline " << pathlineType << endl;
 
     auto cstrIter = dictionaryConstructorTablePtr_->cfind(pathlineType);
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/pathline.H b/src/functionObjects/graphics/runTimePostProcessing/pathline.H
index a665eecf5ff5ff1b01e22c455257d3c48eae3bc8..44aca2e95c18483bc64dc610548001809df5a5c3 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/pathline.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/pathline.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,6 +27,14 @@ Class
 Description
     Visualisation of line data (eg, streamlines)
 
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        representation| none/line/tube/vector               | yes |
+        lineColour    | Override line colour                | no  |
+        tubeRadius    | Radius for tube representation      | yes |
+    \endtable
+
 SourceFiles
     pathline.C
 
@@ -65,16 +71,18 @@ class pathline
 {
 public:
 
-    // Public enumerations
+    // Public Enumerations
 
+        //- Line representations
         enum representationType
         {
-            rtNone,
-            rtLine,
-            rtTube,
-            rtVector
+            rtNone,     //!< "none"
+            rtLine,     //!< "line"
+            rtTube,     //!< "tube"
+            rtVector    //!< "vector"
         };
 
+        //- Names for line representations
         static const Enum<representationType> representationTypeNames;
 
 
@@ -112,7 +120,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("pathline");
+    TypeNameNoDebug("pathline");
 
 
     // Declare run-time constructor selection table
@@ -144,7 +152,7 @@ public:
 
     // Selectors
 
-        //- Return a reference to the selected RAS model
+        //- Return selected pathline
         static autoPtr<pathline> New
         (
             const runTimePostProcessing& parent,
diff --git a/src/functionObjects/graphics/runTimePostProcessing/pointData.C b/src/functionObjects/graphics/runTimePostProcessing/pointData.C
index 0688c4252d23409cbcabb72a0ad970301f520856..577c6d6c5ce6fd1be0461dce8734f2789b9f9a84 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/pointData.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/pointData.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2004-2010, 2015-2018 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2004-2010, 2015-2019 OpenCFD Ltd.
+     \\/     M anipulation  | 
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,8 +33,6 @@ License
 #include "vtkProperty.h"
 #include "vtkRenderer.h"
 #include "vtkSmartPointer.h"
-#include "vtkTubeFilter.h"
-#include "vtkLookupTable.h"
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
 
@@ -46,7 +42,7 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(pointData, 0);
+    defineTypeName(pointData);
     defineRunTimeSelectionTable(pointData, dictionary);
 }
 }
@@ -141,10 +137,7 @@ Foam::functionObjects::runTimePostPro::pointData::New
     const word& pointDataType
 )
 {
-    if (debug)
-    {
-        Info<< "Selecting pointData " << pointDataType << endl;
-    }
+    DebugInfo << "Selecting pointData " << pointDataType << endl;
 
     auto cstrIter = dictionaryConstructorTablePtr_->cfind(pointDataType);
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/pointData.H b/src/functionObjects/graphics/runTimePostProcessing/pointData.H
index 738f42897d46d5bc34087f2b0acf0c361166c9e5..f08ac5727d9492d601c08324a280172ce86c3cc4 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/pointData.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/pointData.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2004-2010, 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2004-2010, 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,6 +27,14 @@ Class
 Description
     Visualisation of point data
 
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        representation| sphere/vector                       | yes |
+        pointColour   | Override point colour               | no  |
+        maxGlyphLength | Limit for glyph representation     | yes |
+    \endtable
+
 SourceFiles
     pointData.C
 
@@ -43,7 +49,7 @@ SourceFiles
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations
 class vtkActor;
 class vtkPolyData;
 class vtkPolyDataMapper;
@@ -65,20 +71,22 @@ class pointData
 {
 public:
 
-    // Public enumerations
+    // Public Enumerations
 
+        //- Point representation types
         enum representationType
         {
-            rtSphere,               //!< Sphere
-            rtVector                //!< Vector
+            rtSphere,           //!< "sphere"
+            rtVector            //!< "vector"
         };
 
+        //- Names for point representation types
         static const Enum<representationType> representationTypeNames;
 
 
 protected:
 
-    // Protected data
+    // Protected Data
 
         //- Representation type
         representationType representation_;
@@ -111,7 +119,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("pointData");
+    TypeNameNoDebug("pointData");
 
 
     // Declare run-time constructor selection table
@@ -143,7 +151,7 @@ public:
 
     // Selectors
 
-        //- Return a reference to the selected RAS model
+        //- Return selected pointData
         static autoPtr<pointData> New
         (
             const runTimePostProcessing& parent,
diff --git a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.C b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.C
index d1bc3196b1891ed4e545fa2c8b0a73ec515487ef..f514b39b714ab220388aad3cb8fef649ec9fa2df 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,6 +32,7 @@ License
 #include "text.H"
 #include "Time.H"
 #include "sigFpe.H"
+#include "polySurfaceFields.H"
 #include "addToRunTimeSelectionTable.H"
 
 // VTK includes
@@ -43,6 +42,16 @@ License
 #include "vtkSmartPointer.h"
 #include "vtkLight.h"
 
+#ifdef FOAM_USING_VTK_MPI
+# include "vtkMPICommunicator.h"
+# include "vtkMPIController.h"
+#endif
+#include "vtkDummyController.h"
+
+#include "vtkSynchronizedRenderWindows.h"
+#include "vtkCompositedSynchronizedRenderers.h"
+
+
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
@@ -103,6 +112,168 @@ static void cleanup(PtrList<Type>& objects)
 } // End namespace Foam
 
 
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::functionObjects::runTimePostProcessing::render
+(
+    vtkMultiProcessController* controller
+)
+{
+    // Some feedback
+    if (controller)
+    {
+        Log << name() << " render (" << controller->GetNumberOfProcesses()
+            << " processes)" << endl;
+    }
+    else
+    {
+        Log << name() << " render" << endl;
+    }
+
+
+    // Normal rendering elements
+    vtkSmartPointer<vtkRenderer> renderer;
+    vtkSmartPointer<vtkRenderWindow> renderWindow;
+
+    // Multi-process synchronization
+    vtkSmartPointer<vtkSynchronizedRenderWindows> syncWindows;
+    vtkSmartPointer<vtkCompositedSynchronizedRenderers> syncRenderers;
+
+
+    // Disable any floating point trapping
+    // (some low-level rendering functionality does not like it)
+
+    sigFpe::ignore sigFpeHandling; //<- disable in local scope
+
+
+    // Initialise render window
+    if (controller || Pstream::master())
+    {
+        renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
+        renderer = vtkSmartPointer<vtkRenderer>::New();
+
+        renderWindow->OffScreenRenderingOn();
+        renderWindow->SetSize(output_.width_, output_.height_);
+
+        // Legacy rendering - was deprecated for 8.1.0
+        #if (VTK_MAJOR_VERSION < 8) || \
+            ((VTK_MAJOR_VERSION == 8) && (VTK_MINOR_VERSION < 2))
+        renderWindow->SetAAFrames(10);
+        #endif
+        renderWindow->SetAlphaBitPlanes(true);
+        renderWindow->SetMultiSamples(0);
+        // renderWindow->PolygonSmoothingOn();
+
+        renderWindow->AddRenderer(renderer);
+    }
+
+    // Synchronization
+    if (controller)
+    {
+        syncWindows =
+            vtkSmartPointer<vtkSynchronizedRenderWindows>::New();
+
+        syncRenderers =
+            vtkSmartPointer<vtkCompositedSynchronizedRenderers>::New();
+
+        syncWindows->SetRenderWindow(renderWindow);
+        syncWindows->SetParallelController(controller);
+        syncWindows->SetIdentifier(1);
+
+        // false = Call Render() manually on each process - don't use RMI
+        syncWindows->SetParallelRendering(true);
+
+        syncRenderers->SetRenderer(renderer);
+        syncRenderers->SetParallelController(controller);
+    }
+
+    // ---------------------
+
+    scene_.initialise(renderer, output_.name_);
+
+    addGeometryToScene(points_, 0, renderer);
+    addGeometryToScene(lines_, 0, renderer);
+    addGeometryToScene(surfaces_, 0, renderer);
+    addGeometryToScene(text_, 0, renderer);
+
+    while (scene_.loop(renderer))
+    {
+        const scalar position = scene_.position();
+
+        updateActors(text_, position);
+        updateActors(points_, position);
+        updateActors(lines_, position);
+        updateActors(surfaces_, position);
+    }
+
+    // Cleanup
+    cleanup(text_);
+    cleanup(points_);
+    cleanup(lines_);
+    cleanup(surfaces_);
+
+
+    // Instead of relying on the destructor, manually restore the previous
+    // SIGFPE state.
+    // This is only to avoid compiler complaints about unused variables.
+
+    sigFpeHandling.restore();
+}
+
+
+void Foam::functionObjects::runTimePostProcessing::render
+(
+    vtkMultiProcessController* controller,
+    void* processData
+)
+{
+    reinterpret_cast<runTimePostProcessing*>(processData)->render(controller);
+}
+
+
+void Foam::functionObjects::runTimePostProcessing::render()
+{
+    #ifdef FOAM_USING_VTK_MPI
+    if (parallel_)
+    {
+        // Create vtkMPIController if MPI is configured,
+        // vtkThreadedController otherwise.
+        auto ctrl = vtkSmartPointer<vtkMPIController>::New();
+        ctrl->Initialize(nullptr, nullptr, 1);
+
+        ctrl->SetSingleMethod(runTimePostProcessing::render, this);
+        ctrl->SingleMethodExecute();
+
+        ctrl->Finalize(1);
+    }
+    else
+    #endif
+    {
+        // Normally we would have a fallback controller like this:
+
+        // if (Pstream::master())
+        // {
+        //     auto ctrl = vtkSmartPointer<vtkDummyController>::New();
+        //     ctrl->Initialize(nullptr, nullptr, 1);
+        //
+        //     ctrl->SetSingleMethod(runTimePostProcessing::render, this);
+        //     ctrl->SingleMethodExecute();
+        //
+        //     ctrl->Finalize(1);
+        // }
+
+        // However, this would prevent us from doing any of our own MPI
+        // since this would only be spawned the master.
+
+        // Instead pass in nullptr for the controller and handling
+        // logic internally.
+
+        vtkDummyController* dummy = nullptr;
+        render(dummy);
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::functionObjects::runTimePostProcessing::runTimePostProcessing
@@ -113,6 +284,8 @@ Foam::functionObjects::runTimePostProcessing::runTimePostProcessing
 )
 :
     fvMeshFunctionObject(name, runTime, dict),
+    output_(),
+    parallel_(false),
     scene_(runTime, name),
     points_(),
     lines_(),
@@ -129,7 +302,20 @@ bool Foam::functionObjects::runTimePostProcessing::read(const dictionary& dict)
 {
     fvMeshFunctionObject::read(dict);
 
-    Info<< type() << " " << name() << ": reading post-processing data" << endl;
+    #ifdef FOAM_USING_VTK_MPI
+    parallel_ = (Pstream::parRun() && dict.lookupOrDefault("parallel", true));
+    #else
+    parallel_ = false;
+    #endif
+
+    Info<< type() << " " << name() << ": reading post-processing data ("
+        << (parallel_ ? "parallel" : "serial") << " rendering)" << endl;
+
+    if (dict.lookupOrDefault("debug", false))
+    {
+        runTimePostPro::geometryBase::debug = 1;
+        Info<< "    debugging on" << endl;
+    }
 
     scene_.read(dict);
 
@@ -179,68 +365,7 @@ bool Foam::functionObjects::runTimePostProcessing::execute()
 
 bool Foam::functionObjects::runTimePostProcessing::write()
 {
-    if (!Pstream::master())
-    {
-        return true;
-    }
-
-    Info<< type() << " " << name() <<  " output:" << nl
-        << "    Constructing scene" << endl;
-
-
-    // Disable any floating point trapping
-    // (some low-level rendering functionality does not like it)
-
-    sigFpe::ignore sigFpeHandling; //<- disable in local scope
-
-    // Initialise render window
-    auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
-    renderWindow->OffScreenRenderingOn();
-    renderWindow->SetSize(output_.width_, output_.height_);
-
-    // Legacy rendering - was deprecated for 8.1.0
-    #if (VTK_MAJOR_VERSION < 8) || \
-        ((VTK_MAJOR_VERSION == 8) && (VTK_MINOR_VERSION < 2))
-    renderWindow->SetAAFrames(10);
-    #endif
-    renderWindow->SetAlphaBitPlanes(true);
-    renderWindow->SetMultiSamples(0);
-//    renderWindow->PolygonSmoothingOn();
-
-    auto renderer = vtkSmartPointer<vtkRenderer>::New();
-    scene_.initialise(renderer, output_.name_);
-
-    renderWindow->AddRenderer(renderer);
-
-
-    addGeometryToScene(points_, 0, renderer);
-    addGeometryToScene(lines_, 0, renderer);
-    addGeometryToScene(surfaces_, 0, renderer);
-    addGeometryToScene(text_, 0, renderer);
-
-    while (scene_.loop(renderer))
-    {
-        const scalar position = scene_.position();
-
-        updateActors(text_, position);
-        updateActors(points_, position);
-        updateActors(lines_, position);
-        updateActors(surfaces_, position);
-    }
-
-    // Cleanup
-    cleanup(text_);
-    cleanup(points_);
-    cleanup(lines_);
-    cleanup(surfaces_);
-
-
-    // Instead of relying on the destructor, manually restore the previous
-    // SIGFPE state.
-    // This is only to avoid compiler complaints about unused variables.
-
-    sigFpeHandling.restore();
-
+    render();
     return true;
 }
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.H b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.H
index ef2d17708bf4f634f7ed627af7638a09029f906f..21ae24b147b7c70e7bf29a87d4b65184baf50a43 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessing.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -56,9 +54,53 @@ Description
     - Selection of colour maps
     .
 
-    Scene configuration is performed using standard OpenFOAM dictionaries, using
-    the main headings of: output=, camera, colours, points, lines,
-    surfaces and text.
+    Mandatory entries:
+    \verbatim
+    output
+    {
+        name        image;
+        width       800;
+        height      600;
+    }
+
+    camera
+    {
+        ...
+    }
+
+    colours
+    {
+        ...
+    }
+
+    text
+    {
+    }
+    \endverbatim
+
+    Optional entries:
+    \verbatim
+    points
+    {
+    }
+
+    lines
+    {
+        ...
+    }
+
+    surfaces
+    {
+        ...
+    }
+    \endverbatim
+
+    Dictionary controls
+    \table
+        Property    | Description                          | Required | Default
+        type        | Type-name: runTimePostProcessing     | yes |
+        debug       | Additional debug information         | no  | false
+    \endtable
 
 SourceFiles
     runTimePostProcessing.C
@@ -76,14 +118,20 @@ SourceFiles
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
-class vtkRenderer;
+// Forward Declarations
+class vtkMultiPieceDataSet;
+class vtkMultiProcessController;
 class vtkRenderWindow;
+class vtkRenderer;
+template<class T> class vtkSmartPointer;
 
 namespace Foam
 {
 namespace functionObjects
 {
+// Forward declarations
+class runTimePostProcessing;
+
 namespace runTimePostPro
 {
 // Forward declarations
@@ -101,11 +149,9 @@ class runTimePostProcessing
 :
     public fvMeshFunctionObject
 {
-private:
-
-    // Private data
+    // Private Data
 
-        // Output
+        //- Output information
         struct outputType
         {
             word name_;
@@ -116,6 +162,9 @@ private:
         //- Output instance
         outputType output_;
 
+        //- Parallel rendering
+        bool parallel_;
+
         //- Scene manager
         runTimePostPro::scene scene_;
 
@@ -138,6 +187,15 @@ private:
         template<class Type>
         void readObjects(const dictionary& dict, PtrList<Type>& objects) const;
 
+        //- Construct controller and dispatch to render
+        void render();
+
+        //- Static function for SetSingleMethod binding
+        static void render(vtkMultiProcessController* ctrl, void* data);
+
+        //- Render scene using given controller
+        void render(vtkMultiProcessController* ctrl);
+
 
 public:
 
@@ -162,11 +220,26 @@ public:
 
     // Member Functions
 
+        //- Parallel rendering
+        bool parallel() const
+        {
+            return parallel_;
+        }
+
+        //- May need to gather parts to render on single-processor
+        //  True when OpenFOAM is running in parallel but VTK is not.
+        bool needsCollective() const
+        {
+            return Pstream::parRun() && !parallel_;
+        }
+
+        //- Reference to the underlying OpenFOAM mesh
         const fvMesh& mesh() const
         {
             return mesh_;
         }
 
+
         //- Read the post-processing controls
         virtual bool read(const dictionary& dict);
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessingTemplates.C b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessingTemplates.C
index 43574edf5e2d1a91b059c81f87a992f299f4ac69..e408f01a891ac9c78431cd8d0401860644d37db2 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessingTemplates.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/runTimePostProcessingTemplates.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
diff --git a/src/functionObjects/graphics/runTimePostProcessing/scalarBar.C b/src/functionObjects/graphics/runTimePostProcessing/scalarBar.C
new file mode 100644
index 0000000000000000000000000000000000000000..cfd24195301f42a56743ec57dbba79494235d5e8
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/scalarBar.C
@@ -0,0 +1,250 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "scalarBar.H"
+
+// #include "doubleVector.H"
+// #include "foamVtkTools.H"
+
+// VTK includes
+#include "vtkCoordinate.h"
+#include "vtkLookupTable.h"
+#include "vtkRenderer.h"
+#include "vtkScalarBarActor.h"
+#include "vtkSmartPointer.h"
+#include "vtkTextActor.h"
+#include "vtkTextProperty.h"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::scalarBar::scalarBar()
+{
+    clear();
+}
+
+
+void Foam::functionObjects::runTimePostPro::scalarBar::clear()
+{
+    visible_ = true;
+    vertical_ = true;
+    bold_ = true;
+    shadow_ = false;
+    italic_ = false;
+    titleHack_ = true;
+    position_ = {0.8, 0.1};
+    size_ = {0.1, 0.5};
+    title_ = "";
+    fontSize_ = 12;
+    titleSize_ = 0;   // 0 == Auto-sizing (factor 3)
+    nLabels_ = 5;
+    labelFormat_ = "%f";
+}
+
+
+void Foam::functionObjects::runTimePostPro::scalarBar::hide()
+{
+    visible_ = false;
+}
+
+
+void Foam::functionObjects::runTimePostPro::scalarBar::read
+(
+    const dictionary& dict
+)
+{
+    clear();
+
+    dict.readIfPresent("visible", visible_);
+
+    if (visible_)
+    {
+        dict.readIfPresent("vertical", vertical_);
+        dict.readIfPresent("bold", bold_);
+        dict.readIfPresent("italic", italic_);
+        dict.readIfPresent("shadow", shadow_);
+        dict.readIfPresent("titleHack", titleHack_);
+
+        if (vertical_)
+        {
+            size_ = { 0.1, 0.75 };
+        }
+        else
+        {
+            size_ = { 0.75, 0.1 };
+        }
+
+        dict.readEntry("position", position_);
+        dict.readIfPresent("size", size_);
+        dict.readEntry("title", title_);
+        dict.readIfPresent("fontSize", fontSize_);
+        dict.readIfPresent("titleSize", titleSize_);
+        dict.readIfPresent("labelFormat", labelFormat_);
+        dict.readIfPresent("numberOfLabels", nLabels_);
+    }
+}
+
+
+bool Foam::functionObjects::runTimePostPro::scalarBar::add
+(
+    const vector& textColour,
+    vtkRenderer* renderer,
+    vtkLookupTable* lut
+) const
+{
+    // Add scalar bar legend
+
+    if (!visible_ || !renderer)
+    {
+        return false;
+    }
+
+    auto sbar = vtkSmartPointer<vtkScalarBarActor>::New();
+    sbar->SetLookupTable(lut);
+    sbar->SetNumberOfLabels(nLabels_);
+    sbar->SetLabelFormat(labelFormat_.c_str());
+
+    /// const vector textColour = colours_["text"]->value(position);
+
+    // Work-around to supply our own scalarbar title
+    // - Default scalar bar title text is scales by the scalar bar box
+    //   dimensions so if the title is a long string, the text is shrunk to fit
+    //   Instead, suppress title and set the title using a vtkTextActor
+
+    vtkSmartPointer<vtkTextActor> titleActor;
+    vtkTextProperty* titleProp;
+
+    if (titleHack_)
+    {
+        // Place the scalar bar title ourselves
+        titleActor = vtkSmartPointer<vtkTextActor>::New();
+        titleActor->SetInput(title_.c_str());
+
+        titleProp = titleActor->GetTextProperty();
+        titleProp->SetJustificationToCentered();
+    }
+    else
+    {
+        // Use the standard scalar bar title
+        sbar->SetTitle(title_.c_str());
+        titleProp = sbar->GetTitleTextProperty();
+    }
+
+    titleProp->SetFontFamilyToArial();
+
+    // Title size was supplied by user (absolute size)
+    // or use preset factor (3) of label font size
+
+    if (titleSize_)
+    {
+        titleProp->SetFontSize(titleSize_);
+    }
+    else
+    {
+        // Auto = Factor 3 of fontSize
+        titleProp->SetFontSize(3*fontSize_);
+
+        // Or this??
+        // if (!titleHack_) titleProp->SetFontSize(fontSize_);
+    }
+
+    titleProp->SetJustificationToCentered();
+    titleProp->SetVerticalJustificationToBottom();
+    titleProp->SetBold(bold_);
+    titleProp->SetItalic(italic_);
+    titleProp->SetShadow(shadow_);
+
+    titleProp->SetColor(textColour[0], textColour[1], textColour[2]);
+
+
+    auto labProp = sbar->GetLabelTextProperty();
+
+    labProp->SetColor(textColour[0], textColour[1], textColour[2]);
+
+    labProp->SetFontSize(fontSize_);
+    labProp->ShadowOff();
+    labProp->BoldOff();
+    labProp->ItalicOff();
+
+    // Positioning
+    {
+        vtkCoordinate* coord = sbar->GetPositionCoordinate();
+
+        coord->SetCoordinateSystemToNormalizedViewport();
+        coord->SetValue(position_.first(), position_.second());
+    }
+
+    if (vertical_)
+    {
+        sbar->SetOrientationToVertical();
+        sbar->SetTextPositionToSucceedScalarBar();
+        sbar->SetWidth(size_.first());
+        sbar->SetHeight(size_.second());
+        // Standard is sbar->SetBarRatio(0.375);
+    }
+    else
+    {
+        sbar->SetOrientationToHorizontal();
+        sbar->SetTextPositionToPrecedeScalarBar();
+
+        // Adjustments since not using scalarbar title property
+        sbar->SetWidth(size_.first());
+        sbar->SetHeight(size_.second());
+        // sbar->SetBarRatio(0.5);
+        // Standard is sbar->SetBarRatio(0.375);
+        // sbar->SetTitleRatio(0.01);
+    }
+
+    if (titleActor)
+    {
+        vtkCoordinate* coord = titleActor->GetPositionCoordinate();
+
+        coord->SetCoordinateSystemToNormalizedViewport();
+
+        coord->SetValue
+        (
+            position_.first() + (0.5 * sbar->GetWidth()),
+            position_.second() + (1.01 * sbar->GetHeight())
+        );
+    }
+
+    // sbar->DrawFrameOn();
+    // sbar->DrawBackgroundOn();
+    // sbar->UseOpacityOff();
+    // sbar->VisibilityOff();
+    sbar->VisibilityOn();
+
+    renderer->AddActor(sbar);
+
+    if (titleActor)
+    {
+        renderer->AddActor2D(titleActor);
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/scalarBar.H b/src/functionObjects/graphics/runTimePostProcessing/scalarBar.H
new file mode 100644
index 0000000000000000000000000000000000000000..ce3280e274592dcd2e9f9468d8d445bb4414e66f
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/scalarBar.H
@@ -0,0 +1,138 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::scalarBar
+
+Description
+    Handling of scalar bar setup.
+
+    Dictionary controls - scalar-bar entries (when \c visible is true)
+    \table
+        Property    | Description                           | Required | Default
+        visible     | Display scalar bar                    | no  | true
+        title       | The title for the scalar bar          | yes |
+        position    | Viewport position (x y) of scalar bar | yes |
+        vertical    | Vertical scalar bar                   | no  | true
+        size        | Viewport size (x y) of scalar bar     | no  | auto
+        fontSize    | Label size                            | no  | 12
+        titleSize   | Title font size                       | no  | 0 == auto
+        labelFormat | Label format string (eg, "%f")        | no  | "%f"
+        numberOfLabels | Total number of labels             | no  | 5
+        bold        | Title in bold                         | no  | yes
+        italic      | Title in italic font                  | no  | no
+        shadow      | Title with shadowont                  | no  | no
+        titleHack   | Alternative placement strategy        | no  | yes
+    \endtable
+
+SourceFiles
+    scalarBar.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_scalarBar_H
+#define functionObjects_runTimePostPro_scalarBar_H
+
+#include "dictionary.H"
+#include "Tuple2.H"
+#include "vector.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Forward Declarations
+class vtkLookupTable;
+class vtkRenderer;
+
+
+namespace Foam
+{
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class scalarBar Declaration
+\*---------------------------------------------------------------------------*/
+
+class scalarBar
+{
+    bool visible_;
+    bool vertical_;
+    bool bold_;
+    bool italic_;
+    bool shadow_;
+    bool titleHack_;
+
+    Tuple2<scalar, scalar> position_;
+
+    Tuple2<scalar, scalar> size_;
+
+    string title_;
+
+    label fontSize_;
+
+    label titleSize_;
+
+    label nLabels_;
+
+    string labelFormat_;
+
+
+public:
+
+    //- Construct with sensible defaults
+    scalarBar();
+
+    //- Reset to sensible defaults
+    void clear();
+
+    //- Make non-visible
+    void hide();
+
+    //- Read dictionary settings
+    void read(const dictionary& dict);
+
+    //- Add colour bar, when visible.
+    bool add
+    (
+        const vector& textColour,
+        vtkRenderer* renderer,
+        vtkLookupTable* lut
+    ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/scene.C b/src/functionObjects/graphics/runTimePostProcessing/scene.C
index 2a1e7c337a95322b4de7c38deb43e54ea85a4815..edbb43c20681be3cbeb8c321e8cdbc0de8ba2624 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/scene.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/scene.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -137,6 +135,8 @@ void Foam::functionObjects::runTimePostPro::scene::setActorVisibility
     const bool visible
 ) const
 {
+    if (!renderer) return;
+
     vtkActorCollection *actors = renderer->GetActors();
     for (int i = 0; i < actors->GetNumberOfItems(); ++i)
     {
@@ -157,27 +157,30 @@ void Foam::functionObjects::runTimePostPro::scene::initialise
 
     outputName_ = outputName;
 
+    if (!renderer) return;
+
+
     // Set the background
-    const vector backgroundColour = colours_["background"]->value(position_);
-    renderer->SetBackground
-    (
-        backgroundColour.x(),
-        backgroundColour.y(),
-        backgroundColour.z()
-    );
+    const vector bgColour = colours_["background"]->value(position_);
+
+    renderer->SetBackground(bgColour.x(), bgColour.y(), bgColour.z());
 
     // Apply gradient background if "background2" defined
     if (colours_.found("background2"))
     {
+        const vector bg2Colour = colours_["background2"]->value(position_);
+
         renderer->GradientBackgroundOn();
-        vector backgroundColour2 = colours_["background2"]->value(position_);
+        renderer->SetBackground2(bg2Colour.x(), bg2Colour.y(), bg2Colour.z());
+    }
+    else if (Pstream::parRun())
+    {
+        // Oddly enough we seem a gradient background for parallel rendering,
+        // otherwise the colours look quite funny.
+        // Doesn't seem to matter if we use SetBackground2() though
 
-        renderer->SetBackground2
-        (
-            backgroundColour2.x(),
-            backgroundColour2.y(),
-            backgroundColour2.z()
-        );
+        renderer->GradientBackgroundOn();
+        renderer->SetBackground2(bgColour.x(), bgColour.y(), bgColour.z());
     }
 
     // Depth peeling
@@ -224,6 +227,8 @@ void Foam::functionObjects::runTimePostPro::scene::setCamera
     vtkRenderer* renderer
 ) const
 {
+    if (!renderer) return;
+
     vtkCamera* camera = renderer->GetActiveCamera();
 
     if (parallelProjection_)
@@ -355,14 +360,18 @@ bool Foam::functionObjects::runTimePostPro::scene::loop(vtkRenderer* renderer)
         return true;
     }
 
-    // Ensure that all objects can be seen without clipping
-    // Note: can only be done after all objects have been added!
-    renderer->ResetCameraClippingRange();
+    if (renderer)
+    {
+
+        // Ensure that all objects can be seen without clipping
+        // Note: can only be done after all objects have been added!
+        renderer->ResetCameraClippingRange();
 
-    // Save image from last iteration
-    saveImage(renderer->GetRenderWindow());
+        // Save image from last iteration
+        saveImage(renderer->GetRenderWindow());
+    }
 
-    currentFrameI_++;
+    ++currentFrameI_;
 
     position_ = startPosition_ + currentFrameI_*dPosition_;
 
@@ -390,16 +399,15 @@ void Foam::functionObjects::runTimePostPro::scene::saveImage
 
     const Time& runTime = obr_.time();
 
-    const fileName prefix
+    const fileName fName
     (
         runTime.globalPath()
       / functionObject::outputPrefix
       / name_
       / runTime.timeName()
+      / outputName_ + '.' + frameIndexStr() + ".png"
     );
 
-    mkDir(prefix);
-
     renderWindow->Render();
 
     // Set up off-screen rendering
@@ -415,15 +423,21 @@ void Foam::functionObjects::runTimePostPro::scene::saveImage
     windowToImageFilter->Update();
 
     // Save the image
-    auto writer = vtkSmartPointer<vtkPNGWriter>::New();
-    fileName fName(prefix/outputName_ + '.' + frameIndexStr() + ".png");
-    writer->SetFileName(fName.c_str());
-    writer->SetInputConnection(windowToImageFilter->GetOutputPort());
 
-    Info<< "    Generating image: " << fName << endl;
+    if (Pstream::master())
+    {
+        mkDir(fName.path());
+
+        auto writer = vtkSmartPointer<vtkPNGWriter>::New();
+        writer->SetFileName(fName.c_str());
+        writer->SetInputConnection(windowToImageFilter->GetOutputPort());
+
+        Info<< "    Generating image: " << runTime.relativePath(fName) << endl;
 
-    writer->Write();
+        writer->Write();
+    }
 }
 
 
+
 // ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/scene.H b/src/functionObjects/graphics/runTimePostProcessing/scene.H
index bcf440e6dbbcea954612ae1776739525cd852618..57c4f2b056e2f2fc8061869e2bfa6be3e9c13f35 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/scene.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/scene.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -48,6 +46,10 @@ Usage
         viewAngle   20;
         zoom        1.1;
     }
+
+    colours
+    {
+    }
     \endverbatim
 
 SourceFiles
diff --git a/src/functionObjects/graphics/runTimePostProcessing/surface.C b/src/functionObjects/graphics/runTimePostProcessing/surface.C
index 06c263f8a35bcd79137ff6d7af57f0802a6bbc9d..62f11d0e605828264677db842a86193e35d14561 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/surface.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/surface.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2015-2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,9 +27,15 @@ License
 #include "surface.H"
 #include "runTimePostProcessing.H"
 
+#include "foamVtkTools.H"
+#include "polySurfaceFields.H"
+#include "polySurfacePointFields.H"
+
 // VTK includes
 #include "vtkActor.h"
+#include "vtkCompositeDataGeometryFilter.h"
 #include "vtkFeatureEdges.h"
+#include "vtkMultiPieceDataSet.h"
 #include "vtkPolyData.h"
 #include "vtkPolyDataMapper.h"
 #include "vtkProperty.h"
@@ -46,7 +50,7 @@ namespace functionObjects
 {
 namespace runTimePostPro
 {
-    defineTypeNameAndDebug(surface, 0);
+    defineTypeName(surface);
     defineRunTimeSelectionTable(surface, dictionary);
 }
 }
@@ -60,13 +64,30 @@ const Foam::Enum
 Foam::functionObjects::runTimePostPro::surface::representationTypeNames
 ({
     { representationType::rtNone, "none" },
+    { representationType::rtGlyph, "glyph" },
     { representationType::rtWireframe, "wireframe" },
     { representationType::rtSurface, "surface" },
     { representationType::rtSurfaceWithEdges, "surfaceWithEdges" },
-    { representationType::rtGlyph, "glyph" },
 });
 
 
+// * * * * * * * * * * * * * * * Specializations * * * * * * * * * * * * * * //
+
+// These need to shift elsewhere
+
+vtkCellData* Foam::vtk::Tools::GetCellData(vtkDataSet* dataset)
+{
+    if (dataset) return dataset->GetCellData();
+    return nullptr;
+}
+
+vtkPointData* Foam::vtk::Tools::GetPointData(vtkDataSet* dataset)
+{
+    if (dataset) return dataset->GetPointData();
+    return nullptr;
+}
+
+
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
 void Foam::functionObjects::runTimePostPro::surface::setRepresentation
@@ -85,7 +106,7 @@ void Foam::functionObjects::runTimePostPro::surface::setRepresentation
         }
         case rtWireframe:
         {
-            // note: colour is set using general SetColor, not SetEdgeColor
+            // Note: colour is set using general SetColor, not SetEdgeColor
             actor->GetProperty()->SetRepresentationToWireframe();
             break;
         }
@@ -108,21 +129,19 @@ void Foam::functionObjects::runTimePostPro::surface::setRepresentation
 void Foam::functionObjects::runTimePostPro::surface::addFeatureEdges
 (
     vtkRenderer* renderer,
-    vtkPolyData* data
+    vtkFeatureEdges* featureEdges
 ) const
 {
-    if (!featureEdges_)
+    if (!featureEdges)
     {
         return;
     }
 
-    auto featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
-    featureEdges->SetInputData(data);
     featureEdges->BoundaryEdgesOn();
     featureEdges->FeatureEdgesOn();
     featureEdges->ManifoldEdgesOff();
     featureEdges->NonManifoldEdgesOff();
-//    featureEdges->SetFeatureAngle(60);
+    /// featureEdges->SetFeatureAngle(60);
     featureEdges->ColoringOff();
     featureEdges->Update();
 
@@ -139,6 +158,38 @@ void Foam::functionObjects::runTimePostPro::surface::addFeatureEdges
 }
 
 
+void Foam::functionObjects::runTimePostPro::surface::addFeatureEdges
+(
+    vtkRenderer* renderer,
+    vtkPolyData* data
+) const
+{
+    if (featureEdges_)
+    {
+        auto featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
+        featureEdges->SetInputData(data);
+
+        addFeatureEdges(renderer, featureEdges);
+    }
+}
+
+
+void Foam::functionObjects::runTimePostPro::surface::addFeatureEdges
+(
+    vtkRenderer* renderer,
+    vtkCompositeDataGeometryFilter* input
+) const
+{
+    if (featureEdges_)
+    {
+        auto featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
+        featureEdges->SetInputConnection(input->GetOutputPort());
+
+        addFeatureEdges(renderer, featureEdges);
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::functionObjects::runTimePostPro::surface::surface
@@ -153,12 +204,12 @@ Foam::functionObjects::runTimePostPro::surface::surface
     (
         representationTypeNames.get("representation", dict)
     ),
-    featureEdges_(false),
+    featureEdges_(dict.lookupOrDefault("featureEdges", false)),
     surfaceColour_(nullptr),
     edgeColour_(nullptr),
     surfaceActor_(),
     edgeActor_(),
-    maxGlyphLength_(0.0)
+    maxGlyphLength_(0)
 {
     surfaceActor_ = vtkSmartPointer<vtkActor>::New();
     edgeActor_ = vtkSmartPointer<vtkActor>::New();
@@ -185,10 +236,6 @@ Foam::functionObjects::runTimePostPro::surface::surface
     {
         dict.readEntry("maxGlyphLength", maxGlyphLength_);
     }
-    else
-    {
-        dict.readEntry("featureEdges", featureEdges_);
-    }
 }
 
 
@@ -203,10 +250,7 @@ Foam::functionObjects::runTimePostPro::surface::New
     const word& surfaceType
 )
 {
-    if (debug)
-    {
-        Info<< "Selecting surface " << surfaceType << endl;
-    }
+    DebugInfo << "Selecting surface " << surfaceType << endl;
 
     auto cstrIter = dictionaryConstructorTablePtr_->cfind(surfaceType);
 
@@ -242,22 +286,14 @@ void Foam::functionObjects::runTimePostPro::surface::updateActors
         return;
     }
 
-    edgeActor_->GetProperty()->SetLineWidth(2);
-    edgeActor_->GetProperty()->SetOpacity(opacity(position));
+    vtkProperty* edgeProp = edgeActor_->GetProperty();
 
-    const vector colour = edgeColour_->value(position);
-    edgeActor_->GetProperty()->SetColor
-    (
-        colour[0],
-        colour[1],
-        colour[2]
-    );
-    edgeActor_->GetProperty()->SetEdgeColor
-    (
-        colour[0],
-        colour[1],
-        colour[2]
-    );
+    edgeProp->SetLineWidth(2);
+    edgeProp->SetOpacity(opacity(position));
+
+    const vector ec = edgeColour_->value(position);
+    edgeProp->SetColor(ec[0], ec[1], ec[2]);
+    edgeProp->SetEdgeColor(ec[0], ec[1], ec[2]);
 }
 
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/surface.H b/src/functionObjects/graphics/runTimePostProcessing/surface.H
index 1de50dd5ee1272f3fa2742c438e34e7cc582991b..0725eb7e32d06fa8d42d644c67ba396ec70ead08 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/surface.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/surface.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,10 +25,23 @@ Class
     Foam::functionObjects::runTimePostPro::surface
 
 Description
-    Visualisation of surface data
+    Visualisation of surface data with additional routines for handling
+    parallel distributed data.
+
+    Dictionary controls
+    \table
+        Property    | Description                           | Required | Default
+        representation| none/glyph/wireframe/surface/surfaceWithEdges | yes |
+        surfaceColour | Override surface colour             | no  |
+        edgeColour    | Override edge colour                | no  |
+        featureEdges  | Display surface feature edges       | no  | false
+        maxGlyphLength | Limit for glyph representation     | yes | 0
+    \endtable
 
 SourceFiles
     surface.C
+    surfaceGather.C
+    surfaceTemplates.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -38,17 +49,80 @@ SourceFiles
 #define functionObjects_runTimePostPro_surface_H
 
 #include "geometryBase.H"
+#include "DimensionedField.H"
 #include "Enum.H"
 #include "runTimeSelectionTables.H"
+
 #include "vtkSmartPointer.h"
+#include "vtkMultiPieceDataSet.h"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations
 class vtkActor;
 class vtkRenderer;
+class vtkCellData;
+class vtkCompositeDataGeometryFilter;
+class vtkFeatureEdges;
+class vtkPointData;
 class vtkPolyData;
 
+namespace Foam
+{
+// Forward Declarations
+class polySurface;
+class polySurfaceGeoMesh;
+class polySurfacePointGeoMesh;
+}
+
+
+// These need to shift elsewhere
+
+namespace Foam
+{
+namespace vtk
+{
+namespace Tools
+{
+
+//- Functional call with null-pointer check
+vtkCellData* GetCellData(vtkDataSet* dataset);
+
+//- Functional call with null-pointer check
+vtkPointData* GetPointData(vtkDataSet* dataset);
+
+
+//- Default field access is vtkCellData
+template<class Type>
+struct FieldAccess
+{
+    vtkCellData* operator()(vtkDataSet* dataset) const
+    {
+        return Tools::GetCellData(dataset);
+    }
+};
+
+
+// Specializations on OpenFOAM type
+
+//- PointAccess for point fields (on polySurfacePointGeoMesh)
+template<>
+struct FieldAccess<::Foam::polySurfacePointGeoMesh>
+{
+    vtkPointData* operator()(vtkDataSet* dataset) const
+    {
+        return Tools::GetPointData(dataset);
+    }
+};
+
+} // End namespace Tools
+} // End namespace vtk
+} // End namespace Foam
+
+
+// More
+
+
 namespace Foam
 {
 namespace functionObjects
@@ -68,21 +142,23 @@ public:
 
     // Public enumerations
 
+        //- Surface representation types
         enum representationType
         {
-            rtNone,
-            rtWireframe,
-            rtSurface,
-            rtSurfaceWithEdges,
-            rtGlyph
+            rtNone,             //!< "none"
+            rtGlyph,            //!< "glyph"
+            rtWireframe,        //!< "wireframe"
+            rtSurface,          //!< "surface"
+            rtSurfaceWithEdges  //!< "surfaceWithEdges"
         };
 
+        //- Names for surface representation types
         static const Enum<representationType> representationTypeNames;
 
 
 protected:
 
-    // Protected data
+    // Protected Data
 
         //- Representation type
         representationType representation_;
@@ -111,6 +187,13 @@ protected:
         //- Set the representation
         void setRepresentation(vtkActor* actor) const;
 
+        //- Add feature edges to scene
+        void addFeatureEdges
+        (
+            vtkRenderer* renderer,
+            vtkFeatureEdges* featureEdges
+        ) const;
+
         //- Add feature edges to scene
         void addFeatureEdges
         (
@@ -118,6 +201,110 @@ protected:
             vtkPolyData* data
         ) const;
 
+        //- Add feature edges to scene
+        void addFeatureEdges
+        (
+            vtkRenderer* renderer,
+            vtkCompositeDataGeometryFilter* input
+        ) const;
+
+
+        //- Gather and convert polySurface to multi-piece dataset with
+        //- vtkPolyData for the leaves.
+        //  If VTK is also running in parallel, each surface is left
+        //  as a processor-local piece. Otherwise all processor-local
+        //  surfaces are gathered onto the master in their correponding
+        //  slots.
+        vtkSmartPointer<vtkMultiPieceDataSet>
+        gatherSurfacePieces(const polySurface* surf) const;
+
+        //- Gather and convert polySurface to multi-piece dataset with
+        //- vtkPolyData for the leaves.
+        //  If VTK is also running in parallel, each surface is left
+        //  as a processor-local piece. Otherwise all processor-local
+        //  surfaces are gathered onto the master in their correponding
+        //  slots.
+        vtkSmartPointer<vtkMultiPieceDataSet>
+        gatherFaceCentres(const polySurface* surf) const;
+
+
+    // Adding Fields - single-piece
+
+        //- Add field of Type to piece as VTK field data in GeoMeshType slot.
+        //  GeoMeshType distinguishes between vtkCellData and vtkPointData
+        template<class Type, class GeoMeshType>
+        bool addField
+        (
+            vtkDataSet* piece,       //!< The VTK piece (null protected)
+            const Field<Type>& fld,  //!< The field values to add
+            const word& fieldName    //!< The field name to use
+        ) const;
+
+        //- Attempt cast of regIOobject to DimensionedField\<Type\> and
+        //- add to piece as VTK field data in GeoMeshType slot.
+        //  GeoMeshType distinguishes between vtkCellData and vtkPointData
+        template<class Type, class GeoMeshType>
+        bool addDimField
+        (
+            vtkDataSet* piece,       //!< The VTK piece (null protected)
+            const regIOobject* ioptr, //!< The field values to add
+            const word& fieldName    //!< The field name to use
+        ) const;
+
+        //- Attempt cast of regIOobject to standard DimensionedField types
+        //- and add to piece when possible
+        template<class GeoMeshType>
+        bool addDimField
+        (
+            vtkDataSet* piece,       //!< The VTK piece (null protected)
+            const regIOobject* ioptr, //!< The field values to add
+            const word& fieldName    //!< The field name to use
+        ) const;
+
+
+    // Adding Fields - multi-piece
+
+        //- Add DimensionedField of Type to multi-piece as VTK field data in
+        //- GeoMeshType slot (CELL | POINT).
+        template<class Type, class GeoMeshType>
+        bool addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const DimensionedField<Type, GeoMeshType>* fldptr,
+            const word& fieldName
+        ) const;
+
+        //- Attempt cast of regIOobject to DimensionedField\<Type\> and
+        //- add in multi-piece as VTK field data in
+        //- GeoMeshType slot (CELL | POINT).
+        template<class Type, class GeoMeshType>
+        bool addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece, //!< The VTK pieces
+            const regIOobject* ioptr, //!< The field values to add
+            const word& fieldName    //!< The field name to use
+        ) const;
+
+        //- Attempt cast of regIOobject to standard DimensionedField types
+        //- and add when possible in GeoMeshType slot (CELL | POINT).
+        template<class GeoMeshType>
+        bool addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece, //!< The VTK pieces
+            const regIOobject* ioptr, //!< The field values to add
+            const word& fieldName    //!< The field name to use
+        ) const;
+
+        //- Add using regIOobject information obtained from surface
+        template<class GeoMeshType>
+        bool addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const polySurface* surf,
+            const word& fieldName
+        ) const;
+
+
         //- No copy construct
         surface(const surface&) = delete;
 
@@ -128,7 +315,7 @@ protected:
 public:
 
     //- Run-time type information
-    TypeName("surface");
+    TypeNameNoDebug("surface");
 
 
     // Declare run-time constructor selection table
@@ -160,7 +347,7 @@ public:
 
     // Selectors
 
-        //- Return a reference to the selected RAS model
+        //- Return selected surface
         static autoPtr<surface> New
         (
             const runTimePostProcessing& parent,
@@ -189,6 +376,12 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+#ifdef NoRepository
+    #include "surfaceTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
 #endif
 
 // ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/surfaceGather.C b/src/functionObjects/graphics/runTimePostProcessing/surfaceGather.C
new file mode 100644
index 0000000000000000000000000000000000000000..218e3e13f3c257aeedd6ba9154f0a56a0f8248b5
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/surfaceGather.C
@@ -0,0 +1,222 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "surface.H"
+#include "runTimePostProcessing.H"
+
+#include "foamVtkTools.H"
+#include "polySurface.H"
+
+// VTK includes
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPolyData.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::surface::gatherSurfacePieces
+(
+    const polySurface* surf
+) const
+{
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+    multiPiece->SetNumberOfPieces(Pstream::nProcs());
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        if (surf)
+        {
+            multiPiece->SetPiece
+            (
+                Pstream::myProcNo(),
+                Foam::vtk::Tools::Patch::mesh(*surf)
+            );
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces on master
+
+        if (surf)
+        {
+            // Add myself
+
+            multiPiece->SetPiece
+            (
+                Pstream::myProcNo(),
+                Foam::vtk::Tools::Patch::mesh(*surf)
+            );
+        }
+
+        // Receive surfaces
+        pointField points;
+        faceList faces;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            points.clear();
+            faces.clear();
+
+            fromSlave >> points >> faces;
+
+            if (points.size())
+            {
+                multiPiece->SetPiece
+                (
+                    slave,
+                    Foam::vtk::Tools::Patch::mesh(points, faces)
+                );
+            }
+        }
+    }
+    else
+    {
+        // Slave - send surfaces
+
+        OPstream toMaster
+        (
+            Pstream::commsTypes::scheduled,
+            Pstream::masterNo()
+        );
+
+        if (surf)
+        {
+            toMaster
+                << surf->points() << surf->faces();
+        }
+        else
+        {
+            toMaster
+                << pointField() << faceList();
+        }
+    }
+
+    return multiPiece;
+}
+
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::surface::gatherFaceCentres
+(
+    const polySurface* surf
+) const
+{
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+    multiPiece->SetNumberOfPieces(Pstream::nProcs());
+
+    if (!needsCollective())
+    {
+        // Simple case
+
+        if (surf)
+        {
+            auto dataset = vtkSmartPointer<vtkPolyData>::New();
+
+            auto geom = vtkSmartPointer<vtkPolyData>::New();
+
+            geom->SetPoints(Foam::vtk::Tools::Patch::faceCentres(*surf));
+            geom->SetVerts(Foam::vtk::Tools::identityVertices(surf->nFaces()));
+
+            multiPiece->SetPiece(Pstream::myProcNo(), geom);
+        }
+    }
+    else if (Pstream::master())
+    {
+        // Gather pieces (face centres) on master
+
+        if (surf)
+        {
+            // Add myself
+
+            auto geom = vtkSmartPointer<vtkPolyData>::New();
+
+            geom->SetPoints(Foam::vtk::Tools::Patch::faceCentres(*surf));
+            geom->SetVerts(Foam::vtk::Tools::identityVertices(surf->nFaces()));
+
+            multiPiece->SetPiece(Pstream::myProcNo(), geom);
+        }
+
+        // Receive points
+        pointField points;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            points.clear();
+
+            fromSlave >> points;
+
+            if (points.size())
+            {
+                multiPiece->SetPiece
+                (
+                    slave,
+                    Foam::vtk::Tools::Vertices(points)
+                );
+            }
+        }
+    }
+    else
+    {
+        // Slave - send face centres
+
+        OPstream toMaster
+        (
+            Pstream::commsTypes::scheduled,
+            Pstream::masterNo()
+        );
+
+        if (surf)
+        {
+            toMaster << surf->faceCentres();
+        }
+        else
+        {
+            toMaster << pointField();
+        }
+    }
+
+    return multiPiece;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/surfaceTemplates.C b/src/functionObjects/graphics/runTimePostProcessing/surfaceTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..eaf1498e8cbd4f858529782c85d6a32d66e344c2
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/surfaceTemplates.C
@@ -0,0 +1,289 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "foamVtkTools.H"
+#include "polySurfaceFields.H"
+#include "polySurfacePointFields.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Type, class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addField
+(
+    vtkDataSet* piece,
+    const Field<Type>& fld,
+    const word& fieldName
+) const
+{
+    if (!piece) return false;
+
+    auto vtkfield = Foam::vtk::Tools::convertFieldToVTK<Type>(fieldName, fld);
+
+    if (piece->GetNumberOfCells() == piece->GetNumberOfPoints())
+    {
+        // Only has verts
+        piece->GetPointData()->AddArray(vtkfield);
+    }
+    else
+    {
+        Foam::vtk::Tools::FieldAccess<GeoMeshType>()(piece)->AddArray(vtkfield);
+    }
+
+    return true;
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkDataSet* piece,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    const auto* fldptr =
+        dynamic_cast<const DimensionedField<Type, GeoMeshType>*>(ioptr);
+
+    if (fldptr)
+    {
+        return addField<Type, GeoMeshType>
+        (
+            piece,
+            fldptr->field(),
+            fieldName
+        );
+    }
+
+    return false;
+}
+
+
+template<class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkDataSet* piece,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return (piece && ioptr) &&
+    (
+        addDimField<scalar, GeoMeshType>
+        (
+            piece, ioptr, fieldName
+        )
+     || addDimField<vector, GeoMeshType>
+        (
+            piece, ioptr, fieldName
+        )
+     || addDimField<sphericalTensor, GeoMeshType>
+        (
+            piece, ioptr, fieldName
+        )
+     || addDimField<symmTensor, GeoMeshType>
+        (
+            piece, ioptr, fieldName
+        )
+     || addDimField<tensor, GeoMeshType>
+        (
+            piece, ioptr, fieldName
+        )
+    );
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const DimensionedField<Type, GeoMeshType>* fldptr,
+    const word& fieldName
+) const
+{
+    if (!multiPiece)
+    {
+        return false;
+    }
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        return fldptr &&
+            addField<Type, GeoMeshType>
+            (
+                multiPiece->GetPiece(Pstream::myProcNo()),
+                fldptr->field(),
+                fieldName
+            );
+    }
+
+
+    // Gather fields
+    const bool ok = returnReduce((fldptr != nullptr), orOp<bool>());
+
+    if (!ok)
+    {
+        return false;
+    }
+
+    if (Pstream::master())
+    {
+        if (fldptr)
+        {
+            // My field data
+            addField<Type, GeoMeshType>
+            (
+                multiPiece->GetPiece(Pstream::myProcNo()),
+                fldptr->field(),
+                fieldName
+            );
+        }
+
+        // Receive field data
+        Field<Type> recv;
+
+        for
+        (
+            int slave=Pstream::firstSlave();
+            slave<=Pstream::lastSlave();
+            ++slave
+        )
+        {
+            IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
+
+            recv.clear();
+
+            fromSlave
+                >> recv;
+
+            if (recv.size())
+            {
+                addField<Type, GeoMeshType>
+                (
+                    multiPiece->GetPiece(slave),
+                    recv,
+                    fieldName
+                );
+            }
+        }
+    }
+    else
+    {
+        // Slave - send field data
+
+        OPstream toMaster
+        (
+            Pstream::commsTypes::scheduled,
+            Pstream::masterNo()
+        );
+
+        if (fldptr)
+        {
+            toMaster
+                << fldptr->field();
+        }
+        else
+        {
+            toMaster
+                << List<Type>();
+        }
+    }
+
+    return ok;
+}
+
+
+template<class Type, class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return addDimField<Type, GeoMeshType>
+    (
+        multiPiece,
+        dynamic_cast<const DimensionedField<Type, GeoMeshType>*>(ioptr),
+        fieldName
+    );
+}
+
+
+template<class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return (multiPiece) &&
+    (
+        addDimField<scalar, GeoMeshType>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addDimField<vector, GeoMeshType>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addDimField<sphericalTensor, GeoMeshType>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addDimField<symmTensor, GeoMeshType>
+        (
+            multiPiece, ioptr, fieldName
+        )
+     || addDimField<tensor, GeoMeshType>
+        (
+            multiPiece, ioptr, fieldName
+        )
+    );
+}
+
+
+template<class GeoMeshType>
+bool Foam::functionObjects::runTimePostPro::surface::addDimField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const polySurface* surf,
+    const word& fieldName
+) const
+{
+    const regIOobject* ioptr =
+    (
+        surf
+      ? surf->findFieldObject<GeoMeshType>(fieldName)
+      : nullptr
+    );
+
+    return addDimField<GeoMeshType>(multiPiece, ioptr, fieldName);
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/text.C b/src/functionObjects/graphics/runTimePostProcessing/text.C
index 09b622c565b24ebdb83c46143aa2007af62cfb3f..688e77486b207ac8eca21cf9b7d5f581c4ae5599 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/text.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/text.C
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,10 +25,12 @@ License
 
 // OpenFOAM includes
 #include "text.H"
+#include "stringOps.H"
 #include "fvMesh.H"
 #include "runTimePostProcessing.H"
 
 // VTK includes
+#include "vtkCoordinate.h"
 #include "vtkRenderer.h"
 #include "vtkSmartPointer.h"
 #include "vtkTextActor.h"
@@ -62,7 +62,7 @@ Foam::functionObjects::runTimePostPro::text::text
 :
     geometryBase(parent, dict, colours),
     string_(dict.get<string>("string")),
-    position_(),
+    positions_(),
     size_(dict.get<scalar>("size")),
     colour_(nullptr),
     halign_
@@ -74,7 +74,20 @@ Foam::functionObjects::runTimePostPro::text::text
     shadow_(dict.lookupOrDefault("shadow", false)),
     timeStamp_(dict.lookupOrDefault("timeStamp", false))
 {
-    dict.readEntry("position", position_);
+    if (!dict.readIfPresent("positions", positions_))
+    {
+        positions_.resize(1);
+        dict.readEntry("position", positions_.first());
+    }
+
+    // Additional safety
+    if (positions_.empty())
+    {
+        positions_.resize(1);
+        positions_.first() = {0, 0};
+    }
+
+    stringOps::inplaceExpand(string_, dict, true, true);
 
     if (dict.found("colour"))
     {
@@ -101,12 +114,13 @@ void Foam::functionObjects::runTimePostPro::text::addGeometryToScene
     vtkRenderer* renderer
 )
 {
-    if (!visible_)
+    if (!visible_ || !renderer || !Pstream::master())
     {
+        // Add text on master only!
         return;
     }
 
-    auto actor = vtkSmartPointer<vtkTextActor>::New();
+    DebugInfo << "    Add text: " << string_ << nl;
 
     // Concatenate string with timeStamp if true
     string str = string_;
@@ -114,31 +128,40 @@ void Foam::functionObjects::runTimePostPro::text::addGeometryToScene
     {
         str += " " + geometryBase::parent_.mesh().time().timeName();
     }
-    actor->SetInput(str.c_str());
 
-    vtkTextProperty* prop = actor->GetTextProperty();
+    const vector textColour = colour_->value(position);
 
-    prop->SetFontFamilyToArial();
-    prop->SetFontSize(size_);
-    prop->SetJustification(int(halign_));
-    prop->SetVerticalJustificationToBottom();
-    prop->SetBold(bold_);
-    prop->SetItalic(italic_);
-    prop->SetShadow(shadow_);
+    const scalar textOpacity = opacity(position);
 
-    const vector colour = colour_->value(position);
+    for (const auto& textPosition : positions_)
+    {
+        auto actor = vtkSmartPointer<vtkTextActor>::New();
 
-    prop->SetColor(colour[0], colour[1], colour[2]);
-    prop->SetOpacity(opacity(position));
+        actor->SetInput(str.c_str());
 
-    actor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
-    actor->GetPositionCoordinate()->SetValue
-    (
-        position_.first(),
-        position_.second()
-    );
+        vtkTextProperty* prop = actor->GetTextProperty();
+
+        prop->SetFontFamilyToArial();
+        prop->SetFontSize(size_);
+        prop->SetJustification(int(halign_));
+        prop->SetVerticalJustificationToBottom();
+        prop->SetBold(bold_);
+        prop->SetItalic(italic_);
+        prop->SetShadow(shadow_);
+
+        prop->SetColor(textColour[0], textColour[1], textColour[2]);
+        prop->SetOpacity(textOpacity);
 
-    renderer->AddActor2D(actor);
+        // Positioning
+        {
+            vtkCoordinate* coord = actor->GetPositionCoordinate();
+
+            coord->SetCoordinateSystemToNormalizedViewport();
+            coord->SetValue(textPosition.first(), textPosition.second());
+        }
+
+        renderer->AddActor2D(actor);
+    }
 }
 
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/text.H b/src/functionObjects/graphics/runTimePostProcessing/text.H
index 5eaf56c571553a799ebd0b902d7546b397e3476f..f298d4cff59518b0a646b18f1bba9cdef64d77ef 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/text.H
+++ b/src/functionObjects/graphics/runTimePostProcessing/text.H
@@ -2,10 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2015-2019 OpenCFD Ltd.
      \\/     M anipulation  |
--------------------------------------------------------------------------------
-                            | Copyright (C) 2015 OpenFOAM Foundation
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,7 +39,6 @@ Description
         // Optional entry
 
         shadow      false;
-        visible     yes;
 
         // Optionally override default colour
         // colour    (0 1 1);
@@ -56,6 +53,7 @@ Description
         Property    | Description                          | Required | Default
         string      | Text to display                      | yes |
         position    | The (x y) viewport position          | yes |
+        positions   | Multiple (x y) viewport positions    | no  |
         size        | The font size in points              | yes |
         halign      | Text justification (left/centre/ right) | no | left
         bold        | Use bold font                        | yes |
@@ -68,10 +66,14 @@ Description
     Inherited controls
     \table
         Property    | Description                          | Required | Default
-        visible     | Display the object                   | yes |
+        visible     | Display the object                   | no  | true
         opacity     | Object opacity                       | no  | 1.0
     \endtable
 
+Note
+    The string text is expanded on input using stringOps::inplaceExpand()
+    to expand dictionary and environment variable entries.
+
 SourceFiles
     text.C
 
@@ -85,7 +87,7 @@ SourceFiles
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Forward declarations
+// Forward Declarations
 class vtkRenderer;
 
 namespace Foam
@@ -96,7 +98,7 @@ namespace runTimePostPro
 {
 
 /*---------------------------------------------------------------------------*\
-                          Class text Declaration
+                            Class text Declaration
 \*---------------------------------------------------------------------------*/
 
 class text
@@ -105,9 +107,9 @@ class text
 {
 public:
 
-    // Public enumerations
+    // Public Enumerations
 
-        //- Horizontal alignment type
+        //- Horizontal alignment type. Values to match VTK definitions
         enum halignType
         {
             LEFT   = 0,   //!< Left-justified text - default ("left")
@@ -126,13 +128,13 @@ protected:
         //- Text
         string string_;
 
-        //- Position
-        Tuple2<scalar, scalar> position_;
+        //- Position(s)
+        List<Tuple2<scalar, scalar>> positions_;
 
-        //- Size
+        //- Font size
         scalar size_;
 
-        //- Colour
+        //- Text colour
         autoPtr<Function1<vector>> colour_;
 
         //- Horizontal alignment
@@ -179,7 +181,7 @@ public:
 
     // Member Functions
 
-        //- Add surface(s) to scene
+        //- Add text to scene
         virtual void addGeometryToScene
         (
             const scalar position,
@@ -189,7 +191,7 @@ public:
         //- Update actors
         virtual void updateActors(const scalar position);
 
-        //- Clear files used to create the object(s)
+        //- Clear files used to create the object(s) - no-op
         virtual bool clear();
 };
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.C b/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.C
new file mode 100644
index 0000000000000000000000000000000000000000..9a3caf7f8b4fbfb60dfe7d64e0d274bf383debbc
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.C
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "volumeFilter.H"
+#include "runTimePostProcessing.H"
+
+// VTK includes
+#include "vtkCellData.h"
+#include "vtkCellDataToPointData.h"
+#include "vtkCompositeDataGeometryFilter.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkCompositePolyDataMapper.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkSmartPointer.h"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::runTimePostPro::volumeFilter::volumeFilter
+(
+    const runTimePostProcessing& parent,
+    const dictionary& dict,
+    const HashPtrTable<Function1<vector>>& colours
+)
+:
+    surface(parent, dict, colours)
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+vtkSmartPointer<vtkMultiPieceDataSet>
+Foam::functionObjects::runTimePostPro::volumeFilter::mesh
+(
+    Foam::vtk::vtuAdaptor& adaptor
+) const
+{
+    auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
+
+    multiPiece->SetNumberOfPieces(Pstream::nProcs());
+    multiPiece->SetPiece
+    (
+        Pstream::myProcNo(),
+        adaptor.internal(parent().mesh())
+    );
+
+    return multiPiece;
+}
+
+
+bool Foam::functionObjects::runTimePostPro::volumeFilter::addDimField
+(
+    vtkDataSet* piece,
+    const vtk::vtuAdaptor& adaptor,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return (piece && ioptr) &&
+    (
+        addDimField<scalar>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<vector>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<sphericalTensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<symmTensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<tensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+    );
+}
+
+
+int Foam::functionObjects::runTimePostPro::volumeFilter::addDimField
+(
+    vtkMultiPieceDataSet* piece,
+    const vtk::vtuAdaptor& adaptor,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    return (piece && ioptr) &&
+    (
+        addDimField<scalar>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<vector>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<sphericalTensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<symmTensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+     || addDimField<tensor>
+        (
+            piece, adaptor, ioptr, fieldName
+        )
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.H b/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.H
new file mode 100644
index 0000000000000000000000000000000000000000..d75a1a7a51e8173e30d9d63c6fe6b48753ce7867
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/volumeFilter.H
@@ -0,0 +1,162 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::runTimePostPro::volumeFilter
+
+Description
+    Visualisation of OpenFOAM volume fields as surface data using
+    a VTK filter cascade.
+
+Note
+    Since this filter includes an OpenFOAM/VTK adaptor level,
+    it is ill-suited to mismatches in data parallelization.
+    If OpenFOAM is running in parallel but VTK is not, it would be rather
+    expensive to collect all the data on the master node for this filter.
+    That approach is acceptable for smaller amounts of data, but do not
+    allow for volume meshes.
+
+SourceFiles
+    volumeFilter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_runTimePostPro_volumeFilter_H
+#define functionObjects_runTimePostPro_volumeFilter_H
+
+#include "runTimePostProcessing.H"
+#include "surface.H"
+#include "foamVtkVtuAdaptor.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+class vtkDataSet;
+class vtkMultiPieceDataSet;
+template<class T> class vtkSmartPointer;
+
+namespace Foam
+{
+namespace vtk
+{
+class vtuAdaptor;
+}
+
+namespace functionObjects
+{
+namespace runTimePostPro
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class volumeFilter Declaration
+\*---------------------------------------------------------------------------*/
+
+class volumeFilter
+:
+    public surface
+{
+protected:
+
+    // Protected Member Functions
+
+        //- Return a vtu mesh with addressing information stored in adaptor
+        vtkSmartPointer<vtkMultiPieceDataSet> mesh
+        (
+            Foam::vtk::vtuAdaptor& adaptor
+        ) const;
+
+
+        bool addDimField
+        (
+            vtkDataSet* piece,
+            const vtk::vtuAdaptor& adaptor,
+            const regIOobject* ioptr,
+            const word& fieldName
+        ) const;
+
+        int addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const vtk::vtuAdaptor& adaptor,
+            const regIOobject* ioptr,
+            const word& fieldName
+        ) const;
+
+        template<class Type>
+        bool addDimField
+        (
+            vtkDataSet* piece,
+            const vtk::vtuAdaptor& adaptor,
+            const regIOobject* ioptr,
+            const word& fieldName
+        ) const;
+
+        template<class Type>
+        int addDimField
+        (
+            vtkMultiPieceDataSet* multiPiece,
+            const vtk::vtuAdaptor& adaptor,
+            const regIOobject* ioptr,
+            const word& fieldName
+        ) const;
+
+
+        //- No copy construct
+        volumeFilter(const volumeFilter&) = delete;
+
+        //- No copy assignment
+        void operator=(const volumeFilter&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct from dictionary
+        volumeFilter
+        (
+            const runTimePostProcessing& parent,
+            const dictionary& dict,
+            const HashPtrTable<Function1<vector>>& colours
+        );
+
+
+    //- Destructor
+    virtual ~volumeFilter() = default;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace runTimePostPro
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "volumeFilterTemplates.C"
+#endif
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/graphics/runTimePostProcessing/volumeFilterTemplates.C b/src/functionObjects/graphics/runTimePostProcessing/volumeFilterTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..5d5c6594c4d1cc7edca7fdbb3670174ff264f5ae
--- /dev/null
+++ b/src/functionObjects/graphics/runTimePostProcessing/volumeFilterTemplates.C
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// OpenFOAM includes
+#include "volumeFilter.H"
+#include "fvMesh.H"
+#include "volFields.H"
+#include "foamVtkTools.H"
+
+// VTK includes
+#include "vtkCellData.h"
+#include "vtkMultiPieceDataSet.h"
+#include "vtkPointData.h"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+bool Foam::functionObjects::runTimePostPro::volumeFilter::addDimField
+(
+    vtkDataSet* piece,
+    const vtk::vtuAdaptor& adaptor,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    vtkSmartPointer<vtkFloatArray> vtkdata;
+
+    const auto* dimptr =
+        dynamic_cast<const DimensionedField<Type, volMesh>*>(ioptr);
+
+    if (dimptr && !vtkdata)
+    {
+        vtkdata = adaptor.convertField(*dimptr);
+    }
+
+    const auto* volptr =
+        dynamic_cast<const GeometricField<Type, fvPatchField, volMesh>*>(ioptr);
+
+    if (volptr && !vtkdata)
+    {
+        vtkdata = adaptor.convertField(volptr->internalField());
+    }
+
+    if (vtkdata)
+    {
+        piece->GetCellData()->AddArray(vtkdata);
+        return true;
+    }
+
+    return false;
+}
+
+
+template<class Type>
+int Foam::functionObjects::runTimePostPro::volumeFilter::addDimField
+(
+    vtkMultiPieceDataSet* multiPiece,
+    const vtk::vtuAdaptor& adaptor,
+    const regIOobject* ioptr,
+    const word& fieldName
+) const
+{
+    if (!multiPiece)
+    {
+        return 0;
+    }
+
+    const int nCmpt(pTraits<Type>::nComponents);
+
+    if (!needsCollective())
+    {
+        // Simple case (serial-serial, parallel-parallel)
+
+        auto piece = multiPiece->GetPiece(Pstream::myProcNo());
+
+        if
+        (
+            addDimField<Type>
+            (
+                piece,
+                adaptor,
+                ioptr,
+                fieldName
+            )
+        )
+        {
+            return nCmpt;
+        }
+    }
+    else
+    {
+    }
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/controlDict b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/controlDict
index 2ca0331bc892109b37f4a5b9044e0dc2bbf87c35..f7779d15ceb72d14c8e9244bd2ae9ea41a6b8093 100644
--- a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/controlDict
+++ b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/controlDict
@@ -53,9 +53,8 @@ maxCo           0.2;
 
 functions
 {
-    #include "sampling"
-    #include "streamLines"
-    #include "runTimePostProcessing"
+    // Demonstrate runTimePostProcessing
+    #include "visualization"
 }
 
 // ************************************************************************* //
diff --git a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/runTimePostProcessing b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/runTimePostProcessing
index 3e9f362b168bd9290a5b37f79ae3c1f17893a67e..321960381f2153e7f7f15d033d403cd0bfe8e731 100644
--- a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/runTimePostProcessing
+++ b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/runTimePostProcessing
@@ -8,15 +8,21 @@
 
 postPro1
 {
-    type            runTimePostProcessing;
-    libs            ("librunTimePostProcessing.so");
-    writeControl    writeTime;
+    #includeEtc "caseDicts/postProcessing/visualization/runTimePostPro.cfg"
+
+    // Time control etc
+    ${_visualization};
+
+    // debug       true;
+    // parallel    true;
+
     output
     {
         name        image;
         width       800;
         height      600;
     }
+
     camera
     {
         // If camera is moving, optionally provide start and end times
@@ -42,14 +48,10 @@ postPro1
     // they are locally overridden
     colours
     {
-        background      (0.317647 0.341176 0.431373);
-        background2     ${background};
-        text            (0.75 0.75 0.75);
-        edge            (1 0 0);
-        surface         (0.5 0.5 0.5);
-        line            (1 0 0);
+        ${..colourScheme.paraview};
     }
 
+    // Line data
     lines
     {
         streamline
@@ -70,11 +72,11 @@ postPro1
         }
     }
 
-    _plane
+    // Surface data
+    _surface
     {
         type            functionObjectSurface;
-        functionObject  cuttingPlane;
-        colourMap       blueWhiteRed;
+        colourMap       coolToWarm;
         representation  glyph;
         maxGlyphLength  0.1;
         visible         yes;
@@ -83,13 +85,8 @@ postPro1
         field           U;
         range           (0 10);
         opacity         1;
-        scalarBar
-        {
-            visible     no;
-        }
     }
 
-
     surfaces
     {
         geom
@@ -102,26 +99,51 @@ postPro1
             visible         yes;
             featureEdges    no;
             opacity         0.8;
+            visible         false;
         }
-        plane0
+
+        patches
         {
-            ${_plane};
-            functionObject  plane0;
+            type            patches;
+            patches         (hole);
+            renderMode      phong;
+            representation  surface;
+            colourMap       coolToWarm;
+            colourBy        field;
+            field           U;
+            range           (0 10);
+            nearCellValue   true;
+            smooth          true;
         }
-        plane1
+
+        plane0
         {
-            ${_plane};
-            functionObject  plane1;
+            ${_surface};
+            functionObject  planes.plane0;
         }
-        plane2
+        plane1
         {
-            ${_plane};
-            functionObject  plane2;
+            ${_surface};
+            functionObject  planes.plane1;
         }
-        plane3
+
+        cutting
         {
-            ${_plane};
-            functionObject  plane3;
+            // Same colours and scaling as surface
+            ${_surface};
+
+            type            plane;
+            planeType       pointAndNormal;
+
+            pointAndNormalDict
+            {
+                point   (0 0 0);
+                normal  (1 0 0);
+            }
+
+            offsets         (0.1 0.2 0.3 0.4 0.5);
+
+            colourMap       coolToWarm;
         }
     }
 
diff --git a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/sampling b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/sampling
index cf6bc155bef10da90cf5221984af725fb34abca3..d54c13cda48554b92b71e7d70d67103ecc59c3b3 100644
--- a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/sampling
+++ b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/sampling
@@ -6,94 +6,58 @@
 |    \\/     M anipulation  |                                                 |
 \*---------------------------------------------------------------------------*/
 
-plane0
+planes
 {
     type            surfaces;
     libs            ("libsampling.so");
-    writeControl    writeTime;
 
-    surfaceFormat   vtk;
+        // Time control etc
+    ${_visualization};
+
     fields          ( p U );
 
-    interpolationScheme cellPoint;
+    // surfaceFormat   vtk;
+    surfaceFormat   none;
 
-    surfaces
-    (
-        plane0
-        {
-            type            cuttingPlane;
-            planeType       pointAndNormal;
-            interpolate     true;
-            pointAndNormalDict
-            {
-                point   (0 0 0);
-                normal  (1 0 0);
-            }
-        }
-    );
-}
+    store           true;
 
+    interpolationScheme cellPoint;
 
-plane1
-{
-    ${plane0}
+    _plane
+    {
+        type        plane; //cuttingPlane;
+        planeType   pointAndNormal;
+        interpolate false;
 
-    surfaces
-    (
-        plane1
+        pointAndNormalDict
         {
-            type            cuttingPlane;
-            planeType       pointAndNormal;
-            interpolate     true;
-            pointAndNormalDict
-            {
-                point   (0.1 0 0);
-                normal  (1 0 0);
-            }
+            point   (0 0 0);
+            normal  (1 0 0);
         }
-    );
-}
-
-
-plane2
-{
-    ${plane0}
+    }
 
     surfaces
-    (
-        plane2
+    {
+        plane0
         {
-            type            cuttingPlane;
-            planeType       pointAndNormal;
-            interpolate     true;
+            ${_plane}
             pointAndNormalDict
             {
-                point   (0.2 0 0);
-                normal  (1 0 0);
+                point   (0 0 0);
             }
         }
-    );
-}
-
-
-plane3
-{
-    ${plane0}
 
-    surfaces
-    (
-        plane3
+        plane1
         {
-            type            cuttingPlane;
-            planeType       pointAndNormal;
-            interpolate     true;
+            ${_plane}
             pointAndNormalDict
             {
-                point   (0.3 0 0);
-                normal  (1 0 0);
+                point   (-0.1 0 0);
             }
         }
-    );
+    }
+
+    #remove _plane
 }
 
 
diff --git a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/streamLines b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/streamLines
index 86dd7663bf61be5a4acda8455c3c54b9c5ae56a3..3aeb39e409c2bbbdcbd3894946c84ae0c15a46a0 100644
--- a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/streamLines
+++ b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/streamLines
@@ -10,7 +10,8 @@ streamLines
 {
     type    streamLine;
 
-    writeControl    writeTime;
+    // Time control etc
+    ${_visualization};
 
     setFormat       vtk;
 
diff --git a/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/visualization b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/visualization
new file mode 100644
index 0000000000000000000000000000000000000000..fb1739f4359a0ef512fe16b38b63bd25fb1d2792
--- /dev/null
+++ b/tutorials/incompressible/pimpleFoam/RAS/ellipsekkLOmega/system/visualization
@@ -0,0 +1,29 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+// Demonstrate runTimePostProcessing
+
+// Timing
+_visualization
+{
+    writeControl    writeTime;
+
+    // enabled         true;
+
+    // Debug: writeControl    timeStep;
+    // Debug: writeInterval   4;
+}
+
+
+#include "streamLines"
+#include "sampling"
+#include "runTimePostProcessing"
+
+#remove _visualization
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/controlDict b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/controlDict
index e302d38c3333025524cf948da44605c0801ae99a..0d11850807f7da587405452ab9d4ce886597830a 100644
--- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/controlDict
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/controlDict
@@ -49,10 +49,8 @@ runTimeModifiable true;
 
 functions
 {
-    // Demonstrate runtime postprocessing
-    #include "streamLines"
-    #include "cuttingPlane"
-    #include "runTimePostProcessing"
+    // Demonstrate runTimePostProcessing
+    #include "visualization"
 
     // Do some other functionObjects
     #include "forceCoeffs"
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/runTimePostProcessing b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/runTimePostProcessing
index 7f4a0e9f96f0bad47f9ba0030ea338edb76e64cd..1f4f0cae237af30ca8cbc515edbaa9b9b887fae0 100644
--- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/runTimePostProcessing
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/runTimePostProcessing
@@ -1,14 +1,34 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
 postPro1
 {
-    type            runTimePostProcessing;
-    libs            ("librunTimePostProcessing.so");
-    writeControl    writeTime;
+    #includeEtc "caseDicts/postProcessing/visualization/runTimePostPro.cfg"
+
+    // Time control etc
+    ${_visualization};
+
+    // debug       true;
+    // parallel    true;
+
     output
     {
         name            image;
         width           2000;
         height          1200;
     }
+
+    // Default colours. Used for colourBy == colour unless locally overridden
+    colours
+    {
+        ${..colourScheme.blueGradient};
+    }
+
     camera
     {
         // If camera is moving, optionally provide start and end times
@@ -19,27 +39,15 @@ postPro1
         nFrameTotal         1;
 
         // Parallel projection flag
-        parallelProjection  no;
+        parallelProjection  off;
 
         clipBox         (-0.2 -0.2 0)(1.65 0.2 1.25); // optional
 
         focalPoint      (1.2 1.1 0.2);
-        up              (0 0 1);
         position        (3.6 5.1 -1.3);
+        up              (0 0 1);
     }
 
-    // Default colours
-    // - If select to colourBy colour, these values are used unless
-    // they are locally overridden
-    colours
-    {
-        background      (1 1 1);
-        background2     (0 0 1);
-        text            (0 0 0);
-        edge            (1 0 0);
-        surface         (0.5 0.5 0.5);
-        line            (1 0 0);
-    }
     // Line data
     lines
     {
@@ -47,8 +55,9 @@ postPro1
         {
             type            functionObjectLine;
             functionObject  streamLines;
+            colourMap       rainbow;
             representation  tube;
-            visible         yes;
+            visible         true;
             tubeRadius      0.01;
             colourBy        field;
             field           U;
@@ -56,13 +65,17 @@ postPro1
             opacity         1;
             scalarBar
             {
-                visible         yes;
+                visible         true;
                 position        (0.8 0.1);
                 vertical        yes;
                 fontSize        16;
-                title           "velocity / [m/s]";
+                title           "velocity [m/s]";
                 labelFormat     "%6.2f";
                 numberOfLabels  5;
+                bold            true;
+                italic          true;
+                italic          true;
+                colour          (0.75 0.75 0.75);
             }
         }
     }
@@ -77,27 +90,40 @@ postPro1
             renderMode      phong;
             representation  surface;
             edgeColour      (0 0 0);
-            visible         yes;
             featureEdges    yes;
             opacity         1;
+            visible         false;
         }
-        cuttingPlane1
+
+        patches
+        {
+            type            patches;
+            patches         ("motorBike.*");
+            renderMode      phong;
+            representation  surface;
+            colourMap       coolToWarm;
+            colourBy        field;
+            field           U;
+            range           (0 30);
+            nearCellValue   true;
+            smooth          true;
+            featureEdges    yes;
+            edgeColour      (0 0 0);
+        }
+
+        plane1
         {
             type            functionObjectSurface;
-            functionObject  cuttingPlane;
-            colourMap       blueWhiteRed;
+            functionObject  samples.yNormal;
+            colourMap       coolToWarm;
             representation  glyph;
             maxGlyphLength  0.1;
-            visible         yes;
+            visible         true;
             featureEdges    no;
             colourBy        field;
             field           U;
             range           (0 30);
             opacity         1;
-            scalarBar
-            {
-                visible         no;
-            }
         }
     }
 
@@ -106,11 +132,14 @@ postPro1
     {
         text1
         {
-            string          "Motorbike";
-            position        (0.1 0.05);
-            size            72;
-            bold            yes;
-            visible         yes;
+            visible     true;
+            string      "Motorbike";
+            position    (0.1 0.05);
+            size        72;
+            bold        true;
+            italic      true;
         }
     }
 }
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/cuttingPlane b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/samples
similarity index 85%
rename from tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/cuttingPlane
rename to tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/samples
index dc4e254faf2b3048fc719eacb4bad67dd63d4b47..6731e79021aa84c5b00dcea77ede04ec52dee7a0 100644
--- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/cuttingPlane
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/samples
@@ -6,31 +6,38 @@
 |    \\/     M anipulation  |                                                 |
 \*---------------------------------------------------------------------------*/
 
-cuttingPlane
+samples
 {
     type            surfaces;
     libs            ("libsampling.so");
-    writeControl    writeTime;
 
-    surfaceFormat   vtk;
+    // Time control etc
+    ${_visualization};
+
     fields          (p U);
 
+    // surfaceFormat   vtk;
+    surfaceFormat   none;
+
+    verbose         false;
+
     interpolationScheme cellPoint;
 
     surfaces
-    (
+    {
         yNormal
         {
             type            cuttingPlane;
             planeType       pointAndNormal;
+            interpolate     true;
+            store           true;
             pointAndNormalDict
             {
                 point   (0 0 0);
                 normal  (0 1 0);
             }
-            interpolate     true;
         }
-    );
+    }
 }
 
 
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/stabilizationSchemes b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/stabilizationSchemes
index d8a71b89ef6514f34c5e674df33d3ecc23d1f921..6285f30aecedc1687e6124ed0540e31b9084d582 100644
--- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/stabilizationSchemes
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/stabilizationSchemes
@@ -8,7 +8,7 @@
 
 residuals
 {
-    type            residuals;
+    type            solverInfo;
     libs            ("libutilityFunctionObjects.so");
     writeFields     true;
     writeControl    outputTime;
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/streamLines b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/streamLines
index 45983df17e78d861ed33b7ebc0ac2aa726d5defd..e87abf94a0df172b1ed9426cc0bef5f67afbaf2a 100644
--- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/streamLines
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/streamLines
@@ -8,11 +8,10 @@
 
 streamLines
 {
-    type            streamLine;
+    type    streamLine;
 
-    // Output every
-    writeControl    writeTime;
-    // writeInterval 10;
+    // Time control etc
+    ${_visualization};
 
     setFormat       vtk; //gnuplot; //xmgr; //raw; //jplot; //csv; //ensight;
 
diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/visualization b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/visualization
new file mode 100644
index 0000000000000000000000000000000000000000..64ae524306fbad078eb9a14095d5c147fcd1f614
--- /dev/null
+++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/system/visualization
@@ -0,0 +1,30 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+// Demonstrate runTimePostProcessing
+
+// Timing
+_visualization
+{
+    writeControl    writeTime;
+
+    // enabled         true;
+
+    // Debug: writeControl    timeStep;
+    // Debug: writeInterval   10;
+}
+
+
+#include "streamLines"
+#include "samples"
+#include "runTimePostProcessing"
+
+#remove _visualization
+
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/controlDict b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/controlDict
index 65bb7238b98345ba49ed0bb4587ba17af9bb8ae3..f97a4fa1d87920ca56a68ca0ff178192638bed8e 100644
--- a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/controlDict
+++ b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/controlDict
@@ -52,6 +52,9 @@ functions
 {
     #include "ensightWrite"
     #include "vtkWrite"
+
+    // Demonstrate runTimePostProcessing
+    #include "visualization"
 }
 
 
diff --git a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/runTimePostProcessing b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/runTimePostProcessing
new file mode 100644
index 0000000000000000000000000000000000000000..4de8c7fb651475e29183e3b917af40a4859a42fb
--- /dev/null
+++ b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/runTimePostProcessing
@@ -0,0 +1,306 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+postPro1
+{
+    #includeEtc "caseDicts/postProcessing/visualization/runTimePostPro.cfg"
+
+    // Time control etc
+    ${_visualization};
+
+    // debug       true;
+    // parallel    true;
+
+    showIsoSurface false;
+
+    output
+    {
+        name        image;
+        width       1280;
+        height      720;
+    }
+
+    camera
+    {
+        // If camera is moving, optionally provide start and end times
+        // startPosition    0.2;
+        // endPosition      0.75;
+
+        // Total number of frames to generate
+        nFrameTotal     1;
+
+        // Parallel projection flag
+        parallelProjection  yes;
+
+        // clipBox is optional
+
+        position        (385 -560 650);
+        focalPoint      (160 90 60);
+        up              (0.06 0.7 0.7);
+
+        position
+        (
+            -41.95
+            -247.55
+            426.87
+        );
+        focalPoint
+        (
+            146
+            76
+            40
+        );
+        up
+        (
+            0.3646
+            0.6194
+            0.6953
+        );
+
+        zoom    1.5;
+        // clipBox (-10 18 0)(280 160 76);
+        clipBox (-30 0 0)(300 200 80);
+    }
+
+    // Default colours
+    colours
+    {
+        ${..colourScheme.paraview};
+    }
+
+    // Line data
+    lines
+    {
+        streamline
+        {
+            type            functionObjectLine;
+            functionObject  streamLines;
+            colourMap       rainbow;
+            representation  tube;
+            visible         yes;
+            tubeRadius      0.5;
+            colourBy        field;
+            field           U;
+            range           (0 20);
+            opacity         1;
+        }
+    }
+
+    _sampled
+    {
+        type            functionObjectSurface;
+        colourMap       coolToWarm;
+        representation  glyph;
+        representation  surface;
+        maxGlyphLength  0.1;
+        visible         yes;
+        featureEdges    no;
+        colourBy        field;
+        field           U;
+        range           (0 20);
+        opacity         1;
+    }
+
+
+    _velocity_scalarBar
+    {
+        visible         yes;
+        vertical        false;
+        titleHack       false;
+        position        (0.8 0);
+        size            (0.2 0.1);
+
+        fontSize        8;
+        titleSize       24;
+        title           "velocity [m/s]";
+        labelFormat     "%.0f";
+        numberOfLabels  5;
+
+        bold        yes;
+        italic      yes;
+        shadow      yes;
+    }
+
+    surfaces
+    {
+        /*
+        geom
+        {
+            type            geometry;
+            files           ("<case>/buildings.vtp");
+            renderMode      phong;
+            representation  surface;
+            edgeColour      (0.5 0.5 0.5);
+            visible         yes;
+            featureEdges    none;
+            opacity         1.0;
+        }
+        */
+
+        ground1
+        {
+            type            patches;
+            patches         ( ground );
+            nearCellValue   true;
+            colourMap       coolToWarm;
+            representation  glyph;
+
+            // maxGlyphLength  5;
+            maxGlyphLength  0;
+            colourBy        field;
+
+            field           U;
+            range           (0 20);
+        }
+
+        ground2
+        {
+            type            patches;
+            patches         ( ground );
+            nearCellValue   true;
+            smooth          true;
+            colourMap       coolToWarm;
+            representation  wireframe;
+
+            // maxGlyphLength  5;
+            maxGlyphLength  0;
+            visible         yes;
+            colourBy        field;
+
+            field           U;
+            range           (0 20);
+        }
+
+        buildings
+        {
+            type            patches;
+            patches         ( buildings );
+            nearCellValue   true;
+            smooth          true;
+            colourMap       coolToWarm;
+            representation  surface;
+            renderMode      phong;
+
+            colourBy        field;
+            field           U;
+            range           (0 20);
+
+            scalarBar
+            {
+                ${_velocity_scalarBar};
+            }
+        }
+
+        // A cutting plane from sampled surfaces:
+        stored1
+        {
+            ${_sampled};
+            functionObject  planes.plane2;
+            smooth          true;
+        }
+
+        // VTK cutting planes:
+        cutting
+        {
+            type            plane;
+            planeType       pointAndNormal;
+
+            pointAndNormalDict
+            {
+                point   (100 100 50);
+                normal  (1 0 0);
+            }
+
+            offsets         (0 200);
+
+            smooth          true;
+            colourMap       coolToWarm;
+            representation  surface;
+
+            // representation  glyph;
+
+            // maxGlyphLength  5;
+            maxGlyphLength  0;
+            visible         yes;
+            featureEdges    none;
+
+            colourBy        field;
+            colourField     U;
+            field           U;
+            range           (0 20);
+        }
+
+        // VTK iso surfaces of something
+        iso
+        {
+            visible         ${showIsoSurface};
+            type            isoSurface;
+
+            field           U;
+            values          (10);
+
+            smooth          true;
+            colourMap       coolToWarm;
+
+            representation  surface;
+
+            // maxGlyphLength  5;
+            maxGlyphLength  0;
+            featureEdges    none;
+
+            colourBy        field;
+            colourField     p;
+            range           (-120 120);
+        }
+    }
+
+    // Text data
+    text
+    {
+        title
+        {
+            string      "Wind around buildings";
+            position    (0 0.95);
+            size        32;
+            bold        yes;
+            italic      yes;
+            shadow      yes;
+            visible     yes;
+        }
+
+        version
+        {
+            ${title};
+            string      "OpenFOAM ${FOAM_API}";
+            position    (0 0);
+            size        24;
+        }
+
+        watermark
+        {
+            string      "www.openfoam.com";
+            halign      centre;
+            size        24;
+            opacity     0.3;
+            bold        yes;
+            italic      yes;
+            shadow      yes;
+
+            positions
+            (
+                (0.50 0.05) (0.50 0.95)
+                (0.25 0.25) (0.75 0.25)
+                (0.25 0.50) (0.75 0.50)
+                (0.25 0.75) (0.75 0.75)
+            );
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/sampling b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/sampling
new file mode 100644
index 0000000000000000000000000000000000000000..35b69cfcff21af0cd0c235afa25b071d9e7d1b9a
--- /dev/null
+++ b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/sampling
@@ -0,0 +1,71 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+planes
+{
+    type            surfaces;
+    libs            ("libsampling.so");
+
+    // Time control etc
+    ${_visualization};
+
+    fields          ( p U );
+
+    // surfaceFormat   vtk;
+    surfaceFormat   none;
+
+    store           true;
+
+    interpolationScheme cellPoint;
+
+    _plane
+    {
+        type            cuttingPlane;
+        planeType       pointAndNormal;
+        interpolate     false;
+    }
+
+    surfaces
+    {
+        plane0
+        {
+            ${_plane}
+            pointAndNormalDict
+            {
+                point   (100 100 50);
+                normal  (1 -1 0);
+            }
+            enabled     false;
+        }
+
+        plane1
+        {
+            ${_plane}
+            pointAndNormalDict
+            {
+                point   (100 100 50);
+                normal  (1 1 0);
+            }
+        }
+
+        plane2
+        {
+            ${_plane}
+            pointAndNormalDict
+            {
+                point   (200 100 50);
+                normal  (1 0 0);
+            }
+        }
+    };
+
+    #remove _plane
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/streamLines b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/streamLines
new file mode 100644
index 0000000000000000000000000000000000000000..9b3a494454acc1e0067d440598ceeafb63080c65
--- /dev/null
+++ b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/streamLines
@@ -0,0 +1,52 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+streamLines
+{
+    type  streamLine;
+
+    libs  ("libfieldFunctionObjects.so");
+
+    // Time control etc
+    ${_visualization};
+
+    setFormat       vtk;
+
+    // Velocity field to use for tracking.
+    U               U;
+
+    // Tracked forwards (+U) or backwards (-U)
+    trackForward    true;
+
+    // Names of fields to sample. Should contain above velocity field!
+    fields          (p U);
+
+    // Steps particles can travel before being removed
+    lifeTime        10000;
+
+    // Number of steps per cell (estimate). Set to 1 to disable subcycling.
+    nSubCycle       5;
+
+    // Cloud name to use
+    cloud           particleTracks;
+
+    // Seeding method.
+    seedSampleSet
+    {
+        type        uniform;
+        axis        x;  //distance;
+
+        // Note: tracks slightly offset so as not to be on a face
+        start       (0 -20 15);
+        end         (0 150 15);
+        nPoints     40;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/simpleFoam/windAroundBuildings/system/visualization b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/visualization
new file mode 100644
index 0000000000000000000000000000000000000000..ca867f071b84ccdcb9d16e3db7dc2a1014ced56e
--- /dev/null
+++ b/tutorials/incompressible/simpleFoam/windAroundBuildings/system/visualization
@@ -0,0 +1,29 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+// Demonstrate runTimePostProcessing
+
+// Timing
+_visualization
+{
+    writeControl    writeTime;
+
+    // enabled         true;
+
+    // Debug: writeControl    timeStep;
+    // Debug: writeInterval   10;
+}
+
+
+#include "streamLines"
+#include "sampling"
+#include "runTimePostProcessing"
+
+#remove _visualization
+
+// ************************************************************************* //
diff --git a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
index e38412357f2201fb1b5094f8ad4ac052bcf77f74..bd195eb50f06ed9e32716013692f2659158f2e8e 100644
--- a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
+++ b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
@@ -53,8 +53,9 @@ maxDeltaT       1;
 
 functions
 {
+    #include "dataCloud"
     #include "vtkCloud"
-    // #include "dataCloud"
+
     #include "runTimePostProcessing"
 }
 
diff --git a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/runTimePostProcessing b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/runTimePostProcessing
index 73d6b3533bb6a2ec069dcaf7569e69498e763cf6..9a4a0f382a5f1c6295562a27e92047e994ba6d48 100644
--- a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/runTimePostProcessing
+++ b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/runTimePostProcessing
@@ -2,9 +2,11 @@
 
 postPro1
 {
-    type            runTimePostProcessing;
-    libs            ("librunTimePostProcessing.so");
-    writeControl    writeTime;
+    #includeEtc "caseDicts/postProcessing/visualization/runTimePostPro.cfg"
+
+    // parallel    false;
+    // debug  true;
+
     output
     {
         name        image;
@@ -23,10 +25,10 @@ postPro1
 
         parallelProjection  yes;
 
-        focalPoint  (0.251 0.415 0.05);
-        position    (0.251 0.415 2.218);
+        focalPoint  (0.25 0.42 0.05);
+        position    (0.15 0.42 2.5);
         up          (0 1 0);
-        zoom        1;
+        zoom        1.5;
     }
 
     // Default colours
@@ -34,13 +36,7 @@ postPro1
     // they are locally overridden
     colours
     {
-        background  (0.5 0.5 0.5);
-        background2 (0.7 0.7 0.7);
-        text        (1 1 1);
-        edge        (1 0 0);
-        surface     (0.5 0.5 0.5);
-        line        (1 0 0);
-        point       (0.5 0.5 0.5);
+        ${..colourScheme.greyGradient};
     }
 
     // Points (cloud) data
@@ -48,55 +44,69 @@ postPro1
     {
         cloud1
         {
-            type            functionObjectCloud;
-            functionObject  cloudWrite1;
+            type            cloud;
             cloud           coalCloud1;
-            colourMap       blueWhiteRed;
+            colourMap       coolToWarm;
             representation  sphere;
-            maxGlyphLength  0.05;
-            visible         yes;
+            maxGlyphLength  0.025;
             featureEdges    no;
             colourBy        field;
             colourField     T;
             field           T;
             range           (290 410);
             opacity         1;
+
             scalarBar
             {
-                visible         yes;
+                visible         true;
                 vertical        yes;
                 position        (0.8 0.1);
                 fontSize        12;
+                titleSize       24;
                 title           "Temperature [K]";
                 labelFormat     "%.0f";
-                numberOfLabels  8;
+                numberOfLabels  9;
+                bold            false;
             }
         }
     }
 
-/* Future...
+    // Surface data
     surfaces
     {
-        container
+        patches
         {
             type            patches;
             patches         (".*");
             renderMode      phong;
             representation  surface;
-            edgeColour      (0 0 0);
-            visible         yes;
+            colourBy        colour;
+            field           U;
+            range           (0 40);
             featureEdges    yes;
-            opacity         0.25;
+            edgeColour      (0 0 0);
+            // Fails in parallel:  opacity         0.25;
+        }
+
+        k
+        {
+            type            isoSurface;
+            renderMode      phong;
+            representation  surface;
+            field           k;
+            values          ( 100 );
+            colourField     T;
+            colourBy        field;
+            range           (290 410);
+            smooth          true;
         }
     }
-*/
 
     // Text data
     text
     {
         text1
         {
-            visible     yes;
             string      "simplifiedSiwek";
             position    (0.1 0.05);
             size        24;