diff --git a/applications/test/objectRegistry2/Make/files b/applications/test/objectRegistry2/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..208771853c25a1483c3222f71cd5639587c9247f
--- /dev/null
+++ b/applications/test/objectRegistry2/Make/files
@@ -0,0 +1,3 @@
+Test-objectRegistry2.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-objectRegistry2
diff --git a/applications/test/objectRegistry2/Make/options b/applications/test/objectRegistry2/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..969020c4afaf5d784299462b9e1af282040ba6b4
--- /dev/null
+++ b/applications/test/objectRegistry2/Make/options
@@ -0,0 +1,8 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lmeshTools \
+    -lgenericPatchFields
diff --git a/applications/test/objectRegistry2/Test-objectRegistry2.C b/applications/test/objectRegistry2/Test-objectRegistry2.C
new file mode 100644
index 0000000000000000000000000000000000000000..eab159ccfc28deecff5009d2e046047fc0ed49b7
--- /dev/null
+++ b/applications/test/objectRegistry2/Test-objectRegistry2.C
@@ -0,0 +1,307 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+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/>.
+
+Application
+    Test-objectRegistry2
+
+Description
+    Print objectRegistry information, with some additional tests.
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "fvCFD.H"
+#include "fvMesh.H"
+#include "volFields.H"
+#include "IOobjectList.H"
+#include "timeSelector.H"
+#include "ReadFields.H"
+#include "IOstreams.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type>
+bool loadField(fvMesh& mesh, const word& fieldName)
+{
+    typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
+    typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
+
+    if (mesh.objectRegistry::found(fieldName))
+    {
+        // Info<< fieldName << " already in database" << endl;
+        return false;
+    }
+
+    IOobject fieldHeader
+    (
+        fieldName,
+        mesh.time().timeName(),
+        mesh,
+        IOobject::MUST_READ,
+        IOobject::NO_WRITE
+    );
+
+    if (fieldHeader.typeHeaderOk<VolFieldType>(true, true, false))
+    {
+        // Store field on mesh database
+        VolFieldType* ptr = new VolFieldType(fieldHeader, mesh);
+        mesh.objectRegistry::store(ptr);
+        return true;
+    }
+    else if (fieldHeader.typeHeaderOk<SurfaceFieldType>(true, true, false))
+    {
+        // Store field on mesh database
+        SurfaceFieldType* ptr = new SurfaceFieldType(fieldHeader, mesh);
+        mesh.objectRegistry::store(ptr);
+        return true;
+    }
+
+    return false;
+}
+
+
+bool loadField(fvMesh& mesh, const word& fieldName)
+{
+    return
+    (
+        !mesh.objectRegistry::found(fieldName)
+    &&
+        (
+            loadField<scalar>(mesh, fieldName)
+         || loadField<vector>(mesh, fieldName)
+         || loadField<sphericalTensor>(mesh, fieldName)
+         || loadField<symmTensor>(mesh, fieldName)
+         || loadField<tensor>(mesh, fieldName)
+        )
+    );
+}
+
+
+void loadFields(fvMesh& mesh, const IOobjectList& objects)
+{
+    for (const word& fieldName : objects.names())
+    {
+        loadField(mesh, fieldName);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+void printRegistry
+(
+    Foam::Ostream& os,
+    const Foam::objectRegistry& obr,
+    Foam::label indent = 4
+);
+
+
+void printRegistry
+(
+    Foam::Ostream& os,
+    const Foam::objectRegistry& obr,
+    Foam::label indent
+)
+{
+    wordList names(obr.sortedNames());
+    wordList regs(obr.sortedNames<objectRegistry>());
+
+    std::string prefix;
+    for (label i=indent; i; --i)
+    {
+        prefix += ' ';
+    }
+
+    os  << '#' << prefix.c_str() << obr.name()
+        << " parent:" << obr.parent().name() << nl;
+
+    os  << ' ' << prefix.c_str() << "objects: " << flatOutput(names) << nl;
+    os  << ' ' << prefix.c_str() << "registries: " << flatOutput(regs) << nl;
+
+
+    // Print, but skip expansion of sub-registries for now
+    for (const word& name : names)
+    {
+        os  << (regs.found(name) ? '-' : ' ')
+            << prefix.c_str() << name << " => " << obr[name]->type() << nl;
+    }
+    for (label i=indent; i; --i)
+    {
+        os  << '-'; // divider
+    }
+    os  << '\n';
+
+    // Now descend into the sub-registries
+    for (const word& name : regs)
+    {
+        const objectRegistry& next = obr.lookupObject<objectRegistry>
+        (
+            name,
+            false // non-recursive
+        );
+
+        os  << prefix.c_str()
+            << "current:" << obr.name() << " next:"
+            << next.name() << " next-parent:" << next.parent().name() << nl;
+
+        os  << prefix.c_str() << name << " => " << obr[name]->type();
+
+        if ("dictionary" == obr[name]->type())
+        {
+            os  << " (skip dictionary)" << nl;
+        }
+        else
+        {
+            os  << nl;
+            printRegistry(os, next, indent + 4);
+        }
+    }
+}
+
+
+template<class Type>
+void filterTest(const objectRegistry& obr, const wordRe& re)
+{
+    Info<< nl << "Filter on names:" << nl;
+
+    Info<< "Filter = " << re << nl;
+
+    const word& typeName = Type::typeName;
+
+    Info<< "    <" << typeName <<">(" << re << ") : "
+        << obr.count<Type>(re) << nl
+        << "    (" << typeName << "::typeName, " << re << ") : "
+        << obr.count(typeName, re) << nl;
+
+    Info<< "    <" << typeName << ">(" << re << ") : "
+        << flatOutput(obr.sortedNames<Type>(re)) << nl
+        // << flatOutput(obr.names<Type>(re)) << nl
+        << "    (" << typeName << "::typeName, " << re << ") : "
+        << flatOutput(obr.sortedNames(typeName, re)) << nl
+        //<< flatOutput(obr.names(typeName, re)) << nl
+        ;
+
+
+    wordRe reClass("vol.*Field", wordRe::REGEX);
+    wordRe re2(re, wordRe::REGEX_ICASE);
+
+    Info<< "General" << nl
+        << "    <void>(" << re << ") : "
+        << flatOutput(obr.sortedNames<void>(re)) << nl
+        << "    (" << reClass << ", " << re2 <<" ignore-case) : "
+        << flatOutput(obr.sortedNames(reClass, re2)) << nl
+        ;
+
+    Info<< nl;
+}
+
+
+void registryTests(const objectRegistry& obr)
+{
+    Info<< nl << "Registry: " << obr.name() << nl
+        << " names: " << flatOutput(obr.sortedNames()) << nl;
+
+    Info<< "count" << nl
+        << "    <void>()    : " << obr.count<void>() << nl
+        << "    <labelList>()   : " << obr.count<labelList>() << nl
+        << "    <labelList>(strict) : " << obr.count<labelList>(true) << nl
+        << "    <scalarList>()   : " << obr.count<scalarList>() << nl
+        << "    <scalarList>(strict) : " << obr.count<scalarList>(true) << nl;
+    Info<< "    <volScalarField>()    : "
+        << obr.count<volScalarField>() << nl
+        << "    (volScalarField::typeName) : "
+        << obr.count(volScalarField::typeName) << nl;
+    Info<< "    <volVectorField>()    : "
+        << obr.count<volVectorField>() << nl
+        << "    (volVectorField::typeName) : "
+        << obr.count(volVectorField::typeName) << nl;
+
+    Info<< nl << "Filter on names:" << nl;
+
+    filterTest<volScalarField>(obr, wordRe("[p-z].*", wordRe::DETECT));
+
+    Info<< nl;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//  Main program:
+
+int main(int argc, char *argv[])
+{
+    argList::noBanner();
+    argList::noParallel();
+//    argList::addOption
+//    (
+//        "filter",
+//        "wordRes",
+//        "filter keys with names or regexs"
+//    );
+
+    // timeSelector::addOptions();
+    timeSelector::addOptions(true, true);
+
+    #include "setRootCase.H"
+
+//    wordRes matcher;
+//    if (args.readListIfPresent<wordRe>("filter", matcher))
+//    {
+//        Info<<"limit names: " << matcher << nl;
+//    }
+
+    #include "createTime.H"
+    #include "createMesh.H"
+
+    instantList timeDirs = timeSelector::select0(runTime, args);
+
+    forAll(timeDirs, timeI)
+    {
+        runTime.setTime(timeDirs[timeI], timeI);
+
+        Info<< "Time: " << runTime.timeName() << endl;
+
+        // Read objects in time directory
+        IOobjectList objects(mesh, runTime.timeName());
+
+        // Read volFields
+        loadFields(mesh, objects);
+
+        printRegistry(Info, mesh);
+
+        registryTests(mesh);
+
+        Info<< nl;
+    }
+
+
+    Info<<"\nEnd\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/db/objectRegistry/objectRegistry.C b/src/OpenFOAM/db/objectRegistry/objectRegistry.C
index d2b0a1a2c68be61a7c5636ec0243b8cb44f24115..a747de2c83a49580e23445227fbf380ea3387e8f 100644
--- a/src/OpenFOAM/db/objectRegistry/objectRegistry.C
+++ b/src/OpenFOAM/db/objectRegistry/objectRegistry.C
@@ -111,6 +111,13 @@ Foam::HashTable<Foam::wordHashSet> Foam::objectRegistry::classes() const
 }
 
 
+Foam::label Foam::objectRegistry::count(const char* clsName) const
+{
+    // No nullptr check - only called with string literals
+    return count(static_cast<word>(clsName));
+}
+
+
 Foam::wordList Foam::objectRegistry::names() const
 {
     return HashTable<regIOobject*>::toc();
diff --git a/src/OpenFOAM/db/objectRegistry/objectRegistry.H b/src/OpenFOAM/db/objectRegistry/objectRegistry.H
index 30c03f8e31b4b05f25aa0e0a7c70710a2af010c1..d05f5edf0a4cc15430bc44c3cab8f1948acfc387 100644
--- a/src/OpenFOAM/db/objectRegistry/objectRegistry.H
+++ b/src/OpenFOAM/db/objectRegistry/objectRegistry.H
@@ -81,6 +81,25 @@ class objectRegistry
         //  Used to terminate searching within the ancestors
         bool parentNotTime() const;
 
+        //- Templated implementation for count()
+        //  The number of items with a matching class
+        template<class MatchPredicate1, class MatchPredicate2>
+        static label countImpl
+        (
+            const objectRegistry& list,
+            const MatchPredicate1& matchClass,
+            const MatchPredicate2& matchName
+        );
+
+        //- Templated implementation for count()
+        //  The number of items with a matching class
+        template<class Type, class MatchPredicate>
+        static label countTypeImpl
+        (
+            const objectRegistry& list,
+            const MatchPredicate& matchName
+        );
+
         //- Templated implementation for classes()
         template<class MatchPredicate>
         static HashTable<wordHashSet> classesImpl
@@ -173,6 +192,45 @@ public:
         HashTable<wordHashSet> classes(const MatchPredicate& matchName) const;
 
 
+    // Number of items
+
+        //- The number of objects of the given class name
+        //  \note uses the class type() method
+        label count(const char* clsName) const;
+
+        //- The number of objects of the given class name
+        //  \note uses the class type() method
+        template<class MatchPredicate>
+        label count(const MatchPredicate& matchClass) const;
+
+        //- The number of objects of the given class name
+        //  \note uses the class type() method
+        template<class MatchPredicate1, class MatchPredicate2>
+        label count
+        (
+            const MatchPredicate1& matchClass,
+            const MatchPredicate2& matchName
+        ) const;
+
+        //- The names of objects with a class satisfying \c isA\<Type\>
+        //
+        //  \param strict use \c isType\<Type\> instead of \c isA\<Type\>
+        //
+        //  \note The values of \c count\<Type\>() and \c count(Type::typeName)
+        //      may be inconsistent, since they use different mechanisms for
+        //      testing the class type.
+        //  \note If \a Type is \c void, no isA check is used (always true).
+        template<class Type>
+        label count(const bool strict = false) const;
+
+        //- The names of objects with a class satisfying \c isA\<Type\>
+        //- that also have a matching object name.
+        //
+        //  \note If \a Type is \c void, no isA check is used (always true).
+        template<class Type, class MatchPredicate>
+        label count(const MatchPredicate& matchName) const;
+
+
     // Summary of names
 
         //- The names of all objects
diff --git a/src/OpenFOAM/db/objectRegistry/objectRegistryTemplates.C b/src/OpenFOAM/db/objectRegistry/objectRegistryTemplates.C
index c63ff76acec7223260566cb99a2130b1d42d2040..e75a304edae4462e03284506fe9de255f4eb001c 100644
--- a/src/OpenFOAM/db/objectRegistry/objectRegistryTemplates.C
+++ b/src/OpenFOAM/db/objectRegistry/objectRegistryTemplates.C
@@ -55,6 +55,59 @@ Foam::HashTable<Foam::wordHashSet> Foam::objectRegistry::classesImpl
 }
 
 
+// Templated implementation for count()
+template<class MatchPredicate1, class MatchPredicate2>
+Foam::label Foam::objectRegistry::countImpl
+(
+    const objectRegistry& list,
+    const MatchPredicate1& matchClass,
+    const MatchPredicate2& matchName
+)
+{
+    label count = 0;
+
+    forAllConstIters(list, iter)
+    {
+        const regIOobject* obj = iter.object();
+
+        if (matchClass(obj->type()) && matchName(obj->name()))
+        {
+            ++count;
+        }
+    }
+
+    return count;
+}
+
+
+// Templated implementation for count()
+template<class Type, class MatchPredicate>
+Foam::label Foam::objectRegistry::countTypeImpl
+(
+    const objectRegistry& list,
+    const MatchPredicate& matchName
+)
+{
+    label count = 0;
+
+    forAllConstIters(list, iter)
+    {
+        const regIOobject* obj = iter.object();
+
+        if
+        (
+            (std::is_void<Type>::value || isA<Type>(*obj))
+         && matchName(obj->name())
+        )
+        {
+            ++count;
+        }
+    }
+
+    return count;
+}
+
+
 // Templated implementation for names(), sortedNames()
 template<class MatchPredicate1, class MatchPredicate2>
 Foam::wordList Foam::objectRegistry::namesImpl
@@ -141,6 +194,63 @@ Foam::objectRegistry::classes
 }
 
 
+template<class MatchPredicate>
+Foam::label Foam::objectRegistry::count
+(
+    const MatchPredicate& matchClass
+) const
+{
+    return countImpl(*this, matchClass, predicates::always());
+}
+
+
+template<class MatchPredicate1, class MatchPredicate2>
+Foam::label Foam::objectRegistry::count
+(
+    const MatchPredicate1& matchClass,
+    const MatchPredicate2& matchName
+) const
+{
+    return countImpl(*this, matchClass, matchName);
+}
+
+
+template<class Type, class MatchPredicate>
+Foam::label Foam::objectRegistry::count
+(
+    const MatchPredicate& matchName
+) const
+{
+    return countTypeImpl<Type>(*this, matchName);
+}
+
+
+template<class Type>
+Foam::label Foam::objectRegistry::count
+(
+    const bool strict
+) const
+{
+    label nObjects = 0;
+
+    forAllConstIters(*this, iter)
+    {
+        const regIOobject* obj = iter.object();
+
+        if
+        (
+            std::is_void<Type>::value
+         || (strict ? isType<Type>(*obj) : bool(isA<Type>(*obj)))
+        )
+        {
+            ++nObjects;
+        }
+    }
+
+    return nObjects;
+}
+
+
 template<class MatchPredicate>
 Foam::wordList Foam::objectRegistry::names
 (
@@ -227,13 +337,11 @@ Foam::HashTable<const Type*> Foam::objectRegistry::lookupClass
 
     forAllConstIters(*this, iter)
     {
-        if (strict ? isType<Type>(*iter()) : bool(isA<Type>(*iter())))
+        const regIOobject* obj = iter.object();
+
+        if (strict ? isType<Type>(*obj) : bool(isA<Type>(*obj)))
         {
-            objectsOfClass.insert
-            (
-                iter()->name(),
-                dynamic_cast<const Type*>(iter())
-            );
+            objectsOfClass.insert(obj->name(), dynamic_cast<const Type*>(obj));
         }
     }
 
@@ -251,13 +359,11 @@ Foam::HashTable<Type*> Foam::objectRegistry::lookupClass
 
     forAllIters(*this, iter)
     {
-        if (strict ? isType<Type>(*iter()) : bool(isA<Type>(*iter())))
+        regIOobject* obj = iter.object();
+
+        if (strict ? isType<Type>(*obj) : bool(isA<Type>(*obj)))
         {
-            objectsOfClass.insert
-            (
-                iter()->name(),
-                dynamic_cast<Type*>(iter())
-            );
+            objectsOfClass.insert(obj->name(), dynamic_cast<Type*>(obj));
         }
     }