diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/checkFieldAvailability.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/checkFieldAvailability.H
index b562bcdf4327bc2131e6b656005fa06784cabe97..0733064cd5f6c0bbec7d60ad9303215db336962c 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/checkFieldAvailability.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/checkFieldAvailability.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2021 OpenCFD Ltd.
+    Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@@ -26,27 +26,37 @@ forAll(meshes, regioni)
 {
     const auto& mesh = meshes[regioni];
 
-    IOobjectList objects(mesh, timeDirs.last().name());
+    IOobjectList objects(0);
 
-    if (!fieldPatterns.empty())
+    if (doConvertFields)
     {
-        objects.filterObjects(fieldPatterns);
-    }
+        objects = IOobjectList(mesh, timeDirs.last().name());
 
-    // Remove "*_0" restart fields
-    objects.prune_0();
+        if (fieldSelector && !fieldSelector().empty())
+        {
+            objects.filterObjects(fieldSelector());
+        }
 
-    if (!doPointValues)
-    {
-        // Prune point fields if disabled
-        objects.filterClasses
-        (
-            [](const word& clsName)
-            {
-                return fieldTypes::point.found(clsName);
-            },
-            true // prune
-        );
+        if (fieldSelector && !fieldSelector().empty())
+        {
+            objects.filterObjects(fieldSelector());
+        }
+
+        // Remove "*_0" restart fields
+        objects.prune_0();
+
+        if (!doPointValues)
+        {
+            // Prune point fields if disabled
+            objects.filterClasses
+            (
+                [](const word& clsName)
+                {
+                    return fieldTypes::point.found(clsName);
+                },
+                true // prune
+            );
+        }
     }
 
     wordList objectNames(objects.sortedNames());
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/createMeshAccounting.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/createMeshAccounting.H
index 13c7a6034d2dee2fbddb495442e37751ecedb137..7d33588a56993343190ceccd9eb395e09bebfbe6 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/createMeshAccounting.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/createMeshAccounting.H
@@ -55,6 +55,7 @@ PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
             regioni,
             new ensightMesh(mesh, writeOpts)
         );
+        ensightMeshes[regioni].verbose(optVerbose);
 
         // New ensight case file, initialize header etc.
         ensightCases.set
@@ -87,6 +88,7 @@ PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
                     regioni,
                     new ensightFaMesh(meshesFa[regioni])
                 );
+                ensightMeshesFa[regioni].verbose(optVerbose);
             }
         }
     }
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/findCloudFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/findCloudFields.H
index 600c5248e02a87315f841f9014045f852f0627d8..c50e6e2a7473a6c331168b1a8802ce62766b31dc 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/findCloudFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/findCloudFields.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C b/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
index f0127734241c87961e82a687812cf0265346b337..45b816fe24566229062cac06f8ed6cdea86f8bf2 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2021 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -108,11 +108,11 @@ Usage
           -patches '( front \".*back\" )'
         \endverbatim
 
-      - \par -excludePatches NAME | LIST
+      - \par -exclude-patches NAME | LIST
         Exclude single or multiple patches (name or regex) from writing.
         For example,
         \verbatim
-          -excludePatches '( inlet_1 inlet_2 "proc.*" )'
+          -exclude-patches '( inlet_1 inlet_2 "proc.*" )'
         \endverbatim
 
 \*---------------------------------------------------------------------------*/
@@ -170,6 +170,8 @@ int main(int argc, char *argv[])
     argList::setAdvanced("decomposeParDict");
     argList::setAdvanced("noFunctionObjects");
 
+    argList::addVerboseOption("Additional verbosity");
+
     #include "addAllRegionOptions.H"
 
     argList::addBoolOption
@@ -259,10 +261,15 @@ int main(int argc, char *argv[])
     // );
     argList::addBoolOption
     (
-        "finite-area",
-        "Write finite area fields",
+        "no-finite-area",
+        "Suppress output of finite-area mesh/fields",
         true  // mark as an advanced option
     );
+    argList::ignoreOptionCompat
+    (
+        {"finite-area", 2112},  // use -no-finite-area to disable
+        false           // bool option, no argument
+    );
 
     argList::addOption
     (
@@ -273,12 +280,14 @@ int main(int argc, char *argv[])
     );
     argList::addOption
     (
-        "excludePatches",
+        "exclude-patches",
         "wordRes",
         "Exclude single or multiple patches from writing\n"
         "Eg, 'outlet' or '( inlet \".*Wall\" )'"
         , true  // mark as an advanced option
     );
+    argList::addOptionCompat("exclude-patches", {"excludePatches", 2112});
+
     argList::addOption
     (
         "faceZones",
@@ -293,6 +302,19 @@ int main(int argc, char *argv[])
         "Specify single or multiple fields to write (all by default)\n"
         "Eg, 'T' or '( \"U.*\" )'"
     );
+    argList::addOption
+    (
+        "exclude-fields",
+        "wordRes",
+        "Exclude single or multiple fields",
+        true  // mark as an advanced option
+    );
+    argList::addBoolOption
+    (
+        "no-fields",
+        "Suppress conversion of fields"
+    );
+
     argList::addOption
     (
         "cellZones",
@@ -315,11 +337,12 @@ int main(int argc, char *argv[])
       : IOstreamOption::BINARY
     );
 
+    const int optVerbose = args.verbose();
     const bool doBoundary    = !args.found("no-boundary");
     const bool doInternal    = !args.found("no-internal");
     const bool doCellZones   = !args.found("no-cellZones");
     const bool doLagrangian  = !args.found("no-lagrangian");
-    const bool doFiniteArea  = args.found("finite-area");
+    const bool doFiniteArea  = !args.found("no-finite-area");
     const bool doPointValues = !args.found("no-point-data");
     const bool nearCellValue = args.found("nearCellValue") && doBoundary;
 
@@ -360,13 +383,14 @@ int main(int argc, char *argv[])
     writeOpts.useInternalMesh(doInternal);
     writeOpts.useCellZones(doCellZones);
 
+    // Patch selection/deselection
     if (args.found("patches"))
     {
         writeOpts.patchSelection(args.getList<wordRe>("patches"));
     }
-    if (args.found("excludePatches"))
+    if (args.found("exclude-patches"))
     {
-        writeOpts.patchExclude(args.getList<wordRe>("excludePatches"));
+        writeOpts.patchExclude(args.getList<wordRe>("exclude-patches"));
     }
 
     if (args.found("faceZones"))
@@ -381,8 +405,35 @@ int main(int argc, char *argv[])
     // Report the setup
     writeOpts.print(Info);
 
-    wordRes fieldPatterns;
-    args.readListIfPresent<wordRe>("fields", fieldPatterns);
+    // Field selection/deselection
+    wordRes includedFields, excludedFields;
+    autoPtr<wordRes::filter> fieldSelector(nullptr);
+    const bool doConvertFields = !args.found("no-fields");
+    if (doConvertFields)
+    {
+        bool resetFilter = false;
+        if (args.readListIfPresent<wordRe>("fields", includedFields))
+        {
+            resetFilter = true;
+            Info<< "Including fields "
+                << flatOutput(includedFields) << nl << endl;
+        }
+        if (args.readListIfPresent<wordRe>("exclude-fields", excludedFields))
+        {
+            resetFilter = true;
+            Info<< "Excluding fields "
+                << flatOutput(excludedFields) << nl << endl;
+        }
+        if (resetFilter)
+        {
+            fieldSelector =
+                autoPtr<wordRes::filter>::New(includedFields, excludedFields);
+        }
+    }
+    else if (doConvertFields)
+    {
+        Info<< "Field conversion disabled with the '-no-fields' option" << nl;
+    }
 
     // ------------------------------------------------------------------------
 
@@ -525,7 +576,6 @@ int main(int argc, char *argv[])
             // Objects at this time
             IOobjectList objects(mesh, runTime.timeName());
 
-            // Restrict to objects that are available for all times
             objects.filterObjects
             (
                 availableRegionObjectNames[regioni]
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/readFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/readFields.H
index 3804b0a3a0b7177a494a75ac0920ab6260764c47..daa27d275eb7fabd8a7e483b1c4f835f97baa258 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/readFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/readFields.H
@@ -81,20 +81,16 @@ tmp<GeoField> getField
 
 //- Convert an internal field to zero-gradient volume field
 template<class Type>
-tmp<GeometricField<Type, fvPatchField, volMesh>>
-makeZeroGradientField
+tmp<VolumeField<Type>> makeZeroGradientField
 (
-    const tmp
-    <
-        typename GeometricField<Type, fvPatchField, volMesh>::Internal
-    >& tdf
+    const tmp<VolumeInternalField<Type>>& tdf
 )
 {
     if (tdf)
     {
         auto& df = tdf.ref();
 
-        auto tfield = GeometricField<Type, fvPatchField, volMesh>::New
+        auto tfield = VolumeField<Type>::New
         (
             df.name(),
             df.mesh(),
@@ -119,17 +115,16 @@ makeZeroGradientField
 
 //- Convert a volume field to zero-gradient volume field
 template<class Type>
-tmp<GeometricField<Type, fvPatchField, volMesh>>
-makeZeroGradientField
+tmp<VolumeField<Type>> makeZeroGradientField
 (
-    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tdf
+    const tmp<VolumeField<Type>>& tdf
 )
 {
     if (tdf)
     {
         auto& df = tdf.ref();
 
-        auto tfield = GeometricField<Type, fvPatchField, volMesh>::New
+        auto tfield = VolumeField<Type>::New
         (
             df.name(),
             df.mesh(),
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeAreaFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeAreaFields.H
index 2e5e1d4fc2131cf67a71f94afe1fdf09e0970b51..e9abb624e967145734246a17f73e2d591944f8a8 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeAreaFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeAreaFields.H
@@ -34,7 +34,7 @@ bool writeAreaField
 (
     ensightCase& ensCase,
     const ensightFaMesh& ensMesh,
-    const tmp<GeometricField<Type, faPatchField, areaMesh>>& tfield
+    const tmp<AreaField<Type>>& tfield
 )
 {
     if (!tfield)
@@ -66,13 +66,13 @@ label writeAreaFields
     const IOobjectList& objects
 )
 {
-    typedef GeometricField<Type, faPatchField, areaMesh> GeoField;
+    typedef AreaField<Type> FieldType;
 
     const faMesh& mesh = ensMesh.mesh();
 
     label count = 0;
 
-    for (const word& fieldName : objects.sortedNames<GeoField>())
+    for (const word& fieldName : objects.sortedNames<FieldType>())
     {
         if
         (
@@ -80,7 +80,7 @@ label writeAreaFields
             (
                 ensCase,
                 ensMesh,
-                getField<GeoField>(objects.findObject(fieldName), mesh)
+                getField<FieldType>(objects.findObject(fieldName), mesh)
             )
         )
         {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeDimFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeDimFields.H
index 35b47ef06ae37e9fb62498c93751a898d49e0228..41b110b11489214e7a1d52914ffcb7742adef0f0 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeDimFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeDimFields.H
@@ -33,7 +33,7 @@ bool writeDimField
 (
     ensightCase& ensCase,
     const ensightMesh& ensMesh,
-    const tmp<DimensionedField<Type, volMesh>>& tdf
+    const tmp<VolumeInternalField<Type>>& tdf
 )
 {
     if (!tdf)
@@ -63,17 +63,13 @@ label writeDimFields
     const IOobjectList& objects
 )
 {
-    typedef typename
-        GeometricField
-        <
-            Type, fvPatchField, volMesh
-        >::Internal DimField;
+    typedef VolumeInternalField<Type> FieldType;
 
     const fvMesh& mesh = dynamicCast<const fvMesh>(ensMesh.mesh());
 
     label count = 0;
 
-    for (const word& fieldName : objects.sortedNames<DimField>())
+    for (const word& fieldName : objects.sortedNames<FieldType>())
     {
         if
         (
@@ -81,7 +77,7 @@ label writeDimFields
             (
                 ensCase,
                 ensMesh,
-                getField<DimField>(objects.findObject(fieldName), mesh)
+                getField<FieldType>(objects.findObject(fieldName), mesh)
             )
         )
         {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writePointFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writePointFields.H
index 714598e81d2a45c34dbeb5db9dd11decd6c14b09..8fb8764ad40ed10fbaa085f0dabc05acf924735e 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writePointFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writePointFields.H
@@ -35,7 +35,7 @@ bool writePointField
 (
     ensightCase& ensCase,
     const ensightMesh& ensMesh,
-    const tmp<GeometricField<Type, pointPatchField, pointMesh>>& tfield
+    const tmp<PointField<Type>>& tfield
 )
 {
     if (!tfield)
@@ -68,13 +68,13 @@ label writePointFields
     const IOobjectList& objects
 )
 {
-    typedef GeometricField<Type, pointPatchField, pointMesh> GeoField;
+    typedef PointField<Type> FieldType;
 
     const pointMesh& ptMesh = pointMesh::New(ensMesh.mesh());
 
     label count = 0;
 
-    for (const word& fieldName : objects.sortedNames<GeoField>())
+    for (const word& fieldName : objects.sortedNames<FieldType>())
     {
         if
         (
@@ -82,7 +82,7 @@ label writePointFields
             (
                 ensCase,
                 ensMesh,
-                getField<GeoField>(ptMesh, objects, fieldName)
+                getField<FieldType>(ptMesh, objects, fieldName)
             )
         )
         {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeVolFields.H b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeVolFields.H
index fbc79a0749a1efce50b52a455b1e93a435befb6f..cb80172b55814c91a2f9035f3c503321f9d173c7 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeVolFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/writeVolFields.H
@@ -34,7 +34,7 @@ bool writeVolField
 (
     ensightCase& ensCase,
     const ensightMesh& ensMesh,
-    const tmp<GeometricField<Type, fvPatchField, volMesh>>& tfield,
+    const tmp<VolumeField<Type>>& tfield,
     const bool nearCellValue = false
 )
 {
@@ -86,13 +86,13 @@ label writeVolFields
     const bool nearCellValue = false
 )
 {
-    typedef GeometricField<Type, fvPatchField, volMesh> GeoField;
+    typedef VolumeField<Type> FieldType;
 
     const fvMesh& mesh = dynamicCast<const fvMesh>(ensMesh.mesh());
 
     label count = 0;
 
-    for (const word& fieldName : objects.sortedNames<GeoField>())
+    for (const word& fieldName : objects.sortedNames<FieldType>())
     {
         if
         (
@@ -100,7 +100,7 @@ label writeVolFields
             (
                 ensCase,
                 ensMesh,
-                getField<GeoField>(objects.findObject(fieldName), mesh),
+                getField<FieldType>(objects.findObject(fieldName), mesh),
                 nearCellValue
             )
         )
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertLagrangian.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertLagrangian.H
index aa92c3bc0a97c4aa68c7ab5cc8486898a3802a95..94659d14dede5683740a408be3ea8f6cfcf61cce 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertLagrangian.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertLagrangian.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,7 +52,6 @@ if (doLagrangian)
     // Consistent order
     Foam::sort(cloudNames);
 
-
     for (const word& cloudName : cloudNames)
     {
         IOobjectList cloudObjs(mesh, runTime.timeName(), cloudPrefix/cloudName);
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertSurfaceFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertSurfaceFields.H
index 6803a25d7461445c20abd4838e2872c7e0a5eae1..6005ffa4a688333bc52d2ab5aa1d56e211595def 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertSurfaceFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertSurfaceFields.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -43,12 +43,7 @@ Description
     {
         if (nSurfaceScalarField == -1)
         {
-            sScalars = readFields<surfaceScalarField>
-            (
-                meshProxy,
-                objects,
-                selectedFields
-            );
+            sScalars = readFields<surfaceScalarField>(meshProxy, objects);
 
             reportFields::print("    surfScalar   :", Info, sScalars);
             nSurfaceScalarField = sScalars.size();
@@ -60,12 +55,7 @@ Description
 
         if (nSurfaceVectorField == -1)
         {
-            sVectors = readFields<surfaceVectorField>
-            (
-                meshProxy,
-                objects,
-                selectedFields
-            );
+            sVectors = readFields<surfaceVectorField>(meshProxy, objects);
 
             reportFields::print("    surfVector   :", Info, sVectors);
             nSurfaceVectorField = sVectors.size();
@@ -155,12 +145,7 @@ Description
     {
         if (nSurfaceScalarField == -1)
         {
-            sScalars = readFields<surfaceScalarField>
-            (
-                meshProxy,
-                objects,
-                selectedFields
-            );
+            sScalars = readFields<surfaceScalarField>(meshProxy, objects);
             nSurfaceScalarField = sScalars.size();
 
             reportFields::print("    surfScalar   :", Info, sScalars);
@@ -172,12 +157,7 @@ Description
 
         if (nSurfaceVectorField == -1)
         {
-            sVectors = readFields<surfaceVectorField>
-            (
-                meshProxy,
-                objects,
-                selectedFields
-            );
+            sVectors = readFields<surfaceVectorField>(meshProxy, objects);
             nSurfaceVectorField = sVectors.size();
 
             reportFields::print("    surfVector   :", Info, sVectors);
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H
index add182f7ecb52cd1e7e5f5efa7aee1ff4d2ecf63..db25c78cdc28bd4892d5b13e6c94e3206d46cc68 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -48,7 +48,7 @@ Description
       : 0
     );
 
-    const label nPointFields =
+    label nPointFields =
     (
         doPointValues
       ? objects.count(stringListOps::foundOp<word>(fieldTypes::point))
@@ -82,11 +82,6 @@ Description
 
     if (doInternal)
     {
-        if (doPointValues)
-        {
-            pInterp.reset(new volPointInterpolation(mesh));
-        }
-
         if (vtuMeshCells.empty())
         {
             // Use the appropriate mesh (baseMesh or subMesh)
@@ -119,6 +114,11 @@ Description
 
         internalWriter->writeTimeValue(mesh.time().value());
         internalWriter->writeGeometry();
+
+        if (doPointValues)
+        {
+            pInterp.reset(new volPointInterpolation(mesh));
+        }
     }
 
 
@@ -132,7 +132,7 @@ Description
     labelList patchIds;
     if (doBoundary)
     {
-        patchIds = getSelectedPatches(patches, includePatches, excludePatches);
+        patchIds = getSelectedPatches(patches, patchSelector);
     }
 
     if (oneBoundary && patchIds.size())
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
index f2e447b5bbb7c475aa0cb486f1602969748df226..9f02a529bccdbb48d9b42ee3d9e24a97918da4ef 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2021 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -112,11 +112,11 @@ Usage
           -patches '( front \".*back\" )'
         \endverbatim
 
-      - \par -excludePatches NAME | LIST
+      - \par -exclude-patches NAME | LIST
         Exclude single or multiple patches (name or regex) from writing.
         For example,
         \verbatim
-          -excludePatches '( inlet_1 inlet_2 "proc.*")'
+          -exclude-patches '( inlet_1 inlet_2 "proc.*")'
         \endverbatim
 
 Note
@@ -169,22 +169,28 @@ Note
 labelList getSelectedPatches
 (
     const polyBoundaryMesh& patches,
-    const wordRes& allow,
-    const wordRes& deny
+    const autoPtr<wordRes::filter>& patchSelector
 )
 {
-    // Name-based selection
-    labelList indices
-    (
-        stringListOps::findMatching
-        (
-            patches,
-            allow,
-            deny,
-            nameOp<polyPatch>()
-        )
-    );
+    labelList indices;
 
+    if (patchSelector && !patchSelector().empty())
+    {
+        // Name-based selection
+        indices =
+        (
+            stringListOps::findMatching
+            (
+                patches,
+                patchSelector(),
+                nameOp<polyPatch>()
+            )
+        );
+    }
+    else
+    {
+        indices = identity(patches.size());
+    }
 
     // Remove undesirable patches
 
@@ -264,6 +270,8 @@ int main(int argc, char *argv[])
     argList::setAdvanced("decomposeParDict");
     argList::setAdvanced("noFunctionObjects");
 
+    argList::addVerboseOption("Additional verbosity");
+
     argList::addBoolOption
     (
         "ascii",
@@ -328,7 +336,6 @@ int main(int argc, char *argv[])
         "Eg, 'cells' or '( slice \"mfp-.*\" )'.",
         true  // mark as an advanced option
     );
-
     argList::addOption
     (
         "fields",
@@ -336,6 +343,18 @@ int main(int argc, char *argv[])
         "Specify single or multiple fields to write (all by default)\n"
         "Eg, 'T' or '(p T U \"alpha.*\")'"
     );
+    argList::addOption
+    (
+        "exclude-fields",
+        "wordRes",
+        "Exclude single or multiple fields",
+        true  // mark as an advanced option
+    );
+    argList::addBoolOption
+    (
+        "no-fields",
+        "Suppress conversion of fields"
+    );
 
     argList::addBoolOption
     (
@@ -351,11 +370,21 @@ int main(int argc, char *argv[])
     );
     argList::addBoolOption
     (
-        "finite-area",
-        "Write finite area fields",
+        "no-finite-area",
+        "Suppress output of finite-area mesh/fields",
         true  // mark as an advanced option
     );
-    argList::addOptionCompat("finite-area", {"finiteAreaFields", 2012});
+    argList::ignoreOptionCompat
+    (
+        {"finite-area", 2112},  // use -no-finite-area to disable
+        false           // bool option, no argument
+    );
+    argList::ignoreOptionCompat
+    (
+        {"finiteAreaFields", 2012},  // use -no-finite-area to disable
+        false           // bool option, no argument
+    );
+
     argList::addBoolOption
     (
         "nearCellValue",
@@ -418,12 +447,14 @@ int main(int argc, char *argv[])
     );
     argList::addOption
     (
-        "excludePatches",
+        "exclude-patches",
         "wordRes",
         "Exclude single or multiple patches from writing\n"
         "Eg, 'outlet' or '( inlet \".*Wall\" )'",
         true  // mark as an advanced option
     );
+    argList::addOptionCompat("exclude-patches", {"excludePatches", 2112});
+
     argList::ignoreOptionCompat
     (
         {"noFaceZones", 1806},  // faceZones are only enabled on demand
@@ -453,13 +484,13 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
+    /// const int optVerbose = args.verbose();
     const bool decomposePoly = args.found("poly-decomp");
     const bool doBoundary    = !args.found("no-boundary");
     const bool doInternal    = !args.found("no-internal");
     const bool doLagrangian  = !args.found("no-lagrangian");
-    const bool doFiniteArea  = args.found("finite-area");
+    const bool doFiniteArea  = !args.found("no-finite-area");
     const bool doSurfaceFields = args.found("surfaceFields");
-
     const bool oneBoundary   = args.found("one-boundary") && doBoundary;
     const bool nearCellValue = args.found("nearCellValue") && doBoundary;
 
@@ -494,7 +525,7 @@ int main(int argc, char *argv[])
             << nl << endl;
     }
 
-    const bool doPointValues = !args.found("no-point-data");
+    bool doPointValues = !args.found("no-point-data");
     if (!doPointValues)
     {
         Info<< "Point fields and interpolated point data"
@@ -521,25 +552,74 @@ int main(int argc, char *argv[])
         Info<< "Writing mesh ids (cell, patch, proc) requested" << nl;
     }
 
-    wordRes includePatches, excludePatches;
+    // Patch selection/deselection
+    wordRes includedPatches, excludedPatches;
+    autoPtr<wordRes::filter> patchSelector(nullptr);
     if (doBoundary)
     {
-        if (args.readListIfPresent<wordRe>("patches", includePatches))
+        bool resetFilter = false;
+        if (args.readListIfPresent<wordRe>("patches", includedPatches))
         {
-            Info<< "Including patches " << flatOutput(includePatches)
-                << nl << endl;
+            resetFilter = true;
+            Info<< "Including patches "
+                << flatOutput(includedPatches) << nl << endl;
         }
-        if (args.readListIfPresent<wordRe>("excludePatches", excludePatches))
+        if (args.readListIfPresent<wordRe>("exclude-patches", excludedPatches))
         {
-            Info<< "Excluding patches " << flatOutput(excludePatches)
-                << nl << endl;
+            resetFilter = true;
+            Info<< "Excluding patches "
+                << flatOutput(excludedPatches) << nl << endl;
+        }
+
+        if (resetFilter)
+        {
+            patchSelector =
+                autoPtr<wordRes::filter>::New(includedPatches, excludedPatches);
         }
     }
 
-    // Can be specified as empty (ie, no fields)
-    wordRes selectedFields;
-    const bool useFieldFilter =
-        args.readListIfPresent<wordRe>("fields", selectedFields);
+    // Field selection/deselection
+    wordRes includedFields, excludedFields;
+    autoPtr<wordRes::filter> fieldSelector(nullptr);
+    bool doConvertFields = !args.found("no-fields");
+    if (doConvertFields)
+    {
+        bool resetFilter = false;
+        if (args.readListIfPresent<wordRe>("fields", includedFields))
+        {
+            Info<< "Including fields "
+                << flatOutput(includedFields) << nl << endl;
+
+            resetFilter = !includedFields.empty();
+
+            if (includedFields.empty())
+            {
+                // Compat: Can be specified as empty (ie, no fields)
+                // Same as "block everything"
+
+                doConvertFields = false;
+                Info<< "Field conversion disabled by '-fields ()' option" << nl
+                    << "Should use -no-fields instead" << endl;
+            }
+        }
+        if (args.readListIfPresent<wordRe>("exclude-fields", excludedFields))
+        {
+            resetFilter = true;
+            Info<< "Excluding fields "
+                << flatOutput(excludedFields) << nl << endl;
+        }
+
+        if (resetFilter && doConvertFields)
+        {
+            fieldSelector =
+                autoPtr<wordRes::filter>::New(includedFields, excludedFields);
+        }
+    }
+    else if (doConvertFields)
+    {
+        Info<< "Field conversion disabled with the '-no-fields' option" << nl;
+    }
+
 
     // Non-mandatory
     const wordRes selectedFaceZones(args.getList<wordRe>("faceZones", false));
@@ -709,28 +789,34 @@ int main(int argc, char *argv[])
                 }
             }
 
-            // Search for list of objects for this time
-            IOobjectList objects(meshProxy.baseMesh(), runTime.timeName());
+            IOobjectList objects(0);
 
-            if (useFieldFilter)
+            if (doConvertFields)
             {
-                objects.filterObjects(selectedFields);
-            }
+                // List of objects for this time
+                objects =
+                    IOobjectList(meshProxy.baseMesh(), runTime.timeName());
 
-            // Prune restart fields
-            objects.prune_0();
+                if (fieldSelector && !fieldSelector().empty())
+                {
+                    objects.filterObjects(fieldSelector());
+                }
 
-            if (!doPointValues)
-            {
-                // Prune point fields if disabled
-                objects.filterClasses
-                (
-                    [](const word& clsName)
-                    {
-                        return fieldTypes::point.found(clsName);
-                    },
-                    true // prune
-                );
+                // Remove "*_0" restart fields
+                objects.prune_0();
+
+                if (!doPointValues)
+                {
+                    // Prune point fields if disabled
+                    objects.filterClasses
+                    (
+                        [](const word& clsName)
+                        {
+                            return fieldTypes::point.found(clsName);
+                        },
+                        true // prune
+                    );
+                }
             }
 
             if (processorFieldsOnly)
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.C
index 40ca84d7095524054865f551639b1f24a4b56d19..3f8311b1c451bcce806f1ab74e051b54c2e09e59 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.C
@@ -94,19 +94,13 @@ template<class GeoField>
 Foam::PtrList<const GeoField> Foam::readFields
 (
     const typename GeoField::Mesh& mesh,
-    const IOobjectList& objects,
-    const wordRes& selection
+    const IOobjectList& objects
 )
 {
     const bool syncPar = true;
 
     // Available fields of type GeoField, sorted order
-    const wordList fieldNames =
-    (
-        selection.empty()
-      ? objects.sortedNames<GeoField>()
-      : objects.sortedNames<GeoField>(selection)
-    );
+    const wordList fieldNames(objects.sortedNames<GeoField>());
 
     // Construct the fields
     PtrList<const GeoField> fields(fieldNames.size());
@@ -133,19 +127,13 @@ template<class GeoField>
 Foam::PtrList<const GeoField> Foam::readFields
 (
     const fvMeshSubsetProxy& proxy,
-    const IOobjectList& objects,
-    const wordRes& selection
+    const IOobjectList& objects
 )
 {
     const bool syncPar = true;
 
     // Available fields of type GeoField, sorted order
-    const wordList fieldNames =
-    (
-        selection.empty()
-      ? objects.sortedNames<GeoField>()
-      : objects.sortedNames<GeoField>(selection)
-    );
+    const wordList fieldNames(objects.sortedNames<GeoField>());
 
     // Construct the fields
     PtrList<const GeoField> fields(fieldNames.size());
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.H
index 9493954449c973ffb159c415645b39ae10b2cc11..f5f15321c57a8fe10f6104697582cf3b8b2003d1 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/readFields.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2018 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,8 +35,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef readFields_H
-#define readFields_H
+#ifndef Foam_readFields_H
+#define Foam_readFields_H
 
 #include "fvMeshSubsetProxy.H"
 #include "IOobjectList.H"
@@ -94,8 +94,7 @@ template<class GeoField>
 PtrList<const GeoField> readFields
 (
     const typename GeoField::Mesh& mesh,
-    const IOobjectList& objects,
-    const wordRes& selection
+    const IOobjectList& objects
 );
 
 
@@ -104,8 +103,7 @@ template<class GeoField>
 PtrList<const GeoField> readFields
 (
     const fvMeshSubsetProxy& proxy,
-    const IOobjectList& objects,
-    const wordRes& selection
+    const IOobjectList& objects
 );
 
 
diff --git a/src/fileFormats/ensight/mesh/ensightMesh.C b/src/fileFormats/ensight/mesh/ensightMesh.C
index 9faae736752c0360f776169c1960eea612b58aba..7b1a28f312eca221fe9817f12d7b2a66ecbb5157 100644
--- a/src/fileFormats/ensight/mesh/ensightMesh.C
+++ b/src/fileFormats/ensight/mesh/ensightMesh.C
@@ -110,7 +110,8 @@ Foam::ensightMesh::ensightMesh
 :
     options_(new options(opts)),
     mesh_(mesh),
-    needsUpdate_(true)
+    needsUpdate_(true),
+    verbose_(0)
 {
     if (!option().lazy())
     {
@@ -121,6 +122,20 @@ Foam::ensightMesh::ensightMesh
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+int Foam::ensightMesh::verbose() const noexcept
+{
+    return verbose_;
+}
+
+
+int Foam::ensightMesh::verbose(const int level) noexcept
+{
+    int old(verbose_);
+    verbose_ = level;
+    return old;
+}
+
+
 void Foam::ensightMesh::correct()
 {
     clear();
@@ -214,6 +229,10 @@ void Foam::ensightMesh::correct()
 
             // Finalize
             part.reduce();
+            if (verbose_)
+            {
+                Info<< part.info();
+            }
         }
     }
 
@@ -235,6 +254,10 @@ void Foam::ensightMesh::correct()
 
             // Finalize
             part.reduce();
+            if (verbose_)
+            {
+                Info<< part.info();
+            }
         }
         else
         {
@@ -253,6 +276,10 @@ void Foam::ensightMesh::correct()
 
                 // Finalize
                 part.reduce();
+                if (verbose_)
+                {
+                    Info<< part.info();
+                }
             }
         }
 
@@ -345,7 +372,10 @@ void Foam::ensightMesh::correct()
 
         // Finalize
         part.reduce();
-
+        if (verbose_)
+        {
+            Info<< part.info();
+        }
         if (!part.total())
         {
             boundaryParts_.erase(patchId);
@@ -379,7 +409,10 @@ void Foam::ensightMesh::correct()
 
         // Finalize
         part.reduce();
-
+        if (verbose_)
+        {
+            Info<< part.info();
+        }
         if (!part.total())
         {
             faceZoneParts_.erase(zoneId);
diff --git a/src/fileFormats/ensight/mesh/ensightMesh.H b/src/fileFormats/ensight/mesh/ensightMesh.H
index 6c0427f9772ffb6608f90d2223bba69dd5517aa8..fb61d716ebd2efce8df315a32ed0ab17138ebb03 100644
--- a/src/fileFormats/ensight/mesh/ensightMesh.H
+++ b/src/fileFormats/ensight/mesh/ensightMesh.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -57,8 +57,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef ensightMesh_H
-#define ensightMesh_H
+#ifndef Foam_ensightMesh_H
+#define Foam_ensightMesh_H
 
 #include "Map.H"
 #include "ensightCells.H"
@@ -114,6 +114,9 @@ private:
         //- Track if it needs an update
         mutable bool needsUpdate_;
 
+        //- Output verbosity level
+        int verbose_;
+
 
     // Private Member Functions
 
@@ -143,6 +146,14 @@ public:
 
     // Member Functions
 
+        //- Output verbosity level
+        int verbose() const noexcept;
+
+        //- Change the output verbosity level.
+        //  \return old level
+        int verbose(const int level) noexcept;
+
+
     // Access
 
         //- Reference to the underlying polyMesh
diff --git a/src/fileFormats/ensight/output/ensightOutput.C b/src/fileFormats/ensight/output/ensightOutput.C
index 61045f2ef4165596ce651a71255996bb5326ef7b..27e12cba9f1df56d8a4dc6eb75b69ce1637980c7 100644
--- a/src/fileFormats/ensight/output/ensightOutput.C
+++ b/src/fileFormats/ensight/output/ensightOutput.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,7 +26,6 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "ensightOutput.H"
-
 #include "cell.H"
 #include "cellShape.H"
 #include "face.H"
@@ -133,20 +132,29 @@ Foam::labelList Foam::ensightOutput::Detail::getPolysNPointsPerFace
 }
 
 
-void Foam::ensightOutput::writeFaceList
+void Foam::ensightOutput::Detail::writeLabelListList
 (
     ensightGeoFile& os,
-    const UList<face>& faces
+    const labelUList& offsets,
+    const labelUList& values,
+    const label pointOffset
 )
 {
-    for (const face& f : faces)
+    const label off = (pointOffset + 1);  // 1-based for Ensight
+
+    const label nLists = (offsets.size() - 1);
+
+    for (label i = 0; i < nLists; ++i)
     {
-        for (const label labi : f)
+        const labelUList list
+        (
+            values.slice(offsets[i], (offsets[i+i] - offsets[i]))
+        );
+        for (const label pointi : list)
         {
-            os.write(labi + 1);
+            os.write(pointi + off);
         }
-
-        os.newline();
+        os.newline();  // One list (cell/faces) per line (ASCII)
     }
 }
 
@@ -154,40 +162,119 @@ void Foam::ensightOutput::writeFaceList
 void Foam::ensightOutput::writeFaceList
 (
     ensightGeoFile& os,
-    const UIndirectList<face>& faces
+    const UList<face>& faces,
+    const label pointOffset
 )
 {
-    for (const face& f : faces)
-    {
-        for (const label labi : f)
-        {
-            os.write(labi + 1);
-        }
+    ensightOutput::Detail::writeLabelListList(os, faces, pointOffset);
+}
 
-        os.newline();
-    }
+
+void Foam::ensightOutput::writeFaceList
+(
+    ensightGeoFile& os,
+    const UIndirectList<face>& faces,
+    const label pointOffset
+)
+{
+    ensightOutput::Detail::writeLabelListList(os, faces, pointOffset);
+}
+
+
+void Foam::ensightOutput::writeFaceList
+(
+    ensightGeoFile& os,
+    const CompactListList<label>& faces,
+    const label pointOffset
+)
+{
+    ensightOutput::Detail::writeLabelListList(os, faces, pointOffset);
 }
 
 
 void Foam::ensightOutput::writeCellShapes
 (
     ensightGeoFile& os,
-    const UList<cellShape>& shapes
+    const UList<cellShape>& shapes,
+    const label pointOffset
+)
+{
+    ensightOutput::Detail::writeLabelListList(os, shapes, pointOffset);
+}
+
+
+Foam::CompactListList<Foam::label>
+Foam::ensightOutput::Detail::getPolysFacePoints
+(
+    const polyMesh& mesh,
+    const labelUList& addr,
+    const labelList& pointMap
 )
 {
-    for (const cellShape& cellPoints : shapes)
+    const cellList& meshCells = mesh.cells();
+    const faceList& meshFaces = mesh.faces();
+    const labelList& owner = mesh.faceOwner();
+
+
+    // The caller should have already checked for possible overflow,
+    // so can skip that here.
+    // but still need the sizing for allocations
+
+    label nFaces = 0, nPoints = 0;
+    for (const label cellId : addr)
     {
-        // Convert global -> local index
-        // (note: Ensight indices start with 1)
+        nFaces += meshCells[cellId].size();
 
-        // In ASCII, write one cell per line
-        for (const label pointi : cellPoints)
+        for (const label faceId : meshCells[cellId])
         {
-            os.write(pointi + 1);
+            nPoints += meshFaces[faceId].size();
         }
+    }
 
-        os.newline();
+
+    CompactListList<label> compact(nFaces, nPoints);
+    labelList& offsets = compact.offsets();
+    labelList& verts = compact.values();
+
+    // Restart counts
+    nFaces = nPoints = 0;
+
+    for (const label cellId : addr)
+    {
+        for (const label faceId : meshCells[cellId])
+        {
+            const face& f = meshFaces[faceId];
+
+            offsets[nFaces++] = nPoints;
+
+            if (faceId < owner.size() && owner[faceId] != cellId)
+            {
+                // The neighbour of an internal face
+                // - handle like face::reverseFace()
+
+                verts[nPoints++] = pointMap[f[0]];
+                for (label pti = f.size()-1; pti > 0; --pti)
+                {
+                    verts[nPoints++] = pointMap[f[pti]];
+                }
+            }
+            else
+            {
+                for (const label pointi : f)
+                {
+                    verts[nPoints++] = pointMap[pointi];
+                }
+            }
+        }
+    }
+
+    // Finally
+    if (nFaces)
+    {
+        offsets[nFaces] = nPoints;
     }
+
+    return compact;
 }
 
 
@@ -203,6 +290,8 @@ void Foam::ensightOutput::writePolysPoints
     const faceList& meshFaces = mesh.faces();
     const labelList& owner = mesh.faceOwner();
 
+    const label off = (1);  // 1-based for Ensight
+
     for (const label cellId : addr)
     {
         for (const label faceId : meshCells[cellId])
@@ -214,21 +303,21 @@ void Foam::ensightOutput::writePolysPoints
                 // The neighbour of an internal face
                 // - write as face::reverseFace()
 
-                os.write(pointMap[f[0]] + 1);
+                os.write(pointMap[f[0]] + off);
                 for (label pti = f.size()-1; pti > 0; --pti)
                 {
-                    os.write(pointMap[f[pti]] + 1);
+                    os.write(pointMap[f[pti]] + off);
                 }
             }
             else
             {
                 for (const label pointi : f)
                 {
-                    os.write(pointMap[pointi] + 1);
+                    os.write(pointMap[pointi] + off);
                 }
             }
 
-            os.newline();
+            os.newline();  // One face per line (ASCII)
         }
     }
 }
@@ -243,6 +332,8 @@ void Foam::ensightOutput::writePolysPoints
     const labelUList& owner
 )
 {
+    const label off = (1);  // 1-based for Ensight
+
     for (const label cellId : addr)
     {
         for (const label faceId : meshCells[cellId])
@@ -254,21 +345,21 @@ void Foam::ensightOutput::writePolysPoints
                 // The neighbour of an internal face
                 // - write as face::reverseFace()
 
-                os.write(f[0] + 1);
+                os.write(f[0] + off);
                 for (label pti = f.size()-1; pti > 0; --pti)
                 {
-                    os.write(f[pti] + 1);
+                    os.write(f[pti] + off);
                 }
             }
             else
             {
                 for (const label pointi : f)
                 {
-                    os.write(pointi + 1);
+                    os.write(pointi + off);
                 }
             }
 
-            os.newline();
+            os.newline();  // One face per line (ASCII)
         }
     }
 }
diff --git a/src/fileFormats/ensight/output/ensightOutput.H b/src/fileFormats/ensight/output/ensightOutput.H
index eeb34f3aca2e89a29f3a295a72e5af5cb5335e00..a9833d2de1703645bd6f48e624f4b7e4302b6b0d 100644
--- a/src/fileFormats/ensight/output/ensightOutput.H
+++ b/src/fileFormats/ensight/output/ensightOutput.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2021 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,8 +35,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef ensightOutput_H
-#define ensightOutput_H
+#ifndef Foam_ensightOutput_H
+#define Foam_ensightOutput_H
 
 #include "ensightFile.H"
 #include "ensightGeoFile.H"
@@ -50,6 +50,7 @@ SourceFiles
 #include "ListOps.H"
 #include "ListListOps.H"
 #include "IndirectList.H"
+#include "CompactListList.H"
 #include "DynamicList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -127,21 +128,32 @@ namespace ensightOutput
 void writeFaceList
 (
     ensightGeoFile& os,
-    const UList<face>& faces
+    const UList<face>& faces,
+    const label pointOffset = 0  //!< Additional point offset for each face
 );
 
-//- Write list of faces
+//- Write list of faces (indirect addressing)
+void writeFaceList
+(
+    ensightGeoFile& os,
+    const UIndirectList<face>& faces,
+    const label pointOffset = 0  //!< Additional point offset for each face
+);
+
+//- Write list of faces (stored in compact form)
 void writeFaceList
 (
     ensightGeoFile& os,
-    const UIndirectList<face>& faces
+    const CompactListList<label>& faces,
+    const label pointOffset = 0  //!< Additional point offset for each face
 );
 
 //- Write cell connectivity via cell shapes
 void writeCellShapes
 (
     ensightGeoFile& os,
-    const UList<cellShape>& shapes
+    const UList<cellShape>& shapes,
+    const label pointOffset = 0  //!< Additional point offset
 );
 
 
@@ -260,6 +272,35 @@ labelList getPolysNFaces(const polyMesh& mesh, const labelUList& addr);
 //- The number of points for each face of the poly elements
 labelList getPolysNPointsPerFace(const polyMesh& mesh, const labelUList& addr);
 
+//- Generate 0-based point ids for each poly element face
+//  The returned CompactListList is divided per output face
+CompactListList<label> getPolysFacePoints
+(
+    const polyMesh& mesh,
+    const labelUList& addr,     //!< Cell ids to write
+    const labelList& pointMap   //!< Point map to use
+);
+
+
+//- Write CompactListList<label> by components
+void writeLabelListList
+(
+    ensightGeoFile& os,
+    const labelUList& offsets,
+    const labelUList& values,
+    const label pointOffset
+);
+
+
+//- Write a list of faces or cell shapes with one-entity per line
+template<class LabelListListType>
+void writeLabelListList
+(
+    ensightGeoFile& os,
+    const LabelListListType& listOfLists,
+    const label pointOffset
+);
+
 
 //- Copy specified field component into a scalar buffer
 //- works for various lists types. Must be adequately sized before calling
diff --git a/src/fileFormats/ensight/output/ensightOutputTemplates.C b/src/fileFormats/ensight/output/ensightOutputTemplates.C
index 9f7b5b96a0bacb4e4e1a07e744ed7c66e1c0a53d..6b43f52c1ebc4ceebbffb49e37d089adf3531764 100644
--- a/src/fileFormats/ensight/output/ensightOutputTemplates.C
+++ b/src/fileFormats/ensight/output/ensightOutputTemplates.C
@@ -31,6 +31,27 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+template<class LabelListListType>
+void Foam::ensightOutput::Detail::writeLabelListList
+(
+    ensightGeoFile& os,
+    const LabelListListType& listOfLists,
+    const label pointOffset
+)
+{
+    const label off = (pointOffset + 1);  // 1-based for Ensight
+
+    forAll(listOfLists, listi)
+    {
+        for (const label pointi : listOfLists[listi])
+        {
+            os.write(pointi + off);
+        }
+        os.newline();  // One list (cell/faces) per line (ASCII)
+    }
+}
+
+
 template<template<typename> class FieldContainer, class Type>
 void Foam::ensightOutput::Detail::copyComponent
 (
diff --git a/src/fileFormats/ensight/part/cells/ensightCells.H b/src/fileFormats/ensight/part/cells/ensightCells.H
index e24e712ad5ba65ed2d057930dc4e5dd7c1bf3cfb..c36a6222e6eb05734d51109620c0ebae4b545375 100644
--- a/src/fileFormats/ensight/part/cells/ensightCells.H
+++ b/src/fileFormats/ensight/part/cells/ensightCells.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,8 +32,8 @@ Description
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef ensightCells_H
-#define ensightCells_H
+#ifndef Foam_ensightCells_H
+#define Foam_ensightCells_H
 
 #include "ensightPart.H"
 #include "FixedList.H"
@@ -47,6 +47,7 @@ namespace Foam
 // Forward Declarations
 class bitSet;
 class polyMesh;
+template<class T> class InfoProxy;
 
 /*---------------------------------------------------------------------------*\
                         Class ensightCells Declaration
@@ -161,7 +162,6 @@ public:
     virtual ~ensightCells() = default;
 
 
-
     // Member Functions
 
     // Access
@@ -245,6 +245,10 @@ public:
 
     // Output
 
+        //- Return info proxy
+        InfoProxy<ensightCells> info() const { return *this; }
+
+
         //- Globally unique mesh points. Required when writing point fields.
         label uniqueMeshPoints
         (
@@ -268,6 +272,10 @@ public:
 };
 
 
+template<>
+Ostream& operator<<(Ostream&, const InfoProxy<ensightCells>&);
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/fileFormats/ensight/part/cells/ensightCellsAddr.C b/src/fileFormats/ensight/part/cells/ensightCellsAddr.C
index 09c9f62af90adca1fda9a67b6ab96ab0198751b5..7633ba2679828c5328f49d7b173d2a2d09b49cb7 100644
--- a/src/fileFormats/ensight/part/cells/ensightCellsAddr.C
+++ b/src/fileFormats/ensight/part/cells/ensightCellsAddr.C
@@ -106,7 +106,7 @@ Foam::label Foam::ensightCells::meshPointMapppings
                     uniqueMeshPointLabels
                 );
 
-            nPoints = globalPointsPtr().size();   // nPoints (global)
+            nPoints = globalPointsPtr().totalSize();   // nPoints (global)
         }
         else
         {
@@ -125,7 +125,7 @@ Foam::label Foam::ensightCells::meshPointMapppings
                     uniqueMeshPointLabels
                 );
 
-            nPoints = globalPointsPtr().size();   // nPoints (global)
+            nPoints = globalPointsPtr().totalSize();   // nPoints (global)
 
             meshPointMap.clear();
 
diff --git a/src/fileFormats/ensight/part/cells/ensightCellsIO.C b/src/fileFormats/ensight/part/cells/ensightCellsIO.C
index 43c1133f82507859b0ad9b1377f3ccae7f96421a..1d640065428da93fadcd8065eb1cc4d3ce2fb1dd 100644
--- a/src/fileFormats/ensight/part/cells/ensightCellsIO.C
+++ b/src/fileFormats/ensight/part/cells/ensightCellsIO.C
@@ -27,6 +27,7 @@ License
 
 #include "ensightCells.H"
 #include "ensightOutput.H"
+#include "InfoProxy.H"
 #include "polyMesh.H"
 #include "globalIndex.H"
 #include "globalMeshData.H"
@@ -326,4 +327,30 @@ void Foam::ensightCells::write
 }
 
 
+// * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
+
+template<>
+Foam::Ostream& Foam::operator<<
+(
+    Ostream& os,
+    const InfoProxy<ensightCells>& ip
+)
+{
+    const ensightCells& part = ip.t_;
+
+    os << part.name().c_str();
+
+    for (label typei=0; typei < ensightCells::nTypes; ++typei)
+    {
+        const auto etype = ensightCells::elemType(typei);
+
+        os  << ' ' << ensightCells::elemNames[etype]
+            << ':' << part.total(etype);
+    }
+    os  << nl;
+
+    return os;
+}
+
+
 // ************************************************************************* //
diff --git a/src/fileFormats/ensight/part/faces/ensightFaces.H b/src/fileFormats/ensight/part/faces/ensightFaces.H
index 38b804859e076d8bcff72be2091da022fe443cf6..275fc3b1dd3f38366456bca5e96e29e4a5dc8ccd 100644
--- a/src/fileFormats/ensight/part/faces/ensightFaces.H
+++ b/src/fileFormats/ensight/part/faces/ensightFaces.H
@@ -49,8 +49,8 @@ Description
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef ensightFaces_H
-#define ensightFaces_H
+#ifndef Foam_ensightFaces_H
+#define Foam_ensightFaces_H
 
 #include "ensightPart.H"
 #include "face.H"
@@ -64,6 +64,7 @@ namespace Foam
 
 // Forward Declarations
 class polyMesh;
+template<class T> class InfoProxy;
 
 /*---------------------------------------------------------------------------*\
                         Class ensightFaces Declaration
@@ -243,6 +244,10 @@ public:
 
     // Output
 
+        //- Return info proxy
+        InfoProxy<ensightFaces> info() const { return *this; }
+
+
         //- Globally unique mesh points.
         //- Required when writing point fields.
         label uniqueMeshPoints
@@ -266,6 +271,10 @@ public:
 };
 
 
+template<>
+Ostream& operator<<(Ostream&, const InfoProxy<ensightFaces>&);
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/fileFormats/ensight/part/faces/ensightFacesAddr.C b/src/fileFormats/ensight/part/faces/ensightFacesAddr.C
index 2f80cde05ffa3cf99b1b164a1f7e707c47ab7d40..79273ba508682b468a2f5b8dd5b2c7f7807f2660 100644
--- a/src/fileFormats/ensight/part/faces/ensightFacesAddr.C
+++ b/src/fileFormats/ensight/part/faces/ensightFacesAddr.C
@@ -76,7 +76,7 @@ Foam::label Foam::ensightFaces::uniqueMeshPoints
                 uniqueMeshPointLabels
             );
 
-        nPoints = globalPointsPtr().size();  // nPoints (global)
+        nPoints = globalPointsPtr().totalSize();  // nPoints (global)
     }
     else
     {
diff --git a/src/fileFormats/ensight/part/faces/ensightFacesIO.C b/src/fileFormats/ensight/part/faces/ensightFacesIO.C
index 4ce459eddb62f3f080fc0e42a3f3435b1a24ebed..3e9b741d98d29dffffb3c2f4ddb3a6d23add9e01 100644
--- a/src/fileFormats/ensight/part/faces/ensightFacesIO.C
+++ b/src/fileFormats/ensight/part/faces/ensightFacesIO.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,7 +27,7 @@ License
 
 #include "ensightFaces.H"
 #include "ensightOutput.H"
-
+#include "InfoProxy.H"
 #include "polyMesh.H"
 #include "globalIndex.H"
 #include "globalMeshData.H"
@@ -78,7 +78,7 @@ void Foam::ensightFaces::write
                 uniqueMeshPointLabels
             );
 
-        nPoints = globalPointsPtr().size();  // nPoints (global)
+        nPoints = globalPointsPtr().totalSize();  // nPoints (global)
     }
     else
     {
@@ -135,4 +135,30 @@ void Foam::ensightFaces::write
 }
 
 
+// * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
+
+template<>
+Foam::Ostream& Foam::operator<<
+(
+    Ostream& os,
+    const InfoProxy<ensightFaces>& ip
+)
+{
+    const ensightFaces& part = ip.t_;
+
+    os << part.name().c_str();
+
+    for (label typei=0; typei < ensightFaces::nTypes; ++typei)
+    {
+        const auto etype = ensightFaces::elemType(typei);
+
+        os  << ' ' << ensightFaces::elemNames[etype]
+            << ':' << part.total(etype);
+    }
+    os  << nl;
+
+    return os;
+}
+
+
 // ************************************************************************* //
diff --git a/src/finiteArea/output/ensight/ensightFaMesh.C b/src/finiteArea/output/ensight/ensightFaMesh.C
index 09a88c5332a31ebba9db422e9eac43561dc38101..601aa319989dcca392ec82304a4715c83fd36657 100644
--- a/src/finiteArea/output/ensight/ensightFaMesh.C
+++ b/src/finiteArea/output/ensight/ensightFaMesh.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2021 OpenCFD Ltd.
+    Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -53,7 +53,8 @@ Foam::ensightFaMesh::ensightFaMesh
 )
 :
     mesh_(mesh),
-    needsUpdate_(true)
+    needsUpdate_(true),
+    verbose_(0)
 {
     // Lazy?
     if (true)
@@ -65,6 +66,20 @@ Foam::ensightFaMesh::ensightFaMesh
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+int Foam::ensightFaMesh::verbose() const noexcept
+{
+    return verbose_;
+}
+
+
+int Foam::ensightFaMesh::verbose(const int level) noexcept
+{
+    int old(verbose_);
+    verbose_ = level;
+    return old;
+}
+
+
 void Foam::ensightFaMesh::correct()
 {
     clear();
@@ -87,6 +102,11 @@ void Foam::ensightFaMesh::correct()
         // Finalize
         part.reduce();
 
+        if (verbose_)
+        {
+            Info<< part.info();
+        }
+
         // if (!part.total())
         // {
         //     areaParts_.erase(areaId);
diff --git a/src/finiteArea/output/ensight/ensightFaMesh.H b/src/finiteArea/output/ensight/ensightFaMesh.H
index ee42b6aa240d8839cde63786d89816bf0557404a..394b4d5463eaff73c661d63fa0a4f17be24b5608 100644
--- a/src/finiteArea/output/ensight/ensightFaMesh.H
+++ b/src/finiteArea/output/ensight/ensightFaMesh.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2021 OpenCFD Ltd.
+    Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,8 +41,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef fa_ensightMesh_H
-#define fa_ensightMesh_H
+#ifndef Foam_fa_ensightMesh_H
+#define Foam_fa_ensightMesh_H
 
 #include "ensightFaces.H"
 
@@ -73,6 +73,9 @@ class ensightFaMesh
         //- Track if it needs an update
         mutable bool needsUpdate_;
 
+        //- Output verbosity level
+        int verbose_;
+
 
     // Private Member Functions
 
@@ -99,6 +102,14 @@ public:
 
     // Member Functions
 
+        //- Output verbosity level
+        int verbose() const noexcept;
+
+        //- Change the output verbosity level.
+        //  \return old level
+        int verbose(const int level) noexcept;
+
+
     // Access
 
         //- Reference to the underlying faMesh