diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
index 1d7cd88a2968f3dbe9cad1aaa28e852140d9c1f4..5f429e1214b9637fd569eb12fc014c0d881c2f7e 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.C
@@ -32,7 +32,7 @@ License
 #include "coupledPolyPatch.H"
 #include "sampledSurface.H"
 #include "mergePoints.H"
-#include "indirectPrimitivePatch.H"
+#include "uindirectPrimitivePatch.H"
 #include "PatchTools.H"
 #include "addToRunTimeSelectionTable.H"
 
@@ -130,115 +130,225 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::obr() const
 
 void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces()
 {
-    const label zoneId = mesh_.faceZones().findZoneID(regionName_);
+    // Indices for all matches, already sorted
+    const labelList zoneIds
+    (
+        mesh_.faceZones().indices(selectionNames_)
+    );
+
+    // Total number of faces selected
+    label numFaces = 0;
+    for (const label zoneId : zoneIds)
+    {
+        numFaces += mesh_.faceZones()[zoneId].size();
+    }
 
-    if (zoneId < 0)
+    if (zoneIds.empty())
     {
         FatalErrorInFunction
-            << type() << " " << name() << ": "
+            << type() << ' ' << name() << ": "
             << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    Unknown face zone name: " << regionName_
-            << ". Valid face zones are: " << mesh_.faceZones().names()
-            << nl << exit(FatalError);
+            << "    No matching face zone(s): "
+            << flatOutput(selectionNames_)  << nl
+            << "    Known face zones: "
+            << flatOutput(mesh_.faceZones().names()) << nl
+            << exit(FatalError);
     }
 
-    const faceZone& fZone = mesh_.faceZones()[zoneId];
+    // Could also check this
+    #if 0
+    if (!returnReduce(bool(numFaces), orOp<bool>()))
+    {
+        WarningInFunction
+            << type() << ' ' << name() << ": "
+            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
+            << "    The faceZone specification: "
+            << flatOutput(selectionNames_) << nl
+            << "    resulted in 0 faces" << nl
+            << exit(FatalError);
+    }
+    #endif
+
+    faceId_.resize(numFaces);
+    facePatchId_.resize(numFaces);
+    faceFlip_.resize(numFaces);
 
-    DynamicList<label> faceIds(fZone.size());
-    DynamicList<label> facePatchIds(fZone.size());
-    DynamicList<bool> faceFlip(fZone.size());
+    numFaces = 0;
 
-    forAll(fZone, i)
+    for (const label zoneId : zoneIds)
     {
-        const label facei = fZone[i];
+        const faceZone& fZone = mesh_.faceZones()[zoneId];
 
-        label faceId = -1;
-        label facePatchId = -1;
-        if (mesh_.isInternalFace(facei))
-        {
-            faceId = facei;
-            facePatchId = -1;
-        }
-        else
+        forAll(fZone, i)
         {
-            facePatchId = mesh_.boundaryMesh().whichPatch(facei);
-            const polyPatch& pp = mesh_.boundaryMesh()[facePatchId];
-            if (isA<coupledPolyPatch>(pp))
+            const label meshFacei = fZone[i];
+            const bool isFlip = fZone.flipMap()[i];
+
+            // Internal faces
+            label faceId = meshFacei;
+            label facePatchId = -1;
+
+            // Boundary faces
+            if (!mesh_.isInternalFace(meshFacei))
             {
-                if (refCast<const coupledPolyPatch>(pp).owner())
+                facePatchId = mesh_.boundaryMesh().whichPatch(meshFacei);
+                const polyPatch& pp = mesh_.boundaryMesh()[facePatchId];
+
+                if (isA<coupledPolyPatch>(pp))
                 {
-                    faceId = pp.whichFace(facei);
+                    if (refCast<const coupledPolyPatch>(pp).owner())
+                    {
+                        faceId = pp.whichFace(meshFacei);
+                    }
+                    else
+                    {
+                        faceId = -1;
+                    }
+                }
+                else if (!isA<emptyPolyPatch>(pp))
+                {
+                    faceId = meshFacei - pp.start();
                 }
                 else
                 {
                     faceId = -1;
+                    facePatchId = -1;
                 }
             }
-            else if (!isA<emptyPolyPatch>(pp))
-            {
-                faceId = facei - pp.start();
-            }
-            else
+
+            if (faceId >= 0)
             {
-                faceId = -1;
-                facePatchId = -1;
-            }
-        }
+                faceId_[numFaces] = faceId;
+                facePatchId_[numFaces] = facePatchId;
+                faceFlip_[numFaces] = isFlip;
 
-        if (faceId >= 0)
-        {
-            faceIds.append(faceId);
-            facePatchIds.append(facePatchId);
-            faceFlip.append(fZone.flipMap()[i] ? true : false);
+                ++numFaces;
+            }
         }
     }
 
-    faceId_.transfer(faceIds);
-    facePatchId_.transfer(facePatchIds);
-    faceFlip_.transfer(faceFlip);
+    // Shrink to size used
+    faceId_.resize(numFaces);
+    facePatchId_.resize(numFaces);
+    faceFlip_.resize(numFaces);
     nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
-
-    if (debug)
-    {
-        Pout<< "Original face zone size = " << fZone.size()
-            << ", new size = " << faceId_.size() << endl;
-    }
 }
 
 
 void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
 {
-    const label patchid = mesh_.boundaryMesh().findPatchID(regionName_);
+    // Patch indices for all matches
+    labelList patchIds;
+
+    // Total number of faces selected
+    label numFaces = 0;
+
+    labelList selected
+    (
+        mesh_.boundaryMesh().patchSet
+        (
+            selectionNames_,
+            false  // warnNotFound - we do that ourselves
+        ).sortedToc()
+    );
+
+    DynamicList<label> bad;
+    for (const label patchi : selected)
+    {
+        const polyPatch& pp = mesh_.boundaryMesh()[patchi];
+
+        if (isA<emptyPolyPatch>(pp))
+        {
+            bad.append(patchi);
+        }
+        else
+        {
+            numFaces += pp.size();
+        }
+    }
+
+    if (bad.size())
+    {
+        label nGood = (selected.size() - bad.size());
+
+        auto& os = (nGood > 0 ? WarningInFunction : FatalErrorInFunction);
+
+        os  << "Cannot sample an empty patch" << nl;
+
+        for (const label patchi : bad)
+        {
+            os  << "    "
+                << mesh_.boundaryMesh()[patchi].name() << nl;
+        }
+
+        if (nGood)
+        {
+            os  << "No non-empty patches selected" << endl
+                << exit(FatalError);
+        }
+        else
+        {
+            os  << "Selected " << nGood << " non-empty patches" << nl;
+        }
 
-    if (patchid < 0)
+        patchIds.resize(nGood);
+        nGood = 0;
+        for (const label patchi : selected)
+        {
+            if (!bad.found(patchi))
+            {
+                patchIds[nGood] = patchi;
+                ++nGood;
+            }
+        }
+    }
+    else
+    {
+        patchIds = std::move(selected);
+    }
+
+    if (patchIds.empty())
     {
         FatalErrorInFunction
-            << type() << " " << name() << ": "
+            << type() << ' ' << name() << ": "
             << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
-            << "    Unknown patch name: " << regionName_
-            << ". Valid patch names are: "
+            << "    No matching patch name(s): "
+            << flatOutput(selectionNames_)  << nl
+            << "    Known patch names:" << nl
             << mesh_.boundaryMesh().names() << nl
             << exit(FatalError);
     }
 
-    const polyPatch& pp = mesh_.boundaryMesh()[patchid];
-
-    label nFaces = pp.size();
-    if (isA<emptyPolyPatch>(pp))
+    // Could also check this
+    #if 0
+    if (!returnReduce(bool(numFaces), orOp<bool>()))
     {
-        nFaces = 0;
+        WarningInFunction
+            << type() << ' ' << name() << ": "
+            << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
+            << "    The patch specification: "
+            << flatOutput(selectionNames_) << nl
+            << "    resulted in 0 faces" << nl
+            << exit(FatalError);
     }
+    #endif
 
-    faceId_.setSize(nFaces);
-    facePatchId_.setSize(nFaces);
-    faceFlip_.setSize(nFaces);
+    faceId_.resize(numFaces);
+    facePatchId_.resize(numFaces);
+    faceFlip_.resize(numFaces);
     nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
 
-    forAll(faceId_, facei)
+    numFaces = 0;
+    for (const label patchi : patchIds)
     {
-        faceId_[facei] = facei;
-        facePatchId_[facei] = patchid;
-        faceFlip_[facei] = false;
+        const polyPatch& pp = mesh_.boundaryMesh()[patchi];
+        const label len = pp.size();
+
+        SubList<label>(faceId_, len, numFaces) = identity(len);
+        SubList<label>(facePatchId_, len, numFaces) = patchi;
+        SubList<bool>(faceFlip_, len, numFaces) = false;
+
+        numFaces += len;
     }
 }
 
@@ -252,24 +362,25 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::combineMeshGeometry
     List<faceList> allFaces(Pstream::nProcs());
     List<pointField> allPoints(Pstream::nProcs());
 
-    labelList globalFacesIs(faceId_);
-    forAll(globalFacesIs, i)
     {
-        if (facePatchId_[i] != -1)
+        IndirectList<face> selectedFaces(mesh_.faces(), labelList(faceId_));
+        labelList& meshFaceIds = selectedFaces.addressing();
+
+        forAll(meshFaceIds, i)
         {
             const label patchi = facePatchId_[i];
-            globalFacesIs[i] += mesh_.boundaryMesh()[patchi].start();
+            if (patchi != -1)
+            {
+                meshFaceIds[i] += mesh_.boundaryMesh()[patchi].start();
+            }
         }
-    }
 
-    // Add local faces and points to the all* lists
-    indirectPrimitivePatch pp
-    (
-        IndirectList<face>(mesh_.faces(), globalFacesIs),
-        mesh_.points()
-    );
-    allFaces[Pstream::myProcNo()] = pp.localFaces();
-    allPoints[Pstream::myProcNo()] = pp.localPoints();
+        // Add local faces and points to the all* lists
+        uindirectPrimitivePatch pp(selectedFaces, mesh_.points());
+
+        allFaces[Pstream::myProcNo()] = pp.localFaces();
+        allPoints[Pstream::myProcNo()] = pp.localPoints();
+    }
 
     Pstream::gatherList(allFaces);
     Pstream::gatherList(allPoints);
@@ -283,53 +394,41 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::combineMeshGeometry
         nPoints += allPoints[proci].size();
     }
 
-    faces.setSize(nFaces);
-    points.setSize(nPoints);
+    faces.resize(nFaces);
+    points.resize(nPoints);
 
     nFaces = 0;
     nPoints = 0;
 
-    // My own data first
+    // My data first
     {
-        const faceList& fcs = allFaces[Pstream::myProcNo()];
-        for (const face& f : fcs)
+        for (const face& f : allFaces[Pstream::myProcNo()])
         {
-            face& newF = faces[nFaces++];
-            newF.setSize(f.size());
-            forAll(f, fp)
-            {
-                newF[fp] = f[fp] + nPoints;
-            }
+            faces[nFaces++] = offsetOp<face>()(f, nPoints);
         }
 
-        const pointField& pts = allPoints[Pstream::myProcNo()];
-        for (const point& pt : pts)
+        for (const point& p : allPoints[Pstream::myProcNo()])
         {
-            points[nPoints++] = pt;
+            points[nPoints++] = p;
         }
     }
 
     // Other proc data follows
     forAll(allFaces, proci)
     {
-        if (proci != Pstream::myProcNo())
+        if (proci == Pstream::myProcNo())
         {
-            const faceList& fcs = allFaces[proci];
-            for (const face& f : fcs)
-            {
-                face& newF = faces[nFaces++];
-                newF.setSize(f.size());
-                forAll(f, fp)
-                {
-                    newF[fp] = f[fp] + nPoints;
-                }
-            }
+            continue;
+        }
 
-            const pointField& pts = allPoints[proci];
-            for (const point& pt : pts)
-            {
-                points[nPoints++] = pt;
-            }
+        for (const face& f : allFaces[proci])
+        {
+            faces[nFaces++] = offsetOp<face>()(f, nPoints);
+        }
+
+        for (const point& p : allPoints[proci])
+        {
+            points[nPoints++] = p;
         }
     }
 
@@ -383,11 +482,7 @@ combineSurfaceGeometry
             PatchTools::gatherAndMerge
             (
                 mergeDim,
-                primitivePatch
-                (
-                    SubList<face>(s.faces(), s.faces().size()),
-                    s.points()
-                ),
+                primitivePatch(SubList<face>(s.faces()), s.points()),
                 points,
                 faces,
                 pointsMap
@@ -413,11 +508,7 @@ combineSurfaceGeometry
             PatchTools::gatherAndMerge
             (
                 mergeDim,
-                primitivePatch
-                (
-                    SubList<face>(s.faces(), s.faces().size()),
-                    s.points()
-                ),
+                primitivePatch(SubList<face>(s.faces()), s.points()),
                 points,
                 faces,
                 pointsMap
@@ -522,7 +613,7 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::update()
     if (nFaces_ == 0)
     {
         FatalErrorInFunction
-            << type() << " " << name() << ": "
+            << type() << ' ' << name() << ": "
             << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
             << "    Region has no faces" << exit(FatalError);
     }
@@ -548,7 +639,7 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::writeFileHeader
     if (canWriteHeader() && (operation_ != opNone))
     {
         writeCommented(os, "Region type : ");
-        os << regionTypeNames_[regionType_] << " " << regionName_ << endl;
+        os << regionTypeNames_[regionType_] << ' ' << regionName_ << nl;
 
         writeHeaderValue(os, "Faces", nFaces_);
         writeHeaderValue(os, "Area", totalArea_);
@@ -570,7 +661,7 @@ void Foam::functionObjects::fieldValues::surfaceFieldValue::writeFileHeader
         for (const word& fieldName : fields_)
         {
             os  << tab << operationTypeNames_[operation_]
-                << "(" << fieldName << ")";
+                << '(' << fieldName << ')';
         }
 
         os  << endl;
@@ -821,9 +912,10 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
             true  // Failsafe behaviour
         )
     ),
-    weightFieldName_("none"),
     needsUpdate_(true),
     writeArea_(false),
+    selectionNames_(),
+    weightFieldName_("none"),
     totalArea_(0),
     nFaces_(0),
     faceId_(),
@@ -854,9 +946,10 @@ Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
             true  // Failsafe behaviour
         )
     ),
-    weightFieldName_("none"),
     needsUpdate_(true),
     writeArea_(false),
+    selectionNames_(),
+    weightFieldName_("none"),
     totalArea_(0),
     nFaces_(0),
     faceId_(),
@@ -876,18 +969,54 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
 {
     fieldValue::read(dict);
 
-    weightFieldName_ = "none";
     needsUpdate_ = true;
     writeArea_ = dict.getOrDefault("writeArea", false);
+    weightFieldName_ = "none";
     totalArea_ = 0;
     nFaces_ = 0;
     faceId_.clear();
     facePatchId_.clear();
     faceFlip_.clear();
-    sampledPtr_.clear();
-    surfaceWriterPtr_.clear();
+    sampledPtr_.reset(nullptr);
+    surfaceWriterPtr_.reset(nullptr);
+
+    // Can have "name" (word) and/or "names" (wordRes)
+    //
+    // If "names" exists AND contains a literal (non-regex) that can be used
+    // as a suitable value for "name", the "name" entry becomes optional.
+
+    regionName_.clear();
+    selectionNames_.clear();
+
+    {
+        dict.readIfPresent("names", selectionNames_);
+
+        for (const auto& item : selectionNames_)
+        {
+            if (item.isLiteral())
+            {
+                regionName_ = item;
+                break;
+            }
+        }
+
+        // Mandatory if we didn't pick up a value from selectionNames_
+        dict.readEntry
+        (
+            "name",
+            regionName_,
+            keyType::LITERAL,
+            regionName_.empty()
+        );
+
+        // Ensure there is always content for selectionNames_
+        if (selectionNames_.empty())
+        {
+            selectionNames_.resize(1);
+            selectionNames_.first() = regionName_;
+        }
+    }
 
-    dict.readEntry("name", regionName_);
 
     // Create sampled surface, but leave 'expired' (ie, no update) since it
     // may depend on fields or data that do not yet exist
@@ -901,7 +1030,7 @@ bool Foam::functionObjects::fieldValues::surfaceFieldValue::read
         );
     }
 
-    Info<< type() << " " << name() << ":" << nl
+    Info<< type() << ' ' << name() << ':' << nl
         << "    operation     = ";
 
     if (postOperation_ != postOpNone)
diff --git a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
index 1dde638e5588f43cb4d1b31b7a4dd0af2d9530af..5687e9f4a46e31e8b8a8cba79b31cf60ffd1e144 100644
--- a/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
+++ b/src/functionObjects/field/fieldValues/surfaceFieldValue/surfaceFieldValue.H
@@ -31,17 +31,17 @@ Group
     grpFieldFunctionObjects
 
 Description
-    Provides a 'face regionType' variant of the \c fieldValues function object.
+    A \c face regionType variant of the \c fieldValues function object.
 
     Given a list of user-specified fields and a selection of mesh (or general
     surface) faces, a number of operations can be performed, such as sums,
     averages and integrations.
 
     For example, to calculate the volumetric or mass flux across a patch,
-    apply the 'sum' operator to the flux field (typically \c phi).
+    apply the \c sum operator to the flux field (typically \c phi).
 
 Usage
-    Minimal example by using \c system/controlDict.functions:
+    A minimal example:
     \verbatim
     surfaceFieldValuePatch1
     {
@@ -56,6 +56,8 @@ Usage
         name            <patch>;
 
         // Optional entries (runtime modifiable)
+        names           (<patch-name> <patch-regex>);
+
         postOperation   none;
         weightField     alpha1;
         scaleFactor     1.0;
@@ -79,6 +81,8 @@ Usage
         name            <faceZone>;
 
         // Optional entries (runtime modifiable)
+        names           (<zone-name> <zone-regex>);
+
         postOperation   none;
         weightField     alpha1;
         scaleFactor     1.0;
@@ -92,12 +96,13 @@ Usage
 
     where the entries mean:
     \table
-      Property     | Description                        | Type | Req'd | Dflt
+      Property     | Description                        | Type | Reqd | Dflt
       type         | Type name: surfaceFieldValue       | word |  yes  | -
-      libs         | Library name: fieldFunctionObjects | word |  yes  | -
-      fields       | Names of operand fields            | wordList | yes | -
+      libs         | Libraries: fieldFunctionObjects    | wordList |  yes  | -
       regionType   | Face regionType: see below         | word |  yes  | -
-      name         | Name for regionType                | word |  yes  | -
+      fields       | Names of operand fields            | wordList | yes | -
+      name         | Name of the regionType             | word |  yes  | -
+      names        | Extended selection                 | word/regex list | no | -
       operation    | Operation type: see below          | word |  yes  | -
       postOperation | Post-operation type: see below    | word |  no   | none
       weightField  | Name of field to apply weighting   | word |  no   | none
@@ -112,9 +117,9 @@ Usage
 
     Options for the \c regionType entry:
     \plaintable
-      faceZone              | The \b name entry to specify the faceZone
-      patch                 | The \b name entry to specify the patch
-      functionObjectSurface | The \b name entry to specify a polySurface
+      faceZone      | The \b name entry specifies a faceZone. Supports \b names
+      patch         | The \b name entry specifies a patch. Supports \b names
+      functionObjectSurface | The \b name entry specifies a polySurface
       sampledSurface        | A \b sampledSurfaceDict sub-dictionary and \b name
     \endplaintable
 
@@ -156,12 +161,16 @@ Usage
     Usage by the \c postProcess utility is not available.
 
 Note
+    - Some types (eg, patch or faceZone) support the selection of multiple
+      entries, which can be specified as list of words or regular expressions
+      by \b names entry.<br>
+      If the \b names enty exists \em and contains a literal that can be used
+      as a suitable value for \b name, the \b name entry becomes optional
+      instead of being mandatory.
     - The values reported by the \c areaNormalAverage and \c areaNormalIntegrate
       operations are written as the first component of a field with the same
       rank as the input field.
-    - Faces on empty patches get ignored.
-    - If the field is a volField the \c faceZone
-      can only consist of boundary faces.
+    - Faces on empty patches are ignored.
     - Using \c functionObjectSurface:
       - The keyword %subRegion should not be used to select surfaces.
         Instead specify the regionType 'functionObjectSurface' and provide
@@ -242,8 +251,8 @@ public:
         //- Region type enumeration
         enum regionTypes
         {
-            stFaceZone = 0x01,   //!< Calculate on a faceZone
-            stPatch    = 0x02,   //!< Calculate on a patch
+            stFaceZone = 0x01,   //!< Calculate with faceZone(s)
+            stPatch    = 0x02,   //!< Calculate with patch(es)
             stObject   = 0x11,   //!< Calculate with function object surface
             stSampled  = 0x12    //!< Sample onto surface and calculate
         };
@@ -385,15 +394,18 @@ protected:
         //- Optional post-evaluation operation
         postOperationType postOperation_;
 
-        //- Weight field name - optional
-        word weightFieldName_;
-
         //- Track if the surface needs an update
         bool needsUpdate_;
 
         //- Optionally write the area of the surfaceFieldValue
         bool writeArea_;
 
+        //- Extended selections
+        wordRes selectionNames_;
+
+        //- Weight field name - optional
+        word weightFieldName_;
+
         //- Total area of the surfaceFieldValue
         scalar totalArea_;
 
@@ -401,17 +413,20 @@ protected:
         label nFaces_;
 
 
-        // If operating on mesh faces (faceZone, patch)
+    // If operating on mesh faces (faceZone, patch)
+
+        //- Local list of face IDs
+        labelList faceId_;
+
+        //- Local list of patch ID per face
+        labelList facePatchId_;
 
-            //- Local list of face IDs
-            labelList faceId_;
+        //- List representing the face flip map
+        //  (false: use as-is, true: negate)
+        boolList faceFlip_;
 
-            //- Local list of patch ID per face
-            labelList facePatchId_;
 
-            //- List representing the face flip map
-            //  (false: use as-is, true: negate)
-            boolList faceFlip_;
+    // Demand-driven
 
         //- The sampledSurface (when operating on sampledSurface)
         autoPtr<sampledSurface> sampledPtr_;
diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun
index 9ec0596bab62acca9301776a93baa8fb237ea0cb..a5d9a2cb057a3a418c92f0fb6eac32e2eef1c21f 100755
--- a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun
+++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun
@@ -3,15 +3,12 @@ cd "${0%/*}" || exit                                # Run from this directory
 . ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
 #------------------------------------------------------------------------------
 
-# create mesh
-runApplication blockMesh
-
 restore0Dir
 
-# initialise with potentialFoam solution
+runApplication blockMesh
+
 runApplication potentialFoam
 
-# run the solver
 runApplication $(getApplication)
 
 #------------------------------------------------------------------------------
diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun-parallel b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun-parallel
new file mode 100755
index 0000000000000000000000000000000000000000..8bb64db8194a2d9f23ba608c072195988d995a34
--- /dev/null
+++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun-parallel
@@ -0,0 +1,16 @@
+#!/bin/sh
+cd "${0%/*}" || exit                                # Run from this directory
+. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
+#------------------------------------------------------------------------------
+
+restore0Dir
+
+runApplication blockMesh
+
+runApplication potentialFoam
+
+runApplication decomposePar
+
+runParallel $(getApplication)
+
+#------------------------------------------------------------------------------
diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/controlDict b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/controlDict
index 9ac52b9bae3ec027b5a85adc5edbdf2de045df9a..3442d41a3a44f32c9d5d2fda51f49b9ba66be949 100644
--- a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/controlDict
+++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/controlDict
@@ -53,7 +53,27 @@ maxDeltaT       1e-03;
 
 functions
 {
-    surfaceFieldValue1
+    avgInlets
+    {
+        type            surfaceFieldValue;
+        libs            (fieldFunctionObjects);
+        enabled         yes;
+        writeControl    writeTime;
+        log             yes;
+        writeFields     off;
+        surfaceFormat   vtk;
+        regionType      patch;
+        // name            inlet;
+        names           (inlet "inlet.*");
+        operation       weightedAverage;
+        weightField     phi;
+        fields
+        (
+            T
+        );
+    }
+
+    avgOutlets
     {
         type            surfaceFieldValue;
         libs            (fieldFunctionObjects);
diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/decomposeParDict b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/decomposeParDict
new file mode 100644
index 0000000000000000000000000000000000000000..e12fff0f4bf86241953797a3a19a4121355ece0e
--- /dev/null
+++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/system/decomposeParDict
@@ -0,0 +1,30 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v2006                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      decomposeParDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+numberOfSubdomains 4;
+
+method          scotch;
+
+coeffs
+{
+    n           (2 2 1);
+}
+
+distributed     no;
+
+roots           ( );
+
+// ************************************************************************* //
diff --git a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/system/controlDict b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/system/controlDict
index b12d88a3d20ddbcf1523e9005f12733063282eeb..ae2bb7accb3697b2af5c2265fc68d0eba4017c47 100644
--- a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/system/controlDict
+++ b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/system/controlDict
@@ -48,7 +48,7 @@ runTimeModifiable yes;
 
 functions
 {
-    surfaceFieldValue1
+    avgOutlets
     {
         type            surfaceFieldValue;
         libs            (fieldFunctionObjects);