diff --git a/applications/utilities/finiteArea/checkFaMesh/checkFaMesh.C b/applications/utilities/finiteArea/checkFaMesh/checkFaMesh.C
index bb34931e9a50ade7a1a5d060a76cfd6cd5e992ea..a31468da21f7d58f17417c928327437dab463dfc 100644
--- a/applications/utilities/finiteArea/checkFaMesh/checkFaMesh.C
+++ b/applications/utilities/finiteArea/checkFaMesh/checkFaMesh.C
@@ -95,7 +95,8 @@ int main(int argc, char *argv[])
 
     Info<< "Time = " << runTime.timeName() << nl << endl;
 
-    #include "printMeshSummary.H"
+    // Mesh information (verbose)
+    faMeshTools::printMeshChecks(aMesh);
 
     if (args.found("write-vtk"))
     {
diff --git a/applications/utilities/finiteArea/makeFaMesh/decomposeFaFields.H b/applications/utilities/finiteArea/makeFaMesh/decomposeFaFields.H
index 00068a1deab9fcd915bad3d1d95180d060c5f7ce..27d1f4d0f0406d0867c6c69cafc2eb3087e155a6 100644
--- a/applications/utilities/finiteArea/makeFaMesh/decomposeFaFields.H
+++ b/applications/utilities/finiteArea/makeFaMesh/decomposeFaFields.H
@@ -30,44 +30,63 @@ do
         break;
     }
 
-    reconstructor.writeAddressing();
+    reconstructor.writeMesh();          // Writes on master only
+    reconstructor.writeAddressing();    // Writes per-proc
 
-    Info<< "Wrote proc-addressing" << nl << endl;
+    Info<< "Wrote proc-addressing and serial mesh" << nl << endl;
 
     // Handle area fields
     // ------------------
 
     faFieldDecomposer::fieldsCache areaFieldsCache;
 
-    const faMesh& fullMesh = reconstructor.mesh();
+    const faMesh& serialMesh = reconstructor.mesh();
 
+    if (doDecompFields)
     {
-        // Use uncollated (or master uncollated) file handler here.
-        // - each processor is reading in the identical serial fields.
-        // - nothing should be parallel-coordinated.
-
-        // Similarly, if we write the serial finite-area mesh, this is only
-        // done from one processor!
-
-        reconstructor.writeMesh();
-
-        if (doDecompFields)
+        // The serial finite-area mesh exists and is identical on all
+        // processors, but its fields can only reliably be read on the
+        // master (eg, running with distributed roots).
+        //
+        // - mark mesh fields as readable on master only (haveMeshOnProc)
+        // - 'subset' entire serial mesh so that a full copy will be
+        //   broadcast to other ranks (subsetterPtr)
+        // - scan available IOobjects on the master only
+
+        bitSet haveMeshOnProc;
+        std::unique_ptr<faMeshSubset> subsetter;
+        IOobjectList objects(0);
+
+        const bool oldDistributed = fileHandler().distributed();
+        auto oldHandler = fileHandler(fileOperation::NewUncollated());
+        fileHandler().distributed(true);
+
+        if (Pstream::master())
         {
-            const bool oldDistributed = fileHandler().distributed();
-            auto oldHandler = fileHandler(fileOperation::NewUncollated());
-            fileHandler().distributed(true);
+            haveMeshOnProc.set(Pstream::myProcNo());
+            subsetter.reset(new faMeshSubset(serialMesh));
 
-            IOobjectList objects(fullMesh.time(), runTime.timeName());
+            const bool oldParRun = Pstream::parRun(false);
 
-            areaFieldsCache.readAllFields(fullMesh, objects);
+            objects = IOobjectList(serialMesh.time(), runTime.timeName());
 
-            // Restore old settings
-            if (oldHandler)
-            {
-                fileHandler(std::move(oldHandler));
-            }
-            fileHandler().distributed(oldDistributed);
+            Pstream::parRun(oldParRun);
         }
+
+        // Restore old settings
+        if (oldHandler)
+        {
+            fileHandler(std::move(oldHandler));
+        }
+        fileHandler().distributed(oldDistributed);
+
+        areaFieldsCache.readAllFields
+        (
+            haveMeshOnProc,
+            subsetter.get(),
+            serialMesh,
+            objects
+        );
     }
 
     const label nAreaFields = areaFieldsCache.size();
@@ -78,7 +97,7 @@ do
 
         faFieldDecomposer fieldDecomposer
         (
-            fullMesh,
+            serialMesh,
             aMesh,
             reconstructor.edgeProcAddressing(),
             reconstructor.faceProcAddressing(),
diff --git a/applications/utilities/finiteArea/makeFaMesh/makeFaMesh.C b/applications/utilities/finiteArea/makeFaMesh/makeFaMesh.C
index ca6c56eca39700a8397b08cfc5677ab894a5ed64..9fcd6e5c0e6411d8f1c01f3168be7cafec082568 100644
--- a/applications/utilities/finiteArea/makeFaMesh/makeFaMesh.C
+++ b/applications/utilities/finiteArea/makeFaMesh/makeFaMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016-2017 Wikki Ltd
-    Copyright (C) 2021 OpenCFD Ltd.
+    Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,13 +42,14 @@ Original Authors
 #include "argList.H"
 #include "OSspecific.H"
 #include "faMesh.H"
+#include "faMeshTools.H"
 #include "IOdictionary.H"
 #include "IOobjectList.H"
-
 #include "areaFields.H"
 #include "edgeFields.H"
 #include "faFieldDecomposer.H"
 #include "faMeshReconstructor.H"
+#include "faMeshSubset.H"
 #include "PtrListOps.H"
 #include "foamVtkIndPatchWriter.H"
 #include "OBJstream.H"
@@ -131,8 +132,8 @@ int main(int argc, char *argv[])
     // Create
     faMesh aMesh(mesh, meshDefDict);
 
-    // Mesh information
-    #include "printMeshSummary.H"
+    // Mesh information (less verbose)
+    faMeshTools::printMeshChecks(aMesh, 0);
 
     if (args.found("write-edges-obj"))
     {
diff --git a/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H b/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H
deleted file mode 100644
index 084759849fe67b2c1af4e8b66817ffa6b214baef..0000000000000000000000000000000000000000
--- a/applications/utilities/finiteArea/makeFaMesh/printMeshSummary.H
+++ /dev/null
@@ -1,104 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2021-2022 OpenCFD Ltd.
--------------------------------------------------------------------------------
-License
-    This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
-
-Description
-    Summary of faMesh information
-
-\*---------------------------------------------------------------------------*/
-
-{
-    const faBoundaryMesh& patches = aMesh.boundary();
-    const label nNonProcessor = patches.nNonProcessor();
-    const label nPatches = patches.size();
-
-    Info<< "----------------" << nl
-        << "Mesh Information" << nl
-        << "----------------" << nl
-        << "  " << "boundingBox: " << boundBox(aMesh.points()) << nl
-        << "  " << "nFaces: " << returnReduce(aMesh.nFaces(), sumOp<label>())
-        << nl;
-
-
-    Info<< "----------------" << nl
-        << "Patches" << nl
-        << "----------------" << nl;
-
-    for (label patchi = 0; patchi < nNonProcessor; ++patchi)
-    {
-        const faPatch& p = patches[patchi];
-
-        // Report physical size (nEdges) not virtual size
-        Info<< "  " << "patch " << p.index()
-            << " (size: " << returnReduce(p.nEdges(), sumOp<label>())
-            << ") name: " << p.name()
-            << nl;
-    }
-
-    Info<< "----------------" << nl
-        << "Used polyPatches: " << flatOutput(aMesh.whichPolyPatches()) << nl;
-
-
-    // Geometry information
-    Info<< nl;
-    {
-        scalarMinMax limit(gMinMax(aMesh.S().field()));
-        Info<< "Face area:" << nl
-            << "    min = " << limit.min() << " max = "  << limit.max() << nl;
-    }
-
-    {
-        scalarMinMax limit(minMax(aMesh.magLe().primitiveField()));
-
-        // Include processor boundaries into 'internal' edges
-        if (Pstream::parRun())
-        {
-            for (label patchi = nNonProcessor; patchi < nPatches; ++patchi)
-            {
-                limit.add(minMax(aMesh.magLe().boundaryField()[patchi]));
-            }
-
-            reduce(limit, minMaxOp<scalar>());
-        }
-
-        Info<< "Edge length (internal):" << nl
-            << "    min = " << limit.min() << " max = "  << limit.max() << nl;
-
-
-        // Include (non-processor) boundaries
-        for (label patchi = 0; patchi < nNonProcessor; ++patchi)
-        {
-            limit.add(minMax(aMesh.magLe().boundaryField()[patchi]));
-        }
-
-        if (Pstream::parRun())
-        {
-            reduce(limit, minMaxOp<scalar>());
-        }
-
-        Info<< "Edge length:" << nl
-            << "    min = " << limit.min()
-            << " max = "  << limit.max() << nl;
-    }
-
-    // Not particularly meaningful
-    #if 0
-    {
-        MinMax<vector> limit(gMinMax(aMesh.faceAreaNormals().field()));
-
-        Info<< "Face area normals:" << nl
-            << "    min = " << limit.min() << " max = " << limit.max() << nl;
-    }
-    #endif
-}
-
-
-// ************************************************************************* //
diff --git a/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributor.H b/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributor.H
index 6f84e399f7cfe67d5de66a18e7e388768265c9d6..aef9d63b55bf62e69c7b978bab584c92fa2311e9 100644
--- a/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributor.H
+++ b/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributor.H
@@ -38,6 +38,7 @@ SourceFiles
 #define Foam_fieldsDistributor_H
 
 #include "IOobjectList.H"
+#include "bitSet.H"
 #include "boolList.H"
 #include "PtrList.H"
 #include "GeometricField.H"
@@ -53,6 +54,22 @@ namespace Foam
 
 class fieldsDistributor
 {
+    // Private Methods
+
+    //- Read volume/surface/point/area fields that may or may not exist
+    //- on all processors
+    template<class BoolListType, class GeoField, class MeshSubsetter>
+    static void readFieldsImpl
+    (
+        const BoolListType& haveMeshOnProc,
+        const MeshSubsetter* subsetter,
+        const typename GeoField::Mesh& mesh,
+        IOobjectList& allObjects,
+        PtrList<GeoField>& fields,
+        const bool deregister
+    );
+
+
 public:
 
     // Reading helpers
@@ -103,6 +120,32 @@ public:
     );
 
 
+    //- Read volume/surface/point/area fields that may or may not exist
+    //- on all processors
+    template<class GeoField, class MeshSubsetter>
+    static void readFields
+    (
+        const bitSet& haveMeshOnProc,
+        const MeshSubsetter* subsetter,
+        const typename GeoField::Mesh& mesh,
+        IOobjectList& allObjects,
+        PtrList<GeoField>& fields,
+        const bool deregister = false
+    );
+
+    //- Read volume/surface/point/area fields that may or may not exist
+    //- on all processors
+    template<class GeoField, class MeshSubsetter>
+    static void readFields
+    (
+        const boolList& haveMeshOnProc,
+        const MeshSubsetter* subsetter,
+        const typename GeoField::Mesh& mesh,
+        IOobjectList& allObjects,
+        PtrList<GeoField>& fields,
+        const bool deregister = false
+    );
+
     //- Read volume/surface/point/area fields that may or may not exist
     //- on all processors
     template<class GeoField, class MeshSubsetter>
@@ -110,7 +153,7 @@ public:
     (
         const boolList& haveMeshOnProc,
         const typename GeoField::Mesh& mesh,
-        const autoPtr<MeshSubsetter>& subsetterPtr,
+        const autoPtr<MeshSubsetter>& subsetter,
         IOobjectList& allObjects,
         PtrList<GeoField>& fields,
         const bool deregister = false
diff --git a/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributorTemplates.C b/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributorTemplates.C
index be6cab6944283b999c5c842299a3dff5ba8b2117..acd6ee1c3d2823f495144e2463b26be2487752e2 100644
--- a/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributorTemplates.C
+++ b/src/OpenFOAM/parallel/fieldsDistributor/fieldsDistributorTemplates.C
@@ -101,12 +101,12 @@ void Foam::fieldsDistributor::readFields
 }
 
 
-template<class GeoField, class MeshSubsetter>
-void Foam::fieldsDistributor::readFields
+template<class BoolListType, class GeoField, class MeshSubsetter>
+void Foam::fieldsDistributor::readFieldsImpl
 (
-    const boolList& haveMeshOnProc,
+    const BoolListType& haveMeshOnProc,
+    const MeshSubsetter* subsetter,
     const typename GeoField::Mesh& mesh,
-    const autoPtr<MeshSubsetter>& subsetterPtr,
     IOobjectList& allObjects,
     PtrList<GeoField>& fields,
     const bool deregister
@@ -122,7 +122,7 @@ void Foam::fieldsDistributor::readFields
     wordList masterNames(objectNames);
     Pstream::broadcast(masterNames);
 
-    if (haveMeshOnProc[Pstream::myProcNo()] && objectNames != masterNames)
+    if (haveMeshOnProc.test(Pstream::myProcNo()) && objectNames != masterNames)
     {
         FatalErrorInFunction
             << "Objects not synchronised across processors." << nl
@@ -173,9 +173,10 @@ void Foam::fieldsDistributor::readFields
         bool decompose = true;
         for (const int proci : Pstream::subProcs())
         {
-            if (haveMeshOnProc[proci])
+            if (haveMeshOnProc.test(proci))
             {
                 decompose = false;
+                break;
             }
         }
 
@@ -197,7 +198,7 @@ void Foam::fieldsDistributor::readFields
 
         Pstream::parRun(oldParRun);  // Restore any changes
     }
-    else if (haveMeshOnProc[Pstream::myProcNo()])
+    else if (haveMeshOnProc.test(Pstream::myProcNo()))
     {
         // Have mesh so just try to load
         forAll(masterNames, i)
@@ -226,20 +227,18 @@ void Foam::fieldsDistributor::readFields
 
         OPBstream toProcs(UPstream::masterNo());  // worldComm
 
-        const label nDicts = (subsetterPtr ? fields.size() : label(0));
+        const label nDicts = (subsetter ? fields.size() : label(0));
 
         toProcs << nDicts << token::BEGIN_LIST;  // Begin list
 
-        if (nDicts)
+        if (nDicts && subsetter)
         {
             // Disable communication for interpolate() method
             const bool oldParRun = Pstream::parRun(false);
 
-            const auto& subsetter = subsetterPtr();
-
-            forAll(fields, i)
+            for (const auto& fld : fields)
             {
-                tmp<GeoField> tsubfld = subsetter.interpolate(fields[i]);
+                tmp<GeoField> tsubfld = subsetter->interpolate(fld);
 
                 // Surround each with {} as dictionary entry
                 toProcs.beginBlock();
@@ -258,15 +257,14 @@ void Foam::fieldsDistributor::readFields
         IPBstream fromMaster(UPstream::masterNo());  // worldComm
 
         // But only consume where needed...
-        if (!haveMeshOnProc[Pstream::myProcNo()])
+        if (!haveMeshOnProc.test(Pstream::myProcNo()))
         {
             fromMaster >> fieldDicts;
         }
     }
 
 
-    // Use the received dictionaries to create fields
-    // (will be empty if we didn't require them)
+    // Use the received dictionaries (if any) to create missing fields.
 
     // Disable communication when constructing from dictionary
     const bool oldParRun = Pstream::parRun(false);
@@ -327,4 +325,76 @@ void Foam::fieldsDistributor::readFields
 }
 
 
+template<class GeoField, class MeshSubsetter>
+void Foam::fieldsDistributor::readFields
+(
+    const bitSet& haveMeshOnProc,
+    const MeshSubsetter* subsetter,
+    const typename GeoField::Mesh& mesh,
+    IOobjectList& allObjects,
+    PtrList<GeoField>& fields,
+    const bool deregister
+)
+{
+    readFieldsImpl
+    (
+        haveMeshOnProc,
+        subsetter,
+
+        mesh,
+        allObjects,
+        fields,
+        deregister
+    );
+}
+
+
+template<class GeoField, class MeshSubsetter>
+void Foam::fieldsDistributor::readFields
+(
+    const boolList& haveMeshOnProc,
+    const MeshSubsetter* subsetter,
+    const typename GeoField::Mesh& mesh,
+    IOobjectList& allObjects,
+    PtrList<GeoField>& fields,
+    const bool deregister
+)
+{
+    readFieldsImpl
+    (
+        haveMeshOnProc,
+        subsetter,
+
+        mesh,
+        allObjects,
+        fields,
+        deregister
+    );
+}
+
+
+template<class GeoField, class MeshSubsetter>
+void Foam::fieldsDistributor::readFields
+(
+    const boolList& haveMeshOnProc,
+    const typename GeoField::Mesh& mesh,
+    const autoPtr<MeshSubsetter>& subsetter,
+    IOobjectList& allObjects,
+    PtrList<GeoField>& fields,
+    const bool deregister
+)
+{
+    readFieldsImpl
+    (
+        haveMeshOnProc,
+        subsetter.get(),
+
+        mesh,
+        allObjects,
+        fields,
+        deregister
+    );
+}
+
+
 // ************************************************************************* //
diff --git a/src/finiteArea/Make/files b/src/finiteArea/Make/files
index 18a6e9abf34257f57da2f8e7e9ddac26d89cb494..2ded5a841581dee1d89bcf2f9cd160f4804a4e6a 100644
--- a/src/finiteArea/Make/files
+++ b/src/finiteArea/Make/files
@@ -11,6 +11,7 @@ faMesh/faBoundaryMesh/faBoundaryMeshEntries.C
 
 faMesh/faMeshSubset/faMeshSubset.C
 faMesh/faMeshTools/faMeshTools.C
+faMesh/faMeshTools/faMeshToolsChecks.C
 faMesh/faMeshTools/faMeshToolsProcAddr.C
 
 faPatches = faMesh/faPatches
diff --git a/src/finiteArea/faMesh/faMesh.C b/src/finiteArea/faMesh/faMesh.C
index 67f687a577f3fe47551944ac4089e2beb14bca41..7d33d34050e1a7a04db4a5b8bc1357976e34c8b3 100644
--- a/src/finiteArea/faMesh/faMesh.C
+++ b/src/finiteArea/faMesh/faMesh.C
@@ -978,7 +978,7 @@ bool Foam::faMesh::movePoints()
                     mesh(),
                     IOobject::NO_READ,
                     IOobject::NO_WRITE,
-                    false
+                    IOobject::NO_REGISTER
                 ),
                 S()
             );
diff --git a/src/finiteArea/faMesh/faMeshNew.C b/src/finiteArea/faMesh/faMeshNew.C
index 1fcfe244588f4cd38a59c163f0123653786c34d0..dcc6c2afec5534c5369c7ff9367a41d2d7ccd196 100644
--- a/src/finiteArea/faMesh/faMeshNew.C
+++ b/src/finiteArea/faMesh/faMeshNew.C
@@ -64,7 +64,7 @@ bool Foam::faMesh::hasSystemFiles(const polyMesh& pMesh)
                     pMesh,
                     IOobject::MUST_READ,
                     IOobject::NO_WRITE,
-                    false
+                    IOobject::NO_REGISTER
                 ),
                 expect // typeName (ununsed?)
             )
@@ -76,6 +76,7 @@ bool Foam::faMesh::hasSystemFiles(const polyMesh& pMesh)
         }
     }
 
+    // Only needed on master
     Pstream::broadcast(looksValid);
 
     return looksValid;
@@ -87,8 +88,8 @@ bool Foam::faMesh::hasFiles(const polyMesh& pMesh)
     // As well as system/{faSchemes,faSolution}
     //
     // expect these:
-    // - timeValue/faMesh/faBoundary
-    // - timeValue/instance/faMesh/faceLabels
+    // - instance/faMesh/faceLabels
+    // - instance/faMesh/faBoundary
 
     bool looksValid = hasSystemFiles(pMesh);
 
@@ -96,15 +97,23 @@ bool Foam::faMesh::hasFiles(const polyMesh& pMesh)
     {
         const fileOperation& fp = Foam::fileHandler();
 
-        fileName subDir(pMesh.dbDir()/faMesh::meshSubDir);
+        // The geometry instance for faMesh/faceLabels
+        // Must use READ_IF_PRESENT to avoid aborting if not available
+
+        const word instance = pMesh.time().findInstance
+        (
+            pMesh.dbDir()/faMesh::meshSubDir,
+            "faceLabels",
+            IOobject::READ_IF_PRESENT
+        );
 
         for
         (
             const wordPair& expect
           : List<wordPair>
             ({
-                {"faBoundary", "faBoundaryMesh"},
-                {"faceLabels", "labelList"}
+                {"faceLabels", "labelList"},
+                {"faBoundary", "faBoundaryMesh"}
             })
         )
         {
@@ -115,18 +124,18 @@ bool Foam::faMesh::hasFiles(const polyMesh& pMesh)
             (
                 fp.filePath
                 (
-                    false,  // non-global
+                    false,      // non-global
                     IOobject
                     (
                         dataFile,
-                        pMesh.time().findInstance(subDir, dataFile),
+                        instance,
                         faMesh::meshSubDir,
                         pMesh,
-                        IOobject::MUST_READ,
+                        IOobject::READ_IF_PRESENT,
                         IOobject::NO_WRITE,
-                        false
+                        IOobject::NO_REGISTER
                     ),
-                    dataClass // typeName (ununsed?)
+                    dataClass   // typeName (ununsed?)
                 )
             );
 
@@ -136,7 +145,8 @@ bool Foam::faMesh::hasFiles(const polyMesh& pMesh)
             }
         }
 
-        Pstream::broadcast(looksValid);
+        // Everybody needs it, or they all fail
+        Pstream::reduceAnd(looksValid);
     }
 
     return looksValid;
diff --git a/src/finiteArea/faMesh/faMeshTools/faMeshTools.C b/src/finiteArea/faMesh/faMeshTools/faMeshTools.C
index cf825a0f4058acaed07e1299438d56158aa964eb..e12c43c7b4f2b0074b3c04b9a5f3b60ae8ce3ced 100644
--- a/src/finiteArea/faMesh/faMeshTools/faMeshTools.C
+++ b/src/finiteArea/faMesh/faMeshTools/faMeshTools.C
@@ -109,7 +109,7 @@ Foam::autoPtr<Foam::faMesh> Foam::faMeshTools::newMesh
                 io.db(),
                 IOobject::MUST_READ,
                 IOobject::NO_WRITE,
-                false
+                IOobject::NO_REGISTER
             )
         );
 
@@ -282,7 +282,7 @@ Foam::autoPtr<Foam::faMesh> Foam::faMeshTools::loadOrCreateMesh
                 io.db(),
                 IOobject::MUST_READ,
                 IOobject::NO_WRITE,
-                false
+                IOobject::NO_REGISTER
             )
         );
 
diff --git a/src/finiteArea/faMesh/faMeshTools/faMeshTools.H b/src/finiteArea/faMesh/faMeshTools/faMeshTools.H
index 82a6513a7822bc6685f617bd430e6d2c986237dc..f5f6ec5abd34188e8ee949e13d70bb082f29db4d 100644
--- a/src/finiteArea/faMesh/faMeshTools/faMeshTools.H
+++ b/src/finiteArea/faMesh/faMeshTools/faMeshTools.H
@@ -31,6 +31,7 @@ Description
 
 SourceFiles
     faMeshTools.C
+    faMeshToolsChecks.C
     faMeshToolsProcAddr.C
     faMeshToolsTemplates.C
 
@@ -134,6 +135,13 @@ public:
         const GeometricField<Type, faePatchField, edgeMesh>& fld,
         const bool primitiveOrdering = false
     );
+
+    //- Report mesh information
+    static void printMeshChecks
+    (
+        const faMesh& mesh,
+        const int verbose = 1
+    );
 };
 
 
diff --git a/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H b/src/finiteArea/faMesh/faMeshTools/faMeshToolsChecks.C
similarity index 68%
rename from applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H
rename to src/finiteArea/faMesh/faMeshTools/faMeshToolsChecks.C
index a0ebeb01c513bd86fda9e75a1f767cf4d94e6c72..7664356be9330945c1042d35d85c21238b48ff79 100644
--- a/applications/utilities/finiteArea/checkFaMesh/printMeshSummary.H
+++ b/src/finiteArea/faMesh/faMeshTools/faMeshToolsChecks.C
@@ -8,15 +8,37 @@
     Copyright (C) 2021-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
-    This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
+    This file is part of OpenFOAM.
 
-Description
-    Summary of faMesh information
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 \*---------------------------------------------------------------------------*/
 
+#include "faMeshTools.H"
+#include "areaFields.H"
+#include "edgeFields.H"
+#include "processorFaPatch.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+void Foam::faMeshTools::printMeshChecks
+(
+    const faMesh& mesh,
+    const int verbose
+)
 {
-    const faBoundaryMesh& patches = aMesh.boundary();
+    const faBoundaryMesh& patches = mesh.boundary();
     const label nNonProcessor = patches.nNonProcessor();
     const label nPatches = patches.size();
 
@@ -36,19 +58,19 @@ Description
 
     const labelList nFaces
     (
-        UPstream::listGatherValues<label>(aMesh.nFaces())
+        UPstream::listGatherValues<label>(mesh.nFaces())
     );
     const labelList nPoints
     (
-        UPstream::listGatherValues<label>(aMesh.nPoints())
+        UPstream::listGatherValues<label>(mesh.nPoints())
     );
     const labelList nEdges
     (
-        UPstream::listGatherValues<label>(aMesh.nEdges())
+        UPstream::listGatherValues<label>(mesh.nEdges())
     );
     const labelList nIntEdges
     (
-        UPstream::listGatherValues<label>(aMesh.nInternalEdges())
+        UPstream::listGatherValues<label>(mesh.nInternalEdges())
     );
 
     // The "real" (non-processor) boundary edges
@@ -56,7 +78,7 @@ Description
     (
         UPstream::listGatherValues<label>
         (
-            aMesh.nBoundaryEdges() - nLocalProcEdges
+            mesh.nBoundaryEdges() - nLocalProcEdges
         )
     );
     const labelList nProcEdges
@@ -70,10 +92,10 @@ Description
     //         per-proc: (...)
 
     const auto reporter =
-        [&](const char* tag, const labelList& list)
+        [&,verbose](const char* tag, const labelList& list)
         {
             Info<< "  Number of " << tag << ": " << sum(list) << nl;
-            if (Pstream::parRun())
+            if (Pstream::parRun() && verbose)
             {
                 int padding = static_cast<int>
                 (
@@ -92,7 +114,7 @@ Description
     Info<< "----------------" << nl
         << "Mesh Information" << nl
         << "----------------" << nl
-        << "  " << "boundingBox: " << boundBox(aMesh.points()) << nl;
+        << "  " << "boundingBox: " << boundBox(mesh.points()) << nl;
 
     if (Pstream::master())
     {
@@ -124,26 +146,26 @@ Description
     }
 
     Info<< "----------------" << nl
-        << "Used polyPatches: " << flatOutput(aMesh.whichPolyPatches()) << nl;
+        << "Used polyPatches: " << flatOutput(mesh.whichPolyPatches()) << nl;
 
 
     // Geometry information
     Info<< nl;
     {
-        scalarMinMax limit(gMinMax(aMesh.S().field()));
+        scalarMinMax limit(gMinMax(mesh.S().field()));
         Info<< "Face area:" << nl
             << "    min = " << limit.min() << " max = "  << limit.max() << nl;
     }
 
     {
-        scalarMinMax limit(minMax(aMesh.magLe().primitiveField()));
+        scalarMinMax limit(minMax(mesh.magLe().primitiveField()));
 
         // Include processor boundaries into 'internal' edges
         if (Pstream::parRun())
         {
             for (label patchi = nNonProcessor; patchi < nPatches; ++patchi)
             {
-                limit.add(minMax(aMesh.magLe().boundaryField()[patchi]));
+                limit.add(minMax(mesh.magLe().boundaryField()[patchi]));
             }
 
             reduce(limit, minMaxOp<scalar>());
@@ -156,7 +178,7 @@ Description
         // Include (non-processor) boundaries
         for (label patchi = 0; patchi < nNonProcessor; ++patchi)
         {
-            limit.add(minMax(aMesh.magLe().boundaryField()[patchi]));
+            limit.add(minMax(mesh.magLe().boundaryField()[patchi]));
         }
 
         if (Pstream::parRun())
@@ -171,7 +193,7 @@ Description
     // Not particularly meaningful
     #if 0
     {
-        MinMax<vector> limit(gMinMax(aMesh.faceAreaNormals().field()));
+        MinMax<vector> limit(gMinMax(mesh.faceAreaNormals().field()));
 
         Info<< "Face area normals:" << nl
             << "    min = " << limit.min() << " max = " << limit.max() << nl;
diff --git a/src/finiteArea/faMesh/faMeshTools/faMeshToolsProcAddr.C b/src/finiteArea/faMesh/faMeshTools/faMeshToolsProcAddr.C
index b8a42738d08ae141c21eb02661ca02889143a8a6..07fdec186f259bc4c85b053eff4631939267d836 100644
--- a/src/finiteArea/faMesh/faMeshTools/faMeshToolsProcAddr.C
+++ b/src/finiteArea/faMesh/faMeshTools/faMeshToolsProcAddr.C
@@ -238,7 +238,7 @@ Foam::faMeshTools::readProcAddressing
         mesh.thisDb(),
         IOobject::READ_IF_PRESENT,
         IOobject::NO_WRITE,
-        false  // no register
+        IOobject::NO_REGISTER
     );
 
     //if (ioAddr.typeHeaderOk<labelIOList>(true))
@@ -327,7 +327,7 @@ void Foam::faMeshTools::writeProcAddressing
         (procMesh && !decompose ? procMesh->thisDb() : mesh.thisDb()),
         IOobject::NO_READ,
         IOobject::NO_WRITE,
-        false  // no register
+        IOobject::NO_REGISTER
     );
 
 
@@ -418,7 +418,7 @@ void Foam::faMeshTools::writeProcAddressing
             mesh.thisDb(),
             IOobject::NO_READ,
             IOobject::NO_WRITE,
-            false  // no register
+            IOobject::NO_REGISTER
         ),
         map
     );
diff --git a/src/parallel/decompose/faDecompose/faFieldDecomposer.H b/src/parallel/decompose/faDecompose/faFieldDecomposer.H
index deaf82a17407b19b3f5eb8491e995db82cba2517..dfadb54f41609ddc2ee6cde93e761c6504e92201 100644
--- a/src/parallel/decompose/faDecompose/faFieldDecomposer.H
+++ b/src/parallel/decompose/faDecompose/faFieldDecomposer.H
@@ -45,6 +45,7 @@ SourceFiles
 #define Foam_faFieldDecomposer_H
 
 #include "faMesh.H"
+#include "faMeshSubset.H"
 #include "faPatchFieldMapper.H"
 #include "edgeFields.H"
 
@@ -446,6 +447,26 @@ public:
             const IOobjectList& objects
         );
 
+        //- Read all fields given mesh and objects.
+        //- Supports reading/sending fields
+        void readAllFields
+        (
+            const bitSet& haveMeshOnProc,
+            const faMeshSubset* subsetter,
+            const faMesh& mesh,
+            IOobjectList& objects
+        );
+
+        //- Read all fields given mesh and objects.
+        //- Supports reading/sending fields
+        void readAllFields
+        (
+            const boolList& haveMeshOnProc,
+            const faMeshSubset* subsetter,
+            const faMesh& mesh,
+            IOobjectList& objects
+        );
+
         //- Decompose and write all fields
         void decomposeAllFields
         (
diff --git a/src/parallel/decompose/faDecompose/faFieldDecomposerCache.C b/src/parallel/decompose/faDecompose/faFieldDecomposerCache.C
index d73243da15af008d66d2fc9638b67bf4422f0eca..493a89e70c709b0c9dc43112c81d1ad2e466729b 100644
--- a/src/parallel/decompose/faDecompose/faFieldDecomposerCache.C
+++ b/src/parallel/decompose/faDecompose/faFieldDecomposerCache.C
@@ -77,7 +77,11 @@ public:
 
     bool empty() const noexcept { return !size(); }
 
-    void readAll(const faMesh& mesh, const IOobjectList& objects)
+    void readAll
+    (
+        const faMesh& mesh,
+        const IOobjectList& objects
+    )
     {
         #undef  doLocalCode
         #define doLocalCode(Type)                                     \
@@ -113,6 +117,45 @@ public:
         #undef doLocalCode
     }
 
+    template<class BoolListType>
+    void readAll
+    (
+        const BoolListType& haveMeshOnProc,
+        const faMeshSubset*& subsetter,
+        const faMesh& mesh,
+        IOobjectList& objects
+    )
+    {
+        #undef  doLocalCode
+        #define doLocalCode(Type)                                     \
+        {                                                             \
+            fieldsDistributor::readFields                             \
+            (                                                         \
+                haveMeshOnProc,                                       \
+                subsetter,                                            \
+                mesh,                                                 \
+                objects,                                              \
+                Type##AreaFields_                                     \
+            );                                                        \
+            fieldsDistributor::readFields                             \
+            (                                                         \
+                haveMeshOnProc,                                       \
+                subsetter,                                            \
+                mesh,                                                 \
+                objects,                                              \
+                Type##EdgeFields_                                     \
+            );                                                        \
+        }
+
+        doLocalCode(scalar);
+        doLocalCode(vector);
+        doLocalCode(sphericalTensor);
+        doLocalCode(symmTensor);
+        doLocalCode(tensor);
+
+        #undef doLocalCode
+    }
+
     template<class GeoField>
     static void decompose
     (
@@ -205,6 +248,36 @@ void Foam::faFieldDecomposer::fieldsCache::readAllFields
 }
 
 
+void Foam::faFieldDecomposer::fieldsCache::readAllFields
+(
+    const bitSet& haveMeshOnProc,
+    const faMeshSubset* subsetter,
+    const faMesh& mesh,
+    IOobjectList& objects
+)
+{
+    if (cache_)
+    {
+        cache_->readAll(haveMeshOnProc, subsetter, mesh, objects);
+    }
+}
+
+
+void Foam::faFieldDecomposer::fieldsCache::readAllFields
+(
+    const boolList& haveMeshOnProc,
+    const faMeshSubset* subsetter,
+    const faMesh& mesh,
+    IOobjectList& objects
+)
+{
+    if (cache_)
+    {
+        cache_->readAll(haveMeshOnProc, subsetter, mesh, objects);
+    }
+}
+
+
 void Foam::faFieldDecomposer::fieldsCache::decomposeAllFields
 (
     const faFieldDecomposer& decomposer,
diff --git a/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Allrun-distributed b/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Allrun-distributed
new file mode 100755
index 0000000000000000000000000000000000000000..498ed6e912e684a562320837a219ccb2e328093b
--- /dev/null
+++ b/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Allrun-distributed
@@ -0,0 +1,90 @@
+#!/bin/sh
+cd "${0%/*}" || exit                                # Run from this directory
+. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
+#------------------------------------------------------------------------------
+
+## ./Allclean
+
+rootDir="test-distributed"
+caseName="${PWD##*/}"
+
+#not yet: fileHandler="-fileHandler collated"
+unset fileHandler
+
+restore0Dir
+
+runApplication blockMesh
+
+if [ -d "$rootDir" ]
+then
+    echo "Directory already exists: $rootDir"
+fi
+
+masterDecompParDict="$rootDir/machineA/$caseName/system/decomposeParDict"
+caseOption="-case $rootDir/machineA/$caseName"
+
+for subdir in machineA machineB machineC machineD
+do
+    mkdir -p "$rootDir/$subdir/$caseName"
+done
+
+# Master
+masterCase="$rootDir/machineA/$caseName"
+
+for instance in 0 constant system
+do
+    if [ -d "$masterCase/$instance" ]
+    then
+        echo "    Directory exists: $targetDir/$instance"
+    else
+        echo "Copy $instance/ to master root: $masterCase"
+        cp -R "$instance" "$masterCase"
+    fi
+done
+
+# others (nothing to copy)
+
+
+# Add distributed roots
+# - seems to be fine with relative root
+## rootDir="$PWD/$rootDir"
+
+cat<< CASE_ROOTS >> "$masterCase/system/decomposeParDict"
+
+distributed true;
+
+roots
+(
+    //master: "$rootDir/machineA"
+    "$rootDir/machineA"
+    "$rootDir/machineA"
+
+    "$rootDir/machineB"
+    "$rootDir/machineB"
+    "$rootDir/machineB"
+
+    "$rootDir/machineC"
+    "$rootDir/machineC"
+    "$rootDir/machineC"
+
+    "$rootDir/machineD"
+    "$rootDir/machineD"
+    "$rootDir/machineD"
+);
+CASE_ROOTS
+
+#------------------------------------------------------------------------------
+
+#export FOAM_ABORT=true
+
+runParallel -s decompose redistributePar -decompose -overwrite \
+    -no-finite-area $caseOption $fileHandler
+
+runParallel checkMesh $caseOption $fileHandler
+
+runParallel makeFaMesh $caseOption $fileHandler
+
+runParallel $(getApplication) $caseOption $fileHandler
+
+
+#------------------------------------------------------------------------------
diff --git a/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Test-distributed b/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Test-distributed
deleted file mode 100755
index d7069fd573a073cc92cc64c27d9f74f080e6535a..0000000000000000000000000000000000000000
--- a/tutorials/incompressible/pimpleFoam/laminar/filmPanel0/Test-distributed
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/sh
-cd "${0%/*}" || exit                                # Run from this directory
-. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
-#------------------------------------------------------------------------------
-
-## ./Allclean
-
-restore0Dir
-
-runApplication blockMesh
-
-rm -rf test-distribute
-masterDecompParDict="test-distribute/machineA/testcase/system/decomposeParDict"
-
-for subdir in machineA machineB machineC machineD
-do
-    mkdir -p test-distribute/"$subdir"/testcase
-done
-
-# master
-cp -R 0 constant system test-distribute/machineA/testcase
-# others (nothing to copy)
-
-
-cat<< CASE_ROOTS >> "$masterDecompParDict"
-
-distributed true;
-
-roots
-(
-    //master: "$PWD/test-distribute/machineA"
-    "$PWD/test-distribute/machineA"
-    "$PWD/test-distribute/machineA"
-
-    "$PWD/test-distribute/machineB"
-    "$PWD/test-distribute/machineB"
-    "$PWD/test-distribute/machineB"
-
-    "$PWD/test-distribute/machineC"
-    "$PWD/test-distribute/machineC"
-    "$PWD/test-distribute/machineC"
-
-    "$PWD/test-distribute/machineD"
-    "$PWD/test-distribute/machineD"
-    "$PWD/test-distribute/machineD"
-);
-CASE_ROOTS
-
-#export FOAM_ABORT=true
-
-runParallel -s decompose redistributePar -decompose -case test-distribute/machineA/testcase
-
-# Currently fails (OpenFOAM-v2206)
-runParallel checkFaMesh -case test-distribute/machineA/testcase
-
-exit 0
-
-#------------------------------------------------------------------------------