diff --git a/src/catalyst/areaMesh/catalystFaMesh.C b/src/catalyst/areaMesh/catalystFaMesh.C
index 8ff3e1129027b00f337c97dd1a4c7a35a9dfa97b..78142d006248432457e115d54281d607ff247d04 100644
--- a/src/catalyst/areaMesh/catalystFaMesh.C
+++ b/src/catalyst/areaMesh/catalystFaMesh.C
@@ -62,13 +62,17 @@ void Foam::catalyst::faMeshInput::update()
 
     forAllConstIters(meshes_, iter)
     {
-        if (!backends_.found(iter.key()))
+        const word& areaName(iter.key());
+
+        if (!backends_.found(areaName))
         {
-            backends_.set
-            (
-                iter.key(),
-                new Foam::vtk::faMeshAdaptor(*(iter.object()))
-            );
+            auto backend =
+                autoPtr<Foam::vtk::faMeshAdaptor>::New(*(iter.object()));
+
+            // Apply any configuration options
+            // ...
+
+            backends_.set(areaName, backend);
         }
     }
 }
@@ -215,7 +219,6 @@ bool Foam::catalyst::faMeshInput::convert
     {
         auto dataset = backends_[areaName]->output(selectFields_);
 
-
         // Existing or new
         vtkSmartPointer<vtkMultiBlockDataSet> block =
             outputs.lookup
diff --git a/src/catalyst/catalystDict b/src/catalyst/catalystDict
index 463d01c39f16647f72b4c0c889420da3b8753f2c..575a8b38cd85e24e95d79db6b0c65b583e002235 100644
--- a/src/catalyst/catalystDict
+++ b/src/catalyst/catalystDict
@@ -38,6 +38,9 @@ catalyst
             // Emit internal mesh/fields
             internal true;
 
+            // Restrict boundary conversion to specific patches
+            patches (walls "*.top");
+
             // Selected fields (words or regex)
             fields  (T U p);
         }
diff --git a/src/catalyst/cloud/catalystCloud.C b/src/catalyst/cloud/catalystCloud.C
index eeb49be9f6da0d6def5dfc33b7f30de9e1d8cd9d..8515025208e9726025eeca038a8ca304b447aa34 100644
--- a/src/catalyst/cloud/catalystCloud.C
+++ b/src/catalyst/cloud/catalystCloud.C
@@ -139,7 +139,6 @@ bool Foam::catalyst::cloudInput::convert
         auto dataset =
             vtk::cloudAdaptor(fvm).getCloud(cloudName, selectFields_);
 
-
         // Existing or new
         vtkSmartPointer<vtkMultiBlockDataSet> block =
             outputs.lookup
diff --git a/src/catalyst/volMesh/catalystFvMesh.C b/src/catalyst/volMesh/catalystFvMesh.C
index deaba5d07d77f195e6b1015db379fc800c0f239d..9f97730e93fc37b49ebdd413193815c441f18c34 100644
--- a/src/catalyst/volMesh/catalystFvMesh.C
+++ b/src/catalyst/volMesh/catalystFvMesh.C
@@ -60,13 +60,22 @@ void Foam::catalyst::fvMeshInput::update()
 
     forAllConstIters(meshes_, iter)
     {
-        if (!backends_.found(iter.key()))
+        const word& regionName = iter.key();
+
+        if (!backends_.found(regionName))
         {
-            backends_.set
-            (
-                iter.key(),
-                new Foam::vtk::fvMeshAdaptor(*(iter.object()), decomposeOpt_)
-            );
+            auto backend =
+                autoPtr<Foam::vtk::fvMeshAdaptor>::New
+                (
+                    *(iter.object()),
+                    channelOpt_,
+                    selectPatches_
+                );
+
+            // Special polyhedral treatment?
+            backend->setDecompose(decomposeOpt_);
+
+            backends_.set(regionName, backend);
         }
     }
 }
@@ -86,6 +95,7 @@ Foam::catalyst::fvMeshInput::fvMeshInput
     channelOpt_(channelType::DEFAULT),
     decomposeOpt_(false),
     selectRegions_(),
+    selectPatches_(),
     selectFields_(),
     meshes_(),
     backends_()
@@ -102,8 +112,9 @@ bool Foam::catalyst::fvMeshInput::read(const dictionary& dict)
 
     meshes_.clear();
     backends_.clear();
-    selectFields_.clear();
     selectRegions_.clear();
+    selectPatches_.clear();
+    selectFields_.clear();
     decomposeOpt_ = dict.lookupOrDefault("decompose", false);
 
     unsigned selected(channelType::NONE);
@@ -132,6 +143,7 @@ bool Foam::catalyst::fvMeshInput::read(const dictionary& dict)
     // All possible meshes
     meshes_ = time_.lookupClass<fvMesh>();
 
+    dict.readIfPresent("patches", selectPatches_);
     dict.readIfPresent("regions", selectRegions_);
 
     if (selectRegions_.empty())
@@ -226,12 +238,8 @@ bool Foam::catalyst::fvMeshInput::convert
 
     for (const word& regionName : regionNames)
     {
-        // Define/redefine output channels (caching)
-        backends_[regionName]->channels(channelOpt_);
-
         auto dataset = backends_[regionName]->output(selectFields_);
 
-
         // Existing or new
         vtkSmartPointer<vtkMultiBlockDataSet> block =
             outputs.lookup
diff --git a/src/catalyst/volMesh/catalystFvMesh.H b/src/catalyst/volMesh/catalystFvMesh.H
index b6ab20e8cb88cf96d6d227be1fb4aaa64dcb896b..e9eb87363655661f0ee3ed1f728c86a6a2036980 100644
--- a/src/catalyst/volMesh/catalystFvMesh.H
+++ b/src/catalyst/volMesh/catalystFvMesh.H
@@ -50,6 +50,7 @@ Usage
         type        | input type: \c default                | no    | default
         region      | name for a single region              | no    | region0
         regions     | wordRe list of regions                | no    |
+        patches     | explicit wordRe list of patches       | no    |
         fields      | wordRe list of fields                 | yes   |
         boundary    | convert boundary fields               | no    | true
         internal    | convert internal fields               | no    | true
@@ -83,6 +84,10 @@ Usage
 
 Note
     The channel name is that of the defining dictionary.
+    If the \c patches entry is missing or an empty list,
+    all non-processor patches will be used for the boundary.
+    When it is non-empty, only the explicitly specified (non-processor)
+    patch names will be used.
 
 See also
     Foam::vtk::fvMeshAdaptor
@@ -135,6 +140,9 @@ protected:
         //- Requested names of regions to process
         wordRes selectRegions_;
 
+        //- Requested names of patches to process
+        wordRes selectPatches_;
+
         //- Names of fields to process
         wordRes selectFields_;
 
diff --git a/src/catalyst/volMesh/foamVtkFvMeshAdaptor.C b/src/catalyst/volMesh/foamVtkFvMeshAdaptor.C
index 1fc8b5a4f90962e2ab65088ebff82513861aacb3..c60e4519de0a55bd7b9461de24f3a1da22588fa2 100644
--- a/src/catalyst/volMesh/foamVtkFvMeshAdaptor.C
+++ b/src/catalyst/volMesh/foamVtkFvMeshAdaptor.C
@@ -59,26 +59,69 @@ Foam::vtk::fvMeshAdaptor::channelNames
 };
 
 
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::vtk::fvMeshAdaptor::definePatchIds()
+{
+    // Generate or update the list of patchIds
+
+    patchIds_.clear();
+
+    if (!usingBoundary())
+    {
+        return;
+    }
+
+    // General patch information
+    // Restrict to non-processor patches.
+    // This value is invariant across all processors.
+
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+    const label nNonProcessor = patches.nNonProcessor();
+
+    if (patchPatterns_.empty())
+    {
+        patchIds_ = identity(nNonProcessor);
+    }
+    else
+    {
+        labelHashSet ids(patches.patchSet(patchPatterns_, false, false));
+
+        // Restricted to non-processor patches
+        ids.filterKeys
+        (
+            [nNonProcessor](const label i){ return i < nNonProcessor; }
+        );
+
+        // MUST be sorted. Other internal logic relies upon this!
+        patchIds_ = ids.sortedToc();
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::vtk::fvMeshAdaptor::fvMeshAdaptor
 (
     const fvMesh& mesh,
-    const bool decompose
+    const channelType channelsOpt,
+    const wordRes& patchSelection
 )
 :
     mesh_(mesh),
-    channels_(ALL),
+    patchPatterns_(patchSelection),
+    patchIds_(),
+    channels_(channelsOpt),
     interpFields_(true),
     extrapPatches_(false),
-    decomposePoly_(decompose),
+    decomposePoly_(false),
     meshState_(polyMesh::TOPO_CHANGE)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-void Foam::vtk::fvMeshAdaptor::channels(const wordList& chanNames)
+void Foam::vtk::fvMeshAdaptor::setChannels(const wordList& chanNames)
 {
     unsigned chanIds = 0;
     for (const word& chan : chanNames)
@@ -89,11 +132,11 @@ void Foam::vtk::fvMeshAdaptor::channels(const wordList& chanNames)
         }
     }
 
-    channels(chanIds);
+    setChannels(chanIds);
 }
 
 
-void Foam::vtk::fvMeshAdaptor::channels(enum channelType chanIds)
+void Foam::vtk::fvMeshAdaptor::setChannels(enum channelType chanIds)
 {
     channels_ = chanIds;
 
@@ -105,11 +148,12 @@ void Foam::vtk::fvMeshAdaptor::channels(enum channelType chanIds)
     if (!usingBoundary())
     {
         cachedVtp_.clear();
+        patchIds_.clear();
     }
 }
 
 
-void Foam::vtk::fvMeshAdaptor::channels(unsigned chanIds)
+void Foam::vtk::fvMeshAdaptor::setChannels(unsigned chanIds)
 {
     channels_ = (chanIds & 0x3);
 
@@ -121,6 +165,17 @@ void Foam::vtk::fvMeshAdaptor::channels(unsigned chanIds)
     if (!usingBoundary())
     {
         cachedVtp_.clear();
+        patchIds_.clear();
+    }
+}
+
+
+void Foam::vtk::fvMeshAdaptor::setDecompose(const bool val)
+{
+    if (usingInternal() && val != decomposePoly_)
+    {
+        cachedVtu_.clear();
+        decomposePoly_ = val;
     }
 }
 
@@ -143,17 +198,9 @@ bool Foam::vtk::fvMeshAdaptor::usingBoundary() const
 }
 
 
-Foam::label Foam::vtk::fvMeshAdaptor::nPatches() const
+const Foam::labelList& Foam::vtk::fvMeshAdaptor::patchIds() const
 {
-    // Restrict to non-processor patches.
-    // This value is invariant across all processors.
-
-    if (usingBoundary())
-    {
-        return mesh_.boundaryMesh().nNonProcessor();
-    }
-
-    return 0;
+    return patchIds_;
 }
 
 
@@ -161,6 +208,14 @@ void Foam::vtk::fvMeshAdaptor::updateContent(const wordRes& selectFields)
 {
     const bool oldDecomp = decomposePoly_;
 
+    // General patch information
+    // Restrict to non-processor patches.
+    // This value is invariant across all processors.
+
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+    const label nNonProcessor = patches.nNonProcessor();
+
+
     // Update cached, saved, unneed values.
 
     HashSet<string> nowActive;
@@ -171,18 +226,15 @@ void Foam::vtk::fvMeshAdaptor::updateContent(const wordRes& selectFields)
         nowActive.insert(internalName());
     }
 
-    // BOUNDARY
-
-    // Restrict to non-processor patches.
-    // This value is invariant across all processors.
-
-    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
-    const label npatches = this->nPatches();
 
-    for (label patchId=0; patchId < npatches; ++patchId)
+    // BOUNDARY
+    if (usingBoundary())
     {
-        const polyPatch& pp = patches[patchId];
-        nowActive.insert(pp.name());
+        for (label patchId=0; patchId < nNonProcessor; ++patchId)
+        {
+            const polyPatch& pp = patches[patchId];
+            nowActive.insert(pp.name());
+        }
     }
 
     // Dispose of unneeded components
@@ -206,6 +258,8 @@ void Foam::vtk::fvMeshAdaptor::updateContent(const wordRes& selectFields)
             iter.object().clearGeom();
             iter.object().clear();
         }
+
+        definePatchIds();
     }
     else if (oldDecomp != decomposePoly_)
     {
@@ -281,8 +335,7 @@ Foam::vtk::fvMeshAdaptor::output(const wordRes& select)
     }
 
     // BOUNDARY
-    const label npatches = this->nPatches();
-    if (npatches)
+    if (!patchIds_.empty())
     {
         unsigned int subBlockNo = 0;
 
@@ -290,7 +343,7 @@ Foam::vtk::fvMeshAdaptor::output(const wordRes& select)
 
         const polyBoundaryMesh& patches = mesh_.boundaryMesh();
 
-        for (label patchId=0; patchId < npatches; ++patchId)
+        for (const label patchId : patchIds_)
         {
             const polyPatch& pp = patches[patchId];
             const word& longName = pp.name();
diff --git a/src/catalyst/volMesh/foamVtkFvMeshAdaptor.H b/src/catalyst/volMesh/foamVtkFvMeshAdaptor.H
index a5e22db8959520746fc32c32e67b9fdd433ef598..b00e944f49b27c4a807b66a73c639644c15fb311 100644
--- a/src/catalyst/volMesh/foamVtkFvMeshAdaptor.H
+++ b/src/catalyst/volMesh/foamVtkFvMeshAdaptor.H
@@ -170,6 +170,12 @@ private:
         //- OpenFOAM mesh
         const fvMesh& mesh_;
 
+        //- In non-empty, restrict to these selected patches only
+        wordRes patchPatterns_;
+
+        //- Cached values for selected patches. *Always* in sorted order.
+        labelList patchIds_;
+
         //- Selected output channels
         unsigned channels_;
 
@@ -195,6 +201,9 @@ private:
 
     // Mesh Conversion
 
+        //- Define patch ids
+        void definePatchIds();
+
         //- Convert internal
         void convertGeometryInternal();
 
@@ -293,7 +302,12 @@ public:
     // Constructors
 
         //- Construct from components
-        fvMeshAdaptor(const fvMesh& mesh, const bool decompose=false);
+        fvMeshAdaptor
+        (
+            const fvMesh& mesh,
+            const channelType channelsOpt = channelType::DEFAULT,
+            const wordRes& patchSelection = wordRes()
+        );
 
 
     //- Destructor
@@ -303,13 +317,17 @@ public:
     // Member Functions
 
         //- Define the output channels by name
-        void channels(const wordList& chanNames);
+        void setChannels(const wordList& chanNames);
 
         //- Define the output channels by enum
-        void channels(enum channelType chanIds);
+        void setChannels(enum channelType chanIds);
 
         //- Define the output channels by value
-        void channels(unsigned chanIds);
+        void setChannels(unsigned chanIds);
+
+        //- Define polyhedral decomposition treatment
+        void setDecompose(const bool on);
+
 
         //- Return the selected output channel ids
         label channels() const;
@@ -320,9 +338,9 @@ public:
         //- True if BOUNDARY channel is being used
         bool usingBoundary() const;
 
-        //- Number of non-processor patches, when the BOUNDARY channel is being
-        //- used - otherwise 0.
-        label nPatches() const;
+        //- Selected (non-processor) patch ids, when the BOUNDARY channel
+        //- is being used. Empty otherwise.
+        const labelList& patchIds() const;
 
         //- Return the names of known (supported) fields
         wordHashSet knownFields(const wordRes& selectFields) const;
diff --git a/src/catalyst/volMesh/foamVtkFvMeshAdaptorFieldTemplates.C b/src/catalyst/volMesh/foamVtkFvMeshAdaptorFieldTemplates.C
index f8e918bdea5cb0e9aa56e8c605d3ddb7e18c9de6..31b74a2a00718ce6fe65232ae475019eb0994a53 100644
--- a/src/catalyst/volMesh/foamVtkFvMeshAdaptorFieldTemplates.C
+++ b/src/catalyst/volMesh/foamVtkFvMeshAdaptorFieldTemplates.C
@@ -69,9 +69,7 @@ void Foam::vtk::fvMeshAdaptor::convertVolField
     convertVolFieldInternal(fld, ptfPtr);
 
     // BOUNDARY
-    const label npatches = this->nPatches();
-
-    for (label patchId=0; patchId < npatches; ++patchId)
+    for (const label patchId : patchIds_)
     {
         const polyPatch& pp = patches[patchId];
         const word& longName = pp.name();
@@ -125,7 +123,11 @@ void Foam::vtk::fvMeshAdaptor::convertVolField
 
             transcribeFloatData(cdata, tpptf());
 
-            if (interpFields_ && patchId < patchInterpList.size())
+            if
+            (
+                patchId < patchInterpList.size()
+             && patchInterpList.set(patchId)
+            )
             {
                 pdata = vtk::Tools::convertFieldToVTK
                 (
@@ -138,7 +140,11 @@ void Foam::vtk::fvMeshAdaptor::convertVolField
         {
             transcribeFloatData(cdata, ptf);
 
-            if (interpFields_ && patchId < patchInterpList.size())
+            if
+            (
+                patchId < patchInterpList.size()
+             && patchInterpList.set(patchId)
+            )
             {
                 pdata = vtk::Tools::convertFieldToVTK
                 (
diff --git a/src/catalyst/volMesh/foamVtkFvMeshAdaptorFields.C b/src/catalyst/volMesh/foamVtkFvMeshAdaptorFields.C
index facca3c52d6948f7474d2082fb02369783554444..33f293f1f3913a55d63f613b2a417ed2cbe74cb8 100644
--- a/src/catalyst/volMesh/foamVtkFvMeshAdaptorFields.C
+++ b/src/catalyst/volMesh/foamVtkFvMeshAdaptorFields.C
@@ -105,21 +105,23 @@ void Foam::vtk::fvMeshAdaptor::convertVolFields
 
     PtrList<patchInterpolator> interpLst;
 
-    if (interpFields_)
+    if (interpFields_ && patchIds_.size())
     {
-        // NOTE: this will be broken with processor patches, but
-        // for the catalyst adaptor we explicitly restrict ourselves
-        // to non-processor patches
-        interpLst.setSize(this->nPatches());
+        // NOTE: this would be broken with processor patches,
+        // but we don't allow them for the catalyst adaptor anyhow
 
-        forAll(interpLst, i)
+        // patchIds_ are sorted, so the last one is also the max
+
+        interpLst.setSize(patchIds_.last() + 1);
+
+        for (const label patchId : patchIds_)
         {
             interpLst.set
             (
-                i,
+                patchId,
                 new PrimitivePatchInterpolation<primitivePatch>
                 (
-                    mesh_.boundaryMesh()[i]
+                    mesh_.boundaryMesh()[patchId]
                 )
             );
         }
diff --git a/src/catalyst/volMesh/foamVtkFvMeshAdaptorGeom.C b/src/catalyst/volMesh/foamVtkFvMeshAdaptorGeom.C
index db8d1a5343525abb34a83770b0ab927fe908ce81..1ab3c860b2f5ad5d37ad29b33027c961ca6129fb 100644
--- a/src/catalyst/volMesh/foamVtkFvMeshAdaptorGeom.C
+++ b/src/catalyst/volMesh/foamVtkFvMeshAdaptorGeom.C
@@ -90,9 +90,8 @@ void Foam::vtk::fvMeshAdaptor::convertGeometryBoundary()
     // BOUNDARY
 
     const polyBoundaryMesh& patches = mesh_.boundaryMesh();
-    const label npatches = this->nPatches();
 
-    for (label patchId=0; patchId < npatches; ++patchId)
+    for (const label patchId : patchIds_)
     {
         const polyPatch& pp = patches[patchId];
         const word& longName = pp.name();
@@ -233,9 +232,8 @@ void Foam::vtk::fvMeshAdaptor::applyGhostingBoundary(const labelUList& types)
     }
 
     const polyBoundaryMesh& patches = mesh_.boundaryMesh();
-    const label npatches = this->nPatches();
 
-    for (label patchId=0; patchId < npatches; ++patchId)
+    for (const label patchId : patchIds_)
     {
         const polyPatch& pp = patches[patchId];
         const word& longName = pp.name();
diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/catalyst b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/catalyst
index 8a03f3fce354041b8451380291d9aae5f8bf31fc..135a3d0bbcc05ca0da991fabee418f9664905759 100644
--- a/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/catalyst
+++ b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/catalyst
@@ -7,6 +7,7 @@ catalyst
     scripts
     (
         "<system>/scripts/slice1.py"
+        // "<system>/scripts/showT.py"
         // "<etc>/caseDicts/insitu/catalyst/writeAll.py"
     );
 
@@ -21,7 +22,19 @@ catalyst
             boundary    false;
 
             // Selected fields (words or regex)
-            fields  (T U p);
+            fields      (T U p);
+        }
+
+
+        // Solid walls only
+        walls
+        {
+            internal    false;
+
+            regions     ( heater "(?i).*solid" );
+            patches     ( "(?i).*solid_to.*" "heater.*(Air|Water)" );
+
+            fields      (T);
         }
     }
 }
diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/showT.py b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/showT.py
new file mode 100644
index 0000000000000000000000000000000000000000..065180f4571ea3ea7be993a06bf22054891124e9
--- /dev/null
+++ b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/showT.py
@@ -0,0 +1,276 @@
+
+from paraview.simple import *
+from paraview import coprocessing
+
+
+#--------------------------------------------------------------
+# Code generated from cpstate.py to create the CoProcessor.
+# paraview version 5.5.2
+
+#--------------------------------------------------------------
+# Global screenshot output options
+imageFileNamePadding=6
+rescale_lookuptable=False
+
+
+# ----------------------- CoProcessor definition -----------------------
+
+def CreateCoProcessor():
+  def _CreatePipeline(coprocessor, datadescription):
+    class Pipeline:
+      # state file generated using paraview version 5.5.2
+
+      # ----------------------------------------------------------------
+      # setup views used in the visualization
+      # ----------------------------------------------------------------
+
+      # trace generated using paraview version 5.5.2
+
+      #### disable automatic camera reset on 'Show'
+      paraview.simple._DisableFirstRenderCameraReset()
+
+      # Create a new 'Render View'
+      renderView1 = CreateView('RenderView')
+      renderView1.ViewSize = [1227, 756]
+      renderView1.AxesGrid = 'GridAxes3DActor'
+      renderView1.OrientationAxesVisibility = 0
+      renderView1.CenterOfRotation = [0.0, -0.0159999993629754, 0.0]
+      renderView1.StereoType = 0
+      renderView1.CameraPosition = [-0.22987670461473275, 0.027908517670887454, 0.37475316483990634]
+      renderView1.CameraFocalPoint = [0.00145289821931723, -0.013255103910183037, 0.0005962967658004288]
+      renderView1.CameraViewUp = [0.20896067334705018, 0.9776847628875804, 0.0216319534902209]
+      renderView1.CameraParallelScale = 0.0945044135920057
+      renderView1.CameraParallelProjection = 1
+      renderView1.Background = [0.32, 0.34, 0.43]
+
+      # init the 'GridAxes3DActor' selected for 'AxesGrid'
+      renderView1.AxesGrid.XTitleFontFile = ''
+      renderView1.AxesGrid.YTitleFontFile = ''
+      renderView1.AxesGrid.ZTitleFontFile = ''
+      renderView1.AxesGrid.XLabelFontFile = ''
+      renderView1.AxesGrid.YLabelFontFile = ''
+      renderView1.AxesGrid.ZLabelFontFile = ''
+
+      # register the view with coprocessor
+      # and provide it with information such as the filename to use,
+      # how frequently to write the images, etc.
+      coprocessor.RegisterView(renderView1,
+          filename='image_%t.png', freq=1, fittoscreen=0, magnification=1, width=1227, height=756, cinema={})
+      renderView1.ViewTime = datadescription.GetTime()
+
+      # ----------------------------------------------------------------
+      # restore active view
+      SetActiveView(renderView1)
+      # ----------------------------------------------------------------
+
+      # ----------------------------------------------------------------
+      # setup the data processing pipelines
+      # ----------------------------------------------------------------
+
+      # create a new 'XML MultiBlock Data Reader'
+      # create a producer from a simulation input
+      region = coprocessor.CreateProducer(datadescription, 'region')
+
+      # create a new 'XML MultiBlock Data Reader'
+      # create a producer from a simulation input
+      walls = coprocessor.CreateProducer(datadescription, 'walls')
+
+      # create a new 'Slice'
+      slice1 = Slice(Input=region)
+      slice1.SliceType = 'Plane'
+      slice1.SliceOffsetValues = [0.0]
+
+      # init the 'Plane' selected for 'SliceType'
+      slice1.SliceType.Normal = [0.0, 0.0, 1.0]
+
+      # ----------------------------------------------------------------
+      # setup the visualization in view 'renderView1'
+      # ----------------------------------------------------------------
+
+      # show data from walls
+      wallsDisplay = Show(walls, renderView1)
+
+      # get color transfer function/color map for 'T'
+      tLUT = GetColorTransferFunction('T')
+      tLUT.RGBPoints = [300.0, 0.231373, 0.298039, 0.752941, 400.0, 0.865003, 0.865003, 0.865003, 500.0, 0.705882, 0.0156863, 0.14902]
+      tLUT.ScalarRangeInitialized = 1.0
+
+      # trace defaults for the display properties.
+      wallsDisplay.Representation = 'Surface'
+      wallsDisplay.ColorArrayName = ['POINTS', 'T']
+      wallsDisplay.LookupTable = tLUT
+      wallsDisplay.Opacity = 0.4
+      wallsDisplay.OSPRayScaleArray = 'T'
+      wallsDisplay.OSPRayScaleFunction = 'PiecewiseFunction'
+      wallsDisplay.SelectOrientationVectors = 'None'
+      wallsDisplay.ScaleFactor = 0.0200000002980232
+      wallsDisplay.SelectScaleArray = 'None'
+      wallsDisplay.GlyphType = 'Arrow'
+      wallsDisplay.GlyphTableIndexArray = 'None'
+      wallsDisplay.GaussianRadius = 0.00100000001490116
+      wallsDisplay.SetScaleArray = ['POINTS', 'T']
+      wallsDisplay.ScaleTransferFunction = 'PiecewiseFunction'
+      wallsDisplay.OpacityArray = ['POINTS', 'T']
+      wallsDisplay.OpacityTransferFunction = 'PiecewiseFunction'
+      wallsDisplay.DataAxesGrid = 'GridAxesRepresentation'
+      wallsDisplay.SelectionCellLabelFontFile = ''
+      wallsDisplay.SelectionPointLabelFontFile = ''
+      wallsDisplay.PolarAxes = 'PolarAxesRepresentation'
+
+      # init the 'PiecewiseFunction' selected for 'ScaleTransferFunction'
+      wallsDisplay.ScaleTransferFunction.Points = [300.0, 0.0, 0.5, 0.0, 451.4297180175781, 1.0, 0.5, 0.0]
+
+      # init the 'PiecewiseFunction' selected for 'OpacityTransferFunction'
+      wallsDisplay.OpacityTransferFunction.Points = [300.0, 0.0, 0.5, 0.0, 451.4297180175781, 1.0, 0.5, 0.0]
+
+      # init the 'GridAxesRepresentation' selected for 'DataAxesGrid'
+      wallsDisplay.DataAxesGrid.XTitleFontFile = ''
+      wallsDisplay.DataAxesGrid.YTitleFontFile = ''
+      wallsDisplay.DataAxesGrid.ZTitleFontFile = ''
+      wallsDisplay.DataAxesGrid.XLabelFontFile = ''
+      wallsDisplay.DataAxesGrid.YLabelFontFile = ''
+      wallsDisplay.DataAxesGrid.ZLabelFontFile = ''
+
+      # init the 'PolarAxesRepresentation' selected for 'PolarAxes'
+      wallsDisplay.PolarAxes.PolarAxisTitleFontFile = ''
+      wallsDisplay.PolarAxes.PolarAxisLabelFontFile = ''
+      wallsDisplay.PolarAxes.LastRadialAxisTextFontFile = ''
+      wallsDisplay.PolarAxes.SecondaryRadialAxesTextFontFile = ''
+
+      # show data from slice1
+      slice1Display = Show(slice1, renderView1)
+
+      # trace defaults for the display properties.
+      slice1Display.Representation = 'Surface'
+      slice1Display.ColorArrayName = ['POINTS', 'T']
+      slice1Display.LookupTable = tLUT
+      slice1Display.Opacity = 0.8
+      slice1Display.OSPRayScaleArray = 'T'
+      slice1Display.OSPRayScaleFunction = 'PiecewiseFunction'
+      slice1Display.SelectOrientationVectors = 'None'
+      slice1Display.ScaleFactor = 0.0100000001490116
+      slice1Display.SelectScaleArray = 'None'
+      slice1Display.GlyphType = 'Arrow'
+      slice1Display.GlyphTableIndexArray = 'None'
+      slice1Display.GaussianRadius = 0.000500000007450581
+      slice1Display.SetScaleArray = ['POINTS', 'T']
+      slice1Display.ScaleTransferFunction = 'PiecewiseFunction'
+      slice1Display.OpacityArray = ['POINTS', 'T']
+      slice1Display.OpacityTransferFunction = 'PiecewiseFunction'
+      slice1Display.DataAxesGrid = 'GridAxesRepresentation'
+      slice1Display.SelectionCellLabelFontFile = ''
+      slice1Display.SelectionPointLabelFontFile = ''
+      slice1Display.PolarAxes = 'PolarAxesRepresentation'
+
+      # init the 'PiecewiseFunction' selected for 'ScaleTransferFunction'
+      slice1Display.ScaleTransferFunction.Points = [300.0, 0.0, 0.5, 0.0, 500.0, 1.0, 0.5, 0.0]
+
+      # init the 'PiecewiseFunction' selected for 'OpacityTransferFunction'
+      slice1Display.OpacityTransferFunction.Points = [300.0, 0.0, 0.5, 0.0, 500.0, 1.0, 0.5, 0.0]
+
+      # init the 'GridAxesRepresentation' selected for 'DataAxesGrid'
+      slice1Display.DataAxesGrid.XTitleFontFile = ''
+      slice1Display.DataAxesGrid.YTitleFontFile = ''
+      slice1Display.DataAxesGrid.ZTitleFontFile = ''
+      slice1Display.DataAxesGrid.XLabelFontFile = ''
+      slice1Display.DataAxesGrid.YLabelFontFile = ''
+      slice1Display.DataAxesGrid.ZLabelFontFile = ''
+
+      # init the 'PolarAxesRepresentation' selected for 'PolarAxes'
+      slice1Display.PolarAxes.PolarAxisTitleFontFile = ''
+      slice1Display.PolarAxes.PolarAxisLabelFontFile = ''
+      slice1Display.PolarAxes.LastRadialAxisTextFontFile = ''
+      slice1Display.PolarAxes.SecondaryRadialAxesTextFontFile = ''
+
+      # setup the color legend parameters for each legend in this view
+
+      # get color legend/bar for tLUT in view renderView1
+      tLUTColorBar = GetScalarBar(tLUT, renderView1)
+      tLUTColorBar.Title = 'T'
+      tLUTColorBar.ComponentTitle = ''
+      tLUTColorBar.TitleFontFile = ''
+      tLUTColorBar.LabelFontFile = ''
+
+      # set color bar visibility
+      tLUTColorBar.Visibility = 1
+
+      # show color legend
+      wallsDisplay.SetScalarBarVisibility(renderView1, True)
+
+      # show color legend
+      slice1Display.SetScalarBarVisibility(renderView1, True)
+
+      # ----------------------------------------------------------------
+      # setup color maps and opacity mapes used in the visualization
+      # note: the Get..() functions create a new object, if needed
+      # ----------------------------------------------------------------
+
+      # get opacity transfer function/opacity map for 'T'
+      tPWF = GetOpacityTransferFunction('T')
+      tPWF.Points = [300.0, 0.0, 0.5, 0.0, 500.0, 1.0, 0.5, 0.0]
+      tPWF.ScalarRangeInitialized = 1
+
+      # ----------------------------------------------------------------
+      # finally, restore active source
+      SetActiveSource(walls)
+      # ----------------------------------------------------------------
+    return Pipeline()
+
+  class CoProcessor(coprocessing.CoProcessor):
+    def CreatePipeline(self, datadescription):
+      self.Pipeline = _CreatePipeline(self, datadescription)
+
+  coprocessor = CoProcessor()
+  # these are the frequencies at which the coprocessor updates.
+  freqs = {'walls': [1], 'region': [1, 1]}
+  coprocessor.SetUpdateFrequencies(freqs)
+  return coprocessor
+
+
+#--------------------------------------------------------------
+# Global variable that will hold the pipeline for each timestep
+# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
+# It will be automatically setup when coprocessor.UpdateProducers() is called the
+# first time.
+coprocessor = CreateCoProcessor()
+
+#--------------------------------------------------------------
+# Enable Live-Visualizaton with ParaView and the update frequency
+coprocessor.EnableLiveVisualization(False, 1)
+
+# ---------------------- Data Selection method ----------------------
+
+def RequestDataDescription(datadescription):
+    "Callback to populate the request for current timestep"
+    global coprocessor
+    if datadescription.GetForceOutput() == True:
+        # We are just going to request all fields and meshes from the simulation
+        # code/adaptor.
+        for i in range(datadescription.GetNumberOfInputDescriptions()):
+            datadescription.GetInputDescription(i).AllFieldsOn()
+            datadescription.GetInputDescription(i).GenerateMeshOn()
+        return
+
+    # setup requests for all inputs based on the requirements of the
+    # pipeline.
+    coprocessor.LoadRequestedData(datadescription)
+
+# ------------------------ Processing method ------------------------
+
+def DoCoProcessing(datadescription):
+    "Callback to do co-processing for current timestep"
+    global coprocessor
+
+    # Update the coprocessor by providing it the newly generated simulation data.
+    # If the pipeline hasn't been setup yet, this will setup the pipeline.
+    coprocessor.UpdateProducers(datadescription)
+
+    # Write output data, if appropriate.
+    coprocessor.WriteData(datadescription);
+
+    # Write image capture (Last arg: rescale lookup table), if appropriate.
+    coprocessor.WriteImages(datadescription, rescale_lookuptable=rescale_lookuptable,
+        image_quality=0, padding_amount=imageFileNamePadding)
+
+    # Live Visualization, if enabled.
+    coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)
diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/slice1.py b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/slice1.py
index aa50ce5db9afed764760b126265f5b8776cbae78..001e6eff2e8119def4fb45aa30c52151d3708554 100644
--- a/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/slice1.py
+++ b/tutorials/heatTransfer/chtMultiRegionFoam/multiRegionHeater/system/scripts/slice1.py
@@ -9,17 +9,21 @@ def CreateCoProcessor():
   def _CreatePipeline(coprocessor, datadescription):
     class Pipeline:
       region = coprocessor.CreateProducer(datadescription, 'region')
+      walls = coprocessor.CreateProducer(datadescription, 'walls')
 
-      slice1 = Slice(Input=region, guiName="Slice1", SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane")
-      slice1.SliceType.Offset = 0.0
-      slice1.SliceType.Origin = [0, 0, 0]
+      slice1 = Slice(Input=region)
+      slice1.SliceType = 'Plane'
+      slice1.SliceOffsetValues = [0.0]
       slice1.SliceType.Normal = [0, 0, 1]
 
       sliceWriter = servermanager.writers.XMLMultiBlockDataWriter(Input=slice1)
       coprocessor.RegisterWriter(sliceWriter, filename='slice_%t.vtm', freq=10, paddingamount=fileNamePadding)
 
-      meshWriter = servermanager.writers.XMLMultiBlockDataWriter(Input=region)
-      coprocessor.RegisterWriter(meshWriter, filename='region_%t.vtm', freq=100, paddingamount=fileNamePadding)
+      regionWriter = servermanager.writers.XMLMultiBlockDataWriter(Input=region)
+      coprocessor.RegisterWriter(regionWriter, filename='region_%t.vtm', freq=100, paddingamount=fileNamePadding)
+
+      wallsWriter = servermanager.writers.XMLMultiBlockDataWriter(Input=walls)
+      coprocessor.RegisterWriter(wallsWriter, filename='walls_%t.vtm', freq=25, paddingamount=fileNamePadding)
 
     return Pipeline()
 
@@ -28,7 +32,7 @@ def CreateCoProcessor():
       self.Pipeline = _CreatePipeline(self, datadescription)
 
   coprocessor = CoProcessor()
-  freqs = {'region': [10, 100]}
+  freqs = {'region': [10, 100], 'walls': [25]}
   coprocessor.SetUpdateFrequencies(freqs)
   return coprocessor