diff --git a/applications/test/mapDistributePolyMesh/cavity/system/renumberMeshDict-random b/applications/test/mapDistributePolyMesh/cavity/system/renumberMeshDict-random
deleted file mode 100644
index d3d55f4fd1235396c0eb1b92f5c58ae95eab40c9..0000000000000000000000000000000000000000
--- a/applications/test/mapDistributePolyMesh/cavity/system/renumberMeshDict-random
+++ /dev/null
@@ -1,112 +0,0 @@
-/*--------------------------------*- C++ -*----------------------------------*\
-| =========                 |                                                 |
-| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
-|  \\    /   O peration     | Version:  v2312                                 |
-|   \\  /    A nd           | Website:  www.openfoam.com                      |
-|    \\/     M anipulation  |                                                 |
-\*---------------------------------------------------------------------------*/
-FoamFile
-{
-    version     2.0;
-    format      ascii;
-    class       dictionary;
-    note        "mesh renumbering dictionary";
-    object      renumberMeshDict;
-}
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Write maps from renumbered back to original mesh
-writeMaps true;
-
-// Optional entry: sort cells on coupled boundaries to last for use with
-// e.g. nonBlockingGaussSeidel.
-sortCoupledFaceCells false;
-
-// Optional entry: renumber on a block-by-block basis. It uses a
-// blockCoeffs dictionary to construct a decompositionMethod to do
-// a block subdivision) and then applies the renumberMethod to each
-// block in turn. This can be used in large cases to keep the blocks
-// fitting in cache with all the cache misses bunched at the end.
-// This number is the approximate size of the blocks - this gets converted
-// to a number of blocks that is the input to the decomposition method.
-//blockSize 1000;
-
-// Optional entry: sort points into internal and boundary points
-//orderPoints false;
-
-// Optional: suppress renumbering cellSets,faceSets,pointSets
-//renumberSets false;
-
-
-//method          CuthillMcKee;
-//method          Sloan;
-//method          manual;
-method          random;
-//method          structured;
-//method          spring;
-//method          zoltan;             // only if compiled with zoltan support
-
-//CuthillMcKeeCoeffs
-//{
-//    // Reverse CuthillMcKee (RCM) or plain
-//    reverse true;
-//}
-
-manualCoeffs
-{
-    // In system directory: new-to-original (i.e. order) labelIOList
-    dataFile "cellMap";
-}
-
-
-// For extruded (i.e. structured in one direction) meshes
-structuredCoeffs
-{
-    // Patches that mesh was extruded from. These determine the starting
-    // layer of cells
-    patches (movingWall);
-    // Method to renumber the starting layer of cells
-    method  random;
-
-    // Renumber in columns (depthFirst) or in layers
-    depthFirst true;
-
-    // Reverse ordering
-    reverse false;
-}
-
-
-springCoeffs
-{
-    // Maximum jump of cell indices. Is fraction of number of cells
-    maxCo 0.01;
-
-    // Limit the amount of movement; the fraction maxCo gets decreased
-    // with every iteration
-    freezeFraction 0.999;
-
-    // Maximum number of iterations
-    maxIter 1000;
-}
-
-
-blockCoeffs
-{
-    method          scotch;
-    //method hierarchical;
-    //hierarchicalCoeffs
-    //{
-    //    n           (1 2 1);
-    //    delta       0.001;
-    //    order       xyz;
-    //}
-}
-
-
-zoltanCoeffs
-{
-    ORDER_METHOD    LOCAL_HSFC;
-}
-
-
-// ************************************************************************* //
diff --git a/applications/utilities/mesh/manipulation/renumberMesh/Allwmake b/applications/utilities/mesh/manipulation/renumberMesh/Allwmake
deleted file mode 100755
index 0f0d32738e739ad9b9c197673e93f0bd0a9746f8..0000000000000000000000000000000000000000
--- a/applications/utilities/mesh/manipulation/renumberMesh/Allwmake
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/sh
-cd "${0%/*}" || exit                                # Run from this directory
-. ${WM_PROJECT_DIR:?}/wmake/scripts/AllwmakeParseArguments # (error catching)
-. ${WM_PROJECT_DIR:?}/wmake/scripts/sysFunctions    # General system functions
-. ${WM_PROJECT_DIR:?}/wmake/scripts/have_zoltan
-
-#------------------------------------------------------------------------------
-
-unset COMP_FLAGS LINK_FLAGS
-
-if findLibrary "$FOAM_LIBBIN/libSloanRenumber" > /dev/null
-then
-    echo "    found libSloanRenumber -- enabling sloan renumbering support."
-    export LINK_FLAGS="$LINK_FLAGS -lSloanRenumber"
-fi
-
-if findLibrary "$FOAM_LIBBIN/libzoltanRenumber" > /dev/null && have_zoltan
-then
-    echo "    found libzoltanRenumber -- enabling zoltan renumbering support."
-    export COMP_FLAGS="$COMP_FLAGS -DHAVE_ZOLTAN"
-    export LINK_FLAGS="$LINK_FLAGS -lzoltanRenumber -L$ZOLTAN_LIB_DIR -lzoltan"
-fi
-
-wmake $targetType
-
-#------------------------------------------------------------------------------
diff --git a/applications/utilities/mesh/manipulation/renumberMesh/Make/options b/applications/utilities/mesh/manipulation/renumberMesh/Make/options
index 004f82049fae5620b6d6bb48d4b7e0a8f61352c8..8aca81f2dcd2af49b6ed14e5ba06c94113173ddd 100644
--- a/applications/utilities/mesh/manipulation/renumberMesh/Make/options
+++ b/applications/utilities/mesh/manipulation/renumberMesh/Make/options
@@ -1,21 +1,22 @@
 EXE_INC = \
-    $(COMP_FLAGS) \
-    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude \
     -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/dynamicMesh/lnInclude \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/renumber/renumberMethods/lnInclude \
-    -I$(LIB_SRC)/renumber/zoltanRenumber/lnInclude \
+    -I$(LIB_SRC)/parallel/decompose/decompose/lnInclude \
     -I$(LIB_SRC)/parallel/decompose/decompositionMethods/lnInclude \
     -I$(LIB_SRC)/parallel/reconstruct/reconstruct/lnInclude
 
 EXE_LIBS = \
-    -lfiniteVolume \
+    -lfileFormats \
     -lmeshTools \
     -ldynamicMesh \
+    -lfiniteVolume \
     -lgenericPatchFields \
     -lrenumberMethods \
+    -ldecompose \
     -lreconstruct \
-    $(LINK_FLAGS) \
     -ldecompositionMethods \
     -L$(FOAM_LIBBIN)/dummy \
     -lkahipDecomp -lmetisDecomp -lscotchDecomp
diff --git a/applications/utilities/mesh/manipulation/renumberMesh/renumberMesh.C b/applications/utilities/mesh/manipulation/renumberMesh/renumberMesh.C
index b35202a4ad2660ffef3744e9c4ecea0cda229f45..7c08c679a5feb1946951dcb98f1ac377392670ae 100644
--- a/applications/utilities/mesh/manipulation/renumberMesh/renumberMesh.C
+++ b/applications/utilities/mesh/manipulation/renumberMesh/renumberMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2022 OpenCFD Ltd.
+    Copyright (C) 2016-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,12 +34,96 @@ Description
     Renumbers the cell list in order to reduce the bandwidth, reading and
     renumbering all fields from all the time directories.
 
-    By default uses bandCompression (CuthillMcKee) but will
-    read system/renumberMeshDict if -dict option is present
+    By default uses bandCompression (Cuthill-McKee) or the method
+    specified by the -renumber-method option, but will read
+    system/renumberMeshDict if -dict option is present
+
+Usage
+    \b renumberMesh [OPTIONS]
+
+    Options:
+      - \par -allRegions
+        Use all regions in regionProperties
+
+      - \par -case \<dir\>
+        Specify case directory to use (instead of the cwd).
+
+      - \par -constant
+        Include the 'constant/' dir in the times list.
+
+      - \par -decompose
+        Aggregate initially with a decomposition method (serial only)
+
+      - \par -decomposeParDict \<file\>
+        Use specified file for decomposePar dictionary.
+
+      - \par -dict \<file\>
+        Use specified file for renumberMeshDict dictionary.
+
+      - \par -dry-run
+        Test only
+
+      - \par -frontWidth
+        Calculate the rms of the front-width
+
+      - \par -latestTime
+        Select the latest time.
+
+      - \par -lib \<name\>
+        Additional library or library list to load (can be used multiple times).
+
+      - \par -no-fields
+        Suppress renumber of fields
+
+      - \par -noZero
+        Exclude the \a 0 dir from the times list.
+
+      - \par -overwrite
+        Overwrite existing mesh/results files
+
+      - \par -parallel
+        Run in parallel
+
+      - \par -region \<regionName\>
+        Renumber named region.
+
+      - \par -regions \<wordRes\>
+        Renumber named regions.
+
+      - \par -renumber-coeffs \<string-content\>
+        String to create renumber dictionary contents.
+
+      - \par -renumber-method \<name\>
+        Specify renumber method (default: CuthillMcKee) without dictionary
+
+      - \par -time \<value\>
+        Specify time to select
+
+      - \par -verbose
+        Additional verbosity.
+
+      - \par -doc
+        Display documentation in browser.
+
+      - \par -doc-source
+        Display source code in browser.
+
+      - \par -help
+        Display short help and exit.
+
+      - \par -help-man
+        Display full help (manpage format) and exit.
+
+      - \par -help-notes
+        Display help notes (description) and exit.
+
+      - \par -help-full
+        Display full help and exit.
 
 \*---------------------------------------------------------------------------*/
 
 #include "argList.H"
+#include "timeSelector.H"
 #include "IOobjectList.H"
 #include "fvMesh.H"
 #include "polyTopoChange.H"
@@ -48,8 +132,9 @@ Description
 #include "surfaceFields.H"
 #include "SortableList.H"
 #include "decompositionMethod.H"
+#include "decompositionModel.H"
 #include "renumberMethod.H"
-#include "zeroGradientFvPatchFields.H"
+#include "foamVtkInternalMeshWriter.H"
 #include "CuthillMcKeeRenumber.H"
 #include "fvMeshSubset.H"
 #include "cellSet.H"
@@ -60,11 +145,6 @@ Description
 #include "regionProperties.H"
 #include "polyMeshTools.H"
 
-#ifdef HAVE_ZOLTAN
-    #include "zoltanRenumber.H"
-#endif
-
-
 using namespace Foam;
 
 
@@ -80,16 +160,17 @@ tmp<volScalarField> createScalarField
     (
         name,
         mesh,
-        dimensionedScalar(dimless, Zero),
+        dimensionedScalar(word::null, dimless, -1),
         fvPatchFieldBase::zeroGradientType()
     );
     auto& fld = tfld.ref();
 
-    forAll(fld, celli)
+    forAll(elems, celli)
     {
        fld[celli] = elems[celli];
     }
 
+    fld.correctBoundaryConditions();
     return tfld;
 }
 
@@ -140,10 +221,10 @@ void getBand
     bandwidth = max(cellBandwidth);
 
     // Do not use field algebra because of conversion label to scalar
-    profile = 0.0;
-    forAll(cellBandwidth, celli)
+    profile = 0;
+    for (const label width : cellBandwidth)
     {
-        profile += 1.0*cellBandwidth[celli];
+        profile += scalar(width);
     }
 
     sumSqrIntersect = 0.0;
@@ -175,54 +256,46 @@ labelList getFaceOrder
 
     label newFacei = 0;
 
-    labelList nbr;
-    labelList order;
+    DynamicList<label> nbr(64);
+    DynamicList<label> order(64);
 
     forAll(cellOrder, newCelli)
     {
-        label oldCelli = cellOrder[newCelli];
+        const label oldCelli = cellOrder[newCelli];
 
         const cell& cFaces = mesh.cells()[oldCelli];
 
         // Neighbouring cells
-        nbr.setSize(cFaces.size());
+        nbr.clear();
 
-        forAll(cFaces, i)
+        for (const label facei : cFaces)
         {
-            label facei = cFaces[i];
+            label nbrCelli = -1;
 
             if (mesh.isInternalFace(facei))
             {
                 // Internal face. Get cell on other side.
-                label nbrCelli = reverseCellOrder[mesh.faceNeighbour()[facei]];
+                nbrCelli = reverseCellOrder[mesh.faceNeighbour()[facei]];
                 if (nbrCelli == newCelli)
                 {
                     nbrCelli = reverseCellOrder[mesh.faceOwner()[facei]];
                 }
 
-                if (newCelli < nbrCelli)
-                {
-                    // Celli is master
-                    nbr[i] = nbrCelli;
-                }
-                else
+                // The nbrCell is actually the master (let it handle the face)
+                if (nbrCelli <= newCelli)
                 {
-                    // nbrCell is master. Let it handle this face.
-                    nbr[i] = -1;
+                    nbrCelli = -1;
                 }
             }
-            else
-            {
-                // External face. Do later.
-                nbr[i] = -1;
-            }
+
+            nbr.push_back(nbrCelli);
         }
 
-        sortedOrder(nbr, order);
+        Foam::sortedOrder(nbr, order);
 
         for (const label index : order)
         {
-            if (nbr[index] != -1)
+            if (nbr[index] >= 0)
             {
                 oldToNewFace[cFaces[index]] = newFacei++;
             }
@@ -266,73 +339,71 @@ labelList getRegionFaceOrder
 
     label newFacei = 0;
 
-    label prevRegion = -1;
+    DynamicList<label> nbr(64);
+    DynamicList<label> order(64);
 
     forAll(cellOrder, newCelli)
     {
-        label oldCelli = cellOrder[newCelli];
-
-        if (cellToRegion[oldCelli] != prevRegion)
-        {
-            prevRegion = cellToRegion[oldCelli];
-        }
+        const label oldCelli = cellOrder[newCelli];
 
         const cell& cFaces = mesh.cells()[oldCelli];
 
-        SortableList<label> nbr(cFaces.size());
+        // Neighbouring cells
+        nbr.clear();
 
-        forAll(cFaces, i)
+        for (const label facei : cFaces)
         {
-            label facei = cFaces[i];
+            label nbrCelli = -1;
 
             if (mesh.isInternalFace(facei))
             {
                 // Internal face. Get cell on other side.
-                label nbrCelli = reverseCellOrder[mesh.faceNeighbour()[facei]];
+                nbrCelli = reverseCellOrder[mesh.faceNeighbour()[facei]];
                 if (nbrCelli == newCelli)
                 {
                     nbrCelli = reverseCellOrder[mesh.faceOwner()[facei]];
                 }
 
-                if (cellToRegion[oldCelli] != cellToRegion[cellOrder[nbrCelli]])
+                // The nbrCell is actually the master (let it handle the face)
+                if (nbrCelli <= newCelli)
                 {
-                    // Treat like external face. Do later.
-                    nbr[i] = -1;
-                }
-                else if (newCelli < nbrCelli)
-                {
-                    // Celli is master
-                    nbr[i] = nbrCelli;
+                    nbrCelli = -1;
                 }
                 else
                 {
-                    // nbrCell is master. Let it handle this face.
-                    nbr[i] = -1;
+                    // A region boundary? - treat like an external face
+                    label ownRegion = cellToRegion[oldCelli];
+                    label neiRegion = cellToRegion[cellOrder[nbrCelli]];
+
+                    if (ownRegion != neiRegion)
+                    {
+                        nbrCelli = -1;
+                    }
                 }
             }
-            else
-            {
-                // External face. Do later.
-                nbr[i] = -1;
-            }
+
+            nbr.push_back(nbrCelli);
         }
 
-        nbr.sort();
+        Foam::sortedOrder(nbr, order);
 
-        forAll(nbr, i)
+        for (const label index : order)
         {
-            if (nbr[i] != -1)
+            if (nbr[index] >= 0)
             {
-                oldToNewFace[cFaces[nbr.indices()[i]]] = newFacei++;
+                oldToNewFace[cFaces[index]] = newFacei++;
             }
         }
     }
 
+    // This seems to be broken...
+
     // Do region interfaces
-    label nRegions = max(cellToRegion)+1;
     {
+        const label nRegions = max(cellToRegion)+1;
+
         // Sort in increasing region
-        SortableList<label> sortKey(mesh.nFaces(), labelMax);
+        SortableList<label> sortKey(mesh.nInternalFaces(), labelMax);
 
         for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
         {
@@ -346,10 +417,10 @@ labelList getRegionFaceOrder
                    +max(ownRegion, neiRegion);
             }
         }
+
         sortKey.sort();
 
         // Extract.
-        label prevKey = -1;
         forAll(sortKey, i)
         {
             label key = sortKey[i];
@@ -359,11 +430,6 @@ labelList getRegionFaceOrder
                 break;
             }
 
-            if (prevKey != key)
-            {
-                prevKey = key;
-            }
-
             oldToNewFace[sortKey.indices()[i]] = newFacei++;
         }
     }
@@ -381,8 +447,7 @@ labelList getRegionFaceOrder
         if (oldToNewFace[facei] == -1)
         {
             FatalErrorInFunction
-                << "Did not determine new position"
-                << " for face " << facei
+                << "Did not determine new position for face " << facei
                 << abort(FatalError);
         }
     }
@@ -426,13 +491,10 @@ autoPtr<mapPolyMesh> reorderMesh
     labelHashSet flipFaceFlux(newOwner.size());
     forAll(newNeighbour, facei)
     {
-        label own = newOwner[facei];
-        label nei = newNeighbour[facei];
-
-        if (nei < own)
+        if (newOwner[facei] > newNeighbour[facei])
         {
-            newFaces[facei].flip();
             std::swap(newOwner[facei], newNeighbour[facei]);
+            newFaces[facei].flip();
             flipFaceFlux.insert(facei);
         }
     }
@@ -543,55 +605,94 @@ autoPtr<mapPolyMesh> reorderMesh
 }
 
 
-// Return new to old cell numbering
-labelList regionRenumber
+// Return new to old cell numbering, region-wise
+CompactListList<label> regionRenumber
 (
     const renumberMethod& method,
     const fvMesh& mesh,
-    const labelList& cellToRegion
+    const labelList& cellToRegion,
+    label nRegions = -1   // Number of regions or auto-detect
 )
 {
-    Info<< "Determining cell order:" << endl;
+    // Info<< "Determining cell order:" << endl;
 
-    labelList cellOrder(cellToRegion.size());
+    if (nRegions < 0)
+    {
+        nRegions = Foam::max(cellToRegion)+1;
+    }
 
-    label nRegions = max(cellToRegion)+1;
+    // Initially the per-region cell selection
+    CompactListList<label> regionCellOrder
+    (
+        invertOneToManyCompact(nRegions, cellToRegion)
+    );
 
-    labelListList regionToCells(invertOneToMany(nRegions, cellToRegion));
+    if (method.needs_mesh())
+    {
+        forAll(regionCellOrder, regioni)
+        {
+            // Info<< "    region " << regioni
+            //     << " starts at " << regionCellOrder.localStart(regioni)
+            //     << nl;
 
-    label celli = 0;
+            // No parallel communication
+            const bool oldParRun = UPstream::parRun(false);
 
-    forAll(regionToCells, regioni)
-    {
-        Info<< "    region " << regioni << " starts at " << celli << endl;
+            // Get local sub-mesh
+            fvMeshSubset subsetter(mesh, regionCellOrder[regioni]);
 
-        // Make sure no parallel comms
-        const bool oldParRun = UPstream::parRun(false);
+            // Note: cellMap will be identical to regionToCells[regioni]
+            // (assuming they are properly sorted!)
+            const labelList& cellMap = subsetter.cellMap();
 
-        // Per region do a reordering.
-        fvMeshSubset subsetter(mesh, regioni, cellToRegion);
+            labelList subCellOrder = method.renumber
+            (
+                subsetter.subMesh(),
+                subsetter.subMesh().cellCentres()
+            );
 
-        const fvMesh& subMesh = subsetter.subMesh();
+            UPstream::parRun(oldParRun);  // Restore parallel state
 
-        labelList subCellOrder = method.renumber
-        (
-            subMesh,
-            subMesh.cellCentres()
-        );
+            // Per region reordering
+            regionCellOrder[regioni] = labelUIndList(cellMap, subCellOrder);
+        }
+    }
+    else
+    {
+        forAll(regionCellOrder, regioni)
+        {
+            // Info<< "    region " << regioni
+            //     << " starts at " << regionCellOrder.localStart(regioni)
+            //     << nl;
 
-        UPstream::parRun(oldParRun);  // Restore parallel state
+            // No parallel communication
+            const bool oldParRun = UPstream::parRun(false);
 
+            // Connectivity of local sub-mesh
+            CompactListList<label> cellCells;
+            labelList cellMap = globalMeshData::calcCellCells
+            (
+                mesh,
+                regionCellOrder[regioni],
+                cellCells
+            );
 
-        const labelList& cellMap = subsetter.cellMap();
+            // Note: cellCentres not needed by every renumber method
+            labelList subCellOrder = method.renumber
+            (
+                cellCells,
+                pointField(mesh.cellCentres(), cellMap)
+            );
 
-        forAll(subCellOrder, i)
-        {
-            cellOrder[celli++] = cellMap[subCellOrder[i]];
+            UPstream::parRun(oldParRun);  // Restore parallel state
+
+            // Per region reordering
+            regionCellOrder[regioni] = labelUIndList(cellMap, subCellOrder);
         }
     }
-    Info<< endl;
+    // Info<< endl;
 
-    return cellOrder;
+    return regionCellOrder;
 }
 
 
@@ -601,48 +702,138 @@ int main(int argc, char *argv[])
 {
     argList::addNote
     (
-        "Renumber mesh cells to reduce the bandwidth"
+        "Renumber mesh cells to reduce the bandwidth. Use the -lib option or"
+        " dictionary 'libs' entry to load additional libraries"
     );
 
-    #include "addAllRegionOptions.H"
-    #include "addOverwriteOption.H"
-    #include "addTimeOptions.H"
+    argList::addDryRunOption
+    (
+        "Test without writing. "
+        "Changes -write-maps to write VTK output."
+    );
+    argList::addVerboseOption();
 
     argList::addOption("dict", "file", "Alternative renumberMeshDict");
 
     argList::addBoolOption
     (
         "frontWidth",
-        "Calculate the rms of the front-width"
+        "Calculate the RMS of the front-width"
+    );
+
+    argList::addBoolOption
+    (
+        "decompose",
+        "Aggregate initially with a decomposition method (serial only)"
+    );
+
+    argList::addBoolOption
+    (
+        "write-maps",
+        "Write renumber mappings"
+    );
+
+    argList::addBoolOption
+    (
+        "no-fields",
+        "Suppress renumbering of fields (eg, when they are only uniform)"
+    );
+
+    argList::addBoolOption
+    (
+        "list-renumber",
+        "List available renumbering methods"
+    );
+
+    argList::addOption
+    (
+        "renumber-method",
+        "name",
+        "Specify renumber method (default: CuthillMcKee) without dictionary"
+    );
+
+    argList::addOption
+    (
+        "renumber-coeffs",
+        "string-content",
+        "Specify renumber coefficients (dictionary content) as string. "
+        "eg, 'reverse true;'"
     );
 
     argList::noFunctionObjects();  // Never use function objects
 
+    #include "addAllRegionOptions.H"
+    #include "addOverwriteOption.H"
+    #include "addTimeOptions.H"
+
+    // -------------------------
+
     #include "setRootCase.H"
+
+    {
+        bool listOptions = false;
+
+        if (args.found("list-renumber"))
+        {
+            listOptions = true;
+            Info<< nl
+                << "Available renumber methods:" << nl
+                << "    "
+                << flatOutput(renumberMethod::supportedMethods()) << nl
+                << nl;
+        }
+
+        if (listOptions)
+        {
+            return 0;
+        }
+    }
+
+    const bool dryrun = args.dryRun();
+
+    const bool readDict = args.found("dict");
+    const bool doFrontWidth = args.found("frontWidth");
+    const bool overwrite = args.found("overwrite");
+    const bool doFields = !args.found("no-fields");
+    const bool doDecompose = args.found("decompose");
+
+    word renumberMethodName;
+    args.readIfPresent("renumber-method", renumberMethodName);
+
+    if (doDecompose && UPstream::parRun())
+    {
+        FatalErrorIn(args.executable())
+            << "Cannot use -decompose option in parallel"
+            << " ... giving up" << nl
+            << exit(FatalError);
+    }
+
     #include "createTime.H"
-    #include "getAllRegionOptions.H"
 
-    // Force linker to include zoltan symbols. This section is only needed since
-    // Zoltan is a static library
-    #ifdef HAVE_ZOLTAN
-        Info<< "renumberMesh built with zoltan support." << nl << endl;
-        (void)zoltanRenumber::typeName;
-    #endif
+    // Allow override of decomposeParDict location
+    fileName decompDictFile;
+    if
+    (
+        args.readIfPresent("decomposeParDict", decompDictFile)
+     && !decompDictFile.empty() && !decompDictFile.isAbsolute()
+    )
+    {
+        decompDictFile = runTime.globalPath()/decompDictFile;
+    }
+
+    // Get region names
+    #include "getAllRegionOptions.H"
 
     // Get times list
     instantList Times = runTime.times();
 
-    // Set startTime and endTime depending on -time and -latestTime options
+    // Set startTime depending on -time and -latestTime options
     #include "checkTimeOptions.H"
 
     runTime.setTime(Times[startTime], startTime);
 
     #include "createNamedMeshes.H"
 
-    const bool readDict = args.found("dict");
-    const bool doFrontWidth = args.found("frontWidth");
-    const bool overwrite = args.found("overwrite");
-
 
     for (fvMesh& mesh : meshes)
     {
@@ -667,13 +858,11 @@ int main(int argc, char *argv[])
 
         reduce(band, maxOp<label>());
         reduce(profile, sumOp<scalar>());
+        reduce(sumSqrIntersect, sumOp<scalar>());
+
         scalar rmsFrontwidth = Foam::sqrt
         (
-            returnReduce
-            (
-                sumSqrIntersect,
-                sumOp<scalar>()
-            )/mesh.globalData().nTotalCells()
+            sumSqrIntersect/mesh.globalData().nTotalCells()
         );
 
         Info<< "Mesh " << mesh.name()
@@ -690,12 +879,12 @@ int main(int argc, char *argv[])
         Info<< endl;
 
         bool sortCoupledFaceCells = false;
-        bool writeMaps = false;
+        bool writeMaps = args.found("write-maps");
         bool orderPoints = false;
         label blockSize = 0;
 
         // Construct renumberMethod
-        autoPtr<IOdictionary> renumberDictPtr;
+        dictionary renumberDict;
         autoPtr<renumberMethod> renumberPtr;
 
         if (readDict)
@@ -705,38 +894,31 @@ int main(int argc, char *argv[])
 
             Info<< "Renumber according to " << dictIO.name() << nl << endl;
 
-            renumberDictPtr.reset(new IOdictionary(dictIO));
-            const IOdictionary& renumberDict = renumberDictPtr();
+            renumberDict = IOdictionary::readContents(dictIO);
 
             renumberPtr = renumberMethod::New(renumberDict);
 
-            sortCoupledFaceCells = renumberDict.getOrDefault
-            (
-                "sortCoupledFaceCells",
-                false
-            );
-            if (sortCoupledFaceCells)
+            // This options are likely orthogonal to -decompose mode
+            if (!doDecompose)
             {
-                Info<< "Sorting cells on coupled boundaries to be last." << nl
-                    << endl;
-            }
+                sortCoupledFaceCells =
+                    renumberDict.getOrDefault("sortCoupledFaceCells", false);
 
-            blockSize = renumberDict.getOrDefault("blockSize", 0);
-            if (blockSize > 0)
-            {
-                Info<< "Ordering cells into regions of size " << blockSize
-                    << " (using decomposition);"
-                    << " ordering faces into region-internal"
-                    << " and region-external."
-                    << nl << endl;
+                if (sortCoupledFaceCells)
+                {
+                    Info<< "Sorting cells on coupled boundaries to be last."
+                        << nl << endl;
+                }
 
-                if (blockSize < 0 || blockSize >= mesh.nCells())
+                blockSize = renumberDict.getOrDefault<label>("blockSize", 0);
+
+                if (blockSize > 0)
                 {
-                    FatalErrorInFunction
-                        << "Block size " << blockSize
-                        << " should be positive integer"
-                        << " and less than the number of cells in the mesh."
-                        << exit(FatalError);
+                    Info<< "Ordering cells into regions of size " << blockSize
+                        << " (using decomposition);"
+                        << " ordering faces into region-internal"
+                        << " and region-external."
+                        << nl << endl;
                 }
             }
 
@@ -744,27 +926,56 @@ int main(int argc, char *argv[])
             if (orderPoints)
             {
                 Info<< "Ordering points into internal and boundary points."
-                    << nl
-                    << endl;
+                    << nl << endl;
             }
 
-            renumberDict.readEntry("writeMaps", writeMaps);
-            if (writeMaps)
+            if
+            (
+                renumberDict.readIfPresent("writeMaps", writeMaps)
+             && writeMaps
+            )
             {
-                Info<< "Writing renumber maps (new to old) to polyMesh." << nl
-                    << endl;
+                Info<< "Writing renumber maps (new to old) to polyMesh."
+                    << nl << endl;
             }
         }
         else
         {
-            Info<< "Using default renumberMethod." << nl << endl;
-            dictionary renumberDict;
-            renumberPtr.reset(new CuthillMcKeeRenumber(renumberDict));
-        }
+            if (args.found("renumber-coeffs"))
+            {
+                ITstream is = args.lookup("renumber-coeffs");
 
-        Info<< "Selecting renumberMethod " << renumberPtr().type() << nl
-            << endl;
+                is >> renumberDict;
 
+                Info<< "Specified renumber coefficients:" << nl
+                    << renumberDict << nl;
+            }
+
+            if (!renumberMethodName.empty())
+            {
+                // Specify/override the "method" within dictionary
+                renumberDict.set("method", renumberMethodName);
+                renumberPtr = renumberMethod::New(renumberDict);
+            }
+            else if (renumberDict.found("method"))
+            {
+                // Use the "method" type within dictionary
+                renumberPtr = renumberMethod::New(renumberDict);
+            }
+        }
+
+        // Default renumbering method
+        if (!renumberPtr)
+        {
+            renumberPtr.reset(new CuthillMcKeeRenumber(renumberDict));
+            Info<< "Using renumber-method: " << renumberPtr().type()
+                << " [default]" << nl << endl;
+        }
+        else
+        {
+            Info<< "Using renumber-method: " << renumberPtr().type()
+                << nl << endl;
+        }
 
 
         // Read parallel reconstruct maps
@@ -776,10 +987,10 @@ int main(int argc, char *argv[])
                 mesh.facesInstance(),
                 polyMesh::meshSubDir,
                 mesh,
-                IOobject::READ_IF_PRESENT,
+                (dryrun ? IOobject::NO_READ : IOobject::READ_IF_PRESENT),
                 IOobject::AUTO_WRITE
             ),
-            labelList(0)
+            labelList()
         );
 
         labelIOList faceProcAddressing
@@ -790,10 +1001,10 @@ int main(int argc, char *argv[])
                 mesh.facesInstance(),
                 polyMesh::meshSubDir,
                 mesh,
-                IOobject::READ_IF_PRESENT,
+                (dryrun ? IOobject::NO_READ : IOobject::READ_IF_PRESENT),
                 IOobject::AUTO_WRITE
             ),
-            labelList(0)
+            labelList()
         );
         labelIOList pointProcAddressing
         (
@@ -803,10 +1014,10 @@ int main(int argc, char *argv[])
                 mesh.pointsInstance(),
                 polyMesh::meshSubDir,
                 mesh,
-                IOobject::READ_IF_PRESENT,
+                (dryrun ? IOobject::NO_READ : IOobject::READ_IF_PRESENT),
                 IOobject::AUTO_WRITE
             ),
-            labelList(0)
+            labelList()
         );
         labelIOList boundaryProcAddressing
         (
@@ -816,75 +1027,77 @@ int main(int argc, char *argv[])
                 mesh.pointsInstance(),
                 polyMesh::meshSubDir,
                 mesh,
-                IOobject::READ_IF_PRESENT,
+                (dryrun ? IOobject::NO_READ : IOobject::READ_IF_PRESENT),
                 IOobject::AUTO_WRITE
             ),
-            labelList(0)
+            labelList()
         );
 
 
-        // Read objects in time directory
-        IOobjectList objects(mesh, runTime.timeName());
-
-
-        // Read vol fields.
-
-        PtrList<volScalarField> vsFlds;
-        ReadFields(mesh, objects, vsFlds);
-
-        PtrList<volVectorField> vvFlds;
-        ReadFields(mesh, objects, vvFlds);
-
-        PtrList<volSphericalTensorField> vstFlds;
-        ReadFields(mesh, objects, vstFlds);
-
-        PtrList<volSymmTensorField> vsymtFlds;
-        ReadFields(mesh, objects, vsymtFlds);
-
-        PtrList<volTensorField> vtFlds;
-        ReadFields(mesh, objects, vtFlds);
-
-
-        // Read surface fields.
-
-        PtrList<surfaceScalarField> ssFlds;
-        ReadFields(mesh, objects, ssFlds);
-
-        PtrList<surfaceVectorField> svFlds;
-        ReadFields(mesh, objects, svFlds);
-
-        PtrList<surfaceSphericalTensorField> sstFlds;
-        ReadFields(mesh, objects, sstFlds);
-
-        PtrList<surfaceSymmTensorField> ssymtFlds;
-        ReadFields(mesh, objects, ssymtFlds);
-
-        PtrList<surfaceTensorField> stFlds;
-        ReadFields(mesh, objects, stFlds);
-
-
-        // Read point fields.
+        // List of objects read from time directory
+        // List of stored objects to clear from mesh
 
-        PtrList<pointScalarField> psFlds;
-        ReadFields(pointMesh::New(mesh), objects, psFlds);
+        IOobjectList objects;
+        DynamicList<regIOobject*> storedObjects;
 
-        PtrList<pointVectorField> pvFlds;
-        ReadFields(pointMesh::New(mesh), objects, pvFlds);
-
-        PtrList<pointSphericalTensorField> pstFlds;
-        ReadFields(pointMesh::New(mesh), objects, pstFlds);
-
-        PtrList<pointSymmTensorField> psymtFlds;
-        ReadFields(pointMesh::New(mesh), objects, psymtFlds);
-
-        PtrList<pointTensorField> ptFlds;
-        ReadFields(pointMesh::New(mesh), objects, ptFlds);
+        if (!dryrun && doFields)
+        {
+            objects = IOobjectList(mesh, runTime.timeName());
+            storedObjects.reserve(objects.size());
+
+            const predicates::always nameMatcher;
+
+            // Read GeometricFields
+
+            #undef  ReadFields
+            #define ReadFields(FieldType)                                     \
+            readFields<FieldType>(mesh, objects, nameMatcher, storedObjects);
+
+            // Read volume fields
+            ReadFields(volScalarField);
+            ReadFields(volVectorField);
+            ReadFields(volSphericalTensorField);
+            ReadFields(volSymmTensorField);
+            ReadFields(volTensorField);
+
+            // Read internal fields
+            ReadFields(volScalarField::Internal);
+            ReadFields(volVectorField::Internal);
+            ReadFields(volSphericalTensorField::Internal);
+            ReadFields(volSymmTensorField::Internal);
+            ReadFields(volTensorField::Internal);
+
+            // Read surface fields
+            ReadFields(surfaceScalarField);
+            ReadFields(surfaceVectorField);
+            ReadFields(surfaceSphericalTensorField);
+            ReadFields(surfaceSymmTensorField);
+            ReadFields(surfaceTensorField);
+
+
+            // Read point fields
+            const pointMesh& pMesh = pointMesh::New(mesh);
+            #undef  ReadPointFields
+            #define ReadPointFields(FieldType)                                \
+            readFields<FieldType>(pMesh, objects, nameMatcher, storedObjects);
+
+            ReadPointFields(pointScalarField);
+            ReadPointFields(pointVectorField);
+            ReadPointFields(pointSphericalTensorField);
+            ReadPointFields(pointSymmTensorField);
+            ReadPointFields(pointTensorField);
+
+            #undef ReadFields
+            #undef ReadPointFields
+        }
 
 
         // Read sets
         PtrList<cellSet> cellSets;
         PtrList<faceSet> faceSets;
         PtrList<pointSet> pointSets;
+
+        if (!dryrun)
         {
             // Read sets
             IOobjectList objects(mesh, mesh.facesInstance(), "polyMesh/sets");
@@ -901,7 +1114,7 @@ int main(int argc, char *argv[])
         labelList cellOrder;
         labelList faceOrder;
 
-        if (blockSize > 0)
+        if (blockSize > 0 && !doDecompose)
         {
             // Renumbering in two phases. Should be done in one so mapping of
             // fields is done correctly!
@@ -910,15 +1123,14 @@ int main(int argc, char *argv[])
             Info<< "nBlocks   = " << nBlocks << endl;
 
             // Read decompositionMethod dictionary
-            dictionary decomposeDict(renumberDictPtr().subDict("blockCoeffs"));
+            dictionary decomposeDict(renumberDict.subDict("blockCoeffs"));
             decomposeDict.set("numberOfSubdomains", nBlocks);
 
+            // No parallel communication
             const bool oldParRun = UPstream::parRun(false);
 
-            autoPtr<decompositionMethod> decomposePtr = decompositionMethod::New
-            (
-                decomposeDict
-            );
+            autoPtr<decompositionMethod> decomposePtr =
+                decompositionMethod::New(decomposeDict);
 
             labelList cellToRegion
             (
@@ -945,7 +1157,10 @@ int main(int argc, char *argv[])
                 << nl << endl;
 
 
-            cellOrder = regionRenumber(renumberPtr(), mesh, cellToRegion);
+            CompactListList<label> regionCellOrder =
+                regionRenumber(renumberPtr(), mesh, cellToRegion);
+
+            cellOrder = regionCellOrder.values();
 
             // Determine new to old face order with new cell numbering
             faceOrder = getRegionFaceOrder
@@ -957,12 +1172,77 @@ int main(int argc, char *argv[])
         }
         else
         {
-            // Determines sorted back to original cell ordering
-            cellOrder = renumberPtr().renumber
-            (
-                mesh,
-                mesh.cellCentres()
-            );
+            if (doDecompose)
+            {
+                // Two-step renumbering.
+                // 1. decompose into regions (like decomposePar)
+                // 2. renumber each sub-region
+
+                // Read decompositionMethod dictionary
+                IOdictionary decomposeDict
+                (
+                    IOobject::selectIO
+                    (
+                        IOobject
+                        (
+                            decompositionModel::canonicalName,
+                            runTime.time().system(),
+                            mesh.regionName(),  // region (if non-default)
+                            runTime,
+                            IOobject::MUST_READ,
+                            IOobject::NO_WRITE,
+                            IOobject::NO_REGISTER
+                        ),
+                        decompDictFile
+                    )
+                );
+
+                // No parallel communication
+                const bool oldParRun = UPstream::parRun(false);
+
+                autoPtr<decompositionMethod> decomposePtr
+                    = decompositionMethod::New(decomposeDict);
+
+                labelList cellToRegion
+                (
+                    decomposePtr().decompose
+                    (
+                        mesh,
+                        mesh.cellCentres()
+                    )
+                );
+
+                UPstream::parRun(oldParRun);  // Restore parallel state
+
+                CompactListList<label> regionCellOrder =
+                    regionRenumber
+                    (
+                        renumberPtr(),
+                        mesh,
+                        cellToRegion,
+                        decomposePtr().nDomains()
+                    );
+
+                cellOrder = regionCellOrder.values();
+
+                // HACK - retain partitioning information for possible use
+                // at a later stage
+                mesh.data().set
+                (
+                    "requested.partition-offsets",
+                    regionCellOrder.offsets()
+                );
+            }
+            else
+            {
+                // Determines sorted back to original cell ordering
+                cellOrder = renumberPtr().renumber
+                (
+                    mesh,
+                    mesh.cellCentres()
+                );
+            }
+
 
             if (sortCoupledFaceCells)
             {
@@ -1003,11 +1283,11 @@ int main(int argc, char *argv[])
                         }
                     }
                 }
-                bndCells.setSize(nBndCells);
-                bndCellMap.setSize(nBndCells);
+                bndCells.resize(nBndCells);
+                bndCellMap.resize(nBndCells);
 
                 // Sort
-                labelList order(sortedOrder(bndCellMap));
+                labelList order(Foam::sortedOrder(bndCellMap));
 
                 // Redo newReverseCellOrder
                 labelList newReverseCellOrder(mesh.nCells(), -1);
@@ -1228,13 +1508,11 @@ int main(int argc, char *argv[])
             );
             reduce(band, maxOp<label>());
             reduce(profile, sumOp<scalar>());
+            reduce(sumSqrIntersect, sumOp<scalar>());
+
             scalar rmsFrontwidth = Foam::sqrt
             (
-                returnReduce
-                (
-                    sumSqrIntersect,
-                    sumOp<scalar>()
-                )/mesh.globalData().nTotalCells()
+                sumSqrIntersect/mesh.globalData().nTotalCells()
             );
 
             Info<< "After renumbering :" << nl
@@ -1243,7 +1521,6 @@ int main(int argc, char *argv[])
 
             if (doFrontWidth)
             {
-
                 Info<< "    rms frontwidth : " << rmsFrontwidth << nl;
             }
 
@@ -1306,110 +1583,191 @@ int main(int argc, char *argv[])
         }
 
 
-        if (overwrite)
+        if (dryrun)
         {
-            mesh.setInstance(oldInstance);
-        }
-        else
-        {
-            mesh.setInstance(runTime.timeName());
-        }
+            if (writeMaps)
+            {
+                fileName file = mesh.time().globalPath()/"renumberMesh";
 
+                const word& regionDir = mesh.regionName();
 
-        Info<< "Writing mesh to " << mesh.facesInstance() << endl;
+                if (!regionDir.empty())
+                {
+                    file += "_" + regionDir;
+                }
 
-        // Remove old procAddressing files
-        processorMeshes::removeFiles(mesh);
+                // VTK output
+                {
+                    const vtk::vtuCells cells(mesh);
 
-        // Update refinement data
-        hexRef8Data refData
-        (
-            IOobject
-            (
-                "dummy",
-                mesh.facesInstance(),
-                polyMesh::meshSubDir,
-                mesh,
-                IOobject::READ_IF_PRESENT,
-                IOobject::NO_WRITE,
-                IOobject::NO_REGISTER
-            )
-        );
-        refData.updateMesh(map());
-        refData.write();
+                    vtk::internalMeshWriter writer
+                    (
+                        mesh,
+                        cells,
+                        file,
+                        UPstream::parRun()
+                    );
+
+                    writer.writeGeometry();
+                    writer.beginCellData();
+                    writer.writeCellIDs();
+
+                    labelList ids;
+
+                    if (UPstream::parRun())
+                    {
+                        const label cellOffset =
+                            mesh.globalData().globalMeshCellAddr().localStart();
+
+                        ids.resize(mesh.nCells());
+                        std::transform
+                        (
+                            map().cellMap().cbegin(),
+                            map().cellMap().cend(),
+                            ids.begin(),
+                            [=](const label id) { return (id + cellOffset); }
+                        );
+
+                        writer.writeCellData("origCellID", ids);
+                        writer.writeProcIDs();
+                    }
+                    else
+                    {
+                        writer.writeCellData("origCellID", map().cellMap());
+
+                        // Recover any partition information (in serial)
+                        globalIndex partitionOffsets;
+                        if
+                        (
+                            mesh.data().readIfPresent
+                            (
+                                "requested.partition-offsets",
+                                partitionOffsets.offsets()
+                            )
+                        )
+                        {
+                            if (partitionOffsets.totalSize() != mesh.nCells())
+                            {
+                                WarningInFunction
+                                    << "Requested partition total-size: "
+                                    << partitionOffsets.totalSize()
+                                    << " mesh total-size: "
+                                    << mesh.nCells()
+                                    << " ... ignoring" << endl;
+
+                                partitionOffsets.clear();
+                            }
+                        }
 
-        // Update sets
-        topoSet::updateMesh(mesh.facesInstance(), map(), cellSets);
-        topoSet::updateMesh(mesh.facesInstance(), map(), faceSets);
-        topoSet::updateMesh(mesh.facesInstance(), map(), pointSets);
+                        ids.resize(partitionOffsets.totalSize());
 
-        mesh.write();
+                        for (const label proci : partitionOffsets.allProcs())
+                        {
+                            ids.slice(partitionOffsets.range(proci)) = proci;
+                        }
+
+                        if (!partitionOffsets.empty())
+                        {
+                            writer.writeCellData("procID", ids);
+                        }
+                    }
 
-        if (writeMaps)
+                    Info<< "Wrote renumbered mesh to "
+                        << mesh.time().relativePath(writer.output())
+                        << " for visualization."
+                        << endl;
+                }
+            }
+        }
+        else
         {
-            // For debugging: write out region
-            createScalarField
-            (
-                mesh,
-                "origCellID",
-                map().cellMap()
-            )().write();
+            if (overwrite)
+            {
+                mesh.setInstance(oldInstance);
+            }
+            else
+            {
+                mesh.setInstance(runTime.timeName());
+            }
 
-            createScalarField
-            (
-                mesh,
-                "cellID",
-                identity(mesh.nCells())
-            )().write();
+            Info<< "Writing mesh to " << mesh.facesInstance() << endl;
 
-            Info<< nl
-                << "Written current cellID and origCellID as volScalarField"
-                << " for use in postprocessing." << nl << endl;
+            // Remove old procAddressing files
+            processorMeshes::removeFiles(mesh);
+
+            // Update refinement data
 
-            labelIOList
+            hexRef8Data refData
             (
                 IOobject
                 (
-                    "cellMap",
+                    "dummy",
                     mesh.facesInstance(),
                     polyMesh::meshSubDir,
                     mesh,
-                    IOobject::NO_READ,
+                    (dryrun ? IOobject::NO_READ : IOobject::READ_IF_PRESENT),
                     IOobject::NO_WRITE,
                     IOobject::NO_REGISTER
-                ),
-                map().cellMap()
-            ).write();
+                )
+            );
+            refData.updateMesh(map());
+            refData.write();
 
-            labelIOList
-            (
-                IOobject
+            // Update sets
+            topoSet::updateMesh(mesh.facesInstance(), map(), cellSets);
+            topoSet::updateMesh(mesh.facesInstance(), map(), faceSets);
+            topoSet::updateMesh(mesh.facesInstance(), map(), pointSets);
+
+            mesh.write();
+
+            if (writeMaps)
+            {
+                // For debugging: write out region
+                createScalarField
                 (
-                    "faceMap",
-                    mesh.facesInstance(),
-                    polyMesh::meshSubDir,
                     mesh,
-                    IOobject::NO_READ,
-                    IOobject::NO_WRITE,
-                    IOobject::NO_REGISTER
-                ),
-                map().faceMap()
-            ).write();
+                    "origCellID",
+                    map().cellMap()
+                )().write();
 
-            labelIOList
-            (
-                IOobject
+                createScalarField
+                (
+                    mesh,
+                    "cellID",
+                    identity(mesh.nCells())
+                )().write();
+
+                Info<< nl
+                    << "Wrote current cellID and origCellID as volScalarField"
+                    << " for use in postprocessing." << nl << endl;
+
+                IOobject meshMapIO
                 (
-                    "pointMap",
+                    "map-name",
                     mesh.facesInstance(),
                     polyMesh::meshSubDir,
                     mesh,
                     IOobject::NO_READ,
                     IOobject::NO_WRITE,
                     IOobject::NO_REGISTER
-                ),
-                map().pointMap()
-            ).write();
+                );
+
+                meshMapIO.resetHeader("cellMap");
+                IOListRef<label>(meshMapIO, map().cellMap()).write();
+
+                meshMapIO.resetHeader("faceMap");
+                IOListRef<label>(meshMapIO, map().faceMap()).write();
+
+                meshMapIO.resetHeader("pointMap");
+                IOListRef<label>(meshMapIO, map().pointMap()).write();
+            }
+        }
+
+        // Cleanup loaded fields
+        while (!storedObjects.empty())
+        {
+            storedObjects.back()->checkOut();
+            storedObjects.pop_back();
         }
     }
 
diff --git a/applications/utilities/parallelProcessing/decomposePar/domainDecompositionDryRunWrite.C b/applications/utilities/parallelProcessing/decomposePar/domainDecompositionDryRunWrite.C
index 56924ebad3a65df4038f9af4c5ac0e1df8402599..86abe225627a95554d0613e83d9201f09c64a5d2 100644
--- a/applications/utilities/parallelProcessing/decomposePar/domainDecompositionDryRunWrite.C
+++ b/applications/utilities/parallelProcessing/decomposePar/domainDecompositionDryRunWrite.C
@@ -83,6 +83,7 @@ void Foam::domainDecompositionDryRun::writeVTK
     writer.writeGeometry();
     writer.beginCellData();
     writer.writeCellData("procID", cellToProc);
+    writer.writeCellIDs();
 
     Info<< "Wrote decomposition to "
         << this->mesh().time().relativePath(writer.output())
diff --git a/bin/tools/CleanFunctions b/bin/tools/CleanFunctions
index 4b54ca4b34debf6b6ba14e3ad32aa65e10142260..bda0428f45909aeca035ef3540347c3652e13c57 100644
--- a/bin/tools/CleanFunctions
+++ b/bin/tools/CleanFunctions
@@ -181,7 +181,7 @@ cleanCase()
     # Debug output (blockMesh, decomposePar)
     rm -f \
         blockTopology.vtu blockFaces.vtp blockTopology.obj blockCentres.obj \
-        cellDist.vtu \
+        cellDist.vtu decomposePar.vtu renumberMesh.vtu \
         0/cellDist
 
     # From mpirunDebug
diff --git a/etc/caseDicts/annotated/renumberMeshDict b/etc/caseDicts/annotated/renumberMeshDict
index 00ce52e88518415d670a229721137c431a5751bb..80f960d0144e9267b44696346393a853c9ba9569 100644
--- a/etc/caseDicts/annotated/renumberMeshDict
+++ b/etc/caseDicts/annotated/renumberMeshDict
@@ -1,7 +1,7 @@
 /*--------------------------------*- C++ -*----------------------------------*\
 | =========                 |                                                 |
 | \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
-|  \\    /   O peration     | Version:  v2312                                 |
+|  \\    /   O peration     | Version:  v2406                                 |
 |   \\  /    A nd           | Website:  www.openfoam.com                      |
 |    \\/     M anipulation  |                                                 |
 \*---------------------------------------------------------------------------*/
@@ -15,8 +15,12 @@ FoamFile
 }
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-// Write maps from renumbered back to original mesh
-writeMaps false;
+// Additional libraries
+//libs        (SloanRenumber);
+//libs        (zoltanRenumber);
+
+// Optional entry: write maps from renumbered back to original mesh
+writeMaps   false;
 
 // Optional entry: sort cells on coupled boundaries to last for use with
 // e.g. nonBlockingGaussSeidel.
@@ -36,61 +40,66 @@ sortCoupledFaceCells false;
 
 
 method          CuthillMcKee;
-//method          Sloan;
+//method          RCM;  // == reverseCuthillMcKee;
+//method          Sloan;        //<-  libs (zoltanRenumber);
 //method          manual;
 //method          random;
 //method          structured;
 //method          spring;
-//method          zoltan;             // only if compiled with zoltan support
+//method          zoltan;       //<-  libs (zoltanRenumber);
 
 //CuthillMcKeeCoeffs
 //{
-//    // Reverse CuthillMcKee (RCM) or plain
-//    reverse true;
+//    // Plain or reverse CuthillMcKee (RCM)
+//    reverse     true;
 //}
 
 manualCoeffs
 {
     // In system directory: new-to-original (i.e. order) labelIOList
-    dataFile "cellMap";
+    dataFile    "cellMap";
 }
 
 
 // For extruded (i.e. structured in one direction) meshes
 structuredCoeffs
 {
-    // Patches that mesh was extruded from. These determine the starting
-    // layer of cells
-    patches (movingWall);
+    // Patches that mesh was extruded from.
+    // These determine the starting layer of cells
+    patches     (movingWall);
+
     // Method to renumber the starting layer of cells
-    method  random;
+    method      random;
 
     // Renumber in columns (depthFirst) or in layers
-    depthFirst true;
+    depthFirst  true;
 
     // Reverse ordering
-    reverse false;
+    reverse     false;
 }
 
 
 springCoeffs
 {
     // Maximum jump of cell indices. Is fraction of number of cells
-    maxCo 0.01;
+    maxCo       0.01;
 
     // Limit the amount of movement; the fraction maxCo gets decreased
     // with every iteration
     freezeFraction 0.999;
 
     // Maximum number of iterations
-    maxIter 1000;
+    maxIter    1000;
+
+    // Enable/disable verbosity
+    verbose    true;
 }
 
 
 blockCoeffs
 {
-    method          scotch;
-    //method hierarchical;
+    method      scotch;
+    //method      hierarchical;
     //hierarchicalCoeffs
     //{
     //    n           (1 2 1);
diff --git a/etc/config.sh/zoltan b/etc/config.sh/zoltan
new file mode 100644
index 0000000000000000000000000000000000000000..f93cb8a4b787bfe183ece59bbde6f49c2dbac33e
--- /dev/null
+++ b/etc/config.sh/zoltan
@@ -0,0 +1,34 @@
+#----------------------------------*-sh-*--------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | www.openfoam.com
+#    \\/     M anipulation  |
+#------------------------------------------------------------------------------
+#     Copyright (C) 2024 OpenCFD Ltd.
+#------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
+#
+# File
+#     etc/config.sh/zoltan
+#     - sourced during wmake process only.
+#
+# Description
+#     Setup for ZOLTAN include/libraries (usually ThirdParty installation).
+#
+#     To disable its use:               ZOLTAN_VERSION=zoltan-none
+#     For system-wide installations:    ZOLTAN_VERSION=zoltan-system
+#
+# Note
+#     A csh version is not needed, since the values here are only sourced
+#     during the wmake process
+#
+#------------------------------------------------------------------------------
+# USER EDITABLE PART: Changes made here may be lost with the next upgrade
+
+export ZOLTAN_VERSION=Zoltan-3.901
+export ZOLTAN_ARCH_PATH=$WM_THIRD_PARTY_DIR/platforms/$WM_ARCH$WM_COMPILER$WM_PRECISION_OPTION$WM_LABEL_OPTION/$ZOLTAN_VERSION
+
+# END OF (NORMAL) USER EDITABLE PART
+#------------------------------------------------------------------------------
diff --git a/src/Allwmake b/src/Allwmake
index 96071901af037d5b58a92046c0b58278c6f25ded..3d2e2ac2224fc4dfd3afd2546b3c9c905797a801 100755
--- a/src/Allwmake
+++ b/src/Allwmake
@@ -72,6 +72,7 @@ wmakeLnInclude -u fvOptions
 wmake $targetType lagrangian/basic
 wmake $targetType lagrangian/distributionModels
 
+renumber/Allwmake $targetType $*
 parallel/Allwmake $targetType $*
 
 wmake $targetType dynamicFvMesh
@@ -100,7 +101,6 @@ wmake $targetType fvMotionSolver
 
 # snappyHexMesh uses overset voxelMesh
 mesh/Allwmake $targetType $*
-renumber/Allwmake $targetType $*
 fvAgglomerationMethods/Allwmake $targetType $*
 wmake $targetType waveModels
 
diff --git a/src/renumber/Allwmake b/src/renumber/Allwmake
index 0e5edabb3e9a2f09a17c65e7ddfa3f0bd2d528bc..a367a8322e230648ba119b897c65495a438ced4a 100755
--- a/src/renumber/Allwmake
+++ b/src/renumber/Allwmake
@@ -21,6 +21,7 @@ fi
 warning="==> skip zoltanRenumber"
 if have_zoltan
 then
+    echo "zoltan - $ZOLTAN_ARCH_PATH"
     wmake $targetType zoltanRenumber || echo "$warning (build issues detected)"
 else
     echo "$warning (no library)"
diff --git a/src/renumber/SloanRenumber/SloanRenumber.C b/src/renumber/SloanRenumber/SloanRenumber.C
index f3dea4f76229096f96e813da1deaffe232a554f3..9ef6ca08edc21bdf472ae58e33ae7891de75b380 100644
--- a/src/renumber/SloanRenumber/SloanRenumber.C
+++ b/src/renumber/SloanRenumber/SloanRenumber.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2017 OpenFOAM Foundation
-    Copyright (C) 2020-2023 OpenCFD Ltd.
+    Copyright (C) 2020-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -74,6 +74,9 @@ typedef adjacency_list
 typedef graph_traits<Graph>::vertex_descriptor Vertex;
 typedef graph_traits<Graph>::vertices_size_type size_type;
 
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
 namespace Foam
 {
     defineTypeNameAndDebug(SloanRenumber, 0);
@@ -89,6 +92,13 @@ namespace Foam
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::SloanRenumber::SloanRenumber(const bool reverse)
+:
+    renumberMethod(),
+    reverse_(reverse)
+{}
+
+
 Foam::SloanRenumber::SloanRenumber(const dictionary& dict)
 :
     renumberMethod(dict),
diff --git a/src/renumber/SloanRenumber/SloanRenumber.H b/src/renumber/SloanRenumber/SloanRenumber.H
index 7f61b3912bb7915cdd77930a21e11488dc612f3c..46aa18535670e0ce9032e94955d35965f1a75517 100644
--- a/src/renumber/SloanRenumber/SloanRenumber.H
+++ b/src/renumber/SloanRenumber/SloanRenumber.H
@@ -55,16 +55,8 @@ class SloanRenumber
 {
     // Private Data
 
-        const bool reverse_;
-
-
-    // Private Member Functions
-
-        //- No copy construct
-        SloanRenumber(const SloanRenumber&) = delete;
-
-        //- No copy assignment
-        void operator=(const SloanRenumber&) = delete;
+        //- Use reverse indexing
+        bool reverse_;
 
 
 public:
@@ -75,6 +67,9 @@ public:
 
     // Constructors
 
+        //- Default construct, optionally with reverse
+        explicit SloanRenumber(const bool reverse = false);
+
         //- Construct given the renumber dictionary
         explicit SloanRenumber(const dictionary& dict);
 
@@ -85,6 +80,13 @@ public:
 
     // Member Functions
 
+        //- Toggle reverse on/off
+        void reverse(bool on) noexcept { reverse_ = on; }
+
+        //- The renumbering method does not require a polyMesh
+        virtual bool needs_mesh() const { return false; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  This is only defined for geometric renumberMethods.
diff --git a/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.C b/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.C
index c5a867decdc962546d8ba7c6752bbc132e69b10a..241be2cbe3077ecbbfb3be5924fa1220705cf7d5 100644
--- a/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.C
+++ b/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.C
@@ -30,11 +30,12 @@ License
 #include "addToRunTimeSelectionTable.H"
 #include "bandCompression.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    defineTypeNameAndDebug(CuthillMcKeeRenumber, 0);
+    defineTypeName(CuthillMcKeeRenumber);
+    defineTypeName(reverseCuthillMcKeeRenumber);
 
     addToRunTimeSelectionTable
     (
@@ -42,11 +43,34 @@ namespace Foam
         CuthillMcKeeRenumber,
         dictionary
     );
+
+    addToRunTimeSelectionTable
+    (
+        renumberMethod,
+        reverseCuthillMcKeeRenumber,
+        dictionary
+    );
+
+    // Select under the name "RCM"
+    addNamedToRunTimeSelectionTable
+    (
+        renumberMethod,
+        reverseCuthillMcKeeRenumber,
+        dictionary,
+        RCM
+    );
 }
 
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::CuthillMcKeeRenumber::CuthillMcKeeRenumber(const bool reverse)
+:
+    renumberMethod(),
+    reverse_(reverse)
+{}
+
+
 Foam::CuthillMcKeeRenumber::CuthillMcKeeRenumber(const dictionary& dict)
 :
     renumberMethod(dict),
@@ -58,19 +82,45 @@ Foam::CuthillMcKeeRenumber::CuthillMcKeeRenumber(const dictionary& dict)
 {}
 
 
+Foam::CuthillMcKeeRenumber::CuthillMcKeeRenumber
+(
+    const dictionary& dict,
+    const bool reverse
+)
+:
+    renumberMethod(dict),
+    reverse_(reverse)
+{}
+
+
+Foam::reverseCuthillMcKeeRenumber::reverseCuthillMcKeeRenumber()
+:
+    CuthillMcKeeRenumber(true)  // reverse = true
+{}
+
+
+Foam::reverseCuthillMcKeeRenumber::reverseCuthillMcKeeRenumber
+(
+    const dictionary& dict
+)
+:
+    CuthillMcKeeRenumber(dict, true)  // reverse = true
+{}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 (
     const polyMesh& mesh,
-    const pointField& points
+    const pointField&
 ) const
 {
     labelList orderedToOld = meshTools::bandCompression(mesh);
 
     if (reverse_)
     {
-        reverse(orderedToOld);
+        Foam::reverse(orderedToOld);
     }
 
     return orderedToOld;
@@ -88,7 +138,7 @@ Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 
     if (reverse_)
     {
-        reverse(orderedToOld);
+        Foam::reverse(orderedToOld);
     }
 
     return orderedToOld;
@@ -98,14 +148,14 @@ Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 (
     const CompactListList<label>& cellCells,
-    const pointField& cc
+    const pointField&
 ) const
 {
     labelList orderedToOld = meshTools::bandCompression(cellCells);
 
     if (reverse_)
     {
-        reverse(orderedToOld);
+        Foam::reverse(orderedToOld);
     }
 
     return orderedToOld;
@@ -115,14 +165,14 @@ Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 Foam::labelList Foam::CuthillMcKeeRenumber::renumber
 (
     const labelListList& cellCells,
-    const pointField& points
+    const pointField&
 ) const
 {
     labelList orderedToOld = meshTools::bandCompression(cellCells);
 
     if (reverse_)
     {
-        reverse(orderedToOld);
+        Foam::reverse(orderedToOld);
     }
 
     return orderedToOld;
diff --git a/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.H b/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.H
index fd6df4cc2755674883396c88dca9f9c2e5cce453..45a07be355476d0f67a8d3eb07ce948e0db8825d 100644
--- a/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.H
+++ b/src/renumber/renumberMethods/CuthillMcKeeRenumber/CuthillMcKeeRenumber.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,7 +28,7 @@ Class
     Foam::CuthillMcKeeRenumber
 
 Description
-    Cuthill-McKee renumbering
+    Cuthill-McKee renumbering (CM or RCM)
 
 SourceFiles
     CuthillMcKeeRenumber.C
@@ -53,29 +53,28 @@ class CuthillMcKeeRenumber
 {
     // Private Data
 
-        const bool reverse_;
-
-
-    // Private Member Functions
-
-        //- No copy construct
-        CuthillMcKeeRenumber(const CuthillMcKeeRenumber&) = delete;
-
-        //- No copy assignment
-        void operator=(const CuthillMcKeeRenumber&) = delete;
+        //- Use reverse indexing
+        bool reverse_;
 
 
 public:
 
     //- Runtime type information
-    TypeName("CuthillMcKee");
+    TypeNameNoDebug("CuthillMcKee");
 
 
     // Constructors
 
+        //- Default construct, optionally with reverse
+        explicit CuthillMcKeeRenumber(const bool reverse = false);
+
         //- Construct given the renumber dictionary
         explicit CuthillMcKeeRenumber(const dictionary& dict);
 
+        //- Construct given the renumber dictionary (ignored)
+        //- and specified reverse handling
+        CuthillMcKeeRenumber(const dictionary& dict, const bool reverse);
+
 
     //- Destructor
     virtual ~CuthillMcKeeRenumber() = default;
@@ -83,6 +82,13 @@ public:
 
     // Member Functions
 
+        //- Toggle reverse on/off
+        void reverse(bool on) noexcept { reverse_ = on; }
+
+        //- The renumbering method does not require a polyMesh
+        virtual bool needs_mesh() const { return false; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  This is only defined for geometric renumberMethods.
@@ -97,8 +103,10 @@ public:
         //  Use the mesh connectivity (if needed)
         virtual labelList renumber
         (
+            //! Mesh connectivity (see globalMeshData::calcCellCells)
             const polyMesh& mesh,
-            const pointField& cc
+            //! \em ignored
+            const pointField& cellCentres
         ) const;
 
         //- Return the order in which cells need to be visited
@@ -109,14 +117,17 @@ public:
         (
             const labelList& cellCells,
             const labelList& offsets,
-            const pointField& cc
+            //! \em ignored
+            const pointField& cellCentres
         ) const;
 
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         virtual labelList renumber
         (
+            //! Mesh connectivity
             const CompactListList<label>& cellCells,
+            //! \em ignored
             const pointField& cellCentres
         ) const;
 
@@ -126,12 +137,43 @@ public:
         //  - the connections are across coupled patches
         virtual labelList renumber
         (
+            //! Mesh connectivity
             const labelListList& cellCells,
+            //! \em ignored
             const pointField& cellCentres
         ) const;
 };
 
 
+/*---------------------------------------------------------------------------*\
+                   Class reverseCuthillMcKeeRenumber Declaration
+\*---------------------------------------------------------------------------*/
+
+//- Reverse Cuthill-McKee renumbering
+class reverseCuthillMcKeeRenumber
+:
+    public CuthillMcKeeRenumber
+{
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("reverseCuthillMcKee");
+
+
+    // Constructors
+
+        //- Default construct
+        reverseCuthillMcKeeRenumber();
+
+        //- Construct given the renumber dictionary (ignored)
+        explicit reverseCuthillMcKeeRenumber(const dictionary& dict);
+
+
+    //- Destructor
+    virtual ~reverseCuthillMcKeeRenumber() = default;
+};
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/renumber/renumberMethods/Make/files b/src/renumber/renumberMethods/Make/files
index 6ee4924d3377e101fd7f70e1e7724bcd34f4879e..863d3d21000216558548fc74da873e1986af2cfd 100644
--- a/src/renumber/renumberMethods/Make/files
+++ b/src/renumber/renumberMethods/Make/files
@@ -1,7 +1,10 @@
 renumberMethod/renumberMethod.C
+
 manualRenumber/manualRenumber.C
-CuthillMcKeeRenumber/CuthillMcKeeRenumber.C
+noRenumber/noRenumber.C
 randomRenumber/randomRenumber.C
+CuthillMcKeeRenumber/CuthillMcKeeRenumber.C
+
 springRenumber/springRenumber.C
 structuredRenumber/structuredRenumber.C
 structuredRenumber/OppositeFaceCellWaveBase.C
diff --git a/src/renumber/renumberMethods/manualRenumber/manualRenumber.C b/src/renumber/renumberMethods/manualRenumber/manualRenumber.C
index bce6268cefc35e3c3496c0664e4e00e90e6c0819..61dc4972394a589a68322b93109f29426d82c8bc 100644
--- a/src/renumber/renumberMethods/manualRenumber/manualRenumber.C
+++ b/src/renumber/renumberMethods/manualRenumber/manualRenumber.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2020-2022 OpenCFD Ltd.
+    Copyright (C) 2020-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,14 +28,13 @@ License
 
 #include "manualRenumber.H"
 #include "addToRunTimeSelectionTable.H"
-#include "IFstream.H"
 #include "labelIOList.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    defineTypeNameAndDebug(manualRenumber, 0);
+    defineTypeName(manualRenumber);
 
     addToRunTimeSelectionTable
     (
@@ -48,12 +47,19 @@ namespace Foam
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::manualRenumber::manualRenumber(const fileName& file)
+:
+    renumberMethod(),
+    dataFile_(file)
+{}
+
+
 Foam::manualRenumber::manualRenumber(const dictionary& dict)
 :
     renumberMethod(dict),
     dataFile_
     (
-        dict.optionalSubDict(typeName+"Coeffs").get<fileName>("dataFile")
+        dict.optionalSubDict(typeName + "Coeffs").get<fileName>("dataFile")
     )
 {}
 
diff --git a/src/renumber/renumberMethods/manualRenumber/manualRenumber.H b/src/renumber/renumberMethods/manualRenumber/manualRenumber.H
index f6dbb1abf5aaa6ac950779bf88464dc603159752..208a6d959b5bee7b97ca64f9f32f3e77c0612bb2 100644
--- a/src/renumber/renumberMethods/manualRenumber/manualRenumber.H
+++ b/src/renumber/renumberMethods/manualRenumber/manualRenumber.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -55,23 +55,17 @@ class manualRenumber
 
         fileName dataFile_;
 
-
-    // Private Member Functions
-
-        //- No copy construct
-        manualRenumber(const manualRenumber&) = delete;
-
-        //- No copy assignment
-        void operator=(const manualRenumber&) = delete;
-
 public:
 
     //- Runtime type information
-    TypeName("manual");
+    TypeNameNoDebug("manual");
 
 
     // Constructors
 
+        //- Construct with given data file
+        explicit manualRenumber(const fileName& file);
+
         //- Construct given the renumber dictionary
         explicit manualRenumber(const dictionary& dict);
 
@@ -82,6 +76,10 @@ public:
 
     // Member Functions
 
+        //- The renumbering method needs a polyMesh (for its IOobject)
+        virtual bool needs_mesh() const { return true; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         virtual labelList renumber(const pointField&) const
diff --git a/src/renumber/renumberMethods/noRenumber/noRenumber.C b/src/renumber/renumberMethods/noRenumber/noRenumber.C
new file mode 100644
index 0000000000000000000000000000000000000000..638783039990626343df9bb40f08340037d4cbfc
--- /dev/null
+++ b/src/renumber/renumberMethods/noRenumber/noRenumber.C
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2024 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "noRenumber.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeName(noRenumber);
+
+    addNamedToRunTimeSelectionTable
+    (
+        renumberMethod,
+        noRenumber,
+        dictionary,
+        none
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::noRenumber::noRenumber()
+:
+    renumberMethod()
+{}
+
+
+Foam::noRenumber::noRenumber(const dictionary& dict)
+:
+    renumberMethod(dict)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::labelList Foam::noRenumber::renumber
+(
+    const pointField& cellCentres
+) const
+{
+    return Foam::identity(cellCentres.size());
+}
+
+
+Foam::labelList Foam::noRenumber::renumber
+(
+    const polyMesh& mesh,
+    const pointField&
+) const
+{
+    return Foam::identity(mesh.nCells());
+}
+
+
+Foam::labelList Foam::noRenumber::renumber
+(
+    const CompactListList<label>& cellCells,
+    const pointField&
+) const
+{
+    return Foam::identity(cellCells.size());
+}
+
+
+Foam::labelList Foam::noRenumber::renumber
+(
+    const labelListList& cellCells,
+    const pointField&
+) const
+{
+    return Foam::identity(cellCells.size());
+}
+
+
+// ************************************************************************* //
diff --git a/src/renumber/renumberMethods/noRenumber/noRenumber.H b/src/renumber/renumberMethods/noRenumber/noRenumber.H
new file mode 100644
index 0000000000000000000000000000000000000000..98273362da1608c953062f057dd308ac06d6ae0f
--- /dev/null
+++ b/src/renumber/renumberMethods/noRenumber/noRenumber.H
@@ -0,0 +1,124 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2024 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::noRenumber
+
+Description
+    A dummy renumber method, selected as \c none.
+
+    Method coefficients: \a none
+
+SourceFiles
+    noRenumber.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_noRenumber_H
+#define Foam_noRenumber_H
+
+#include "renumberMethod.H"
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class noRenumber Declaration
+\*---------------------------------------------------------------------------*/
+
+class noRenumber
+:
+    public renumberMethod
+{
+public:
+
+    //- Runtime type information
+    TypeNameNoDebug("none");
+
+
+    // Constructors
+
+        //- Default construct
+        noRenumber();
+
+        //- Construct given the renumber dictionary (unused)
+        explicit noRenumber(const dictionary& dict);
+
+
+    //- Destructor
+    virtual ~noRenumber() = default;
+
+
+    // Member Functions
+
+        //- The renumbering method does not require a polyMesh
+        virtual bool needs_mesh() const { return false; }
+
+
+        //- Return the order in which cells need to be visited
+        //- (ie. from ordered back to original cell label).
+        virtual labelList renumber(const pointField&) const;
+
+        //- Return the order in which cells need to be visited
+        //- (ie. from ordered back to original cell label).
+        virtual labelList renumber
+        (
+            //! Mesh number of cells
+            const polyMesh& mesh,
+            //! \em ignored
+            const pointField& cellCentres
+        ) const;
+
+        //- Return the order in which cells need to be visited
+        //- (ie. from ordered back to original cell label).
+        virtual labelList renumber
+        (
+            //! Mesh connectivity for number of cells
+            const CompactListList<label>& cellCells,
+            //! \em ignored
+            const pointField& cellCentres
+        ) const;
+
+        //- Return the order in which cells need to be visited
+        //- (ie. from ordered back to original cell label).
+        virtual labelList renumber
+        (
+            //! Mesh connectivity for number of cells
+            const labelListList& cellCells,
+            //! \em ignored
+            const pointField& cellCentres
+        ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/renumber/renumberMethods/randomRenumber/randomRenumber.C b/src/renumber/renumberMethods/randomRenumber/randomRenumber.C
index a18be4d3c8d424f0d0b5ab6d90f00c9c28fcc7a1..4ae111352d71f6036e6897d731445137b8438ae4 100644
--- a/src/renumber/renumberMethods/randomRenumber/randomRenumber.C
+++ b/src/renumber/renumberMethods/randomRenumber/randomRenumber.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2012 OpenFOAM Foundation
-    Copyright (C) 2021-2022 OpenCFD Ltd.
+    Copyright (C) 2021-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -30,11 +30,11 @@ License
 #include "Random.H"
 #include "addToRunTimeSelectionTable.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    defineTypeNameAndDebug(randomRenumber, 0);
+    defineTypeName(randomRenumber);
 
     addToRunTimeSelectionTable
     (
@@ -45,8 +45,39 @@ namespace Foam
 }
 
 
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+labelList randomMap(const label nCells)
+{
+    Random rndGen(0);
+
+    // Full coverage
+    labelList newToOld(Foam::identity(nCells));
+
+    // Fisher-Yates shuffle algorithm
+    for (label i = nCells - 1; i > 0; --i)
+    {
+        label j = rndGen.position<label>(0, i);
+        std::swap(newToOld[i], newToOld[j]);
+    }
+
+    return newToOld;
+}
+
+} // End namespace Foam
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::randomRenumber::randomRenumber()
+:
+    renumberMethod()
+{}
+
+
 Foam::randomRenumber::randomRenumber(const dictionary& dict)
 :
     renumberMethod(dict)
@@ -57,52 +88,40 @@ Foam::randomRenumber::randomRenumber(const dictionary& dict)
 
 Foam::labelList Foam::randomRenumber::renumber
 (
-    const pointField& points
+    const pointField& cellCentres
 ) const
 {
-    Random rndGen(0);
-
-    labelList newToOld(identity(points.size()));
-
-    for (label iter = 0; iter < 10; iter++)
-    {
-        forAll(newToOld, i)
-        {
-            label j = rndGen.position<label>(0, newToOld.size()-1);
-            std::swap(newToOld[i], newToOld[j]);
-        }
-    }
-    return newToOld;
+    return randomMap(cellCentres.size());
 }
 
 
 Foam::labelList Foam::randomRenumber::renumber
 (
     const polyMesh& mesh,
-    const pointField& points
+    const pointField&
 ) const
 {
-    return renumber(points);
+    return randomMap(mesh.nCells());
 }
 
 
 Foam::labelList Foam::randomRenumber::renumber
 (
     const CompactListList<label>& cellCells,
-    const pointField& points
+    const pointField&
 ) const
 {
-    return renumber(points);
+    return randomMap(cellCells.size());
 }
 
 
 Foam::labelList Foam::randomRenumber::renumber
 (
     const labelListList& cellCells,
-    const pointField& points
+    const pointField&
 ) const
 {
-    return renumber(points);
+    return randomMap(cellCells.size());
 }
 
 
diff --git a/src/renumber/renumberMethods/randomRenumber/randomRenumber.H b/src/renumber/renumberMethods/randomRenumber/randomRenumber.H
index 233a98638c6f5f14864f11aa28ba6cac6cf404dc..5769be0ce7448c50b89ffab162409bf7bf2422c1 100644
--- a/src/renumber/renumberMethods/randomRenumber/randomRenumber.H
+++ b/src/renumber/renumberMethods/randomRenumber/randomRenumber.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2012 OpenFOAM Foundation
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -51,24 +51,18 @@ class randomRenumber
 :
     public renumberMethod
 {
-    // Private Member Functions
-
-        //- No copy construct
-        randomRenumber(const randomRenumber&) = delete;
-
-        //- No copy assignment
-        void operator=(const randomRenumber&) = delete;
-
-
 public:
 
     //- Runtime type information
-    TypeName("random");
+    TypeNameNoDebug("random");
 
 
     // Constructors
 
-        //- Construct given the renumber dictionary
+        //- Default construct
+        randomRenumber();
+
+        //- Construct given the renumber dictionary (unused)
         explicit randomRenumber(const dictionary& dict);
 
 
@@ -78,19 +72,31 @@ public:
 
     // Member Functions
 
+        //- The renumbering method does not require a polyMesh
+        virtual bool needs_mesh() const { return false; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         virtual labelList renumber(const pointField&) const;
 
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
-        virtual labelList renumber(const polyMesh&, const pointField&) const;
+        virtual labelList renumber
+        (
+            //! Mesh number of cells
+            const polyMesh& mesh,
+            //! \em ignored
+            const pointField& cellCentres
+        ) const;
 
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         virtual labelList renumber
         (
+            //! Mesh connectivity for number of cells
             const CompactListList<label>& cellCells,
+            //! \em ignored
             const pointField& cellCentres
         ) const;
 
@@ -98,7 +104,9 @@ public:
         //- (ie. from ordered back to original cell label).
         virtual labelList renumber
         (
+            //! Mesh connectivity for number of cells
             const labelListList& cellCells,
+            //! \em ignored
             const pointField& cellCentres
         ) const;
 };
diff --git a/src/renumber/renumberMethods/renumberMethod/renumberMethod.C b/src/renumber/renumberMethods/renumberMethod/renumberMethod.C
index 7be4654d920b04d95c1c318cb30defb46e46731e..7cb556bd8f2ba1e1aeb30a1bd4d2a168e3fe13cd 100644
--- a/src/renumber/renumberMethods/renumberMethod/renumberMethod.C
+++ b/src/renumber/renumberMethods/renumberMethod/renumberMethod.C
@@ -27,18 +27,30 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "renumberMethod.H"
+#include "dlLibraryTable.H"
 #include "globalMeshData.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
-    defineTypeNameAndDebug(renumberMethod, 0);
+    defineTypeName(renumberMethod);
     defineRunTimeSelectionTable(renumberMethod, dictionary);
 }
 
+
 // * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
 
+Foam::wordList Foam::renumberMethod::supportedMethods()
+{
+    if (dictionaryConstructorTablePtr_)
+    {
+        return dictionaryConstructorTablePtr_->sortedToc();
+    }
+    return wordList();
+}
+
+
 Foam::autoPtr<Foam::renumberMethod> Foam::renumberMethod::New
 (
     const dictionary& dict
@@ -48,6 +60,9 @@ Foam::autoPtr<Foam::renumberMethod> Foam::renumberMethod::New
 
     //Info<< "Selecting renumberMethod " << methodType << endl;
 
+    // Load any additional libs (verbose = false)
+    dlLibraryTable::libs().open("libs", dict, false);
+
     auto* ctorPtr = dictionaryConstructorTable(methodType);
 
     if (!ctorPtr)
@@ -67,6 +82,16 @@ Foam::autoPtr<Foam::renumberMethod> Foam::renumberMethod::New
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+Foam::labelList Foam::renumberMethod::renumber
+(
+    const pointField&
+) const
+{
+    NotImplemented;
+    return labelList();
+}
+
+
 Foam::labelList Foam::renumberMethod::renumber
 (
     const polyMesh& mesh,
diff --git a/src/renumber/renumberMethods/renumberMethod/renumberMethod.H b/src/renumber/renumberMethods/renumberMethod/renumberMethod.H
index bc23c854041a001e731a93ba7eed4a99d9c3830b..9bfa6dd6c5412e3989034600564871ce4117e252 100644
--- a/src/renumber/renumberMethods/renumberMethod/renumberMethod.H
+++ b/src/renumber/renumberMethods/renumberMethod/renumberMethod.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,6 +41,7 @@ SourceFiles
 #include "polyMesh.H"
 #include "pointField.H"
 #include "CompactListList.H"
+#include "wordList.H"
 
 namespace Foam
 {
@@ -51,26 +52,10 @@ namespace Foam
 
 class renumberMethod
 {
-protected:
-
-    // Protected Data
-
-        const dictionary& renumberDict_;
-
-
-    // Protected Member Functions
-
-        //- No copy construct
-        renumberMethod(const renumberMethod&) = delete;
-
-        //- No copy assignment
-        void operator=(const renumberMethod&) = delete;
-
-
 public:
 
     //- Runtime type information
-    TypeName("renumberMethod");
+    TypeNameNoDebug("renumberMethod");
 
 
     // Declare run-time constructor selection tables
@@ -87,22 +72,24 @@ public:
         );
 
 
-    // Selectors
+    // Constructors
 
-        //- Return a reference to the selected renumbering method
-        static autoPtr<renumberMethod> New
-        (
-            const dictionary& renumberDict
-        );
+        //- Default construct
+        renumberMethod()
+        {}
 
+        //- Construct with renumber dictionary (which is currently unused)
+        explicit renumberMethod(const dictionary&)
+        {}
 
-    // Constructors
 
-        //- Construct given the renumber dictionary
-        explicit renumberMethod(const dictionary& dict)
-        :
-            renumberDict_(dict)
-        {}
+    // Selectors
+
+        //- Construct/select a renumbering method
+        static autoPtr<renumberMethod> New(const dictionary& dict);
+
+        //- Return a list of the known methods
+        static wordList supportedMethods();
 
 
     //- Destructor
@@ -111,31 +98,25 @@ public:
 
     // Member Functions
 
+        //- Does renumbering method require a polyMesh?
+        virtual bool needs_mesh() const { return false; }
+
+
+    // No topology
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
-        //  This is only defined for geometric renumberMethods.
-        virtual labelList renumber(const pointField&) const
-        {
-            NotImplemented;
-            return labelList();
-        }
+        //  This is only defined for geometric renumber methods.
+        virtual labelList renumber(const pointField&) const;
+
+
+    // Topology provided by mesh
 
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  Use the mesh connectivity (if needed)
         virtual labelList renumber(const polyMesh&, const pointField&) const;
 
-        //- Return the order in which cells need to be visited
-        //- (ie. from ordered back to original cell label).
-        //  Addressing in losort addressing (= neighbour + offsets into
-        //  neighbour)
-        virtual labelList renumber
-        (
-            const labelList& cellCells,
-            const labelList& offsets,
-            const pointField&
-        ) const;
-
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  Gets passed agglomeration map (from fine to coarse cells) and coarse
@@ -151,6 +132,20 @@ public:
             const pointField& coarsePoints
         ) const;
 
+
+    // Topology provided explicitly
+
+        //- Return the order in which cells need to be visited
+        //- (ie. from ordered back to original cell label).
+        //  Addressing in losort addressing (= neighbour + offsets into
+        //  neighbour)
+        virtual labelList renumber
+        (
+            const labelList& cellCells,
+            const labelList& offsets,
+            const pointField&
+        ) const;
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  Uses 'unpack' internally, so should be overloaded when possible
diff --git a/src/renumber/renumberMethods/springRenumber/springRenumber.C b/src/renumber/renumberMethods/springRenumber/springRenumber.C
index c331b23ad8031a54941eeba073c5ae2ade378d7a..b0063d6b59b4af0ade078017297738f8ec2e45fe 100644
--- a/src/renumber/renumberMethods/springRenumber/springRenumber.C
+++ b/src/renumber/renumberMethods/springRenumber/springRenumber.C
@@ -50,10 +50,11 @@ namespace Foam
 Foam::springRenumber::springRenumber(const dictionary& dict)
 :
     renumberMethod(dict),
-    coeffsDict_(dict.optionalSubDict(typeName+"Coeffs")),
+    coeffsDict_(dict.optionalSubDict(typeName + "Coeffs")),
     maxIter_(coeffsDict_.get<label>("maxIter")),
     maxCo_(coeffsDict_.get<scalar>("maxCo")),
-    freezeFraction_(coeffsDict_.get<scalar>("freezeFraction"))
+    freezeFraction_(coeffsDict_.get<scalar>("freezeFraction")),
+    verbose_(coeffsDict_.getOrDefault("verbose", true))
 {}
 
 
@@ -71,10 +72,7 @@ Foam::labelList Foam::springRenumber::renumberImpl
     // Move cells to the average 'position' of their neighbour.
 
     scalarField position(nOldCells);
-    forAll(position, celli)
-    {
-        position[celli] = celli;
-    }
+    std::iota(position.begin(), position.end(), 0);
 
     // Sum force per cell. Also reused for the displacement
     scalarField sumForce(nOldCells);
@@ -90,9 +88,9 @@ Foam::labelList Foam::springRenumber::renumberImpl
         //    << endl;
 
         //Pout<< "Position :" << nl
-        //    << "    min : " << min(position) << nl
-        //    << "    max : " << max(position) << nl
-        //    << "    avg : " << average(position) << nl
+        //    << "    min : " << Foam::min(position) << nl
+        //    << "    max : " << Foam::max(position) << nl
+        //    << "    avg : " << Foam::average(position) << nl
         //    << endl;
 
         // Sum force per cell.
@@ -100,9 +98,8 @@ Foam::labelList Foam::springRenumber::renumberImpl
         for (label oldCelli = 0; oldCelli < nOldCells; ++oldCelli)
         {
             const label celli = oldToNew[oldCelli];
-            const auto& neighbours = cellCells[oldCelli];
 
-            for (const label nbr : neighbours)
+            for (const label nbr : cellCells[oldCelli])
             {
                 const label nbrCelli = oldToNew[nbr];
 
@@ -111,35 +108,38 @@ Foam::labelList Foam::springRenumber::renumberImpl
         }
 
         //Pout<< "Force :" << nl
-        //    << "    min    : " << min(sumForce) << nl
-        //    << "    max    : " << max(sumForce) << nl
-        //    << "    avgMag : " << average(mag(sumForce)) << nl
+        //    << "    min    : " << Foam::min(sumForce) << nl
+        //    << "    max    : " << Foam::max(sumForce) << nl
+        //    << "    avgMag : " << Foam::average(mag(sumForce)) << nl
         //    << "DeltaT : " << deltaT << nl
         //    << endl;
 
         // Limit displacement
         scalar deltaT = maxCo / max(mag(sumForce));
 
-        Info<< "Iter:" << iter
-            << "  maxCo:" << maxCo
-            << "  deltaT:" << deltaT
-            << "  average force:" << average(mag(sumForce)) << endl;
+        if (verbose_)
+        {
+            Info<< "Iter:" << iter
+                << "  maxCo:" << maxCo
+                << "  deltaT:" << deltaT
+                << "  average force:" << average(mag(sumForce)) << endl;
+        }
 
         // Determine displacement
         scalarField& displacement = sumForce;
         displacement *= deltaT;
 
         //Pout<< "Displacement :" << nl
-        //    << "    min    : " << min(displacement) << nl
-        //    << "    max    : " << max(displacement) << nl
-        //    << "    avgMag : " << average(mag(displacement)) << nl
+        //    << "    min    : " << Foam::min(displacement) << nl
+        //    << "    max    : " << Foam::max(displacement) << nl
+        //    << "    avgMag : " << Foam::average(mag(displacement)) << nl
         //    << endl;
 
         // Calculate new position and scale to be within original range
         // (0..nCells-1) for ease of postprocessing.
         position += displacement;
-        position -= min(position);
-        position *= (position.size()-1)/max(position);
+        position -= Foam::min(position);
+        position *= (position.size()-1)/Foam::max(position);
 
         // Slowly freeze.
         maxCo *= freezeFraction_;
@@ -148,7 +148,7 @@ Foam::labelList Foam::springRenumber::renumberImpl
     //writeOBJ("endPosition.obj", cellCells, position);
 
     // Move cells to new position
-    labelList shuffle(sortedOrder(position));
+    labelList shuffle(Foam::sortedOrder(position));
 
     // Reorder oldToNew
     inplaceReorder(shuffle, oldToNew);
diff --git a/src/renumber/renumberMethods/springRenumber/springRenumber.H b/src/renumber/renumberMethods/springRenumber/springRenumber.H
index c7e6ae4f9e9e47e4840c21d59fa693312ebc1f2a..bf0d8ac8b5177c14709376139c2dbdee3d30debc 100644
--- a/src/renumber/renumberMethods/springRenumber/springRenumber.H
+++ b/src/renumber/renumberMethods/springRenumber/springRenumber.H
@@ -72,6 +72,9 @@ class springRenumber
 
         const scalar freezeFraction_;
 
+        //- Verbose reporting of progress
+        bool verbose_;
+
 
     // Private Member Functions
 
diff --git a/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.C b/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.C
index e94aa0406b1f71271a0e6e6910810ee599303e50..f5a13168a975a74e95511ae588a85ed1c1130d71 100644
--- a/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.C
+++ b/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.C
@@ -32,7 +32,7 @@ License
 #include "fvMeshSubset.H"
 #include "OppositeFaceCellWave.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
@@ -59,7 +59,7 @@ Foam::structuredRenumber::structuredRenumber
     patches_(coeffsDict_.get<wordRes>("patches")),
     nLayers_(coeffsDict_.getOrDefault<label>("nLayers", labelMax)),
     depthFirst_(coeffsDict_.get<bool>("depthFirst")),
-    reverse_(coeffsDict_.get<bool>("reverse")),
+    reverse_(coeffsDict_.getOrDefault("reverse", false)),
     method_(renumberMethod::New(coeffsDict_))
 {}
 
@@ -70,10 +70,10 @@ bool Foam::structuredRenumber::layerLess::operator()
 (
     const label a,
     const label b
-)
+) const
 {
-    const topoDistanceData<label>& ta = distance_[a];
-    const topoDistanceData<label>& tb = distance_[b];
+    const auto& ta = distance_[a];
+    const auto& tb = distance_[b];
 
     int dummy;
 
@@ -273,7 +273,7 @@ Foam::labelList Foam::structuredRenumber::renumber
     // Return furthest away cell first
     if (reverse_)
     {
-        reverse(orderedToOld);
+        Foam::reverse(orderedToOld);
     }
 
     return orderedToOld;
diff --git a/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.H b/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.H
index c79ce09a46a695d8df41628c412e2a6ff040beee..f436862832320b7226580b91950894563b63aa79 100644
--- a/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.H
+++ b/src/renumber/renumberMethods/structuredRenumber/structuredRenumber.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2020-2022 OpenCFD Ltd.
+    Copyright (C) 2020-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,7 +52,7 @@ namespace Foam
 template<class Type> class topoDistanceData;
 
 /*---------------------------------------------------------------------------*\
-                   Class structuredRenumber Declaration
+                     Class structuredRenumber Declaration
 \*---------------------------------------------------------------------------*/
 
 class structuredRenumber
@@ -63,8 +63,7 @@ public:
 
     // Public Classes
 
-        //- Less function class that can be used for sorting according to
-        //  column and layer
+        //- Function class for sorting according to column and layer
         class layerLess
         {
             const bool depthFirst_;
@@ -85,10 +84,12 @@ public:
                 distance_(distance)
             {}
 
-            bool operator()(const label a, const label b);
+            bool operator()(const label a, const label b) const;
         };
 
 
+private:
+
     // Private Data
 
         const dictionary& coeffsDict_;
@@ -131,6 +132,10 @@ public:
 
     // Member Functions
 
+        //- Renumbering method requires a polyMesh
+        virtual bool needs_mesh() const { return true; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  This is only defined for geometric renumberMethods.
diff --git a/src/renumber/zoltanRenumber/Make/files b/src/renumber/zoltanRenumber/Make/files
index 15659a52d0ff71ca05f4f1f116cfe6f74df85cae..91cd6da72b5f2a8444da76f4ed2d5dbf801e24ce 100644
--- a/src/renumber/zoltanRenumber/Make/files
+++ b/src/renumber/zoltanRenumber/Make/files
@@ -1,3 +1,4 @@
 zoltanRenumber.C
 
-LIB = $(FOAM_LIBBIN)/libzoltanRenumber
+/* Build with MPI dependency */
+LIB = $(FOAM_MPI_LIBBIN)/libzoltanRenumber
diff --git a/src/renumber/zoltanRenumber/Make/options b/src/renumber/zoltanRenumber/Make/options
index 589a3edc8b56484dbbe4de6d3b33f3d2eec0fb34..54063354a0eac983a7649946b001b1781e7c9a50 100644
--- a/src/renumber/zoltanRenumber/Make/options
+++ b/src/renumber/zoltanRenumber/Make/options
@@ -8,5 +8,7 @@ EXE_INC = \
     -I$(LIB_SRC)/meshTools/lnInclude
 
 LIB_LIBS = \
-    -L$(ZOLTAN_LIB_DIR) -lzoltan \
-    -lmeshTools
+    -lmeshTools \
+    -lrenumberMethods \
+    $(PLIBS) \
+    -L$(ZOLTAN_LIB_DIR) -lzoltan
diff --git a/src/renumber/zoltanRenumber/zoltanRenumber.C b/src/renumber/zoltanRenumber/zoltanRenumber.C
index 7b50f620d081950e16af164d881841d982721e4f..5cd44e11932450fbc3ec902af2c39ad586b90016 100644
--- a/src/renumber/zoltanRenumber/zoltanRenumber.C
+++ b/src/renumber/zoltanRenumber/zoltanRenumber.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -57,7 +58,10 @@ SourceFiles
 #include "polyMesh.H"
 #include "globalMeshData.H"
 #include "globalIndex.H"
+#include "CStringList.H"
 #include "uint.H"
+#include <algorithm>
+#include <numeric>
 
 // Include MPI without any C++ bindings
 #ifndef MPICH_SKIP_MPICXX
@@ -71,7 +75,7 @@ SourceFiles
 #include "zoltan.h"
 #include <mpi.h>
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
 {
@@ -86,43 +90,80 @@ namespace Foam
 }
 
 
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+// [ZOLTAN_NUM_OBJ_FN]
+// The number of graph vertices (locally)
 static int get_number_of_vertices(void *data, int *ierr)
 {
-    const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
     *ierr = ZOLTAN_OK;
+    const auto& mesh = *static_cast<const Foam::polyMesh*>(data);
+
     return mesh.nCells();
 }
 
 
-static void get_vertex_list(void *data, int sizeGID, int sizeLID,
-            ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
-                  int wgt_dim, float *obj_wgts, int *ierr)
+// [ZOLTAN_OBJ_LIST_FN]
+// The ids for the graph vertices
+static void get_vertex_list
+(
+    void *data,
+    int sizeGID,                // (currently unused)
+    int sizeLID,                // (currently unused)
+    ZOLTAN_ID_PTR global_ids,
+    ZOLTAN_ID_PTR local_ids,
+    int wgt_dim,                // (currently unused)
+    float *obj_wgts,            // (currently unused)
+    int *ierr
+)
 {
-    const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
-
     *ierr = ZOLTAN_OK;
+    const auto& mesh = *static_cast<const Foam::polyMesh*>(data);
 
-  /* In this example, return the IDs of our vertices, but no weights.
-   * Zoltan will assume equally weighted vertices.
-   */
+    const auto nCells = mesh.nCells();
 
-    wgt_dim = 0;
-    obj_wgts = nullptr;
+    // Offset for globally unique IDs
+    const auto myProci = Foam::UPstream::myProcNo();
+    const auto myProcOffset =
+        mesh.globalData().globalMeshCellAddr().localStart(myProci);
 
-    for (Foam::label i=0; i<mesh.nCells(); i++)
-    {
-        globalID[i] = i;        // should be global
-        localID[i] = i;
-    }
+    // Global indices
+    std::iota(global_ids, (global_ids + nCells), ZOLTAN_ID_TYPE(myProcOffset));
+
+    // Local indices
+    std::iota(local_ids, (local_ids + nCells), ZOLTAN_ID_TYPE(0));
+
+    // No weights?
+    // Zoltan will assume equally weighted vertices.
+
+    // if (obj_wgts && wgt_dim > 0)
+    // {
+    //     std::fill_n(obj_wgts, wgt_dim, float(1));
+    // }
 }
 
 
-static void get_num_edges_list(void *data, int sizeGID, int sizeLID,
-                      int num_obj,
-             ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
-             int *numEdges, int *ierr)
+// [ZOLTAN_NUM_EDGES_MULTI_FN]
+// query function returns the number of edges in the communication
+// graph of the application for each object in a list of objects.
+// That is, for each object in the global_ids/local_ids arrays, the
+// number of objects with which the given object must share
+// information is returned.
+
+static void get_num_edges_list
+(
+    void *data,
+    int sizeGID,
+    int sizeLID,
+    int num_obj,                // Number of graph vertices (mesh cells)
+    ZOLTAN_ID_PTR global_ids,   // (currently unused)
+    ZOLTAN_ID_PTR local_ids,    // rank-local vertex id (mesh cell id)
+    int *numEdges,
+    int *ierr
+)
 {
-  const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
+    *ierr = ZOLTAN_OK;
+    const auto& mesh = *static_cast<const Foam::polyMesh*>(data);
 
     if ((sizeGID != 1) || (sizeLID != 1) || (num_obj != mesh.nCells()))
     {
@@ -130,32 +171,46 @@ static void get_num_edges_list(void *data, int sizeGID, int sizeLID,
         return;
     }
 
-    for (Foam::label i=0; i < num_obj ;i++)
+    const Foam::label nCells = num_obj;
+
+    for (Foam::label i=0; i < nCells; ++i)
     {
-        Foam::label celli = localID[i];
-        const Foam::cell& cFaces = mesh.cells()[celli];
-        forAll(cFaces, cFacei)
+        const Foam::label celli = local_ids[i];
+        int numNbr = 0;
+
+        for (const auto facei : mesh.cells()[celli])
         {
-            Foam::label n = 0;
-            if (mesh.isInternalFace(cFaces[cFacei]))
+            if (mesh.isInternalFace(facei))
             {
-                n++;
+                ++numNbr;
             }
-            numEdges[i] = n;
+            // TBD: check coupled etc
         }
-    }
 
-    *ierr = ZOLTAN_OK;
+        numEdges[i] = numNbr;
+    }
 }
 
 
-static void get_edge_list(void *data, int sizeGID, int sizeLID,
-        int num_obj, ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
-        int *num_edges,
-        ZOLTAN_ID_PTR nborGID, int *nborProc,
-        int wgt_dim, float *ewgts, int *ierr)
+// [ZOLTAN_EDGE_LIST_MULTI_FN]
+static void get_edge_list
+(
+    void *data,
+    int sizeGID,
+    int sizeLID,
+    int num_obj,
+    ZOLTAN_ID_PTR global_ids,   // (currently unused)
+    ZOLTAN_ID_PTR local_ids,    // rank-local vertex id (mesh cell id)
+    int *num_edges,
+    ZOLTAN_ID_PTR nborGID,
+    int *nborProc,
+    int wgt_dim,
+    float *ewgts,
+    int *ierr
+)
 {
-    const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
+    *ierr = ZOLTAN_OK;
+    const auto& mesh = *static_cast<const Foam::polyMesh*>(data);
 
     if
     (
@@ -169,20 +224,24 @@ static void get_edge_list(void *data, int sizeGID, int sizeLID,
         return;
     }
 
-    ZOLTAN_ID_TYPE* nextNbor = nborGID;
-    int* nextProc = nborProc;
-    float* nextWgt = ewgts;
+    // Offset for globally unique IDs
+    const auto myProci = Foam::UPstream::myProcNo();
+    const auto myProcOffset =
+        mesh.globalData().globalMeshCellAddr().localStart(myProci);
+
+    auto* nextNbor = nborGID;
+    auto* nextProc = nborProc;
+    auto* nextWgt = ewgts;
 
-    for (Foam::label i=0; i < num_obj; i++)
+    const Foam::label nCells = num_obj;
+
+    for (Foam::label i=0; i < nCells; ++i)
     {
-        Foam::label celli = localID[i];
+        const Foam::label celli = local_ids[i];
+        int numNbr = 0;
 
-        const Foam::cell& cFaces = mesh.cells()[celli];
-        forAll(cFaces, cFacei)
+        for (const auto facei : mesh.cells()[celli])
         {
-            Foam::label n = 0;
-
-            Foam::label facei = cFaces[cFacei];
             if (mesh.isInternalFace(facei))
             {
                 Foam::label nbr = mesh.faceOwner()[facei];
@@ -191,32 +250,37 @@ static void get_edge_list(void *data, int sizeGID, int sizeLID,
                     nbr = mesh.faceNeighbour()[facei];
                 }
 
-                // Note: global index
-                *nextNbor++ = nbr;
-                *nextProc++ = 0;
+                *nextNbor++ = (nbr + myProcOffset); // global id
+                *nextProc++ = myProci;              // rank-local connection
                 *nextWgt++ = 1.0;
 
-                n++;
-            }
-            if (n != num_edges[i])
-            {
-                *ierr = ZOLTAN_FATAL;
-                return;
+                ++numNbr;
             }
         }
+
+        // Sanity check
+        if (numNbr != num_edges[i])
+        {
+            *ierr = ZOLTAN_FATAL;
+            return;
+        }
     }
-    *ierr = ZOLTAN_OK;
 }
 
 
+// [ZOLTAN_NUM_GEOM_FN]
+// The dimensionality of the mesh geometry (2D,3D, etc)
 static int get_mesh_dim(void *data, int *ierr)
 {
-    const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
+    *ierr = ZOLTAN_OK;
+    const auto& mesh = *static_cast<const Foam::polyMesh*>(data);
 
     return mesh.nSolutionD();
 }
 
 
+// [ZOLTAN_GEOM_MULTI_FN]
+// The geometric location of the graph vertices (mesh cellCentres)
 static void get_geom_list
 (
     void *data,
@@ -225,11 +289,12 @@ static void get_geom_list
     int num_obj,
     ZOLTAN_ID_PTR global_ids,
     ZOLTAN_ID_PTR local_ids,
-    int num_dim,
-    double *geom_vec,
+    int num_dim,                // dimensionality (2|3)
+    double *geom_vec,           // [out] cellCentres
     int *ierr
 )
 {
+    *ierr = ZOLTAN_OK;
     const Foam::polyMesh& mesh = *static_cast<const Foam::polyMesh*>(data);
 
     if
@@ -244,26 +309,46 @@ static void get_geom_list
         return;
     }
 
-    double* p = geom_vec;
-
-
-    const Foam::Vector<Foam::label>& sol = mesh.solutionD();
-
     const Foam::pointField& cc = mesh.cellCentres();
 
-    for (Foam::label celli = 0; celli < num_obj; celli++)
+    if (num_dim == 3)
+    {
+        // Fast path for 3D - assumes that local_ids are still ordered
+        std::copy_n
+        (
+            reinterpret_cast<const Foam::point::cmptType*>(cc.cdata()),
+            (3*num_obj),
+            geom_vec
+        );
+    }
+    else
     {
-        const Foam::point& pt = cc[celli];
+        // [out] cellCentres
+        double* p = geom_vec;
+
+        const Foam::Vector<Foam::label>& sol = mesh.solutionD();
+
+        const Foam::label nCells = num_obj;
 
-        for (Foam::direction cmpt = 0; cmpt < Foam::vector::nComponents; cmpt++)
+        // Assumes that local_ids are still ordered
+        for (Foam::label celli = 0; celli < nCells; ++celli)
         {
-            if (sol[cmpt] == 1)
+            const Foam::point& pt = cc[celli];
+
+            for
+            (
+                Foam::direction cmpt = 0;
+                cmpt < Foam::vector::nComponents;
+                ++cmpt
+            )
             {
-                *p++ = pt[cmpt];
+                if (sol[cmpt] == 1)
+                {
+                    *p++ = pt[cmpt];
+                }
             }
         }
     }
-    *ierr = ZOLTAN_OK;
 }
 
 
@@ -272,7 +357,7 @@ static void get_geom_list
 Foam::zoltanRenumber::zoltanRenumber(const dictionary& dict)
 :
     renumberMethod(dict),
-    coeffsDict_(dict.optionalSubDict(typeName+"Coeffs"))
+    coeffsDict_(dict.optionalSubDict(typeName + "Coeffs"))
 {}
 
 
@@ -284,20 +369,15 @@ Foam::labelList Foam::zoltanRenumber::renumber
     const pointField& points
 ) const
 {
-    stringList args(1);
-    args[0] = "zoltanRenumber";
+    // Zoltan_Initialize will trigger MPI_Init() if not already done.
+    // - use UPstream::initNull() so that OpenFOAM also knows about MPI
 
-    int argc = args.size();
-    char* argv[argc];
-    for (label i = 0; i < argc; i++)
-    {
-        argv[i] = strdup(args[i].c_str());
-    }
+    UPstream::initNull();
 
-    float ver;
-    int rc = Zoltan_Initialize(argc, argv, &ver);
+    CStringList args({"zoltan-renumber"});
 
-    Foam::Pout<< "Initialised to " << ver << Foam::endl;
+    float ver;
+    int rc = Zoltan_Initialize(args.size(), args.strings(), &ver);
 
     if (rc != ZOLTAN_OK)
     {
@@ -307,24 +387,55 @@ Foam::labelList Foam::zoltanRenumber::renumber
 
     struct Zoltan_Struct *zz = Zoltan_Create(MPI_COMM_WORLD);
 
-    polyMesh& mesh = const_cast<polyMesh&>(pMesh);
+    // const bool verbose = coeffsDict_.getOrDefault(verbose, false);
+    const bool verbose = true;
 
+    // Default order method
+    Zoltan_Set_Param(zz, "ORDER_METHOD", "LOCAL_HSFC");
+
+    if (false)
+    {
+        Info<< typeName << " : default ORDER_METHOD = LOCAL_HSFC" << nl
+            << typeName << " : default ORDER_TYPE = LOCAL" << nl;
+    }
 
     for (const entry& dEntry : coeffsDict_)
     {
+        const word& key = dEntry.keyword();
+
+        // Internal keywords
+        if
+        (
+            key == "method"
+         || key == "verbose"
+        )
+        {
+            continue;
+        }
+
         if (!dEntry.isDict())
         {
-            const word& key = dEntry.keyword();
             const word value(dEntry.get<word>());
 
-            Info<< typeName << " : setting parameter " << key
-                << " to " << value << endl;
+            if (verbose)
+            {
+                Info<< typeName
+                    << " : setting parameter "
+                    << key << " = " << value << nl;
+            }
 
             Zoltan_Set_Param(zz, key.c_str(), value.c_str());
         }
     }
 
+    // Always use rank LOCAL ordering
+    Zoltan_Set_Param(zz, "ORDER_TYPE", "LOCAL");
 
+
+    // Set callbacks
+    polyMesh& mesh = const_cast<polyMesh&>(pMesh);
+
+    // Callbacks for graph vertex IDs
     Zoltan_Set_Num_Obj_Fn(zz, get_number_of_vertices, &mesh);
     Zoltan_Set_Obj_List_Fn(zz, get_vertex_list, &mesh);
 
@@ -337,25 +448,26 @@ Foam::labelList Foam::zoltanRenumber::renumber
     Zoltan_Set_Edge_List_Multi_Fn(zz, get_edge_list, &mesh);
 
 
+    const auto nCells = mesh.nCells();
 
-    //Note: !global indices
-    List<ZOLTAN_ID_TYPE> wantedCells(mesh.nCells());
+    List<ZOLTAN_ID_TYPE> globalIds(nCells);
+    List<ZOLTAN_ID_TYPE> oldToNew(nCells);
 
-    globalIndex globalCells(mesh.nCells());
-    forAll(wantedCells, i)
-    {
-        //wantedCells[i] = i;
-        wantedCells[i] = globalCells.toGlobal(i);
-    }
+    // Offset for globally unique IDs
+    const label myProci = UPstream::myProcNo();
+    const label myProcOffset =
+        mesh.globalData().globalMeshCellAddr().localStart(myProci);
 
-    List<ZOLTAN_ID_TYPE> oldToNew(mesh.nCells());
+
+    // Global indices
+    std::iota(globalIds.begin(), globalIds.end(), ZOLTAN_ID_TYPE(myProcOffset));
 
     int err = Zoltan_Order
     (
         zz,
-        1,                                //int num_gid_entries,
-        mesh.globalData().nTotalCells(),  //int num_obj,
-        wantedCells.begin(),
+        1,                          //int num_gid_entries,
+        nCells,                     //int num_obj,
+        globalIds.begin(),
         oldToNew.begin()
     );
 
@@ -365,18 +477,22 @@ Foam::labelList Foam::zoltanRenumber::renumber
             << "Failed Zoltan_Order" << exit(FatalError);
     }
 
-
-    for (label i = 0; i < argc; i++)
-    {
-        free(argv[i]);
-    }
+    Zoltan_Destroy(&zz);
 
 
+    // From global number to rank-local numbering
+    globalIds.clear();
     labelList order(oldToNew.size());
-    forAll(order, i)
-    {
-        order[i] = oldToNew[i];
-    }
+
+    // From global to local (without checks)
+    std::transform
+    (
+        oldToNew.begin(),
+        oldToNew.end(),
+        order.begin(),
+        [=](const ZOLTAN_ID_TYPE id) -> label { return (id - myProcOffset); }
+    );
+
     return order;
 }
 
diff --git a/src/renumber/zoltanRenumber/zoltanRenumber.H b/src/renumber/zoltanRenumber/zoltanRenumber.H
index e2574b057a1e103fae67945bce92acf00d3298a8..b6f7c625ef23049a2001a6233bb3a0732d342526 100644
--- a/src/renumber/zoltanRenumber/zoltanRenumber.H
+++ b/src/renumber/zoltanRenumber/zoltanRenumber.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2022 OpenCFD Ltd.
+    Copyright (C) 2022-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -83,6 +83,10 @@ public:
 
     // Member Functions
 
+        //- Renumbering method requires a polyMesh
+        virtual bool needs_mesh() const { return true; }
+
+
         //- Return the order in which cells need to be visited
         //- (ie. from ordered back to original cell label).
         //  This is only defined for geometric renumberMethods.
diff --git a/tutorials/mesh/parallel/cavity/Allrun b/tutorials/mesh/parallel/cavity/Allrun
index 370935618e35e2048d0eb52f45417f9530bcca10..057db8e73e4f11a7b64e09458846917a2a960fd9 100755
--- a/tutorials/mesh/parallel/cavity/Allrun
+++ b/tutorials/mesh/parallel/cavity/Allrun
@@ -18,8 +18,7 @@ runParallel -s CuthillMcKee renumberMesh -overwrite $fileHandler
 runParallel -s CuthillMcKee icoFoam $fileHandler
 
 # Bit of bad renumbering and running
-runParallel -s random renumberMesh \
-    -overwrite -dict system/renumberMeshDict-random $fileHandler
+runParallel -s random renumberMesh -renumber-method random -overwrite $fileHandler
 runParallel -s random icoFoam $fileHandler
 
 # Pick up last result
diff --git a/tutorials/mesh/parallel/cavity/system/renumberMeshDict-random b/tutorials/mesh/parallel/cavity/system/renumberMeshDict-random
deleted file mode 100644
index 80b09ee2c3fc32c67202f07fb3982a3e1da12ceb..0000000000000000000000000000000000000000
--- a/tutorials/mesh/parallel/cavity/system/renumberMeshDict-random
+++ /dev/null
@@ -1,109 +0,0 @@
-/*--------------------------------*- C++ -*----------------------------------*\
-| =========                 |                                                 |
-| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
-|  \\    /   O peration     | Version:  v2312                                 |
-|   \\  /    A nd           | Website:  www.openfoam.com                      |
-|    \\/     M anipulation  |                                                 |
-\*---------------------------------------------------------------------------*/
-FoamFile
-{
-    version     2.0;
-    format      ascii;
-    class       dictionary;
-    object      renumberMeshDict;
-}
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-// Write maps from renumbered back to original mesh
-writeMaps true;
-
-// Optional entry: sort cells on coupled boundaries to last for use with
-// e.g. nonBlockingGaussSeidel.
-sortCoupledFaceCells false;
-
-// Optional entry: renumber on a block-by-block basis. It uses a
-// blockCoeffs dictionary to construct a decompositionMethod to do
-// a block subdivision) and then applies the renumberMethod to each
-// block in turn. This can be used in large cases to keep the blocks
-// fitting in cache with all the cache misses bunched at the end.
-// This number is the approximate size of the blocks - this gets converted
-// to a number of blocks that is the input to the decomposition method.
-//blockSize 1000;
-
-// Optional entry: sort points into internal and boundary points
-//orderPoints false;
-
-// Optional: suppress renumbering cellSets,faceSets,pointSets
-//renumberSets false;
-
-
-//method          CuthillMcKee;
-//method          Sloan;
-//method          manual;
-method          random;
-//method          structured;
-//method          spring;
-//method          zoltan;             // only if compiled with zoltan support
-
-//CuthillMcKeeCoeffs
-//{
-//    // Reverse CuthillMcKee (RCM) or plain
-//    reverse true;
-//}
-
-manualCoeffs
-{
-    // In system directory: new-to-original (i.e. order) labelIOList
-    dataFile "cellMap";
-}
-
-
-// For extruded (i.e. structured in one direction) meshes
-structuredCoeffs
-{
-    // Patches that mesh was extruded from. These determine the starting
-    // layer of cells
-    patches (movingWall);
-    // Method to renumber the starting layer of cells
-    method  random;
-
-    // Renumber in columns (depthFirst) or in layers
-    depthFirst true;
-
-    // Reverse ordering
-    reverse false;
-}
-
-
-springCoeffs
-{
-    // Maximum jump of cell indices. Is fraction of number of cells
-    maxCo 0.01;
-
-    // Limit the amount of movement; the fraction maxCo gets decreased
-    // with every iteration
-    freezeFraction 0.999;
-
-    // Maximum number of iterations
-    maxIter 1000;
-}
-
-
-blockCoeffs
-{
-    method          scotch;
-    //method hierarchical;
-    //hierarchicalCoeffs
-    //{
-    //    n           (1 2 1);
-    //}
-}
-
-
-zoltanCoeffs
-{
-    ORDER_METHOD    LOCAL_HSFC;
-}
-
-
-// ************************************************************************* //
diff --git a/tutorials/preProcessing/decompositionConstraints/geometric/Allclean b/tutorials/preProcessing/decompositionConstraints/geometric/Allclean
index b23011b1c266985824e7f83bbe57f5cc64bfff2d..4afce75b5c8e2ba0162a1dbb03f904c6cd7b936b 100755
--- a/tutorials/preProcessing/decompositionConstraints/geometric/Allclean
+++ b/tutorials/preProcessing/decompositionConstraints/geometric/Allclean
@@ -6,6 +6,5 @@ cd "${0%/*}" || exit                                # Run from this directory
 cleanCase0
 
 rm -rf constant/triSurface
-rm -f cellDist.vtu
 
 #------------------------------------------------------------------------------
diff --git a/wmake/scripts/have_zoltan b/wmake/scripts/have_zoltan
index b78fcbcdaf3a8b8b9452c3f01df3a9616f07693b..5222fa5365dfbc9a900df596feceeff20970c8a1 100644
--- a/wmake/scripts/have_zoltan
+++ b/wmake/scripts/have_zoltan
@@ -5,7 +5,7 @@
 #   \\  /    A nd           | www.openfoam.com
 #    \\/     M anipulation  |
 #------------------------------------------------------------------------------
-#     Copyright (C) 2018-2020 OpenCFD Ltd.
+#     Copyright (C) 2018-2024 OpenCFD Ltd.
 #------------------------------------------------------------------------------
 # License
 #     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@@ -64,6 +64,9 @@ search_zoltan()
     local prefix="${1:-system}"
     local header library
 
+    local mpiPrefix="$MPI_ARCH_PATH"
+    local mpiName="${MPI_ARCH_PATH##*/}"
+
     # ----------------------------------
     if isNone "$prefix"
     then
@@ -71,11 +74,29 @@ search_zoltan()
         return 1
     elif hasAbsdir "$prefix"
     then
-        header=$(findFirstFile "$prefix/include/$incName")
-        library=$(findExtLib "$libName")
+        header=$(findFirstFile \
+            "$prefix/include/$FOAM_MPI/$incName" \
+            "$prefix/include/zoltan/$incName" \
+            "$prefix/include/$incName" \
+            "$mpiPrefix/include/$incName" \
+            "$prefix/include/$mpiName/$incName" \
+            "$prefix/include/${mpiName}-$(uname -m)/$incName" \
+        )
+        library=$(findExtLib \
+            "$FOAM_MPI/$libName" \
+            "$libName" \
+        )
     elif isSystem "$prefix"
     then
-        header=$(findSystemInclude -name="$incName")
+        header=$(findFirstFile \
+            "/usr/local/include/zoltan/$incName" \
+            "/usr/local/include/$incName" \
+            "/usr/include/zoltan/$incName" \
+            "/usr/include/$incName" \
+            "$mpiPrefix/include/$incName" \
+            "/usr/include/$mpiName/$incName" \
+            "$prefix/include/${mpiName}-$(uname -m)/$incName" \
+        )
         prefix=$(sysPrefix "$header")
     else
         unset prefix
@@ -91,6 +112,7 @@ search_zoltan()
     # Library
     [ -n "$library" ] \
     || library=$(findLibrary -prefix="$prefix" -name="$libName") \
+    || library=$(findLibrary -prefix="$mpiPrefix" -name="$libName") \
     || {
         [ -n "$warn" ] && echo "$warn (no library)"
         return 2
@@ -99,6 +121,7 @@ search_zoltan()
     # ----------------------------------
 
     # OK
+    # echo "zoltan - $prefix"
     export HAVE_ZOLTAN=true
     export ZOLTAN_ARCH_PATH="$prefix"
     export ZOLTAN_INC_DIR="${header%/*}"     # Basename