diff --git a/applications/solvers/multiphase/compressibleMultiphaseInterFoam/multiphaseMixtureThermo/multiphaseMixtureThermo.H b/applications/solvers/multiphase/compressibleMultiphaseInterFoam/multiphaseMixtureThermo/multiphaseMixtureThermo.H
index 368df01043d0bd8eb865b72658815b27b1892889..5172f537f2792b9cda5c0bce84a46781036a751a 100644
--- a/applications/solvers/multiphase/compressibleMultiphaseInterFoam/multiphaseMixtureThermo/multiphaseMixtureThermo.H
+++ b/applications/solvers/multiphase/compressibleMultiphaseInterFoam/multiphaseMixtureThermo/multiphaseMixtureThermo.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -59,24 +59,23 @@ class multiphaseMixtureThermo
 {
 public:
 
+    //- Symmetric pair of interface names
     class interfacePair
     :
         public Pair<word>
     {
     public:
 
-        struct hash
-        {
-            label operator()(const interfacePair& key) const
-            {
-                return word::hash()(key.first()) + word::hash()(key.second());
-            }
-        };
+        // Always use symmetric hashing
+        using hasher = Pair<word>::symmHasher;
+
+        // Always use symmetric hashing (alias)
+        using hash = Pair<word>::symmHasher;
 
 
         // Constructors
 
-            interfacePair() {} // = default
+            interfacePair() = default;
 
             interfacePair(const word& alpha1Name, const word& alpha2Name)
             :
@@ -97,11 +96,7 @@ public:
                 const interfacePair& b
             )
             {
-                return
-                (
-                    ((a.first() == b.first()) && (a.second() == b.second()))
-                 || ((a.first() == b.second()) && (a.second() == b.first()))
-                );
+                return (0 != Pair<word>::compare(a, b));
             }
 
             friend bool operator!=
@@ -117,7 +112,7 @@ public:
 
 private:
 
-    // Private data
+    // Private Data
 
         //- Dictionary of phases
         PtrDictionary<phaseModel> phases_;
@@ -140,7 +135,7 @@ private:
         const dimensionedScalar deltaN_;
 
 
-    // Private member functions
+    // Private Member Functions
 
         void calcAlphas();
 
diff --git a/applications/solvers/multiphase/multiphaseInterFoam/multiphaseMixture/multiphaseMixture.H b/applications/solvers/multiphase/multiphaseInterFoam/multiphaseMixture/multiphaseMixture.H
index b320374e069767a33b20c605f041b7ab72536637..ddaa807a931be86401dcf3c9a159865eaf463bf5 100644
--- a/applications/solvers/multiphase/multiphaseInterFoam/multiphaseMixture/multiphaseMixture.H
+++ b/applications/solvers/multiphase/multiphaseInterFoam/multiphaseMixture/multiphaseMixture.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -67,24 +68,23 @@ class multiphaseMixture
 {
 public:
 
+    //- Symmetric pair of interface names
     class interfacePair
     :
         public Pair<word>
     {
     public:
 
-        struct hash
-        {
-            label operator()(const interfacePair& key) const
-            {
-                return word::hash()(key.first()) + word::hash()(key.second());
-            }
-        };
+        // Always use symmetric hashing
+        using hasher = Pair<word>::symmHasher;
+
+        // Always use symmetric hashing (alias)
+        using hash = Pair<word>::symmHasher;
 
 
         // Constructors
 
-            interfacePair() {} // = default
+            interfacePair() = default;
 
             interfacePair(const word& alpha1Name, const word& alpha2Name)
             :
@@ -105,11 +105,7 @@ public:
                 const interfacePair& b
             )
             {
-                return
-                (
-                    ((a.first() == b.first()) && (a.second() == b.second()))
-                 || ((a.first() == b.second()) && (a.second() == b.first()))
-                );
+                return (0 != Pair<word>::compare(a, b));
             }
 
             friend bool operator!=
diff --git a/applications/test/FixedList/Test-FixedList.C b/applications/test/FixedList/Test-FixedList.C
index 1257c970916e86b7f7927428176fdbcb2044b292..f75d77e3c800fa0389fd7fcda2f7a0425c02dd8f 100644
--- a/applications/test/FixedList/Test-FixedList.C
+++ b/applications/test/FixedList/Test-FixedList.C
@@ -186,14 +186,14 @@ int main(int argc, char *argv[])
         FixedList<label, 4> list1{2, 3, 4, 5};
 
         Info<< "list1:" << list1
-            << " hash:" << FixedList<label, 4>::Hash<>()(list1) << nl
+            << " hash:" << FixedList<label, 4>::hasher()(list1) << nl
             << " hash:" << Hash<FixedList<label, 4>>()(list1) << nl;
 
         label a[4] = {0, 1, 2, 3};
         FixedList<label, 4> list2(a);
 
         Info<< "list2:" << list2
-            << " hash:" << FixedList<label, 4>::Hash<>()(list2) << nl
+            << " hash:" << FixedList<label, 4>::hasher()(list2) << nl
             << " hash:" << Hash<FixedList<label, 4>>()(list2) << nl;
 
 
diff --git a/applications/test/HashPtrTable/Test-HashPtrTable.C b/applications/test/HashPtrTable/Test-HashPtrTable.C
index db15bdde27ae0e1811f060afc0bf96380e65f681..31dfb6220032c41c5aed549e4ca02842737abbf7 100644
--- a/applications/test/HashPtrTable/Test-HashPtrTable.C
+++ b/applications/test/HashPtrTable/Test-HashPtrTable.C
@@ -127,13 +127,13 @@ int main()
     myTable.insert("sqrt2", autoPtr<double>::New(1.414214));
     myTable.insert("euler", autoPtr<double>::New(0.577216));
 
-    HashTable<std::unique_ptr<double>, word, string::hash> myTable1;
+    HashTable<std::unique_ptr<double>> myTable1;
 
     myTable1.set("abc", std::unique_ptr<double>(new double(42.1)));
     myTable1.set("pi", std::unique_ptr<double>(new double(3.14159)));
     myTable1.set("natlog", std::unique_ptr<double>(new double(2.718282)));
 
-    HashTable<autoPtr<double>, word, string::hash> myTable2;
+    HashTable<autoPtr<double>> myTable2;
 
     myTable2.set("abc", autoPtr<double>(new double(42.1)));
     myTable2.set("pi", autoPtr<double>(new double(3.14159)));
@@ -148,7 +148,7 @@ int main()
 
     {
         auto iter2 = myTable2.find("pi");
-        Info<< nl "Got pi=";
+        Info<< nl << "Got pi=";
         if (iter2.good())
         {
             Info<< **iter2 << nl;
diff --git a/applications/test/HashSet/Test-hashSet.C b/applications/test/HashSet/Test-hashSet.C
index 8e2ac0c409e33b7291d0145e3d7225baa72775e3..c84231f91f1b0b3d77810763f5dc353e15d89af9 100644
--- a/applications/test/HashSet/Test-hashSet.C
+++ b/applications/test/HashSet/Test-hashSet.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -80,6 +80,11 @@ void printMinMax(const HashSet<Key, Hash>& set)
 
 int main(int argc, char *argv[])
 {
+    Info<< "labelHashSet hasher: "
+        << typeid(labelHashSet::hasher).name() << nl
+        << "HashSet<label> hasher: "
+        << typeid(HashSet<label>::hasher).name() << nl << nl;
+
     hashedWordList words
     {
         "abc",
diff --git a/applications/test/Hashing/Make/files b/applications/test/Hashing/Make/files
deleted file mode 100644
index fd270ac21768b0bdfa43381ae8ff82b99464bc1c..0000000000000000000000000000000000000000
--- a/applications/test/Hashing/Make/files
+++ /dev/null
@@ -1,3 +0,0 @@
-Test-Hashing.C
-
-EXE = $(FOAM_USER_APPBIN)/Test-Hashing
diff --git a/applications/test/Hashing1/HashFunction.H b/applications/test/Hashing1/HashFunction.H
new file mode 100644
index 0000000000000000000000000000000000000000..964b5b2e290d7299c4cfed353fbf41fa3ee5bb7e
--- /dev/null
+++ b/applications/test/Hashing1/HashFunction.H
@@ -0,0 +1,224 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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::HashFunction
+
+Description
+    Hash function class.
+
+    Verify that template overloads are properly resolved
+
+Note
+    The second template parameter (bool) is used for SFINAE overloading,
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef HashFunction_H
+#define HashFunction_H
+
+#include "Hash.H"
+
+#ifdef FULLDEBUG
+#define HashTypeInfo(Args)  void info() { std::cerr<< "" Args << "\n"; }
+#else
+#define HashTypeInfo(Args)  void info() {}
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                            Class Hash Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class T, class SFINAEType=bool>
+struct HashFun
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "default"; }
+    #endif
+    HashTypeInfo("plain hash")
+
+    unsigned operator()(const T& obj, unsigned seed=0) const
+    {
+        return Foam::Hasher(&obj, sizeof(obj), seed);
+    }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Hashing for label
+template<> struct HashFun<Foam::label> : Hash<label>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "label"; }
+    #endif
+    HashTypeInfo("hash label")
+};
+
+
+//- Hashing for pointers, interpret pointer as a integer type
+template<> struct HashFun<void*> : Hash<void *>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "pointer"; }
+    #endif
+    HashTypeInfo("hash ptr")
+};
+
+
+//- Hashing for string types
+template<class StringType>
+struct HashFun
+<
+    StringType,
+    typename std::enable_if
+    <
+        std::is_base_of<std::string, StringType>::value, bool
+    >::type
+>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "string"; }
+    #endif
+    HashTypeInfo("hash string")
+
+    unsigned operator()(const std::string& obj, unsigned seed=0) const
+    {
+        return Foam::Hasher(obj.data(), obj.size(), seed);
+    }
+};
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Various
+#include "edge.H"
+#include "face.H"
+#include "triFace.H"
+#include "Pair.H"
+#include "Tuple2.H"
+#include "DynamicList.H"
+#include "FixedList.H"
+
+namespace Foam
+{
+
+template<> struct HashFun<edge> : Hash<edge>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "edge"; }
+    #endif
+    HashTypeInfo("hash edge")
+};
+
+
+template<> struct HashFun<face> : Hash<face>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "face"; }
+    #endif
+    HashTypeInfo("hash face")
+};
+
+
+template<> struct HashFun<triFace> : Hash<triFace>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "triFace"; }
+    #endif
+    HashTypeInfo("hash triFace")
+};
+
+
+template<class T>
+struct HashFun<Pair<T>> : Hash<Pair<T>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "Pair"; }
+    #endif
+    HashTypeInfo("hash Pair")
+};
+
+template<class T1, class T2>
+struct HashFun<Tuple2<T1, T2>> : Hash<Tuple2<T1, T2>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "Tuple2"; }
+    #endif
+    HashTypeInfo("hash Tuple2")
+};
+
+
+template<class T>
+struct HashFun<List<T>> : Hash<List<T>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "List"; }
+    #endif
+    HashTypeInfo("hash List")
+};
+
+template<class T> struct HashFun<UList<T>> : Hash<UList<T>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "UList"; }
+    #endif
+    HashTypeInfo("hash UList")
+};
+
+template<class T, int SizeMin>
+struct HashFun<DynamicList<T, SizeMin>> : Hash<DynamicList<T, SizeMin>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "DynamicList"; }
+    #endif
+    HashTypeInfo("hash DynamicList")
+};
+
+template<class T, unsigned N>
+struct HashFun<FixedList<T, N>> : Hash<FixedList<T, N>>
+{
+    #ifdef FULLDEBUG
+    static constexpr const char* name() noexcept { return "FixedList"; }
+    #endif
+    HashTypeInfo("hash FixedList")
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/test/Hashing1/Make/files b/applications/test/Hashing1/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..771978f3eeace19f118e181bca39ed5e3e7dbf0a
--- /dev/null
+++ b/applications/test/Hashing1/Make/files
@@ -0,0 +1,3 @@
+Test-Hashing1.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-Hashing1
diff --git a/applications/test/Hashing/Make/options b/applications/test/Hashing1/Make/options
similarity index 100%
rename from applications/test/Hashing/Make/options
rename to applications/test/Hashing1/Make/options
diff --git a/applications/test/Hashing1/Test-Hashing1.C b/applications/test/Hashing1/Test-Hashing1.C
new file mode 100644
index 0000000000000000000000000000000000000000..17672ec662b159f4dd950891cb48b55070c68a13
--- /dev/null
+++ b/applications/test/Hashing1/Test-Hashing1.C
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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/>.
+
+Application
+    Test-Hashing1
+
+Description
+    Test/verify overloads of Hash function
+
+\*---------------------------------------------------------------------------*/
+
+#include "IOstreams.H"
+#include "IOobject.H"
+#include "IFstream.H"
+
+#include "stringList.H"
+#include "labelList.H"
+#include "labelPair.H"
+#include "wordPair.H"
+#include "edgeList.H"
+#include "faceList.H"
+#include "triFaceList.H"
+
+#define FULLDEBUG
+#include "HashFunction.H"
+
+using namespace Foam;
+
+template<class T>
+unsigned rawHasher(const T& obj)
+{
+    return Foam::Hasher(&obj, sizeof(T), 0u);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+//  Main program:
+
+int main(int argc, char *argv[])
+{
+    {
+        typedef unsigned Type;
+        Type value = 100;
+
+        Info<< "hash " << typeid(value).name() << " of " << value << nl;
+        Info<< "    Hasher: " << rawHasher(value) << nl;
+        Info<< "    Hash<>: " << Hash<Type>()(value) << nl;
+    }
+    {
+        typedef int32_t Type;
+        Type value = 100;
+
+        Info<< "hash " << typeid(value).name() << " of " << value << nl;
+        Info<< "    Hasher: " << rawHasher(value) << nl;
+        Info<< "    Hash<>: " << Hash<Type>()(value) << nl;
+    }
+    {
+        typedef int64_t Type;
+        Type value = 100;
+
+        Info<< "hash " << typeid(value).name() << " of " << value << nl;
+        Info<< "    Hasher: " << rawHasher(value) << nl;
+        Info<< "    Hash<>: " << Hash<Type>()(value) << nl;
+    }
+
+    HashFun<std::string>().info();
+    HashFun<string>().info();
+    HashFun<Foam::word>().info();
+    HashFun<Foam::keyType>().info();
+
+    HashFun<int>().info();
+    HashFun<label>().info();
+    HashFun<float>().info();
+    HashFun<double>().info();
+
+    {
+        float value = 15.f;
+
+        Info<< "hash of " << Foam::name(&value)
+            << " = " << HashFun<void*>()(&value) << nl;
+    }
+
+    HashFun<labelList>().info();
+    HashFun<wordUList>().info();
+    HashFun<edge>().info();
+
+    HashFun<Pair<label>>().info();
+    HashFun<labelPair>().info();
+    HashFun<labelPairPair>().info();
+
+    HashFun<Tuple2<label, word>>().info();
+
+    {
+        typedef Tuple2<label, word> Type;
+        Type obj(10, "test");
+        Info<< obj << " hash=" << Hash<Type>()(obj) << nl;
+    }
+
+    {
+        typedef Tuple2<label, label> Type;
+        Type obj(10, 12);
+        Info<< obj << " hash=" << Hash<Type>()(obj) << nl;
+    }
+
+    {
+        typedef Pair<label> Type;
+        Type obj(10, 12);
+        Info<< obj << " hash=" << Hash<Type>()(obj) << nl;
+    }
+
+    {
+        typedef std::pair<label, label> Type;
+        Type obj(10, 12);
+        Info<< obj << " hash=" << Hash<Type>()(obj) << nl;
+
+        HashSet<Type> hs;
+        hs.insert(obj);
+        hs.erase(obj);
+    }
+
+    {
+        Pair<label>::hasher op;
+        Info<< "hasher: " << op(Pair<label>(10, 12)) << nl;
+    }
+
+    // Not supported
+    #if 0
+    {
+        Tuple2<label, label>::hasher op;
+        Info<< "hasher: " << op(Tuple2<label>(10, 12)) << nl;
+    }
+    #endif
+
+
+    Info<< "\nEnd\n" << nl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/Hashing2/Make/files b/applications/test/Hashing2/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..86776cfe25bbcc62ec934ff5a5a516410981f337
--- /dev/null
+++ b/applications/test/Hashing2/Make/files
@@ -0,0 +1,3 @@
+Test-Hashing2.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-Hashing2
diff --git a/applications/test/Hashing2/Make/options b/applications/test/Hashing2/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..7dba39797af85b1eab2a90d719c7d43abc2309f1
--- /dev/null
+++ b/applications/test/Hashing2/Make/options
@@ -0,0 +1,3 @@
+EXE_INC = ${c++LESSWARN}
+
+/* EXE_LIBS = */
diff --git a/applications/test/Hashing/Test-Hashing.C b/applications/test/Hashing2/Test-Hashing2.C
similarity index 91%
rename from applications/test/Hashing/Test-Hashing.C
rename to applications/test/Hashing2/Test-Hashing2.C
index 0028ef638b833466fdb2bd9be5cca9b8a6e8890b..3506e2da4f9e2b63b6f8dae834bdbce14ed5dd92 100644
--- a/applications/test/Hashing/Test-Hashing.C
+++ b/applications/test/Hashing2/Test-Hashing2.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -25,7 +25,7 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Application
-    testHashing
+    Test-Hashing2
 
 Description
 
@@ -44,6 +44,7 @@ Description
 #include "triFaceList.H"
 
 #include "Hash.H"
+#include "HashSet.H"
 
 using namespace Foam;
 
@@ -84,7 +85,7 @@ void reportHashList(const UList<string>& list)
 
     for (const string& val : list)
     {
-        unsigned hash1 = string::hash()(val);
+        unsigned hash1 = string::hasher()(val);
 
         Info<< hex << hash1 << ": " << val << nl;
     }
@@ -117,11 +118,11 @@ void reportHashList(const UList<face>& list)
     for (const face& f : list)
     {
         // Direct value
-        unsigned hash1 = face::Hash<>()(f);
+        unsigned hash1 = face::hasher()(f);
 
         unsigned hash2 = Hash<face>()(f);
 
-        Info<< hex << "face::Hash<> " << hash1
+        Info<< hex << "face hash " << hash1
             << " Hash<face> " << hash2
             << ": " << dec << flatOutput(f) << nl;
     }
@@ -132,14 +133,10 @@ void reportHashList(const UList<labelList>& list)
 {
     for (const labelList& val : list)
     {
-        unsigned hash1 = Hasher
-        (
-            val.cdata(),
-            val.size() * sizeof(label)
-        );
+        unsigned hash1 = Foam::Hasher(val.cdata(), val.size_bytes());
 
         unsigned hash2 = Hash<labelList>()(val);
-        unsigned hash2b = labelList::Hash<>()(val);
+        unsigned hash2b = labelList::hasher()(val);
 
         Info<< hex << hash1 << " or " << hash2
             << "(" << hash2b << ") "
@@ -147,7 +144,7 @@ void reportHashList(const UList<labelList>& list)
     }
 
     unsigned hash2 = Hash<labelListList>()(list);
-    unsigned hash2bad = HasherT(list);
+    unsigned hash2bad = Foam::Hasher(&list, sizeof(list));
 
     Info<< hex << hash2 << " : " << dec << flatOutput(list) << nl
         << hex << hash2bad << " as direct hash would be wrong"
@@ -164,10 +161,10 @@ void reportHashList(const UList<wordPair>& list)
         unsigned hash1 = Hash<wordPair>()(pr);
 
         // as FixedList
-        unsigned hash2 = wordPair::Hash<>()(pr);
+        unsigned hash2 = wordPair::hasher()(pr);
 
         // as FixedList
-        unsigned hash2sym = wordPair::SymmHash<>()(pr);
+        unsigned hash2sym = wordPair::symmHasher()(pr);
 
         // as FixedList
         unsigned hash3 = Hash<FixedList<word,2>>()(pr);
@@ -189,7 +186,7 @@ void reportHashList(const UList<labelPair>& list)
         unsigned hash1 = Hash<labelPair>()(pr);
 
         // as FixedList
-        unsigned hash2 = labelPair::Hash<>()(pr);
+        unsigned hash2 = labelPair::hasher()(pr);
 
         // as FixedList
         unsigned hash3 = Hash<labelPair>()(pr);
@@ -210,7 +207,7 @@ void reportHashList(const UList<labelPairPair>& list)
         unsigned hash1 = Hash<labelPairPair>()(pr);
 
         // as FixedList
-        unsigned hash2 = labelPairPair::Hash<>()(pr);
+        unsigned hash2 = labelPairPair::hasher()(pr);
 
         // as FixedList
         unsigned hash3 = Hash<labelPairPair>()(pr);
@@ -231,7 +228,7 @@ void reportHashList(const UList<edge>& list)
         unsigned hash1 = Hash<edge>()(e);
 
         // as FixedList
-        unsigned hash2 = labelPair::Hash<>()(e);
+        unsigned hash2 = labelPair::hasher()(e);
 
         // as FixedList
         unsigned hash3 = Hash<labelPair>()(e);
@@ -251,7 +248,7 @@ void reportHashList(const UList<triFace>& list)
     {
         // direct value
         unsigned hash1 = Hash<triFace>()(f);
-        unsigned hash2 = FixedList<label, 3>::Hash<>()(f);
+        unsigned hash2 = FixedList<label, 3>::hasher()(f);
 
         Info<< hex << hash1 << " (as FixedList: " << hash2
             << "): " << dec << f << nl;
diff --git a/applications/test/Hashing/hashingTests b/applications/test/Hashing2/hashingTests
similarity index 100%
rename from applications/test/Hashing/hashingTests
rename to applications/test/Hashing2/hashingTests
diff --git a/applications/test/HashingSpeed/Test-HashingSpeed.C b/applications/test/HashingSpeed/Test-HashingSpeed.C
index 2d759ab99f5d95a7ab847e5b3a0c1df0ed530b78..cc31bf3d49e6794c9ab3b6bef03b86a0fb8bae7c 100644
--- a/applications/test/HashingSpeed/Test-HashingSpeed.C
+++ b/applications/test/HashingSpeed/Test-HashingSpeed.C
@@ -3,8 +3,8 @@
 #include "Hasher.H"
 #include "int.H"
 
-#include <stdio.h>
-#include <time.h>
+#include <cstdio>
+#include <ctime>
 
 #ifndef CLOCKS_PER_SEC
     #ifdef CLK_TCK
@@ -1048,7 +1048,7 @@ int32_t i;
 
 struct tagtest {
         double res;
-        char * name;
+        const char * name;
         hashFn hash;
 } tests[] = {
 //      { 0.0, "CRC32\t\t", GetCRC32                  },
diff --git a/applications/test/List/Test-List.C b/applications/test/List/Test-List.C
index c78ddaac88cefb33c3904757efdd6d1f33b5e063..16b3210958eac0c4f0db2ffa54648e3cab095f9a 100644
--- a/applications/test/List/Test-List.C
+++ b/applications/test/List/Test-List.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -658,11 +658,11 @@ int main(int argc, char *argv[])
 
         Info<<"hash of " << flatOutput(list1)
             << " = " << Hash<labelList>()(list1) << " or "
-            << labelList::Hash<>()(list1) << nl;
+            << labelList::hasher()(list1) << nl;
 
         Info<<"hash of " << flatOutput(list2) << " = "
             << Hash<labelList>()(list2) << " or "
-            << labelList::Hash<>()(list2) << nl;
+            << labelList::hasher()(list2) << nl;
     }
 
     return 0;
diff --git a/applications/test/List3/Test-List3.C b/applications/test/List3/Test-List3.C
index 685500821169a327338c575366ca636cba394642..e23b36579bea92b145e897a859ab9f1d27f0ffe7 100644
--- a/applications/test/List3/Test-List3.C
+++ b/applications/test/List3/Test-List3.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -101,7 +101,7 @@ int main(int argc, char *argv[])
         {
             if (true)
             {
-                IFstream is(args[argi]);
+                IFstream is(args.get<fileName>(argi));
 
                 Info<< nl << nl
                     << "read from " << is.name() << nl << endl;
@@ -132,7 +132,7 @@ int main(int argc, char *argv[])
 
             if (true)
             {
-                IFstream is(args[argi]);
+                IFstream is(args.get<fileName>(argi));
 
                 Info<< nl << nl
                     << "read from " << is.name() << nl << endl;
diff --git a/applications/test/ListOps2/Test-ListOps2.C b/applications/test/ListOps2/Test-ListOps2.C
index cf33113a66b7d551c937bcea5a8a817acd7829b4..09afd7946dcfad85a3452ef12f07f8369bb64a40 100644
--- a/applications/test/ListOps2/Test-ListOps2.C
+++ b/applications/test/ListOps2/Test-ListOps2.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -57,7 +57,7 @@ Ostream& operator<<
 );
 
 
-template<class T, class Key, class Hash>
+template<class T, class Key, class Hash=Foam::Hash<Key>>
 class HashSorter
 {
     const HashTable<T, Key, Hash>& table;
@@ -219,8 +219,8 @@ int main(int argc, char *argv[])
         Info<< nl << "Test inplaceMapValue" << nl << nl;
 
         HashTable<label> input;
-        typedef HashSorter<label, label, Hash<label>> Mapper;
-        typedef HashSorter<label, word, string::hash> Sorter;
+        typedef HashSorter<label, label> Mapper;
+        typedef HashSorter<label, word> Sorter;
 
         for (label i=0; i < 10; ++i)
         {
diff --git a/applications/test/PackedList/Test-PackedList.C b/applications/test/PackedList/Test-PackedList.C
index b6f5582a839e3692fd7767fb030163fe6931aaae..771be2a35528a39e7045275eb04d949688e0f0e6 100644
--- a/applications/test/PackedList/Test-PackedList.C
+++ b/applications/test/PackedList/Test-PackedList.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -125,9 +126,9 @@ int main(int argc, char *argv[])
     }
 
 
-    for (label argI=1; argI < args.size(); ++argI)
+    for (label argi=1; argi < args.size(); ++argi)
     {
-        const string& srcFile = args[argI];
+        const auto srcFile = args.get<fileName>(argi);
         Info<< nl << "reading " << srcFile << nl;
 
         IFstream ifs(srcFile);
diff --git a/applications/test/PointEdgeWave/Test-PointEdgeWave.C b/applications/test/PointEdgeWave/Test-PointEdgeWave.C
index 4004d2720a155c33d25965d4b53eb8531adf02a3..441f2bc1d4331003c125711129acd309f90a2aa7 100644
--- a/applications/test/PointEdgeWave/Test-PointEdgeWave.C
+++ b/applications/test/PointEdgeWave/Test-PointEdgeWave.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -49,11 +50,13 @@ int main(int argc, char *argv[])
     #include "createTime.H"
     #include "createPolyMesh.H"
 
+    const wordRes patchSelection(args.getList<wordRe>(1));
+
     const polyBoundaryMesh& pbm = mesh.boundaryMesh();
 
     labelList patchIDs
     (
-        pbm.patchSet(args.getList<wordRe>(1)).sortedToc()
+        pbm.patchSet(patchSelection).sortedToc()
     );
 
     Info<< "Starting walk from patches "
diff --git a/applications/test/checkDecomposePar/Test-checkDecomposePar.C b/applications/test/checkDecomposePar/Test-checkDecomposePar.C
index d664615503b24740d8f89212e61a57b0f43d8553..41e46a9cf6af75afac9c51d6d81a806094bf0131 100644
--- a/applications/test/checkDecomposePar/Test-checkDecomposePar.C
+++ b/applications/test/checkDecomposePar/Test-checkDecomposePar.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -74,8 +74,7 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
-    const fileName decompFile = args[1];
-
+    const auto decompFile = args.get<fileName>(1);
     const bool region     = args.found("region");
     const bool allRegions = args.found("allRegions");
     const bool verbose    = args.found("verbose");
@@ -100,10 +99,7 @@ int main(int argc, char *argv[])
             const wordList& regions = iter();
             forAll(regions, i)
             {
-                if (!regionNames.found(regions[i]))
-                {
-                    regionNames.append(regions[i]);
-                }
+                regionNames.appendUniq(regions[i]);
             }
         }
         regionDirs = regionNames;
diff --git a/applications/test/codeStream/Test-codeStream.C b/applications/test/codeStream/Test-codeStream.C
index fc3b656436a8cae5ddf049ece3c2ee0225ceddb3..49b04ac2f1de474e17b924ce72a2bd98849a4d1f 100644
--- a/applications/test/codeStream/Test-codeStream.C
+++ b/applications/test/codeStream/Test-codeStream.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -59,9 +60,9 @@ int main(int argc, char *argv[])
     else
     {
         IOobject::writeDivider(Info);
-        for (label argI=1; argI < args.size(); ++argI)
+        for (label argi=1; argi < args.size(); ++argi)
         {
-            const string& dictFile = args[argI];
+            const auto dictFile = args.get<fileName>(argi);
             IFstream is(dictFile);
 
             dictionary dict(is);
diff --git a/applications/test/coordinateSystem/Test-coordinateSystem.C b/applications/test/coordinateSystem/Test-coordinateSystem.C
index 402038e6a92fb6045e3b2cb60adb5d5bbd3b4f5c..7931a97abfc8d0e6b7fc844d3afc5366d0be27a4 100644
--- a/applications/test/coordinateSystem/Test-coordinateSystem.C
+++ b/applications/test/coordinateSystem/Test-coordinateSystem.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -179,7 +179,7 @@ int main(int argc, char *argv[])
 
         for (label argi=1; argi < args.size(); ++argi)
         {
-            const string& dictFile = args[argi];
+            const auto dictFile = args.get<fileName>(argi);
             IFstream is(dictFile);
 
             dictionary inputDict(is);
@@ -201,7 +201,7 @@ int main(int argc, char *argv[])
     {
         for (label argi=1; argi < args.size(); ++argi)
         {
-            const string& dictFile = args[argi];
+            const auto dictFile = args.get<fileName>(argi);
             IFstream is(dictFile);
 
             dictionary inputDict(is);
diff --git a/applications/test/copyFile/Test-copyFile.C b/applications/test/copyFile/Test-copyFile.C
index c3be05dc1394b30c3f74eff84aae2b7aa8ce53f5..616aefc410da5f028b014ab889065b64cc5f4ab1 100644
--- a/applications/test/copyFile/Test-copyFile.C
+++ b/applications/test/copyFile/Test-copyFile.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -135,8 +135,8 @@ int main(int argc, char *argv[])
     args.readIfPresent("maxPath", maxPath);
     #endif
 
-    const fileName srcFile(fileName::validate(args[1]));
-    const fileName dstFile(fileName::validate(args[2]));
+    const auto srcFile = args.get<fileName>(1);
+    const auto dstFile = args.get<fileName>(2);
     const fileName tmpFile(dstFile + Foam::name(pid()));
 
     Info<< "src   : " << srcFile << nl
diff --git a/applications/test/decomposedBlockData/Test-decomposedBlockData.C b/applications/test/decomposedBlockData/Test-decomposedBlockData.C
index 9edc26ee7e267f957100d21091a2529966dde841..2402055210dee31d62b44040bb942fe6403eadc3 100644
--- a/applications/test/decomposedBlockData/Test-decomposedBlockData.C
+++ b/applications/test/decomposedBlockData/Test-decomposedBlockData.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -54,7 +55,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    const fileName file(args[1]);
+    const auto file = args.get<fileName>(1);
 
     Info<< "Reading " << file << nl << endl;
     decomposedBlockData data
diff --git a/applications/test/dictionary/Test-dictionary.C b/applications/test/dictionary/Test-dictionary.C
index 5e12dbd7ef3e40c3920999248c0ed9f38237b54e..1c5c7a30e11286043bf7653221cfbb32b493d51e 100644
--- a/applications/test/dictionary/Test-dictionary.C
+++ b/applications/test/dictionary/Test-dictionary.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2012 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -123,9 +124,9 @@ int main(int argc, char *argv[])
     else
     {
         IOobject::writeDivider(Info);
-        for (label argI=1; argI < args.size(); ++argI)
+        for (label argi=1; argi < args.size(); ++argi)
         {
-            const string& dictFile = args[argI];
+            const auto dictFile = args.get<fileName>(argi);
             IFstream is(dictFile);
 
             dictionary dict(is);
diff --git a/applications/test/dictionaryCopy/Test-dictionaryCopy.C b/applications/test/dictionaryCopy/Test-dictionaryCopy.C
index 264c88e3e8dbd3fb16d1825454ad0bfcdea0f0ef..4ab58d3a0a4e2dbcf07d076c82f1b888b232434e 100644
--- a/applications/test/dictionaryCopy/Test-dictionaryCopy.C
+++ b/applications/test/dictionaryCopy/Test-dictionaryCopy.C
@@ -69,7 +69,7 @@ int main(int argc, char *argv[])
 
     for (label argi=1; argi < args.size(); ++argi)
     {
-        const string& dictFile = args[argi];
+        const auto dictFile = args.get<fileName>(argi);
         IFstream is(dictFile);
 
         dictionary input(is);
diff --git a/applications/test/dictionaryTokens/Test-dictionaryTokens.C b/applications/test/dictionaryTokens/Test-dictionaryTokens.C
index 3760124cab1f7050609779b0af2ce91ad80dec26..1b8967b7638e4d65e5c9a5f0fae3826f97aed496 100644
--- a/applications/test/dictionaryTokens/Test-dictionaryTokens.C
+++ b/applications/test/dictionaryTokens/Test-dictionaryTokens.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -61,7 +61,7 @@ int main(int argc, char *argv[])
 
     for (label argi=1; argi < args.size(); ++argi)
     {
-        IFstream is(args[argi]);
+        IFstream is(args.get<fileName>(argi));
 
         dictionary dict(is);
 
diff --git a/applications/test/dynamicLibrary/Test-dynamicLibrary.C b/applications/test/dynamicLibrary/Test-dynamicLibrary.C
index ee7009b96180a811b609b4ab5ddfcea9d7e4cca5..0e878a5616c02c13cd6fa2ccc6fd3d39504d0a52 100644
--- a/applications/test/dynamicLibrary/Test-dynamicLibrary.C
+++ b/applications/test/dynamicLibrary/Test-dynamicLibrary.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -70,7 +70,7 @@ int main(int argc, char *argv[])
 
     for (int argi = 1; argi < args.size(); ++argi)
     {
-        const fileName libName(fileName::validate(args[argi]));
+        const auto libName = args.get<fileName>(argi);
 
         if (libName.empty())
         {
diff --git a/applications/test/exprEntry/Test-exprEntry.C b/applications/test/exprEntry/Test-exprEntry.C
index e5b76da7b70396021f687737c958f58113b2a56e..1f14bbf45aa65f55c218bd8a1aded1036fdf01a4 100644
--- a/applications/test/exprEntry/Test-exprEntry.C
+++ b/applications/test/exprEntry/Test-exprEntry.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -79,7 +79,7 @@ int main(int argc, char *argv[])
     {
         IOobject::writeDivider(Info);
 
-        IFstream is(args[argi]);
+        IFstream is(args.get<fileName>(argi));
 
         const dictionary dict(is);
 
diff --git a/applications/test/faceHashing/Make/files b/applications/test/faceHashing/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..2b69436389bf4cec39691cb1d6077624bd63709a
--- /dev/null
+++ b/applications/test/faceHashing/Make/files
@@ -0,0 +1,3 @@
+Test-faceHashing.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-faceHashing
diff --git a/applications/test/faceHashing/Make/options b/applications/test/faceHashing/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..7dba39797af85b1eab2a90d719c7d43abc2309f1
--- /dev/null
+++ b/applications/test/faceHashing/Make/options
@@ -0,0 +1,3 @@
+EXE_INC = ${c++LESSWARN}
+
+/* EXE_LIBS = */
diff --git a/applications/test/faceHashing/Test-faceHashing.C b/applications/test/faceHashing/Test-faceHashing.C
new file mode 100644
index 0000000000000000000000000000000000000000..c0d1f9e7ea19349cac234c31aab37b158e3b7bac
--- /dev/null
+++ b/applications/test/faceHashing/Test-faceHashing.C
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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/>.
+
+Application
+    Test-faceHashing
+
+Description
+    Basic tests of face/triFace hashing
+
+\*---------------------------------------------------------------------------*/
+
+#include "IOstreams.H"
+#include "IOobject.H"
+#include "IFstream.H"
+
+#include "faceList.H"
+#include "triFaceList.H"
+#include "HashSet.H"
+#include "HashTable.H"
+
+using namespace Foam;
+
+template<class HashSetType, class FaceListType>
+void checkHashSet(const HashSetType& hs, const FaceListType& faces)
+{
+    Info<< hs << nl;
+    for (const auto& f : faces)
+    {
+        Info<< "  " << f << " found=" << hs.found(f) << nl;
+    }
+    Info<< nl;
+}
+
+
+void checkHashes(const faceList& faces)
+{
+    face::hasher op1;
+    face::symmHasher op2;
+
+    Info<< "face hasher/symmHasher: " << faces.size() << " faces" << nl;
+    for (const face& f : faces)
+    {
+        Info<< "  " << f << " symmhash=" << op2(f)
+            << " hash=" << op1(f) << nl;
+    }
+    Info<< nl;
+}
+
+
+void checkHashes(const triFaceList& faces)
+{
+    triFace::hasher op1;
+
+    Info<< "triFace hasher: " << faces.size() << " faces" << nl;
+    for (const triFace& f : faces)
+    {
+        Info<< "  " << f << " hash=" << op1(f) << nl;
+    }
+    Info<< nl;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+//  Main program:
+
+int main(int argc, char *argv[])
+{
+    {
+        faceList faces
+        ({
+            face{0, 1, 2, 3},  // regular
+            face{0, 3, 2, 1},  // flip
+            face{2, 3, 0, 1},  // rotate
+            face{2, 1, 0, 3},  // rotate (flip)
+            face{2, 0, 1, 3},  // permute
+            face{2, 1, 0, 3},  // permute
+        });
+
+        checkHashes(faces);
+
+        HashSet<face, face::hasher> hs1;
+        HashSet<face, face::symmHasher> hs2;
+        hs1.insert(faces);
+        hs2.insert(faces);
+
+        Info<< "hashset (hasher)" << nl;
+        checkHashSet(hs1, faces);
+        Info<< nl;
+
+        hs1.erase(faces[0]);
+        Info<< "remove " << faces[0] << nl;
+        Info<< "hashset (hasher)" << nl;
+        checkHashSet(hs1, faces);
+        Info<< nl;
+
+        Info<< "hashset (symmHasher)" << nl;
+        checkHashSet(hs2, faces);
+        Info<< nl;
+
+        hs2.erase(faces[0]);
+        Info<< "remove " << faces[0] << nl;
+        Info<< "hashset (symmHasher)" << nl;
+        checkHashSet(hs2, faces);
+        Info<< nl;
+    }
+
+    {
+        triFaceList faces
+        ({
+            triFace{0, 1, 2},  // regular
+            triFace{0, 2, 1},  // flip
+            triFace{2, 0, 1},  // rotate
+        });
+
+        checkHashes(faces);
+
+        HashSet<triFace> hs1;
+        hs1.insert(faces);
+
+        Info<< "hashset (symmHasher)" << nl;
+        checkHashSet(hs1, faces);
+        Info<< nl;
+    }
+
+    Info<< "\nEnd\n" << nl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/fileName/Test-fileName.C b/applications/test/fileName/Test-fileName.C
index 5c368dbdfb7e0e4ecbf7348f07209d2a2cce9dca..c6bdd486df50a15eb97020d823fa09b2a8677301 100644
--- a/applications/test/fileName/Test-fileName.C
+++ b/applications/test/fileName/Test-fileName.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2017 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -60,7 +60,7 @@ unsigned testClean(std::initializer_list<Pair<std::string>> tests)
         const std::string& expected = test.second();
 
         fileName cleaned(test.first());
-        cleaned.clean();
+        cleaned.clean();  // Remove unneeded ".."
 
         if (cleaned == expected)
         {
diff --git a/applications/test/fileNameClean/Test-fileNameClean.C b/applications/test/fileNameClean/Test-fileNameClean.C
index f368778a4479e141c5aed0691e7a8c276d04568b..b6a23325ce6688d5e67318df43919a4023c0dda4 100644
--- a/applications/test/fileNameClean/Test-fileNameClean.C
+++ b/applications/test/fileNameClean/Test-fileNameClean.C
@@ -5,7 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2011 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -24,11 +25,10 @@ License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
 Application
-    fileNameCleanTest
+    Test-fileNameClean
 
 Description
 
-
 \*---------------------------------------------------------------------------*/
 
 #include "argList.H"
@@ -51,7 +51,7 @@ void printCleaning(fileName& pathName)
     Info<< "components  = " << flatOutput(pathName.components()) << nl;
     Info<< "component 2 = " << pathName.component(2) << nl;
 
-    pathName.clean();
+    pathName.clean();  // Remove unneeded ".."
 
     Info<< "cleaned  = " << pathName << nl
         << "  path() = " << pathName.path() << nl
@@ -94,9 +94,15 @@ int main(int argc, char *argv[])
         printCleaning(pathName);
     }
 
-    for (label argI=1; argI < args.size(); ++argI)
+    for (label argi=1; argi < args.size(); ++argi)
     {
-        pathName = args[argI];
+        fileName fn(args[argi], false); // no strip
+        Info<< "Input = " << fn << nl;
+        fn.clean();  // Remove unneeded ".."
+        Info<< "cleaned = " << fn << nl;
+        Info<< "get = " << args.get<fileName>(argi) << nl;
+
+        pathName = fileName::validate(args[argi]);
         printCleaning(pathName);
     }
 
diff --git a/applications/test/fstreamPointer/Test-fstreamPointer.C b/applications/test/fstreamPointer/Test-fstreamPointer.C
index 6f12abd9a0122abfed52637bb0179c74ac4c2ad5..2cad909ace13fda75eb6a1e1643ee00bd898ffb7 100644
--- a/applications/test/fstreamPointer/Test-fstreamPointer.C
+++ b/applications/test/fstreamPointer/Test-fstreamPointer.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@@ -81,7 +81,7 @@ int main(int argc, char *argv[])
 
     for (label argi = 1; argi < args.size(); ++argi)
     {
-        const fileName inputName(args[argi]);
+        const auto inputName = args.get<fileName>(argi);
 
         InfoErr<< "input: " << inputName;
 
diff --git a/applications/test/fvSolutionCombine/Test-fvSolutionCombine.C b/applications/test/fvSolutionCombine/Test-fvSolutionCombine.C
index 0db31c8b828d503b41d111afe4c075a2fb007c53..9a3e8b181098681426a08a5a1eb829624f77acd6 100644
--- a/applications/test/fvSolutionCombine/Test-fvSolutionCombine.C
+++ b/applications/test/fvSolutionCombine/Test-fvSolutionCombine.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -140,8 +141,8 @@ int main(int argc, char *argv[])
     bool changed = false;
     for (label orig = 0; orig < names.size()-1; ++orig)
     {
-        // skip patterns or entries that have already been done
-        if (names[orig].empty() || wordRe::isPattern(names[orig]))
+        // Skip patterns or entries that have already been done
+        if (names[orig].empty() || regExp::is_meta(names[orig]))
         {
             continue;
         }
@@ -150,15 +151,15 @@ int main(int argc, char *argv[])
 
         for (label check = orig+1; check < names.size(); ++check)
         {
-            // skip patterns or entries that have already been done
-            if (names[check].empty() || wordRe::isPattern(names[check]))
+            // Skip patterns or entries that have already been done
+            if (names[check].empty() || regExp::is_meta(names[check]))
             {
                 continue;
             }
 
             const dictionary& dict2 = solverDict.subDict(names[check]);
 
-            // check for identical content
+            // Check for identical content
             if (checkDictionaryContent(dict1, dict2))
             {
                 names[orig] += "|" + names[check];
diff --git a/applications/test/mvBak/Test-mvBak.C b/applications/test/mvBak/Test-mvBak.C
index 9c9b6c7172d73dd936cddac56fbae8afb27b2e7d..7c6ff169ff1bd3892a9ef7012ed5246d2de770b9 100644
--- a/applications/test/mvBak/Test-mvBak.C
+++ b/applications/test/mvBak/Test-mvBak.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -53,9 +54,9 @@ int main(int argc, char *argv[])
 
     label ok = 0;
 
-    for (label argI=1; argI < args.size(); ++argI)
+    for (label argi=1; argi < args.size(); ++argi)
     {
-        const string& srcFile = args[argI];
+        const auto srcFile = args.get<fileName>(argi);
 
         if (args.found("ext"))
         {
diff --git a/applications/test/plotFunction1/Test-plotFunction1.C b/applications/test/plotFunction1/Test-plotFunction1.C
index 9ddc5cc0246ddde13496db5cd7ecf31357a0aa55..99bc8769a80e672bf3256d662ddc08a892215d62 100644
--- a/applications/test/plotFunction1/Test-plotFunction1.C
+++ b/applications/test/plotFunction1/Test-plotFunction1.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
 
     for (label argi=1; argi < args.size(); ++argi)
     {
-        IFstream is(args[argi]);
+        IFstream is(args.get<fileName>(argi));
 
         dictionary dict(is);
 
diff --git a/applications/test/regex1/Test-regex1.C b/applications/test/regex1/Test-regex1.C
index 533304f7cf9102de9f5e034262f3ca4f1b1ace0b..9f917355a96e58b44f8c7822cf1e319b26469969 100644
--- a/applications/test/regex1/Test-regex1.C
+++ b/applications/test/regex1/Test-regex1.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,6 +34,7 @@ Description
 #include "IFstream.H"
 #include "Switch.H"
 
+#include "stringOps.H"
 #include "SubStrings.H"
 #include "regExpCxx.H"
 #ifndef _WIN32
@@ -294,19 +295,16 @@ int main(int argc, char *argv[])
     argList::noFunctionObjects();
     argList::noParallel();
 
-    argList::addBoolOption
-    (
-        "cxx",
-        "Test C++11 regular expressions"
-    );
-
+    argList::addBoolOption("cxx", "Test C++11 regular expressions");
     #ifndef _WIN32
-    argList::addBoolOption
+    argList::addBoolOption("posix", "Test POSIX regular expressions");
+    #endif
+    argList::addOption
     (
-        "posix",
-        "Test POSIX regular expressions"
+        "regex",
+        "expression",
+        "regular expression to test"
     );
-    #endif
 
     argList::addArgument("file");
     argList::addArgument("...");
@@ -315,24 +313,50 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
+    // Newer compilers support regex directly
+    #ifdef _GLIBCXX_RELEASE
+    Info<< "_GLIBCXX_RELEASE = " << (_GLIBCXX_RELEASE) << nl;
+    #endif
+
     if (std::is_same<regExp, regExpCxx>::value)
     {
-        Info<<"Foam::regExp uses C++11 regex" << nl << nl;
+        Info<< "Foam::regExp uses C++11 regex" << nl;
     }
     #ifndef _WIN32
     if (std::is_same<regExp, regExpPosix>::value)
     {
-        Info<<"Foam::regExp uses POSIX regex" << nl << nl;
+        Info<< "Foam::regExp uses POSIX regex" << nl;
     }
     #endif
 
     if (!args.count({"cxx", "posix"}))
     {
-        Info<< "Specified one or more of -cxx, -posix" << nl;
-        return 1;
+        args.setOption("cxx");
+        Info<< "Assuming -cxx as default" << nl;
     }
+    Info<< nl;
 
-    if (args.size() < 2)
+    if (args.found("regex"))
+    {
+        std::string expr(args["regex"]);
+        Info<< "regex: " << expr << nl;
+
+        Info<< "(cxx)" << nl
+            << "meta : " << Switch(regExpCxx::is_meta(expr)) << nl
+            << "quotemeta: "
+            << stringOps::quotemeta(expr, regExpCxx::meta()) << nl
+            << nl;
+
+        #ifndef _WIN32
+        Info<< "(posix):" << nl
+            << "meta : " << Switch(regExpPosix::is_meta(expr)) << nl
+            << "quotemeta: "
+            << stringOps::quotemeta(expr, regExpPosix::meta()) << nl
+            << nl;
+        #endif
+        Info<< nl;
+    }
+    else if (args.size() < 2)
     {
         Info<< "No test files specified .. restrict to general tests" << nl;
 
@@ -351,7 +375,8 @@ int main(int argc, char *argv[])
 
     for (label argi = 1; argi < args.size(); ++argi)
     {
-        List<regexTest> tests(IFstream(args[argi])());
+        IFstream is(args.get<fileName>(argi));
+        List<regexTest> tests(is);
 
         Info<< "Test expressions:" << tests << endl;
         IOobject::writeDivider(Info) << endl;
diff --git a/applications/test/spline/Test-spline.C b/applications/test/spline/Test-spline.C
index 4d9af262dcb574b8c4edb7e939e4a3d71fbe658d..9785da0fdb78a248356b2b8dc9dacbb9f719d05d 100644
--- a/applications/test/spline/Test-spline.C
+++ b/applications/test/spline/Test-spline.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -76,9 +76,9 @@ int main(int argc, char *argv[])
         useCatmullRom = true;
     }
 
-    for (label argI=1; argI < args.size(); ++argI)
+    for (label argi=1; argi < args.size(); ++argi)
     {
-        const string& srcFile = args[argI];
+        const auto srcFile = args.get<fileName>(argi);
         Info<< nl << "reading " << srcFile << nl;
         IFstream ifs(srcFile);
 
diff --git a/applications/test/string/Test-string.C b/applications/test/string/Test-string.C
index 88f4579df368f5bd6a15491a5ec8a47c90d8dffc..11648ba523168cccc92e30d26fe1db2d6f5290ff 100644
--- a/applications/test/string/Test-string.C
+++ b/applications/test/string/Test-string.C
@@ -167,7 +167,7 @@ int main(int argc, char *argv[])
 
 
     Info<< "string:" << test << nl << "hash:"
-        << unsigned(string::hash()(test)) << endl;
+        << unsigned(string::hasher()(test)) << endl;
 
     Info<<"trimLeft: " << stringOps::trimLeft(test) << endl;
     Info<<"trimRight: " << stringOps::trimRight(test) << endl;
@@ -333,11 +333,11 @@ int main(int argc, char *argv[])
     cout<< "output string with " << s2.length() << " characters\n";
     cout<< "ostream<<  >" << s2 << "<\n";
     Info<< "Ostream<<  >" << s2 << "<\n";
-    Info<< "hash:" << hex << string::hash()(s2) << dec << endl;
+    Info<< "hash:" << hex << string::hasher()(s2) << dec << endl;
 
     cout<< "\ntest Foam::name()\n";
 
-    Info<< "hash: = " << word::printf("0x%012X", string::hash()(s2)) << endl;
+    Info<< "hash: = " << word::printf("0x%012X", string::hasher()(s2)) << endl;
 
     // Test formatting on int
     {
diff --git a/applications/test/stringList/Test-stringList.C b/applications/test/stringList/Test-stringList.C
index f7628aa2543f274ea3421df909a73492a8d6f250..23e441a8699883c639856b9407b1b4c6a2ffa63c 100644
--- a/applications/test/stringList/Test-stringList.C
+++ b/applications/test/stringList/Test-stringList.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,7 +41,7 @@ using namespace Foam;
 
 int main(int argc, char *argv[])
 {
-    stringList strLst
+    stringList strings
     {
         "hello",
         "heello",
@@ -53,51 +53,57 @@ int main(int argc, char *argv[])
         "okkey",
         "okkkey",
     };
+    labelList matches;
 
-    wordRes reLst(IStringStream("( okey \"[hy]e+.*\" )")());
+    wordRes matcher1(IStringStream("( okey \"[hy]e+.*\" )")());
 
-    Info<< "stringList " << strLst << nl;
+    Info<< "stringList " << strings << nl;
 
-    labelList matches = findStrings(regExp(".*ee.*"), strLst);
+    {
+        keyType key(".*ee.*", keyType::REGEX);
+        matches = findMatchingStrings(regExp(key), strings);
 
-    Info<< "matches found for regexp .*ee.* :" << nl << matches << nl;
+        Info<< "matches found for regexp " << key << " :" << nl
+            << matches << nl;
 
-    forAll(matches, i)
-    {
-        Info<< " -> " << strLst[matches[i]] << nl;
+        for (const label idx : matches)
+        {
+            Info<< " -> " << strings[idx] << nl;
+        }
     }
 
     Info<< "Match found using ListOps = "
-        << ListOps::found(strLst, regExp(".*ee.*")) << nl;
+        << ListOps::found(strings, regExp(".*ee.*")) << nl;
 
     Info<< "First index = "
-        << ListOps::find(strLst, regExp(".*ee.*")) << nl;
+        << ListOps::find(strings, regExp(".*ee.*")) << nl;
 
     Info<< endl;
 
-    matches = findStrings(reLst, strLst);
+    matches = findMatchingStrings(matcher1, strings);
 
-    Info<< "matching " << flatOutput(reLst) << " => "
-        << reLst.matching(strLst) << nl;
-    Info<< "matches found for " << flatOutput(reLst) << " => "
+    Info<< "matching " << flatOutput(matcher1) << " => "
+        << matcher1.matching(strings) << nl;
+    Info<< "matches found for " << flatOutput(matcher1) << " => "
         << matches << nl;
-    forAll(matches, i)
+
+    for (const label idx : matches)
     {
-        Info<< " -> " << strLst[matches[i]] << nl;
+        Info<< " -> " << strings[idx] << nl;
     }
     Info<< endl;
 
-    stringList subLst = subsetStrings(regExp(".*ee.*"), strLst);
+    stringList subLst = subsetStrings(regExp(".*ee.*"), strings);
     Info<< "subset stringList: " << subLst << nl;
 
-    subLst = subsetStrings(reLst, strLst);
+    subLst = subsetStrings(matcher1, strings);
     Info<< "subset stringList: " << subLst << nl;
 
-    inplaceSubsetStrings(reLst, strLst);
-    Info<< "subsetted stringList: " << strLst << nl;
+    inplaceSubsetStrings(matcher1, strings);
+    Info<< "subsetted stringList: " << strings << nl;
 
-    inplaceSubsetStrings(regExp(".*l.*"), strLst);
-    Info<< "subsetted stringList: " << strLst << nl;
+    inplaceSubsetStrings(regExp(".*l.*"), strings);
+    Info<< "subsetted stringList: " << strings << nl;
 
     Info<< "\nEnd\n" << endl;
 
diff --git a/applications/test/stringSplit/Test-stringSplit.C b/applications/test/stringSplit/Test-stringSplit.C
index 2994d384f4dc530eb5a6fefac344bf97a7025fcf..2dc4e6ab98e65e9d11de540e1102887b7196cbb4 100644
--- a/applications/test/stringSplit/Test-stringSplit.C
+++ b/applications/test/stringSplit/Test-stringSplit.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -38,8 +38,12 @@ Description
 using namespace Foam;
 
 // Simple utility
-template<class String>
-void printSubStrings(const String& str, const SubStrings<String>& split)
+template<class StringType>
+void printSubStrings
+(
+    const StringType& str,
+    const SubStrings<StringType>& split
+)
 {
     Info<< "string {" << str.size() << " chars} = " << str << nl
         << split.size() << " elements {" << split.length() << " chars}"
diff --git a/applications/test/surfaceIntersection/Test-surfaceIntersection.C b/applications/test/surfaceIntersection/Test-surfaceIntersection.C
index c3e2731b937a3faee5dd36e0f95aa524952682af..e0c6c218b3d43e74c498013202248b4b0bb8107d 100644
--- a/applications/test/surfaceIntersection/Test-surfaceIntersection.C
+++ b/applications/test/surfaceIntersection/Test-surfaceIntersection.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -139,13 +139,13 @@ int main(int argc, char *argv[])
 
     const word outputFile(args.executable() + ".obj");
 
-    const fileName surf1Name(args[1]);
+    const auto surf1Name = args.get<fileName>(1);
     triSurface surf1 = loadSurface(runTime, surf1Name, scaleFactor)();
     Info<< surf1Name << " statistics:" << endl;
     surf1.writeStats(Info);
     Info<< endl;
 
-    const fileName surf2Name(args[2]);
+    const auto surf2Name = args.get<fileName>(2);
     triSurface surf2 = loadSurface(runTime, surf2Name, scaleFactor)();
     Info<< surf2Name << " statistics:" << endl;
     surf2.writeStats(Info);
diff --git a/applications/test/surfaceReading/Test-surfaceReading.C b/applications/test/surfaceReading/Test-surfaceReading.C
index ea6572039cf78c3199b3cb1d82d524fd4007db97..bd0c39d12b024d6d5b9c1fe95f159c29856faab7 100644
--- a/applications/test/surfaceReading/Test-surfaceReading.C
+++ b/applications/test/surfaceReading/Test-surfaceReading.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -89,7 +89,7 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
-    const fileName importName = args[1];
+    const auto importName = args.get<fileName>(1);
 
     word ext;
     if (!args.readIfPresent("ext", ext))
diff --git a/applications/test/surfaceWriter/Test-surfaceWriter.C b/applications/test/surfaceWriter/Test-surfaceWriter.C
index c1826f2fe17588fdf17c316426fdec57402de193..3d8ee9cf258ef6148e1590c926b56ae7908b7584 100644
--- a/applications/test/surfaceWriter/Test-surfaceWriter.C
+++ b/applications/test/surfaceWriter/Test-surfaceWriter.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -68,8 +68,8 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
-    const fileName importName = args[1];
-    const fileName exportName = args[2];
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.get<fileName>(2);
 
     if (importName == exportName)
     {
diff --git a/applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C b/applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C
index 1cc4ad302641edf48168da487902f64bb69648e3..c01b249f733f856d164cb104a737a22bf0f95151 100644
--- a/applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C
+++ b/applications/test/vtkSeriesWriter/Test-vtkSeriesWriter.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -54,7 +54,7 @@ int main(int argc, char *argv[])
 
     for (label argi=1; argi < args.size(); ++argi)
     {
-        const auto& input = args[argi];
+        const auto input = args.get<fileName>(argi);
 
         Info << "load from " << input << nl;
 
diff --git a/applications/test/wordRe/Test-wordRe.C b/applications/test/wordRe/Test-wordRe.C
index 37c34a71dc844c19ceab5f715c41e5dbde351588..f91258511da77eb3c7408c2ccb240bdf4c990edc 100644
--- a/applications/test/wordRe/Test-wordRe.C
+++ b/applications/test/wordRe/Test-wordRe.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,6 +32,7 @@ Description
 #include "IOstreams.H"
 #include "IOobject.H"
 #include "IFstream.H"
+#include "ITstream.H"
 #include "List.H"
 #include "Tuple2.H"
 #include "keyType.H"
@@ -51,11 +52,84 @@ word typeOf(wordRe::compOption retval)
 }
 
 
+Ostream& printInfo(const wordRe& wre)
+{
+    if (wre.isPattern())
+    {
+        Info<< "wordRe(regex) ";
+    }
+    else
+    {
+        Info<< "wordRe(plain) ";
+    }
+    Info<< wre;
+    return Info;
+}
+
+
+// Could use something like this for reading wordRes
+void exptl_reading(Istream& is, wordRes& list)
+{
+    token tok(is);
+
+    bool ok = ((tok.isWord() || tok.isQuotedString()) && !tok.isCompound());
+    if (ok)
+    {
+        list.resize(1);
+        ok = list[0].assign(tok);
+    }
+    if (!ok)
+    {
+        if (tok.good())
+        {
+            is.putBack(tok);
+        }
+        list.readList(is);
+    }
+}
+
+
+bool testReadList_wordRes(const std::string& input)
+{
+    ITstream is("input", input);
+    wordRes list;
+
+    exptl_reading(is, list);
+
+    const label nTrailing = is.nRemainingTokens();
+
+    Info<< "input:<<<<" << nl << input.c_str() << nl
+        << ">>>> with " << nTrailing << " tokens remaining" << nl
+        << "list: " << flatOutput(list) << nl;
+
+    return !nTrailing;
+}
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 // Main program:
 
 int main(int argc, char *argv[])
 {
+    if (true)
+    {
+        Info<< "Test input for wordRes" << nl << nl;
+
+        testReadList_wordRes
+        (
+            "("
+            "this \"x.*\" \"file[a-b]\" xvalues \"xv.*\""
+            ")"
+        );
+
+        testReadList_wordRes
+        (
+            "\".*\""
+        );
+
+        Info<< nl << nl;
+    }
+
     wordRe wre;
     std::string s1("this .* file");
     Foam::string s2("this .* file");
@@ -64,13 +138,13 @@ int main(int argc, char *argv[])
     keyType keyre("x.*", keyType::REGEX);
 
     wordRes wordrelist
-    {
+    ({
         {"this", wordRe::LITERAL},
         {"x.*", wordRe::REGEX},
         {"file[a-b]", wordRe::REGEX},
         {"xvalues", wordRe::LITERAL},
         {"xv.*", wordRe::REGEX},
-    };
+    });
 
     if (false)
     {
@@ -169,45 +243,45 @@ int main(int argc, char *argv[])
     }
     Info<< nl;
 
-    wordRe(s1, wordRe::DETECT).info(Info) << nl;
-    wordRe(s2).info(Info) << nl;
-    wordRe(s2, wordRe::DETECT).info(Info) << nl;
-    wordRe(s3, wordRe::REGEX).info(Info) << nl;
+    printInfo(wordRe(s1, wordRe::DETECT)) << nl;
+    printInfo(wordRe(s2)) << nl;
+    printInfo(wordRe(s2, wordRe::DETECT)) << nl;
+    printInfo(wordRe(s3, wordRe::REGEX)) << nl;
 
     wre = "this .* file";
 
     Info<<"substring: " << wre.substr(4) << nl;
 
-    wre.info(Info) << nl;
+    printInfo(wre) << nl;
     wre = s1;
-    wre.info(Info) << nl;
+    printInfo(wre) << nl;
     wre.uncompile();
-    wre.info(Info) << nl;
+    printInfo(wre) << nl;
 
     wre = "something";
-    wre.info(Info) << " before" << nl;
+    printInfo(wre) << " before" << nl;
     wre.uncompile();
-    wre.info(Info) << " uncompiled" << nl;
+    printInfo(wre) << " uncompiled" << nl;
     wre.compile(wordRe::DETECT);
-    wre.info(Info) << " after DETECT" << nl;
+    printInfo(wre) << " after DETECT" << nl;
     wre.compile(wordRe::ICASE);
-    wre.info(Info) << " after ICASE" << nl;
+    printInfo(wre) << " after ICASE" << nl;
     wre.compile(wordRe::DETECT_ICASE);
-    wre.info(Info) << " after DETECT_ICASE" << nl;
+    printInfo(wre) << " after DETECT_ICASE" << nl;
 
     wre = "something .* value";
-    wre.info(Info) << " before" << nl;
+    printInfo(wre) << " before" << nl;
     wre.uncompile();
-    wre.info(Info) << " uncompiled" << nl;
+    printInfo(wre) << " uncompiled" << nl;
     wre.compile(wordRe::DETECT);
-    wre.info(Info) << " after DETECT" << nl;
+    printInfo(wre) << " after DETECT" << nl;
     wre.uncompile();
-    wre.info(Info) << " uncompiled" << nl;
+    printInfo(wre) << " uncompiled" << nl;
     wre.compile();
-    wre.info(Info) << " re-compiled" << nl;
+    printInfo(wre) << " re-compiled" << nl;
 
     wre.set("something .* value", wordRe::LITERAL);
-    wre.info(Info) << " set as LITERAL" << nl;
+    printInfo(wre) << " set as LITERAL" << nl;
 
     IOobject::writeDivider(Info);
 
@@ -220,7 +294,7 @@ int main(int argc, char *argv[])
         const wordRe& wre = rawList[elemI].first();
         const string& str = rawList[elemI].second();
 
-        wre.info(Info)
+        printInfo(wre)
             << " equals:" << (wre == str)
             << "(" << wre.match(str, true) << ")"
             << " match:" << wre.match(str)
@@ -230,11 +304,10 @@ int main(int argc, char *argv[])
         wordRe wre2;
         wre2.set(wre, wordRe::ICASE);
 
-        wre2.info(Info)
+        printInfo(wre2)
             << " match:" << wre2.match(str)
             << "  str=" << str
             << nl;
-
     }
 
     Info<< "\nEnd\n" << endl;
diff --git a/applications/utilities/mesh/conversion/ansysToFoam/ansysToFoam.L b/applications/utilities/mesh/conversion/ansysToFoam/ansysToFoam.L
index cafcdf67adc4bbc216358089585946e172d4c58b..66cb8534a5be9ae4e8cc5d2bf212db5eafc39c7a 100644
--- a/applications/utilities/mesh/conversion/ansysToFoam/ansysToFoam.L
+++ b/applications/utilities/mesh/conversion/ansysToFoam/ansysToFoam.L
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -327,7 +327,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    const fileName ansysFile(args[1]);
+    const auto ansysFile = args.get<fileName>(1);
     std::ifstream ansysStream(ansysFile);
 
     if (!ansysStream)
diff --git a/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C b/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C
index 14855f09315f071cc200f960b1a3aafbe8f965f8..876a6d59becbc1d6765ffc3af78b8c0bb3da1ef4 100644
--- a/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C
+++ b/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -225,7 +225,7 @@ int main(int argc, char *argv[])
     }
 
     // CCM reader for reading geometry/solution
-    ccm::reader reader(args[1], rOpts);
+    ccm::reader reader(args.get<fileName>(1), rOpts);
 
     // list the geometry information
     if (optList)
diff --git a/applications/utilities/mesh/conversion/cfx4ToFoam/cfx4ToFoam.C b/applications/utilities/mesh/conversion/cfx4ToFoam/cfx4ToFoam.C
index 903767ab43b7fc21fa7b99241a351db10aee08e6..3039c16a5662bd8308c61a40c5956915c2196488 100644
--- a/applications/utilities/mesh/conversion/cfx4ToFoam/cfx4ToFoam.C
+++ b/applications/utilities/mesh/conversion/cfx4ToFoam/cfx4ToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -76,7 +76,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    IFstream cfxFile(args[1]);
+    IFstream cfxFile(args.get<fileName>(1));
 
     // Read the cfx information using a fixed format reader.
     // Comments in the file are in C++ style, so the stream parser will remove
diff --git a/applications/utilities/mesh/conversion/datToFoam/datToFoam.C b/applications/utilities/mesh/conversion/datToFoam/datToFoam.C
index 2e3dd0d452416a6761163ec2f28d29f5eb64e5d8..8a526ab5cf936e973d615049f745df5ca0b74f0a 100644
--- a/applications/utilities/mesh/conversion/datToFoam/datToFoam.C
+++ b/applications/utilities/mesh/conversion/datToFoam/datToFoam.C
@@ -66,7 +66,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    std::ifstream plot3dFile(args[1]);
+    std::ifstream plot3dFile(args.get<fileName>(1));
 
     string line;
     std::getline(plot3dFile, line);
diff --git a/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C
index 021907f09674a44086491ebd7729a94944f77a29..b3809ff16038ff14f5984db269ad221dc2b8bdd1 100644
--- a/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C
+++ b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -103,7 +103,7 @@ int main(int argc, char *argv[])
 
     fileFormats::FIREMeshReader reader
     (
-        args[1],
+        args.get<fileName>(1),
         // Default no scaling
         args.getOrDefault<scalar>("scale", 1)
     );
diff --git a/applications/utilities/mesh/conversion/fluent3DMeshToFoam/fluent3DMeshToFoam.L b/applications/utilities/mesh/conversion/fluent3DMeshToFoam/fluent3DMeshToFoam.L
index 066da1368a18f087601ecde64cd6f60bf308cbc7..98d751ee0dfb340e540c8773d4d6c3ec8cafc921 100644
--- a/applications/utilities/mesh/conversion/fluent3DMeshToFoam/fluent3DMeshToFoam.L
+++ b/applications/utilities/mesh/conversion/fluent3DMeshToFoam/fluent3DMeshToFoam.L
@@ -835,7 +835,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    const fileName fluentFile = args[1];
+    const auto fluentFile = args.get<fileName>(1);
     IFstream fluentStream(fluentFile);
 
     if (!fluentStream)
diff --git a/applications/utilities/mesh/conversion/fluentMeshToFoam/fluentMeshToFoam.L b/applications/utilities/mesh/conversion/fluentMeshToFoam/fluentMeshToFoam.L
index 8623da54ecfd594c8feac1600b37f55f0fd06fca..8d70aa39f96e257b75271d5ffdde09b86251c2a1 100644
--- a/applications/utilities/mesh/conversion/fluentMeshToFoam/fluentMeshToFoam.L
+++ b/applications/utilities/mesh/conversion/fluentMeshToFoam/fluentMeshToFoam.L
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -912,7 +912,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    const fileName fluentFile = args[1];
+    const auto fluentFile = args.get<fileName>(1);
     std::ifstream fluentStream(fluentFile);
 
     if (!fluentStream)
diff --git a/applications/utilities/mesh/conversion/foamToSurface/foamToSurface.C b/applications/utilities/mesh/conversion/foamToSurface/foamToSurface.C
index 17703601d86f1ceb6925e1f284a003b85f6079f2..53b359a2f2c5fb8904a1dfb551ad561686316962 100644
--- a/applications/utilities/mesh/conversion/foamToSurface/foamToSurface.C
+++ b/applications/utilities/mesh/conversion/foamToSurface/foamToSurface.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -82,7 +82,7 @@ int main(int argc, char *argv[])
 
     #include "setRootCase.H"
 
-    fileName exportName = args[1];
+    auto exportName = args.get<fileName>(1);
 
     const scalar scaleFactor = args.getOrDefault<scalar>("scale", 0);
     const bool doTriangulate = args.found("tri");
diff --git a/applications/utilities/mesh/conversion/gambitToFoam/gambitToFoam.L b/applications/utilities/mesh/conversion/gambitToFoam/gambitToFoam.L
index cbd4172c75043613d97660d7678586a3cd3aa414..f8a226a63585d9de47b4be49bf7b758139721b79 100644
--- a/applications/utilities/mesh/conversion/gambitToFoam/gambitToFoam.L
+++ b/applications/utilities/mesh/conversion/gambitToFoam/gambitToFoam.L
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -653,7 +653,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    const fileName gambitFile = args[1];
+    const auto gambitFile = args.get<fileName>(1);
     std::ifstream gambitStream(gambitFile);
 
     if (!gambitStream)
diff --git a/applications/utilities/mesh/conversion/gmshToFoam/gmshToFoam.C b/applications/utilities/mesh/conversion/gmshToFoam/gmshToFoam.C
index 02a17f42352ff531ef6d951076083dcec51446f3..b7e6909ff27e8208802b533c230f4c85557e7466 100644
--- a/applications/utilities/mesh/conversion/gmshToFoam/gmshToFoam.C
+++ b/applications/utilities/mesh/conversion/gmshToFoam/gmshToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -1325,7 +1325,7 @@ int main(int argc, char *argv[])
     }
 
     const bool keepOrientation = args.found("keepOrientation");
-    IFstream inFile(args[1]);
+    IFstream inFile(args.get<fileName>(1));
 
     // Storage for points
     pointField points;
diff --git a/applications/utilities/mesh/conversion/ideasUnvToFoam/ideasUnvToFoam.C b/applications/utilities/mesh/conversion/ideasUnvToFoam/ideasUnvToFoam.C
index 6c2d2188b6c1737045422b4cb46e1ea6ca39ef52..1d9f361e9dd272d19558c2c96bc9455cb05b982a 100644
--- a/applications/utilities/mesh/conversion/ideasUnvToFoam/ideasUnvToFoam.C
+++ b/applications/utilities/mesh/conversion/ideasUnvToFoam/ideasUnvToFoam.C
@@ -666,7 +666,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const fileName ideasName = args[1];
+    const auto ideasName = args.get<fileName>(1);
     IFstream inFile(ideasName);
 
     if (!inFile.good())
@@ -850,35 +850,39 @@ int main(int argc, char *argv[])
     Map<label> faceToCell[2];
 
     {
-        HashTable<label, face, face::Hash<>> faceToFaceID(boundaryFaces.size());
-        forAll(boundaryFaces, facei)
+        // Can use face::symmHasher or use sorted indices instead
+        // - choose the latter in case UNV has anything odd
+        HashTable<label, face> faceToFaceID(2*boundaryFaces.size());
+
+        forAll(boundaryFaces, bfacei)
         {
-            SortableList<label> sortedVerts(boundaryFaces[facei]);
-            faceToFaceID.insert(face(sortedVerts), facei);
+            face sortedVerts(boundaryFaces[bfacei]);
+            Foam::sort(sortedVerts);
+            faceToFaceID.insert(sortedVerts, bfacei);
         }
 
         forAll(cellVerts, celli)
         {
-            faceList faces = cellVerts[celli].faces();
-            forAll(faces, i)
-            {
-                SortableList<label> sortedVerts(faces[i]);
-                const auto fnd = faceToFaceID.find(face(sortedVerts));
+            const cellShape& shape = cellVerts[celli];
 
-                if (fnd.found())
+            for (const face& f : shape.faces())
+            {
+                face sortedVerts(f);
+                Foam::sort(sortedVerts);
+                const label bfacei = faceToFaceID.lookup(sortedVerts, -1);
+                if (bfacei != -1)
                 {
-                    label facei = *fnd;
-                    int stat = face::compare(faces[i], boundaryFaces[facei]);
+                    const int cmp = face::compare(f, boundaryFaces[bfacei]);
 
-                    if (stat == 1)
+                    if (cmp == 1)
                     {
                         // Same orientation. Cell is owner.
-                        own[facei] = celli;
+                        own[bfacei] = celli;
                     }
-                    else if (stat == -1)
+                    else if (cmp == -1)
                     {
                         // Opposite orientation. Cell is neighbour.
-                        nei[facei] = celli;
+                        nei[bfacei] = celli;
                     }
                 }
             }
@@ -958,15 +962,13 @@ int main(int argc, char *argv[])
         {
             const cellShape& shape = cellVerts[celli];
 
-            const faceList shapeFaces(shape.faces());
-
-            forAll(shapeFaces, i)
+            for (const face& f : shape.faces())
             {
-                label patchi = findPatch(dofGroups, shapeFaces[i]);
+                label patchi = findPatch(dofGroups, f);
 
                 if (patchi != -1)
                 {
-                    dynPatchFaces[patchi].append(shapeFaces[i]);
+                    dynPatchFaces[patchi].append(f);
                 }
             }
         }
diff --git a/applications/utilities/mesh/conversion/mshToFoam/mshToFoam.C b/applications/utilities/mesh/conversion/mshToFoam/mshToFoam.C
index 20088ce4ca2e2d7ab0b993c0d500116e44a5514f..1bfa8f7183af506829787b74e056802f2eeef434 100644
--- a/applications/utilities/mesh/conversion/mshToFoam/mshToFoam.C
+++ b/applications/utilities/mesh/conversion/mshToFoam/mshToFoam.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -77,7 +78,7 @@ int main(int argc, char *argv[])
     #include "createTime.H"
 
     const bool readHex = args.found("hex");
-    IFstream mshStream(args[1]);
+    IFstream mshStream(args.get<fileName>(1));
 
     label nCells;
     mshStream >> nCells;
diff --git a/applications/utilities/mesh/conversion/netgenNeutralToFoam/netgenNeutralToFoam.C b/applications/utilities/mesh/conversion/netgenNeutralToFoam/netgenNeutralToFoam.C
index cbae422f00f6bc236bdbbce649927d6e3476795a..3dc8bfb9ed068c9633b1857ee9da890d2a723a51 100644
--- a/applications/utilities/mesh/conversion/netgenNeutralToFoam/netgenNeutralToFoam.C
+++ b/applications/utilities/mesh/conversion/netgenNeutralToFoam/netgenNeutralToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -104,7 +104,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    IFstream str(args[1]);
+    IFstream str(args.get<fileName>(1));
 
     //
     // Read nodes.
@@ -173,7 +173,7 @@ int main(int argc, char *argv[])
     label maxPatch = 0;
 
     // Boundary faces as three vertices
-    HashTable<label, triFace, triFace::Hash<>> vertsToBoundary(nFaces);
+    HashTable<label, triFace> vertsToBoundary(nFaces);
 
     forAll(boundaryFaces, facei)
     {
diff --git a/applications/utilities/mesh/conversion/plot3dToFoam/plot3dToFoam.C b/applications/utilities/mesh/conversion/plot3dToFoam/plot3dToFoam.C
index 78e8381bb63b43f0ea8abb8c4a2c0a5599675c58..1a95f8fec2ed2ce64d7f478d9291d4686ecd55e1 100644
--- a/applications/utilities/mesh/conversion/plot3dToFoam/plot3dToFoam.C
+++ b/applications/utilities/mesh/conversion/plot3dToFoam/plot3dToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -111,7 +111,7 @@ int main(int argc, char *argv[])
 
     #include "createTime.H"
 
-    IFstream plot3dFile(args[1]);
+    IFstream plot3dFile(args.get<fileName>(1));
 
     // Read the plot3d information using a fixed format reader.
     // Comments in the file are in C++ style, so the stream parser will remove
diff --git a/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C b/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
index 24a6c28f7059fef77435fac541ec2376ecaea6de..b48f1b6bd052b1e014ae532ee091d918836fb245 100644
--- a/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
+++ b/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -108,7 +108,7 @@ int main(int argc, char *argv[])
 
 
     // Remove extensions and/or trailing '.'
-    const fileName prefix = fileName(args[1]).lessExt();
+    const auto prefix = args.get<fileName>(1).lessExt();
 
 
     fileFormats::STARCDMeshReader reader
diff --git a/applications/utilities/mesh/conversion/tetgenToFoam/tetgenToFoam.C b/applications/utilities/mesh/conversion/tetgenToFoam/tetgenToFoam.C
index 55ac13b40ca594558211b133b3621f3a7838a5d3..1d457708d84bf98d63fa211e7ec99e6a9d8e91d9 100644
--- a/applications/utilities/mesh/conversion/tetgenToFoam/tetgenToFoam.C
+++ b/applications/utilities/mesh/conversion/tetgenToFoam/tetgenToFoam.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const fileName prefix = args[1];
+    const auto prefix = args.get<fileName>(1);
     const bool readFaceFile = !args.found("noFaceFile");
 
     const fileName nodeFile(prefix + ".node");
diff --git a/applications/utilities/mesh/conversion/vtkUnstructuredToFoam/vtkUnstructuredToFoam.C b/applications/utilities/mesh/conversion/vtkUnstructuredToFoam/vtkUnstructuredToFoam.C
index 377458839f7f0c9daa8b6b79de1b4b6e133f3c6c..cb8f5577349c84fefc0aee28513f5a9af991864f 100644
--- a/applications/utilities/mesh/conversion/vtkUnstructuredToFoam/vtkUnstructuredToFoam.C
+++ b/applications/utilities/mesh/conversion/vtkUnstructuredToFoam/vtkUnstructuredToFoam.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -65,7 +66,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    IFstream mshStream(args[1]);
+    IFstream mshStream(args.get<fileName>(1));
 
     vtkUnstructuredReader reader(runTime, mshStream);
 
diff --git a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
index 00a843e531228bcd7aacfba900ea63ed219a5850..136967f73b6175d3773e0d2526a59dc6de349b44 100644
--- a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
+++ b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -289,7 +289,7 @@ void Foam::conformalVoronoiMesh::insertSurfacePointPairs
         }
     }
 
-    if (foamyHexMeshControls().objOutput() && fName != fileName::null)
+    if (foamyHexMeshControls().objOutput() && !fName.empty())
     {
         DelaunayMeshTools::writeOBJ(time().path()/fName, pts);
     }
@@ -324,7 +324,7 @@ void Foam::conformalVoronoiMesh::insertEdgePointGroups
         }
     }
 
-    if (foamyHexMeshControls().objOutput() && fName != fileName::null)
+    if (foamyHexMeshControls().objOutput() && !fName.empty())
     {
         DelaunayMeshTools::writeOBJ(time().path()/fName, pts);
     }
diff --git a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
index 4a548c89ef1abe7638c82481fe0d8d0c0385a471..14a2f32fd515a053ad7af4ee24dcd74c501104ad 100644
--- a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
+++ b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C
@@ -894,15 +894,8 @@ void Foam::conformalVoronoiMesh::checkCellSizing()
             const label faceOwner = pMesh.faceOwner()[facei];
             const label faceNeighbour = pMesh.faceNeighbour()[facei];
 
-            if (!cellsToResizeMap.found(faceOwner))
-            {
-                cellsToResizeMap.insert(faceOwner);
-            }
-
-            if (!cellsToResizeMap.found(faceNeighbour))
-            {
-                cellsToResizeMap.insert(faceNeighbour);
-            }
+            cellsToResizeMap.insert(faceOwner);
+            cellsToResizeMap.insert(faceNeighbour);
         }
 
         cellsToResizeMap += protrudingCells;
diff --git a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
index 4affaa57581ca7225b987eb0c17e9a80873b0c0b..fa734f5a18fac4ffa2e225a7ad56f7dc077c4123 100644
--- a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
+++ b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshConformToSurface.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -727,11 +727,7 @@ Foam::label Foam::conformalVoronoiMesh::synchroniseSurfaceTrees
             if (nearest.hit() || nearestEdge.hit())
             {
                 nStoppedInsertion++;
-
-                if (!hits[proci].found(peI))
-                {
-                    hits[proci].insert(peI);
-                }
+                hits[proci].insert(peI);
             }
         }
     }
@@ -822,11 +818,7 @@ Foam::label Foam::conformalVoronoiMesh::synchroniseEdgeTrees
 //                    << endl;
 
                 nStoppedInsertion++;
-
-                if (!hits[proci].found(peI))
-                {
-                    hits[proci].insert(peI);
-                }
+                hits[proci].insert(peI);
             }
         }
     }
diff --git a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
index caac10dbc87f6f346817150815d7826cbc7a344f..9d71ce889cf7ef45f1f94548cfaef5a3e6977d68 100644
--- a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
+++ b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2015 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -467,15 +468,8 @@ inline Foam::List<Foam::label> Foam::conformalVoronoiMesh::processorsAttached
 
     forAll(c1Procs, aPI)
     {
-        if (!procsAttached.found(c1Procs[aPI]))
-        {
-            procsAttached.append(c1Procs[aPI]);
-        }
-
-        if (!procsAttached.found(c2Procs[aPI]))
-        {
-            procsAttached.append(c2Procs[aPI]);
-        }
+        procsAttached.appendUniq(c1Procs[aPI]);
+        procsAttached.appendUniq(c2Procs[aPI]);
     }
 
     return List<label>(procsAttached);
diff --git a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/pointPairs/pointPairs.H b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/pointPairs/pointPairs.H
index d56e4af09d44a0d2c6de56b2397d395a8b0bbcdb..11feda75d47d0e3c6457f46cb124012c30e98064 100644
--- a/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/pointPairs/pointPairs.H
+++ b/applications/utilities/mesh/generation/foamyMesh/conformalVoronoiMesh/pointPairs/pointPairs.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2015 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -55,15 +56,15 @@ namespace Foam
 template<class Triangulation>
 class pointPairs
 :
-    public HashSet<labelPairPair, labelPairPair::Hash<>>
+    public HashSet<labelPairPair>
 {
-    // Private typedefs
+    // Private Typedefs
 
-    typedef HashSet<labelPairPair, labelPairPair::Hash<>> StorageContainer;
+    typedef HashSet<labelPairPair> StorageContainer;
     typedef typename Triangulation::Vertex_handle Vertex_handle;
 
 
-    // Private data
+    // Private Data
 
         const Triangulation& triangulation_;
 
diff --git a/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify.C b/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify.C
index 699c0f1a7d82f87ac9b77c41b8ed29c87850238d..2a355d70c1cff74dd8d13b67600c49b972dffce7 100644
--- a/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify.C
+++ b/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -378,7 +378,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const fileName exportName = args[1];
+    const auto exportName = args.get<fileName>(1);
 
     Info<< "Reading surfaces as specified in the foamyHexMeshDict and"
         << " writing a re-sampled surface to " << exportName
diff --git a/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify_non_octree.C b/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify_non_octree.C
index 611bc5279309cd7d6ba2bd3831395f250008883a..1cfd57f9e8c09df3759ffe4b86dff6e99f4429e5 100644
--- a/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify_non_octree.C
+++ b/applications/utilities/mesh/generation/foamyMesh/foamyHexMeshSurfaceSimplify/foamyHexMeshSurfaceSimplify_non_octree.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -64,8 +64,8 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const labelVector n(args.get<labelVector>(1));
-    const fileName exportName = args[2];
+    const auto n = args.get<labelVector>(1);
+    const auto exportName = args.get<fileName>(2);
 
     Info<< "Reading surfaces as specified in the foamyHexMeshDict and"
         << " writing re-sampled " << n << " to " << exportName
diff --git a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/foamyQuadMesh.C b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/foamyQuadMesh.C
index 82f0ab9731e5bc72951ece46fc1064546b616786..919b8120713d24fd37f05fe8f4a111e23fd863db 100644
--- a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/foamyQuadMesh.C
+++ b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/foamyQuadMesh.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -97,7 +98,7 @@ int main(int argc, char *argv[])
 
     if (args.found("pointsFile"))
     {
-        mesh.insertPoints(args["pointsFile"]);
+        mesh.insertPoints(args.get<fileName>("pointsFile"));
     }
     else
     {
diff --git a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.C b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.C
index 445416cc9ea738480951919c3e250725d15e61b5..80ba11fc9c27c2104227685c34a1a660d2d74454 100644
--- a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.C
+++ b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,23 +36,6 @@ namespace Foam
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-void Foam::shortEdgeFilter2D::addRegion
-(
-    const label regionI,
-    DynamicList<label>& bPointRegions
-) const
-{
-    if (bPointRegions.empty())
-    {
-        bPointRegions.append(regionI);
-    }
-    else if (!bPointRegions.found(regionI))
-    {
-        bPointRegions.append(regionI);
-    }
-}
-
-
 void Foam::shortEdgeFilter2D::assignBoundaryPointRegions
 (
     List<DynamicList<label>>& boundaryPointRegions
@@ -61,13 +44,10 @@ void Foam::shortEdgeFilter2D::assignBoundaryPointRegions
     forAllConstIters(mapEdgesRegion_, iter)
     {
         const edge& e = iter.key();
-        const label& regionI = iter();
-
-        const label startI = e.start();
-        const label endI = e.end();
+        const label regi = iter.val();
 
-        addRegion(regionI, boundaryPointRegions[startI]);
-        addRegion(regionI, boundaryPointRegions[endI]);
+        boundaryPointRegions[e.start()].appendUniq(regi);
+        boundaryPointRegions[e.end()].appendUniq(regi);
     }
 }
 
diff --git a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.H b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.H
index 9c0343e5f74a2334da3829cf3ca713a949e4e62f..315e45944452ec108610b5d89b6b0b73e97c127e 100644
--- a/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.H
+++ b/applications/utilities/mesh/generation/foamyMesh/foamyQuadMesh/shortEdgeFilter2D.H
@@ -51,7 +51,7 @@ namespace Foam
 
 class shortEdgeFilter2D
 {
-    // Private data
+    // Private Data
 
         const CV2D& cv2Dmesh_;
 
@@ -72,12 +72,6 @@ class shortEdgeFilter2D
 
     // Private Member Functions
 
-        void addRegion
-        (
-            const label regionI,
-            DynamicList<label>& bPointRegions
-        ) const;
-
         void assignBoundaryPointRegions
         (
             List<DynamicList<label>>& boundaryPointRegions
diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
index b9fedffe615356f19853e3e3b97d221eb6633f91..1218dadddefefd96d95546ffd4d3992b4da0988f 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
@@ -541,7 +541,7 @@ void extractSurface
           ? runTime.globalPath()/outFileName
           : runTime.path()/outFileName
         );
-        globalCasePath.clean();
+        globalCasePath.clean();  // Remove unneeded ".."
 
         Info<< "Writing merged surface to " << globalCasePath << endl;
 
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkTools.C b/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
index e7ab6c2d17f12d55b13e4996f274bb8d22c319ed..ad29e420dc3f8eb32f9915f6a961a1ab90c3948d 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkTools.C
@@ -306,7 +306,7 @@ void Foam::mergeAndWrite
       / mesh.pointsInstance()
       / set.name()
     );
-    outputDir.clean();
+    outputDir.clean();  // Remove unneeded ".."
 
     mergeAndWrite(mesh, writer, set.name(), setPatch, outputDir);
 }
@@ -399,7 +399,7 @@ void Foam::mergeAndWrite
       / mesh.pointsInstance()
       / set.name()
     );
-    outputDir.clean();
+    outputDir.clean();  // Remove unneeded ".."
 
     mergeAndWrite(mesh, writer, set.name(), setPatch, outputDir);
 }
@@ -498,7 +498,7 @@ void Foam::mergeAndWrite
           / mesh.pointsInstance()
           // set.name()
         );
-        outputDir.clean();
+        outputDir.clean();  // Remove unneeded ".."
         mkDir(outputDir);
 
         fileName outputFile(outputDir/writer.getFileName(points, wordList()));
diff --git a/applications/utilities/mesh/manipulation/insideCells/insideCells.C b/applications/utilities/mesh/manipulation/insideCells/insideCells.C
index a3d19eb384fc8dcbe654ad220bc9d38e8b2c0e36..07b4a629d2ee71206889e4d80865ad702c925f15 100644
--- a/applications/utilities/mesh/manipulation/insideCells/insideCells.C
+++ b/applications/utilities/mesh/manipulation/insideCells/insideCells.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -66,8 +66,8 @@ int main(int argc, char *argv[])
     #include "createTime.H"
     #include "createPolyMesh.H"
 
-    const fileName surfName = args[1];
-    const fileName setName  = args[2];
+    const auto surfName = args.get<fileName>(1);
+    const auto setName  = args.get<fileName>(2);
 
     // Read surface
     Info<< "Reading surface from " << surfName << endl;
diff --git a/applications/utilities/mesh/manipulation/mergeMeshes/mergeMeshes.C b/applications/utilities/mesh/manipulation/mergeMeshes/mergeMeshes.C
index 92b38c546b51f541af15cdadd7285866b341b746..5fa4a4f626a0e33661e514da204254b893edebb8 100644
--- a/applications/utilities/mesh/manipulation/mergeMeshes/mergeMeshes.C
+++ b/applications/utilities/mesh/manipulation/mergeMeshes/mergeMeshes.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -45,7 +45,7 @@ using namespace Foam;
 
 void getRootCase(fileName& casePath)
 {
-    casePath.clean();
+    casePath.clean();  // Remove unneeded ".."
 
     if (casePath.empty() || casePath == ".")
     {
@@ -56,7 +56,7 @@ void getRootCase(fileName& casePath)
     {
         // avoid relative cases ending in '..' - makes for very ugly names
         casePath = cwd()/casePath;
-        casePath.clean();
+        casePath.clean();  // Remove unneeded ".."
     }
 }
 
@@ -102,8 +102,8 @@ int main(int argc, char *argv[])
 
     const bool overwrite = args.found("overwrite");
 
-    fileName masterCase = args[1];
-    fileName addCase = args[2];
+    auto masterCase = args.get<fileName>(1);
+    auto addCase = args.get<fileName>(2);
 
     const word masterRegion =
         args.getOrDefault<word>("masterRegion", polyMesh::defaultRegion);
diff --git a/applications/utilities/mesh/manipulation/objToVTK/objToVTK.C b/applications/utilities/mesh/manipulation/objToVTK/objToVTK.C
index 8965219067ce7ddb42e0acc9fabeba1e82daf641..4ff92cf77a8a93a0b9a5c4f87b259c53f2e7f2a1 100644
--- a/applications/utilities/mesh/manipulation/objToVTK/objToVTK.C
+++ b/applications/utilities/mesh/manipulation/objToVTK/objToVTK.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -123,8 +123,8 @@ int main(int argc, char *argv[])
     argList::addArgument("vtk-file", "The output vtk file");
     argList args(argc, argv);
 
-    const fileName objName = args[1];
-    const fileName outName = args[2];
+    const auto objName = args.get<fileName>(1);
+    const auto outName = args.get<fileName>(2);
 
     std::ifstream OBJfile(objName);
 
diff --git a/applications/utilities/mesh/manipulation/polyDualMesh/meshDualiser.C b/applications/utilities/mesh/manipulation/polyDualMesh/meshDualiser.C
index 0abc91fb2842e2795c35cd730592d8f981e81f93..c512857b76c6d1f21eb091d78caee09eb96d5be5 100644
--- a/applications/utilities/mesh/manipulation/polyDualMesh/meshDualiser.C
+++ b/applications/utilities/mesh/manipulation/polyDualMesh/meshDualiser.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -516,9 +516,9 @@ void Foam::meshDualiser::createFacesAroundEdge
             {
                 label startDual = faceToDualPoint_[startFaceLabel];
 
-                if (startDual != -1 && !verts.found(startDual))
+                if (startDual != -1)
                 {
-                    verts.append(startDual);
+                    verts.appendUniq(startDual);
                 }
             }
             break;
diff --git a/applications/utilities/mesh/manipulation/setSet/setSet.C b/applications/utilities/mesh/manipulation/setSet/setSet.C
index 1473bf293fa84e1f855f24b6d8adcbbb31b3fffd..b866b07cb25285fbed2eb3787e5ad54ac1a4dae2 100644
--- a/applications/utilities/mesh/manipulation/setSet/setSet.C
+++ b/applications/utilities/mesh/manipulation/setSet/setSet.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2018 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -793,7 +793,7 @@ int main(int argc, char *argv[])
 
         if (batch)
         {
-            const fileName batchFile = args["batch"];
+            const auto batchFile = args.get<fileName>("batch");
 
             Info<< "Reading commands from file " << batchFile << endl;
 
diff --git a/applications/utilities/mesh/manipulation/subsetMesh/subsetMesh.C b/applications/utilities/mesh/manipulation/subsetMesh/subsetMesh.C
index f20f3f4e68696f28a2f6b4b7925106e3c9cdcca3..d09c36f7a469bb19d7b963f8ced09c99718b623c 100644
--- a/applications/utilities/mesh/manipulation/subsetMesh/subsetMesh.C
+++ b/applications/utilities/mesh/manipulation/subsetMesh/subsetMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -495,7 +495,7 @@ int main(int argc, char *argv[])
     wordRes zoneNames;
     if (useCellZone)
     {
-        List<wordRe> selectionNames = args.getList<wordRe>(1);
+        wordRes selectionNames(args.getList<wordRe>(1));
         zoneNames.transfer(selectionNames);
 
         Info<< "Using cellZone " << flatOutput(zoneNames) << nl << endl;
diff --git a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C
index 97eeb2ccbaacef36aed7adfb4852804aa634dabb..fddf9b20c2f28e3273c7e1d5c27e12e12e52a0b3 100644
--- a/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C
+++ b/applications/utilities/miscellaneous/foamDictionary/foamDictionary.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016-2017 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -373,7 +373,7 @@ int main(int argc, char *argv[])
         }
     }
 
-    const fileName dictFileName(args[1]);
+    const auto dictFileName = args.get<fileName>(1);
 
     autoPtr<IFstream> dictFile(new IFstream(dictFileName));
     if (!dictFile().good())
diff --git a/applications/utilities/miscellaneous/foamHasLibrary/foamHasLibrary.C b/applications/utilities/miscellaneous/foamHasLibrary/foamHasLibrary.C
index 07243cac4007caa2fb3e6176c5ad2885b2ae99fc..398b2397163e29f8adb486f9ffaccde7a1c628e4 100644
--- a/applications/utilities/miscellaneous/foamHasLibrary/foamHasLibrary.C
+++ b/applications/utilities/miscellaneous/foamHasLibrary/foamHasLibrary.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -105,7 +105,7 @@ int main(int argc, char *argv[])
 
     for (int argi = 1; argi < args.size(); ++argi)
     {
-        const fileName libName(fileName::validate(args[argi]));
+        const auto libName = args.get<fileName>(argi);  // with validate
 
         if (libName.empty())
         {
diff --git a/applications/utilities/postProcessing/dataConversion/smapToFoam/smapToFoam.C b/applications/utilities/postProcessing/dataConversion/smapToFoam/smapToFoam.C
index af91270a9e59fc055c131ac49e495020a8fd59ae..49bb2b1239a5d9cc87a0e7f3d17bd041ebdee032 100644
--- a/applications/utilities/postProcessing/dataConversion/smapToFoam/smapToFoam.C
+++ b/applications/utilities/postProcessing/dataConversion/smapToFoam/smapToFoam.C
@@ -77,7 +77,7 @@ int main(int argc, char *argv[])
 
     #include "createNamedMesh.H"
 
-    IFstream smapFile(args[1]);
+    IFstream smapFile(args.get<fileName>(1));
 
     if (!smapFile.good())
     {
diff --git a/applications/utilities/postProcessing/lumped/lumpedPointMovement/lumpedPointMovement.C b/applications/utilities/postProcessing/lumped/lumpedPointMovement/lumpedPointMovement.C
index 1925d5d56992281c3d4cbc5b8ddb8cdc9dc66671..ef5597dcae0b4988a37ff76d5eb19948db43e38f 100644
--- a/applications/utilities/postProcessing/lumped/lumpedPointMovement/lumpedPointMovement.C
+++ b/applications/utilities/postProcessing/lumped/lumpedPointMovement/lumpedPointMovement.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
 
     args.readIfPresent("visual-length", lumpedPointState::visLength);
 
-    const fileName responseFile(args[1]);
+    const auto responseFile = args.get<fileName>(1);
 
     // ----------------------------------------------------------------------
     // Slave mode
diff --git a/applications/utilities/preProcessing/mapFields/mapFields.C b/applications/utilities/preProcessing/mapFields/mapFields.C
index 9e7b984745e6f24548d141d4df84f6a3ee893b5e..78645b20b4a14272be1dd30d212bebba81879210 100644
--- a/applications/utilities/preProcessing/mapFields/mapFields.C
+++ b/applications/utilities/preProcessing/mapFields/mapFields.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -286,7 +286,7 @@ int main(int argc, char *argv[])
     fileName rootDirTarget(args.rootPath());
     fileName caseDirTarget(args.globalCaseName());
 
-    fileName casePath = args[1];
+    const auto casePath = args.get<fileName>(1);
     const fileName rootDirSource = casePath.path().toAbsolute();
     const fileName caseDirSource = casePath.name();
 
diff --git a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
index 7c6435f1e424cba6338ecc46f94ce734bc2aae68..025c400b2675b3eabd584333b9d507d7e530da08 100644
--- a/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
+++ b/applications/utilities/preProcessing/mapFieldsPar/mapFieldsPar.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2018 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -214,7 +214,7 @@ int main(int argc, char *argv[])
     fileName rootDirTarget(args.rootPath());
     fileName caseDirTarget(args.globalCaseName());
 
-    const fileName casePath = args[1];
+    const auto casePath = args.get<fileName>(1);
     const fileName rootDirSource = casePath.path();
     const fileName caseDirSource = casePath.name();
 
diff --git a/applications/utilities/surface/surfaceAdd/surfaceAdd.C b/applications/utilities/surface/surfaceAdd/surfaceAdd.C
index 6195dd3c6878aa6f0ccd539f08a142f3bfd4a26c..0db4feefd1f46e579204accf40405a44e94bb0a1 100644
--- a/applications/utilities/surface/surfaceAdd/surfaceAdd.C
+++ b/applications/utilities/surface/surfaceAdd/surfaceAdd.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -83,9 +83,9 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName inFileName1 = args[1];
-    const fileName inFileName2 = args[2];
-    const fileName outFileName = args[3];
+    const auto inFileName1 = args.get<fileName>(1);
+    const auto inFileName2 = args.get<fileName>(2);
+    const auto outFileName = args.get<fileName>(3);
 
     const bool addPoint     = args.found("points");
     const bool mergeRegions = args.found("mergeRegions");
@@ -99,7 +99,7 @@ int main(int argc, char *argv[])
             << nl << endl;
 
         Info<< "Surface  : " << inFileName1<< nl
-            << "Points   : " << args["points"] << nl
+            << "Points   : " << args.get<fileName>("points") << nl
             << "Writing  : " << outFileName << nl << endl;
     }
     else
@@ -145,7 +145,7 @@ int main(int argc, char *argv[])
 
     if (addPoint)
     {
-        IFstream pointsFile(args["points"]);
+        IFstream pointsFile(args.get<fileName>("points"));
         const pointField extraPoints(pointsFile);
 
         Info<< "Additional Points:" << extraPoints.size() << endl;
diff --git a/applications/utilities/surface/surfaceCheck/surfaceCheck.C b/applications/utilities/surface/surfaceCheck/surfaceCheck.C
index 87a04e1e2166170d4298fb5b96182c3ebdc5b3d4..4d3b9e39feca5ecd61813c91a01c4a6bd8767763 100644
--- a/applications/utilities/surface/surfaceCheck/surfaceCheck.C
+++ b/applications/utilities/surface/surfaceCheck/surfaceCheck.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -335,7 +335,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName surfFileName = args[1];
+    const auto surfFileName = args.get<fileName>(1);
     const bool checkSelfIntersect = args.found("checkSelfIntersection");
     const bool splitNonManifold = args.found("splitNonManifold");
     const label outputThreshold =
diff --git a/applications/utilities/surface/surfaceClean/surfaceClean.C b/applications/utilities/surface/surfaceClean/surfaceClean.C
index 98c850c6950da2e1f8c9d6e1a67e7a3b7e09ecff..f1e62642318fe8e1ffe7c99301cd1ad6b54fabba 100644
--- a/applications/utilities/surface/surfaceClean/surfaceClean.C
+++ b/applications/utilities/surface/surfaceClean/surfaceClean.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -82,10 +82,10 @@ int main(int argc, char *argv[])
     );
     argList args(argc, argv);
 
-    const fileName inFileName = args[1];
-    const scalar minLen = args.get<scalar>(2);
-    const scalar minQuality = args.get<scalar>(3);
-    const fileName outFileName = args[4];
+    const auto inFileName = args.get<fileName>(1);
+    const auto minLen = args.get<scalar>(2);
+    const auto minQuality = args.get<scalar>(3);
+    const auto outFileName = args.get<fileName>(4);
 
     Info<< "Reading surface " << inFileName << nl
         << "Collapsing all triangles with" << nl
diff --git a/applications/utilities/surface/surfaceCoarsen/surfaceCoarsen.C b/applications/utilities/surface/surfaceCoarsen/surfaceCoarsen.C
index 1adad6a7f819ecf0673fd223f0726155274636fb..6db75af3df8b990e4d60dbff2759d101e1e456fe 100644
--- a/applications/utilities/surface/surfaceCoarsen/surfaceCoarsen.C
+++ b/applications/utilities/surface/surfaceCoarsen/surfaceCoarsen.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -92,9 +92,9 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName inFileName = args[1];
-    const scalar reduction = args.get<scalar>(2);
-    const fileName outFileName = args[3];
+    const auto inFileName = args.get<fileName>(1);
+    const auto reduction = args.get<scalar>(2);
+    const auto outFileName = args.get<fileName>(3);
 
     if (reduction <= 0 || reduction > 1)
     {
diff --git a/applications/utilities/surface/surfaceConvert/surfaceConvert.C b/applications/utilities/surface/surfaceConvert/surfaceConvert.C
index f65f316f8d2a9ca64a134a5599038f9180f47e0c..3530da1ae181d8f7feaa2e7821ee533dc6a8334b 100644
--- a/applications/utilities/surface/surfaceConvert/surfaceConvert.C
+++ b/applications/utilities/surface/surfaceConvert/surfaceConvert.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -159,8 +159,8 @@ int main(int argc, char *argv[])
         }
     }
 
-    const fileName importName(args[1]);
-    const fileName exportName(args[2]);
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.get<fileName>(2);
 
     if (importName == exportName)
     {
diff --git a/applications/utilities/surface/surfaceFeatureConvert/surfaceFeatureConvert.C b/applications/utilities/surface/surfaceFeatureConvert/surfaceFeatureConvert.C
index 087b0eed278c2e1bf8d1d1e578a19de3484808de..03c64a2f390b0246388cabc0feaf453e9af3b1e9 100644
--- a/applications/utilities/surface/surfaceFeatureConvert/surfaceFeatureConvert.C
+++ b/applications/utilities/surface/surfaceFeatureConvert/surfaceFeatureConvert.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -106,8 +106,8 @@ int main(int argc, char *argv[])
     argList args(argc, argv);
     Time runTime(args.rootPath(), args.caseName());
 
-    const fileName importName(args[1]);
-    const fileName exportName(args[2]);
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.get<fileName>(2);
 
     // Disable inplace editing
     if (importName == exportName)
diff --git a/applications/utilities/surface/surfaceFind/surfaceFind.C b/applications/utilities/surface/surfaceFind/surfaceFind.C
index e23ce768478ec95df61d8c7db124af56edffd716..1f69a792e6993e391b313b81b04df82e2839b5a1 100644
--- a/applications/utilities/surface/surfaceFind/surfaceFind.C
+++ b/applications/utilities/surface/surfaceFind/surfaceFind.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -72,7 +72,7 @@ int main(int argc, char *argv[])
 
 
     Info<< "Reading surf ..." << endl;
-    meshedSurface surf1(args[1]);
+    meshedSurface surf1(args.get<fileName>(1));
 
     //
     // Nearest vertex
diff --git a/applications/utilities/surface/surfaceInertia/surfaceInertia.C b/applications/utilities/surface/surfaceInertia/surfaceInertia.C
index 7147ce4656edbcd339ae08262a1ffdf864b61126..2b5120978843c4882df69b4f804cabbf95485a11 100644
--- a/applications/utilities/surface/surfaceInertia/surfaceInertia.C
+++ b/applications/utilities/surface/surfaceInertia/surfaceInertia.C
@@ -6,7 +6,7 @@
     \\/      M anipulation   |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -88,7 +88,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName surfFileName = args[1];
+    const auto surfFileName = args.get<fileName>(1);
     const scalar density = args.getOrDefault<scalar>("density", 1);
 
     vector refPt = Zero;
diff --git a/applications/utilities/surface/surfaceInflate/surfaceInflate.C b/applications/utilities/surface/surfaceInflate/surfaceInflate.C
index 333533f2fa927851cbb861f8211a7bd5b46c7e3c..a2d52a992d08a9980dbd2f3e60024a0fcb670fb3 100644
--- a/applications/utilities/surface/surfaceInflate/surfaceInflate.C
+++ b/applications/utilities/surface/surfaceInflate/surfaceInflate.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2015 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -609,12 +609,12 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const word inputName(args[1]);
-    const scalar distance(args.get<scalar>(2));
-    const scalar extendFactor(args.get<scalar>(3));
+    const auto inputName = args.get<word>(1);
+    const auto distance = args.get<scalar>(2);
+    const auto extendFactor = args.get<scalar>(3);
     const bool checkSelfIntersect = args.found("checkSelfIntersection");
-    const label nSmooth = args.getOrDefault<label>("nSmooth", 10);
-    const scalar featureAngle = args.getOrDefault<scalar>("featureAngle", 180);
+    const auto nSmooth = args.getOrDefault<label>("nSmooth", 10);
+    const auto featureAngle = args.getOrDefault<scalar>("featureAngle", 180);
     const bool debug = args.found("debug");
 
 
diff --git a/applications/utilities/surface/surfaceLambdaMuSmooth/surfaceLambdaMuSmooth.C b/applications/utilities/surface/surfaceLambdaMuSmooth/surfaceLambdaMuSmooth.C
index a3b99deebc8e685ea8f180342300fa4c91971765..cde4e69ae59ebd074e20a8c875b170b3e44acd41 100644
--- a/applications/utilities/surface/surfaceLambdaMuSmooth/surfaceLambdaMuSmooth.C
+++ b/applications/utilities/surface/surfaceLambdaMuSmooth/surfaceLambdaMuSmooth.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -156,11 +156,11 @@ int main(int argc, char *argv[])
     );
     argList args(argc, argv);
 
-    const fileName surfFileName = args[1];
-    const scalar lambda = args.get<scalar>(2);
-    const scalar mu = args.get<scalar>(3);
-    const label  iters = args.get<label>(4);
-    const fileName outFileName = args[5];
+    const auto surfFileName = args.get<fileName>(1);
+    const auto lambda = args.get<scalar>(2);
+    const auto mu = args.get<scalar>(3);
+    const auto iters = args.get<label>(4);
+    const auto outFileName = args.get<fileName>(5);
 
     if (lambda < 0 || lambda > 1)
     {
@@ -192,7 +192,7 @@ int main(int argc, char *argv[])
 
     if (args.found("featureFile"))
     {
-        const fileName featureFileName(args["featureFile"]);
+        const auto featureFileName = args.get<fileName>("featureFile");
         Info<< "Reading features from " << featureFileName << " ..." << endl;
 
         edgeMesh feMesh(featureFileName);
diff --git a/applications/utilities/surface/surfaceMeshConvert/surfaceMeshConvert.C b/applications/utilities/surface/surfaceMeshConvert/surfaceMeshConvert.C
index b2257f39f1d557716793a081dbe62f8c5c38283e..53cfa9651134bbc4de13eae9ca2e0622a294fa0c 100644
--- a/applications/utilities/surface/surfaceMeshConvert/surfaceMeshConvert.C
+++ b/applications/utilities/surface/surfaceMeshConvert/surfaceMeshConvert.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -181,8 +181,8 @@ int main(int argc, char *argv[])
     argList args(argc, argv);
     Time runTime(args.rootPath(), args.caseName());
 
-    const fileName importName(args[1]);
-    const fileName exportName(args[2]);
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.get<fileName>(2);
 
     if (importName == exportName)
     {
diff --git a/applications/utilities/surface/surfaceMeshExport/surfaceMeshExport.C b/applications/utilities/surface/surfaceMeshExport/surfaceMeshExport.C
index d80437d35a3a3e36d3fd088d5c94d5cedcaf1a1a..8469ed0b337b662d18a7a101d6fc45b3052b52db 100644
--- a/applications/utilities/surface/surfaceMeshExport/surfaceMeshExport.C
+++ b/applications/utilities/surface/surfaceMeshExport/surfaceMeshExport.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -155,8 +155,8 @@ int main(int argc, char *argv[])
     argList args(argc, argv);
     Time runTime(args.rootPath(), args.caseName());
 
-    const fileName exportName(args[1]);
-    const word importName(args.getOrDefault<word>("name", "default"));
+    const auto exportName = args.get<fileName>(1);
+    const auto importName = args.getOrDefault<word>("name", "default");
 
     const word writeFileType
     (
diff --git a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
index 38b364c949dcc3a0ac808c3a0ecda8fefcb0b8f8..25e6bac8a94094b13501e228edcf422d4787d8bc 100644
--- a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
+++ b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -156,7 +156,7 @@ int main(int argc, char *argv[])
     #include "setRootCase.H"
     #include "createTime.H"
 
-    const fileName userOutFileName(args[1]);
+    const auto userOutFileName = args.get<fileName>(1);
 
     if (!userOutFileName.hasExt())
     {
diff --git a/applications/utilities/surface/surfaceMeshImport/surfaceMeshImport.C b/applications/utilities/surface/surfaceMeshImport/surfaceMeshImport.C
index 469c835a6ecad3851055144c5340ca0cc5ef7477..ba7ca665d2ad3c0de0d06ec5b70946827b4d7867 100644
--- a/applications/utilities/surface/surfaceMeshImport/surfaceMeshImport.C
+++ b/applications/utilities/surface/surfaceMeshImport/surfaceMeshImport.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -167,8 +167,8 @@ int main(int argc, char *argv[])
     }
 
 
-    const fileName importName(args[1]);
-    const word exportName(args.getOrDefault<word>("name", "default"));
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.getOrDefault<word>("name", "default");
 
     const word readFileType
     (
diff --git a/applications/utilities/surface/surfaceMeshInfo/surfaceMeshInfo.C b/applications/utilities/surface/surfaceMeshInfo/surfaceMeshInfo.C
index 6409990bfd85755114ded3b3f08f687d5f214d90..f270125da1b84df5fccb52a783cc83d9237a65b6 100644
--- a/applications/utilities/surface/surfaceMeshInfo/surfaceMeshInfo.C
+++ b/applications/utilities/surface/surfaceMeshInfo/surfaceMeshInfo.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -107,7 +107,7 @@ int main(int argc, char *argv[])
     argList args(argc, argv);
     Time runTime(args.rootPath(), args.caseName());
 
-    const fileName importName = args[1];
+    const auto importName = args.get<fileName>(1);
 
     // check that reading is supported
     if (!UnsortedMeshedSurface<face>::canRead(importName, true))
diff --git a/applications/utilities/surface/surfaceOrient/surfaceOrient.C b/applications/utilities/surface/surfaceOrient/surfaceOrient.C
index 4158dc04d95d5bf2815c13691bd83ac2e4679592..60750eee2961af17a41196c3a4d67699c3a60b32 100644
--- a/applications/utilities/surface/surfaceOrient/surfaceOrient.C
+++ b/applications/utilities/surface/surfaceOrient/surfaceOrient.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -76,9 +76,9 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName surfFileName = args[1];
-    const point visiblePoint    = args.get<point>(2);
-    const fileName outFileName  = args[3];
+    const auto surfFileName = args.get<fileName>(1);
+    const auto visiblePoint = args.get<point>(2);
+    const auto outFileName  = args.get<fileName>(3);
 
     const bool orientInside = args.found("inside");
     const bool usePierceTest = args.found("usePierceTest");
diff --git a/applications/utilities/surface/surfacePointMerge/surfacePointMerge.C b/applications/utilities/surface/surfacePointMerge/surfacePointMerge.C
index 954a5064d9d433b4c2de6efeccf6a9c9cd6963d4..f4057978c0f3c70cc1e44c73f18794dc51549b20 100644
--- a/applications/utilities/surface/surfacePointMerge/surfacePointMerge.C
+++ b/applications/utilities/surface/surfacePointMerge/surfacePointMerge.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -67,9 +67,9 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName surfFileName = args[1];
-    const scalar   mergeTol = args.get<scalar>(2);
-    const fileName outFileName = args[3];
+    const auto surfFileName = args.get<fileName>(1);
+    const auto mergeTol = args.get<scalar>(2);
+    const auto outFileName = args.get<fileName>(3);
 
     const scalar scaling = args.getOrDefault<scalar>("scale", -1);
 
diff --git a/applications/utilities/surface/surfaceRedistributePar/surfaceRedistributePar.C b/applications/utilities/surface/surfaceRedistributePar/surfaceRedistributePar.C
index b50d924bd180435535d1e8487e4fc64183243a92..72e79526a5dc3c17ac1223ffcfa6d89794f88439 100644
--- a/applications/utilities/surface/surfaceRedistributePar/surfaceRedistributePar.C
+++ b/applications/utilities/surface/surfaceRedistributePar/surfaceRedistributePar.C
@@ -124,8 +124,8 @@ int main(int argc, char *argv[])
     #include "createTime.H"
     runTime.functionObjects().off();
 
-    const fileName surfFileName = args[1];
-    const word distTypeName = args[2];
+    const auto surfFileName = args.get<fileName>(1);
+    const auto distTypeName = args.get<word>(2);
     const label distType =
         distributedTriSurfaceMesh::distributionTypeNames_[distTypeName];
 
diff --git a/applications/utilities/surface/surfaceRefineRedGreen/surfaceRefineRedGreen.C b/applications/utilities/surface/surfaceRefineRedGreen/surfaceRefineRedGreen.C
index b92418e1d43d08c9ce0d29286b041851767c71db..b1b51f1a19c5fc27d996d6527234d4171c0ee433 100644
--- a/applications/utilities/surface/surfaceRefineRedGreen/surfaceRefineRedGreen.C
+++ b/applications/utilities/surface/surfaceRefineRedGreen/surfaceRefineRedGreen.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -72,8 +72,8 @@ int main(int argc, char *argv[])
     );
     argList args(argc, argv);
 
-    const fileName surfFileName(args[1]);
-    const fileName outFileName(args[2]);
+    const auto surfFileName = args.get<fileName>(1);
+    const auto outFileName = args.get<fileName>(2);
 
     Info<< "Reading surface from " << surfFileName << " ..." << endl;
 
diff --git a/applications/utilities/surface/surfaceSplitByPatch/surfaceSplitByPatch.C b/applications/utilities/surface/surfaceSplitByPatch/surfaceSplitByPatch.C
index 65649dfd8a5fee7167a9b011416f0680b5ced2f2..940c15848f35fa8b0b0af899828eff50d2559d10 100644
--- a/applications/utilities/surface/surfaceSplitByPatch/surfaceSplitByPatch.C
+++ b/applications/utilities/surface/surfaceSplitByPatch/surfaceSplitByPatch.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -89,7 +89,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName surfName = args[1];
+    const auto surfName = args.get<fileName>(1);
 
     const fileName surfBase(surfName.lessExt());
 
diff --git a/applications/utilities/surface/surfaceSplitByTopology/surfaceSplitByTopology.C b/applications/utilities/surface/surfaceSplitByTopology/surfaceSplitByTopology.C
index 7afa04af6cc8432d961b49ac166de6fcdcaee130..66f8b117468d103decb9d344074411652b8323e5 100644
--- a/applications/utilities/surface/surfaceSplitByTopology/surfaceSplitByTopology.C
+++ b/applications/utilities/surface/surfaceSplitByTopology/surfaceSplitByTopology.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -59,12 +60,12 @@ int main(int argc, char *argv[])
     argList::addArgument("output", "The output surface file");
     argList args(argc, argv);
 
-    fileName surfFileName(args[1]);
+    const auto surfFileName = args.get<fileName>(1);
     Info<< "Reading surface from " << surfFileName << endl;
 
-    fileName outFileName(args[2]);
-    fileName outFileBaseName = outFileName.lessExt();
-    word outExtension = outFileName.ext();
+    const auto outFileName = args.get<fileName>(2);
+    const fileName outFileBaseName = outFileName.lessExt();
+    const word outExtension = outFileName.ext();
 
     // Load surface
     triSurface surf(surfFileName);
diff --git a/applications/utilities/surface/surfaceSplitNonManifolds/surfaceSplitNonManifolds.C b/applications/utilities/surface/surfaceSplitNonManifolds/surfaceSplitNonManifolds.C
index 8c8cd23c8b72764e1904eecb400674dc51b88023..978af67fae7e3bbbb5685d82c430a2fe310c9ed4 100644
--- a/applications/utilities/surface/surfaceSplitNonManifolds/surfaceSplitNonManifolds.C
+++ b/applications/utilities/surface/surfaceSplitNonManifolds/surfaceSplitNonManifolds.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -691,8 +691,8 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName inSurfName  = args[1];
-    const fileName outSurfName = args[2];
+    const auto inSurfName  = args.get<fileName>(1);
+    const auto outSurfName = args.get<fileName>(2);
     const bool debug = args.found("debug");
 
     Info<< "Reading surface from " << inSurfName << endl;
diff --git a/applications/utilities/surface/surfaceSubset/surfaceSubset.C b/applications/utilities/surface/surfaceSubset/surfaceSubset.C
index 04031d3f046e71539425bd55f6e972d524195cf3..0f7b60e154fed6d1ff7dda55071744c58b57b0cd 100644
--- a/applications/utilities/surface/surfaceSubset/surfaceSubset.C
+++ b/applications/utilities/surface/surfaceSubset/surfaceSubset.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -67,15 +67,13 @@ int main(int argc, char *argv[])
     argList args(argc, argv);
 
     Info<< "Reading dictionary " << args[1] << " ..." << endl;
-    IFstream dictFile(args[1]);
+    IFstream dictFile(args.get<fileName>(1));
     dictionary meshSubsetDict(dictFile);
 
     Info<< "Reading surface " << args[2] << " ..." << endl;
+    meshedSurface surf1(args.get<fileName>(2));
 
-    meshedSurface surf1(args[2]);
-
-    const fileName outFileName(args[3]);
-
+    const auto outFileName(args.get<fileName>(3));
 
     Info<< "Original:" << endl;
     surf1.writeStats(Info);
@@ -217,7 +215,7 @@ int main(int argc, char *argv[])
     {
         const dictionary& surfDict = meshSubsetDict.subDict("surface");
 
-        const fileName surfName(surfDict.get<fileName>("name"));
+        const auto surfName(surfDict.get<fileName>("name"));
 
         const volumeType::type volType =
         (
diff --git a/applications/utilities/surface/surfaceToPatch/surfaceToPatch.C b/applications/utilities/surface/surfaceToPatch/surfaceToPatch.C
index 8a78994144f8c6234ecfdf832f30726612035110..d4d31c1ca76df69af279ae60050af17c45b7ef93 100644
--- a/applications/utilities/surface/surfaceToPatch/surfaceToPatch.C
+++ b/applications/utilities/surface/surfaceToPatch/surfaceToPatch.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -196,7 +196,7 @@ int main(int argc, char *argv[])
     #include "createTime.H"
     #include "createPolyMesh.H"
 
-    const fileName surfName = args[1];
+    const auto surfName = args.get<fileName>(1);
 
     Info<< "Reading surface from " << surfName << " ..." << endl;
 
diff --git a/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C b/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C
index 85948f878d6aa29ad1b8431c50f446e063332c32..4322a0a530c92150bbd235af9066f440e5e0819a 100644
--- a/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C
+++ b/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -283,8 +283,8 @@ int main(int argc, char *argv[])
         }
     }
 
-    const fileName importName(args[1]);
-    const fileName exportName(args[2]);
+    const auto importName = args.get<fileName>(1);
+    const auto exportName = args.get<fileName>(2);
 
     const word readFileType
     (
diff --git a/applications/utilities/thermophysical/adiabaticFlameT/adiabaticFlameT.C b/applications/utilities/thermophysical/adiabaticFlameT/adiabaticFlameT.C
index 780d4eb6c15fce02e902297abf5beac394676b65..220a0da6ddbac025d5ab37ec8636299996be7a23 100644
--- a/applications/utilities/thermophysical/adiabaticFlameT/adiabaticFlameT.C
+++ b/applications/utilities/thermophysical/adiabaticFlameT/adiabaticFlameT.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -70,7 +71,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName controlFileName = args[1];
+    const auto controlFileName = args.get<fileName>(1);
 
     // Construct control dictionary
     IFstream controlFile(controlFileName);
diff --git a/applications/utilities/thermophysical/chemkinToFoam/chemkinToFoam.C b/applications/utilities/thermophysical/chemkinToFoam/chemkinToFoam.C
index 2508069f3936c43bb9ca883695e76c827b0d341e..e248c45c979cad3911aed2aa86ad32e67ef0f425 100644
--- a/applications/utilities/thermophysical/chemkinToFoam/chemkinToFoam.C
+++ b/applications/utilities/thermophysical/chemkinToFoam/chemkinToFoam.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -76,11 +77,18 @@ int main(int argc, char *argv[])
 
     speciesTable species;
 
-    chemkinReader cr(species, args[1], args[3], args[2], newFormat);
+    chemkinReader cr
+    (
+        species,
+        args.get<fileName>(1),  // chemkin fileName
+        args.get<fileName>(3),  // thermo fileName
+        args.get<fileName>(2),  // transport fileName
+        newFormat
+    );
 
     {
         // output: reactions file
-        OFstream reactionsFile(args[4]);
+        OFstream reactionsFile(args.get<fileName>(4));
 
         reactionsFile.writeEntry("elements", cr.elementNames()) << nl;
         reactionsFile.writeEntry("species", cr.species()) << nl;
@@ -113,7 +121,7 @@ int main(int argc, char *argv[])
 
     // output: thermo file
 
-    thermoDict.write(OFstream(args[5])(), false);
+    thermoDict.write(OFstream(args.get<fileName>(5))(), false);
 
 
     Info<< "End\n" << endl;
diff --git a/applications/utilities/thermophysical/equilibriumFlameT/equilibriumFlameT.C b/applications/utilities/thermophysical/equilibriumFlameT/equilibriumFlameT.C
index 8cdbe5060e14972a5544012d7d8e96586fc2d3b0..a6f2e4fb63fb8476784bf4338a36600d5fcfe8fb 100644
--- a/applications/utilities/thermophysical/equilibriumFlameT/equilibriumFlameT.C
+++ b/applications/utilities/thermophysical/equilibriumFlameT/equilibriumFlameT.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -73,7 +74,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName controlFileName = args[1];
+    const auto controlFileName = args.get<fileName>(1);
 
     // Construct control dictionary
     IFstream controlFile(controlFileName);
diff --git a/applications/utilities/thermophysical/mixtureAdiabaticFlameT/mixtureAdiabaticFlameT.C b/applications/utilities/thermophysical/mixtureAdiabaticFlameT/mixtureAdiabaticFlameT.C
index 5d2c3386863bd83e30cc55d925198831c41475e9..c0580d03b9a4cd129346691ce35bed749ee8a404 100644
--- a/applications/utilities/thermophysical/mixtureAdiabaticFlameT/mixtureAdiabaticFlameT.C
+++ b/applications/utilities/thermophysical/mixtureAdiabaticFlameT/mixtureAdiabaticFlameT.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -69,7 +70,7 @@ int main(int argc, char *argv[])
 
     argList args(argc, argv);
 
-    const fileName controlFileName(args[1]);
+    const auto controlFileName = args.get<fileName>(1);
 
     // Construct control dictionary
     IFstream controlFile(controlFileName);
diff --git a/etc/config.csh/mpi b/etc/config.csh/mpi
index 13f56b64017de57332d00b38ce427833f427dca8..83cc770c7efbde61ea6eb9ef9e6df9cf2ac85449 100644
--- a/etc/config.csh/mpi
+++ b/etc/config.csh/mpi
@@ -331,8 +331,7 @@ case INTELMPI*:
         setenv MPI_ARCH_PATH "$MPI_ROOT"
     else if ( "$MPI_ARCH_PATH" != "" ) then
         # MPI_ARCH_PATH: Set I_MPI_ROOT accordingly
-        export I_MPI_ROOT="$MPI_ARCH_PATH"
-
+        setenv I_MPI_ROOT "$MPI_ARCH_PATH"
     endif
 
     if ( -d "$MPI_ARCH_PATH" ) then
diff --git a/src/Allwmake b/src/Allwmake
index a2109cae24d40c5231193c55bd517a433e84942f..e488f9397822ef92eef76442a0ac7560ed00db3f 100755
--- a/src/Allwmake
+++ b/src/Allwmake
@@ -55,6 +55,7 @@ wmake $targetType finiteVolume
 wmake $targetType mesh/blockMesh
 wmake $targetType mesh/extrudeModel  # Requires: blockMesh
 wmake $targetType dynamicMesh  # Requires: extrudeModel
+wmake $targetType genericPatchFields
 
 wmake $targetType parallel/decompose/decompositionMethods
 
@@ -100,8 +101,6 @@ wmake $targetType waveModels
 
 wmake $targetType engine
 
-wmake $targetType genericPatchFields
-
 conversion/Allwmake $targetType $*
 
 functionObjects/Allwmake $targetType $*
diff --git a/src/OSspecific/MSwindows/fileMonitor/fileMonitor.C b/src/OSspecific/MSwindows/fileMonitor/fileMonitor.C
index db66c0c443c6dcbd6a3778452fc060dcd42f5fc6..2a34a5c42dab67a52de48f22ead3026c6cfab014 100644
--- a/src/OSspecific/MSwindows/fileMonitor/fileMonitor.C
+++ b/src/OSspecific/MSwindows/fileMonitor/fileMonitor.C
@@ -498,10 +498,8 @@ bool Foam::fileMonitor::removeWatch(const label watchFd)
             << watchFile_[watchFd] << endl;
     }
 
-    if (!freeWatchFds_.found(watchFd))
-    {
-        freeWatchFds_.append(watchFd);
-    }
+    freeWatchFds_.appendUniq(watchFd);
+
     return watcher_->removeWatch(watchFd);
 }
 
diff --git a/src/OSspecific/POSIX/fileMonitor/fileMonitor.C b/src/OSspecific/POSIX/fileMonitor/fileMonitor.C
index 19af5b424399f6553cfceca30eff447415b61ee6..4f9a95b450783df9b7c5afbfe7e960be866b033c 100644
--- a/src/OSspecific/POSIX/fileMonitor/fileMonitor.C
+++ b/src/OSspecific/POSIX/fileMonitor/fileMonitor.C
@@ -494,10 +494,8 @@ bool Foam::fileMonitor::removeWatch(const label watchFd)
             << watchFile_[watchFd] << endl;
     }
 
-    if (!freeWatchFds_.found(watchFd))
-    {
-        freeWatchFds_.append(watchFd);
-    }
+    freeWatchFds_.appendUniq(watchFd);
+
     return watcher_->removeWatch(watchFd);
 }
 
diff --git a/src/OSspecific/POSIX/regExp/regExpPosix.C b/src/OSspecific/POSIX/regExp/regExpPosix.C
index d3669f3ba5da2f2ec5c29c434ceeb2f01032b2b6..dc05361b3c9e6d12b10205085ccee27b87747f53 100644
--- a/src/OSspecific/POSIX/regExp/regExpPosix.C
+++ b/src/OSspecific/POSIX/regExp/regExpPosix.C
@@ -40,7 +40,7 @@ int Foam::regExpPosix::grammar(0);
 namespace
 {
 
-// Verify that the entire len was matched
+// Matched entire length
 static inline bool fullMatch(const regmatch_t& m, const regoff_t len)
 {
     return (m.rm_so == 0 && m.rm_eo == len);
diff --git a/src/OSspecific/POSIX/regExp/regExpPosix.H b/src/OSspecific/POSIX/regExp/regExpPosix.H
index ea34fa8b8c57d40bef72ff1dcbf4cc55323f75fb..74e5854af3b44466ddbccb1426fbf58ce069ac07 100644
--- a/src/OSspecific/POSIX/regExp/regExpPosix.H
+++ b/src/OSspecific/POSIX/regExp/regExpPosix.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -57,8 +57,8 @@ SourceFiles
 #ifndef regExpPosix_H
 #define regExpPosix_H
 
+#include "regExpCxx.H"
 #include <regex.h>
-#include <string>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -66,8 +66,7 @@ namespace Foam
 {
 
 // Forward Declarations
-template<class String> class SubStrings;
-
+template<class StringType> class SubStrings;
 
 /*---------------------------------------------------------------------------*\
                          Class regExpPosix Declaration
@@ -77,7 +76,7 @@ class regExpPosix
 {
     // Private Data
 
-        //- Precompiled regular expression
+        //- Compiled regular expression
         regex_t* preg_;
 
 public:
@@ -96,39 +95,53 @@ public:
 
     // Static Member Functions
 
-        //- Test if character appears to be a regular expression meta-character
-        //  \return true if character is one of the following:
-        //  - any character: '.' \n
-        //  - quantifiers:  '*', '+', '?' \n
-        //  - grouping: '(', '|', ')' \n
-        //  - range: '[', ']' \n
-        //
-        // \note The presence of '{', '}' regex bounds is not considered
-        inline static bool meta(char c);
+        //- Test if character is a regex meta-character
+        inline static bool is_meta(const char c) noexcept
+        {
+            return regExpCxx::is_meta(c);
+        }
+
+        //- Test if string contains any (unquoted) meta-characters
+        inline static bool is_meta
+        (
+            const std::string& str,
+            const char quote = '\\'
+        )
+        {
+            return regExpCxx::is_meta(str, quote);
+        }
+
+
+    // Public Classes
+
+        //- Functor wrapper for testing meta-characters
+        using meta = regExpCxx::meta;
 
 
     // Constructors
 
-        //- Construct null
-        inline regExpPosix();
+        //- Default construct
+        inline regExpPosix() noexcept;
 
         //- Copy construct - disallowed
         regExpPosix(const regExpPosix&) = delete;
 
         //- Move construct
-        inline regExpPosix(regExpPosix&& rgx);
-
-        //- Construct from character array
-        inline explicit regExpPosix(const char* pattern);
-
-        //- Construct from string
-        inline explicit regExpPosix(const std::string& pattern);
+        inline regExpPosix(regExpPosix&& rgx) noexcept;
 
         //- Construct from character array, optionally ignore case
-        inline regExpPosix(const char* pattern, bool ignoreCase);
+        inline explicit regExpPosix
+        (
+            const char* pattern,
+            const bool ignoreCase = false
+        );
 
         //- Construct from string, optionally ignore case
-        inline regExpPosix(const std::string& pattern, bool ignoreCase);
+        inline explicit regExpPosix
+        (
+            const std::string& pattern,
+            const bool ignoreCase = false
+        );
 
 
     //- Destructor
diff --git a/src/OSspecific/POSIX/regExp/regExpPosixI.H b/src/OSspecific/POSIX/regExp/regExpPosixI.H
index 532d7e688c78fefb1a323ca6b08c5720ea91a789..d0f7bf69bc69aded1bea42392299833782a78a0d 100644
--- a/src/OSspecific/POSIX/regExp/regExpPosixI.H
+++ b/src/OSspecific/POSIX/regExp/regExpPosixI.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,45 +27,27 @@ License
 
 #include <algorithm>
 
-// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
-
-inline bool Foam::regExpPosix::meta(char c)
-{
-    return
-    (
-        (c == '.')                           // any character
-     || (c == '*' || c == '+' || c == '?')   // quantifiers
-     || (c == '(' || c == ')' || c == '|')   // grouping/branching
-     || (c == '[' || c == ']')               // range
-    );
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-inline Foam::regExpPosix::regExpPosix()
+inline Foam::regExpPosix::regExpPosix() noexcept
 :
     preg_(nullptr)
 {}
 
 
-inline Foam::regExpPosix::regExpPosix(const char* pattern)
+inline Foam::regExpPosix::regExpPosix(regExpPosix&& rgx) noexcept
 :
-    preg_(nullptr)
-{
-    set(pattern, false);
-}
-
-
-inline Foam::regExpPosix::regExpPosix(const std::string& pattern)
-:
-    preg_(nullptr)
+    preg_(rgx.preg_)
 {
-    set(pattern, false);
+    rgx.preg_ = nullptr;
 }
 
 
-inline Foam::regExpPosix::regExpPosix(const char* pattern, bool ignoreCase)
+inline Foam::regExpPosix::regExpPosix
+(
+    const char* pattern,
+    const bool ignoreCase
+)
 :
     preg_(nullptr)
 {
@@ -73,7 +55,11 @@ inline Foam::regExpPosix::regExpPosix(const char* pattern, bool ignoreCase)
 }
 
 
-inline Foam::regExpPosix::regExpPosix(const std::string& pattern, bool ignoreCase)
+inline Foam::regExpPosix::regExpPosix
+(
+    const std::string& pattern,
+    const bool ignoreCase
+)
 :
     preg_(nullptr)
 {
@@ -81,14 +67,6 @@ inline Foam::regExpPosix::regExpPosix(const std::string& pattern, bool ignoreCas
 }
 
 
-inline Foam::regExpPosix::regExpPosix(regExpPosix&& rgx)
-:
-    preg_(rgx.preg_)
-{
-    rgx.preg_ = nullptr;
-}
-
-
 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
 
 inline Foam::regExpPosix::~regExpPosix()
@@ -143,8 +121,12 @@ inline bool Foam::regExpPosix::operator()(const std::string& text) const
 
 inline void Foam::regExpPosix::operator=(regExpPosix&& rgx)
 {
-    clear();
-    swap(rgx);
+    if (this != &rgx)
+    {
+        // Self-assignment is a no-op
+        clear();
+        swap(rgx);
+    }
 }
 
 
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index 5892fa798b9d7f4074a19fda96923619b3589522..747dbd433a5ff3acb5072757fb7b10470958e55c 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -180,7 +180,8 @@ $(ops)/flipOp.C
 
 primitives/predicates/scalar/scalarPredicates.C
 
-primitives/hashes/Hasher/Hasher.C
+hash = primitives/hashes/Hash
+$(hash)/Hasher.C
 
 sha1 = primitives/hashes/SHA1
 $(sha1)/SHA1.C
diff --git a/src/OpenFOAM/containers/Bits/PackedList/PackedList.H b/src/OpenFOAM/containers/Bits/PackedList/PackedList.H
index 09f3668cc10ec54baa49502fb9d27e87279fac5f..6bc3942c81617e926d51a3458a20836e5c2ccae8 100644
--- a/src/OpenFOAM/containers/Bits/PackedList/PackedList.H
+++ b/src/OpenFOAM/containers/Bits/PackedList/PackedList.H
@@ -518,6 +518,24 @@ public:
         );
 
 
+    // Hashing
+
+        //- Hashing functor for PackedList
+        //  Seeded with logical size for disambiguation of padding
+        struct hasher
+        {
+            unsigned operator()(const PackedList<Width>& obj) const
+            {
+                return Foam::Hasher
+                (
+                    obj.cdata(),
+                    obj.size_bytes(),
+                    unsigned(obj.size())
+                );
+            }
+        };
+
+
     // Housekeeping
 
         //- Deprecated(2020-11) use fill()
@@ -533,6 +551,13 @@ public:
 };
 
 
+// * * * * * * * * * * * * * * * * * Traits  * * * * * * * * * * * * * * * * //
+
+//- Hashing for PackedList data
+template<unsigned Width>
+struct Hash<PackedList<Width>> : PackedList<Width>::hasher {};
+
+
 // * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //
 
 //- Write List to Ostream, as per UList::writeList() with default length.
diff --git a/src/OpenFOAM/containers/Bits/bitSet/bitSet.H b/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
index 5730f29c9386255f28cdfb3a6b3ce5e49c1c7787..04c524713c65fad0a7f9348f9b26813a2764bc52 100644
--- a/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
+++ b/src/OpenFOAM/containers/Bits/bitSet/bitSet.H
@@ -594,6 +594,12 @@ public:
 };
 
 
+// * * * * * * * * * * * * * * * * * Traits  * * * * * * * * * * * * * * * * //
+
+//- Hashing for bitSet data
+template<> struct Hash<bitSet> : bitSet::hasher {};
+
+
 // * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //
 
 //- Write bitset to Ostream with 40 items per line.
diff --git a/src/OpenFOAM/containers/HashTables/HashOps/HashOps.H b/src/OpenFOAM/containers/HashTables/HashOps/HashOps.H
index 288195399f3b2a86e5b9b662d43df031071a36fc..ba54b61440e8f1dff902afd7ee7fc859453e7a52 100644
--- a/src/OpenFOAM/containers/HashTables/HashOps/HashOps.H
+++ b/src/OpenFOAM/containers/HashTables/HashOps/HashOps.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -62,10 +62,10 @@ namespace HashSetOps
 {
 
 //- Combine HashSet operation. Equivalent to 'a |= b'
-template<class Key=word, class Hash=string::hash>
+template<class Key=word, class HashType=Foam::Hash<Key>>
 struct plusEqOp
 {
-    typedef HashSet<Key, Hash> value_type;
+    typedef HashSet<Key, HashType> value_type;
 
     void operator()(value_type& a, const value_type& b) const
     {
@@ -131,10 +131,10 @@ namespace HashTableOps
 {
 
 //- Combine HashTable operation. Equivalent to 'a += b'
-template<class T, class Key=word, class Hash=string::hash>
+template<class T, class Key=word, class HashType=Foam::Hash<Key>>
 struct plusEqOp
 {
-    typedef HashTable<T, Key, Hash> value_type;
+    typedef HashTable<T, Key, HashType> value_type;
 
     void operator()(value_type& a, const value_type& b) const
     {
diff --git a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
index 173de687e3d04628140daa46d38460ce8cd73a2f..549b4df75e936e0c778d6de0c7af8bbd93e1a12d 100644
--- a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
+++ b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
@@ -62,7 +62,7 @@ Istream& operator>>(Istream& is, HashPtrTable<T, Key, Hash>& tbl);
                         Class HashPtrTable Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class T, class Key=word, class Hash=string::hash>
+template<class T, class Key=word, class Hash=Foam::Hash<Key>>
 class HashPtrTable
 :
     public HashTable<T*, Key, Hash>
diff --git a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
index 2b0f31e341be4332b49e54c67346dea641b9e03c..413dda4aa4b988b105d09ec253cb2e66442f097b 100644
--- a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
+++ b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -80,7 +80,7 @@ template<class T> class MinMax;
                            Class HashSet Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class Key=word, class Hash=string::hash>
+template<class Key=word, class Hash=Foam::Hash<Key>>
 class HashSet
 :
     public HashTable<zero::null, Key, Hash>
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
index ab1c2302d9da3c3b7332dfe83d5d1f59427d3f24..b3cd98f19047d6ca82a071b0121c1a9035ab18d1 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
@@ -115,7 +115,7 @@ Ostream& operator<<(Ostream&, const HashTable<T, Key, Hash>&);
                           Class HashTable Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class T, class Key=word, class Hash=string::hash>
+template<class T, class Key=word, class Hash=Foam::Hash<Key>>
 class HashTable
 :
     public HashTableCore
diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H
index 76670941fec3b24570218f4ab1b20a5ad1cba201..e5e71f558a704098deda3cc7bd0e0bc633150c51 100644
--- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H
+++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H
@@ -258,6 +258,10 @@ public:
             inline DynamicList<T, SizeMin>&
             append(SortableList<T>&& lst);
 
+            //- Append an element if not already in the list.
+            //  \return the change in list length
+            inline label appendUniq(const T& val);
+
             //- Remove and return the last element. Fatal on an empty list.
             inline T remove();
 
@@ -368,6 +372,11 @@ inline void Swap(DynamicList<T, SizeMinA>& a, DynamicList<T, SizeMinB>& b)
 }
 
 
+//- Hashing for List data
+template<class T, int SizeMin>
+struct Hash<DynamicList<T, SizeMin>> : List<T>::hasher {};
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H
index fdab0b30d2edaa4fb6539717f22f47ce1fbd8ca8..79da196a1eceaf13514a53cf26f28c0fd4aa56e7 100644
--- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H
+++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H
@@ -608,6 +608,21 @@ Foam::DynamicList<T, SizeMin>::append
 }
 
 
+template<class T, int SizeMin>
+inline Foam::label Foam::DynamicList<T, SizeMin>::appendUniq(const T& val)
+{
+    if (this->found(val))
+    {
+        return 0;
+    }
+    else
+    {
+        this->append(val);
+        return 1;  // Increased list length by one
+    }
+}
+
+
 template<class T, int SizeMin>
 inline T Foam::DynamicList<T, SizeMin>::remove()
 {
diff --git a/src/OpenFOAM/containers/Lists/FixedList/FixedList.H b/src/OpenFOAM/containers/Lists/FixedList/FixedList.H
index cc4d642f410fdb16fdc4aa96d48ac52fd004a412..59588b2c6266934cdb5352c7368fa59d199e631e 100644
--- a/src/OpenFOAM/containers/Lists/FixedList/FixedList.H
+++ b/src/OpenFOAM/containers/Lists/FixedList/FixedList.H
@@ -46,8 +46,8 @@ SourceFiles
 #include "zero.H"
 #include "contiguous.H"
 #include "autoPtr.H"
+#include "Hash.H"
 #include "Swap.H"
-#include "HashFwd.H"
 #include "SLListFwd.H"
 #include "ListPolicy.H"
 
@@ -455,11 +455,8 @@ public:
 
     // Hashing
 
-        //- Hashing function class for FixedList
-        //  Normally use the global Hash specialization, but can also use
-        //  this one for inheritance in sub-classes
-        template<class HashT=Foam::Hash<T>>
-        struct Hash
+        //- Hashing functor for FixedList.
+        struct hasher
         {
             inline unsigned operator()
             (
@@ -469,18 +466,28 @@ public:
             {
                 if (is_contiguous<T>::value)
                 {
-                    return Hasher(obj.cdata(), N*sizeof(T), seed);
+                    return Foam::Hasher(obj.cdata(), obj.size_bytes(), seed);
                 }
 
+                Foam::Hash<T> op;
                 for (const T& val : obj)
                 {
-                    seed = HashT()(val, seed);
+                    seed = op(val, seed);
                 }
                 return seed;
             }
         };
+
+        //- Deprecated(2021-04) hashing functor. Use hasher()
+        // \deprecated(2021-04) - use hasher() functor
+        template<class Unused=bool>
+        struct Hash : FixedList<T, N>::hasher
+        {
+            FOAM_DEPRECATED_FOR(2021-04, "hasher()") Hash() {}
+        };
 };
 
+
 // * * * * * * * * * * * * * * * * * Traits  * * * * * * * * * * * * * * * * //
 
 //- FixedList is contiguous if the type is contiguous
@@ -495,6 +502,10 @@ struct is_contiguous_label<FixedList<T, N>> : is_contiguous_label<T> {};
 template<class T, unsigned N>
 struct is_contiguous_scalar<FixedList<T, N>> : is_contiguous_scalar<T> {};
 
+//- Hashing for FixedList data
+template<class T, unsigned N>
+struct Hash<FixedList<T, N>> : FixedList<T, N>::hasher {};
+
 
 // * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
@@ -507,31 +518,6 @@ inline void Swap(FixedList<T, N>& a, FixedList<T, N>& b)
 }
 
 
-//- Hashing for FixedList data, which uses Hasher for contiguous data and
-//- element-wise incrementally hashing otherwise.
-template<class T, unsigned N>
-struct Hash<FixedList<T, N>>
-{
-    inline unsigned operator()
-    (
-        const FixedList<T, N>& obj,
-        unsigned seed=0
-    ) const
-    {
-        if (is_contiguous<T>::value)
-        {
-            return Hasher(obj.cdata(), N*sizeof(T), seed);
-        }
-
-        for (const T& val : obj)
-        {
-            seed = Hash<T>()(val, seed);
-        }
-        return seed;
-    }
-};
-
-
 // * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
 
 //- Write List to Ostream, as per FixedList::writeList() with default length.
diff --git a/src/OpenFOAM/containers/Lists/List/List.H b/src/OpenFOAM/containers/Lists/List/List.H
index 7dbcd5077c2eb5f63befb98f38ba6a601fbf2e46..68da584394f0d97fe890d4fd160eafa5257e9a07 100644
--- a/src/OpenFOAM/containers/Lists/List/List.H
+++ b/src/OpenFOAM/containers/Lists/List/List.H
@@ -242,6 +242,10 @@ public:
             template<class Addr>
             inline void append(const IndirectListBase<T, Addr>& list);
 
+            //- Append an element if not already in the list.
+            //  \return the change in list length
+            inline label appendUniq(const T& val);
+
             //- Transfer the contents of the argument List into this list
             //- and annul the argument list
             void transfer(List<T>& list);
@@ -360,6 +364,10 @@ public:
 template<>
 Istream& List<char>::readList(Istream& is);
 
+//- Hashing for List data
+template<class T>
+struct Hash<List<T>> : List<T>::hasher {};
+
 
 // * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
@@ -368,27 +376,6 @@ Istream& List<char>::readList(Istream& is);
 labelList identity(const label len, label start=0);
 
 
-//- Hashing for List data, which uses Hasher for contiguous data and
-//- element-wise incrementally hashing otherwise.
-template<class T>
-struct Hash<List<T>>
-{
-    inline unsigned operator()(const UList<T>& obj, unsigned seed=0) const
-    {
-        if (is_contiguous<T>::value)
-        {
-            return Hasher(obj.cdata(), obj.size()*sizeof(T), seed);
-        }
-
-        for (const T& val : obj)
-        {
-            seed = Hash<T>()(val, seed);
-        }
-        return seed;
-    }
-};
-
-
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/OpenFOAM/containers/Lists/List/ListI.H b/src/OpenFOAM/containers/Lists/List/ListI.H
index 4e499c0f20691fca785da1f5556c9b98bb8d96f4..e24aa6c4864e6e436529d7cd4db155c69ec6a0ea 100644
--- a/src/OpenFOAM/containers/Lists/List/ListI.H
+++ b/src/OpenFOAM/containers/Lists/List/ListI.H
@@ -218,6 +218,21 @@ inline void Foam::List<T>::append(const IndirectListBase<T, Addr>& list)
 }
 
 
+template<class T>
+inline Foam::label Foam::List<T>::appendUniq(const T& val)
+{
+    if (this->found(val))
+    {
+        return 0;
+    }
+    else
+    {
+        this->append(val);
+        return 1;  // Increased list length by one
+    }
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 template<class T>
diff --git a/src/OpenFOAM/containers/Lists/UList/UList.H b/src/OpenFOAM/containers/Lists/UList/UList.H
index f0a3ab0d5845ebb40ea9c8cf05bb2cfcda969977..9df3e1f9122e0708325843bdd60183eb5f18c7a3 100644
--- a/src/OpenFOAM/containers/Lists/UList/UList.H
+++ b/src/OpenFOAM/containers/Lists/UList/UList.H
@@ -53,8 +53,8 @@ SourceFiles
 #include "contiguous.H"
 #include "nullObject.H"
 #include "stdFoam.H"
+#include "Hash.H"
 #include "Swap.H"
-#include "HashFwd.H"
 #include "ListPolicy.H"
 
 #include <initializer_list>
@@ -522,13 +522,10 @@ public:
         }
 
 
-    // Other
+    // Hashing
 
-        //- Hashing function class for UList.
-        //  Can use this one (instead of the global Hash<>) for inheritance
-        //  in sub-classes
-        template<class HashT=Foam::Hash<T>>
-        struct Hash
+        //- Hashing functor for UList
+        struct hasher
         {
             inline unsigned operator()
             (
@@ -538,16 +535,25 @@ public:
             {
                 if (is_contiguous<T>::value)
                 {
-                    return Hasher(obj.cdata(), obj.size_bytes(), seed);
+                    return Foam::Hasher(obj.cdata(), obj.size_bytes(), seed);
                 }
 
+                Foam::Hash<T> op;
                 for (const T& val : obj)
                 {
-                    seed = HashT()(val, seed);
+                    seed = op(val, seed);
                 }
                 return seed;
             }
         };
+
+        //- Deprecated(2021-04) hashing functor. Use hasher()
+        // \deprecated(2021-04) - use hasher() functor
+        template<class Unused=bool>
+        struct Hash : UList<T>::hasher
+        {
+            FOAM_DEPRECATED_FOR(2021-04, "hasher()") Hash() {}
+        };
 };
 
 
@@ -617,27 +623,12 @@ inline void Swap(UList<T>& a, UList<T>& b)
 }
 
 
-//- Hashing for UList data, which uses Hasher for contiguous data and
-//- element-wise incrementally hashing otherwise.
-template<class T>
-struct Hash<UList<T>>
-{
-    inline unsigned operator()(const UList<T>& obj, unsigned seed=0) const
-    {
-        if (is_contiguous<T>::value)
-        {
-            return Hasher(obj.cdata(), obj.size_bytes(), seed);
-        }
-        for (const T& val : obj)
-        {
-            seed = Hash<T>()(val, seed);
-        }
-        return seed;
-    }
-};
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+//- Hashing for List data
+template<class T>
+struct Hash<UList<T>> : UList<T>::hasher {};
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 //- Object access operator or list access operator.
 //- \sa ListListOps::combine()
diff --git a/src/OpenFOAM/db/IOobject/IOobject.C b/src/OpenFOAM/db/IOobject/IOobject.C
index 66f39bded28cedea6d9153b63045af7cbf5256d5..cd478fea9691f130feb8fb0c25d58ff8de399686 100644
--- a/src/OpenFOAM/db/IOobject/IOobject.C
+++ b/src/OpenFOAM/db/IOobject/IOobject.C
@@ -154,8 +154,8 @@ bool Foam::IOobject::fileNameComponents
     // Convert explicit relative file-system path to absolute file-system path.
     if (path.starts_with("./") || path.starts_with("../"))
     {
-        fileName absPath = cwd()/path;
-        absPath.clean();
+        fileName absPath(cwd()/path);
+        absPath.clean();  // Remove unneeded ".."
 
         return fileNameComponents(absPath, instance, local, name);
     }
diff --git a/src/OpenFOAM/db/dictionary/dictionarySearch.C b/src/OpenFOAM/db/dictionary/dictionarySearch.C
index 7b7a9e65cbe605a15ef889300c55217350da9c90..401c7825d7a2fc75b4198fd1a6073d0ec513fb79 100644
--- a/src/OpenFOAM/db/dictionary/dictionarySearch.C
+++ b/src/OpenFOAM/db/dictionary/dictionarySearch.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -184,7 +184,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped
     }
     else if (slash == 0)
     {
-        // (isAbsolute)
+        // isAbsolute:
         // Ascend to top-level
         while (&dictPtr->parent_ != &dictionary::null)
         {
@@ -385,8 +385,9 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
     }
 
     const dictionary* dictPtr = this;
-    if (fileName::isAbsolute(dictPath))
+    if (dictPath[0] == '/')
     {
+        // isAbsolute:
         // Ascend to top-level
         while (&dictPtr->parent_ != &dictionary::null)
         {
@@ -394,10 +395,11 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
         }
     }
 
-    fileName path = dictPath.clean();
-    const wordList cmpts = path.components();
+    fileName path(dictPath); // Work on copy
+    path.clean();  // Remove unneeded ".."
+    const wordList dictCmpts(path.components()); // Split on '/'
 
-    for (const word& cmpt : cmpts)
+    for (const word& cmpt : dictCmpts)
     {
         if (cmpt == ".")
         {
@@ -486,8 +488,9 @@ Foam::dictionary* Foam::dictionary::makeScopedDict(const fileName& dictPath)
     }
 
     dictionary* dictPtr = this;
-    if (fileName::isAbsolute(dictPath))
+    if (dictPath[0] == '/')
     {
+        // isAbsolute:
         // Ascend to top-level
         while (&dictPtr->parent_ != &dictionary::null)
         {
@@ -495,14 +498,11 @@ Foam::dictionary* Foam::dictionary::makeScopedDict(const fileName& dictPath)
         }
     }
 
-    // Work on a copy, without any assumptions
-    std::string path = dictPath;
-    fileName::clean(path);
+    std::string path(dictPath); // Work on a copy
+    fileName::clean(path);  // Remove unneeded ".."
+    auto dictCmpts = stringOps::split(path, '/'); // Split on '/'
 
-    // Split on '/'
-    auto cmpts = stringOps::split(path, '/');
-
-    for (const auto& cmpt : cmpts)
+    for (const auto& cmpt : dictCmpts)
     {
         if (cmpt == ".")
         {
diff --git a/src/OpenFOAM/db/dictionary/functionEntries/calcEntry/calcEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/calcEntry/calcEntry.C
index 1f0c0751f870304ab2ccc5ea8db196a4d12b5311..94c128ef7d2333b166ae63eabaf9e018723c3ca4 100644
--- a/src/OpenFOAM/db/dictionary/functionEntries/calcEntry/calcEntry.C
+++ b/src/OpenFOAM/db/dictionary/functionEntries/calcEntry/calcEntry.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,10 +27,10 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "calcEntry.H"
-#include "addToMemberFunctionSelectionTable.H"
+#include "codeStream.H"
 #include "dictionary.H"
 #include "dynamicCode.H"
-#include "codeStream.H"
+#include "addToMemberFunctionSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
diff --git a/src/OpenFOAM/db/dictionary/functionEntries/codeStream/codeStream.C b/src/OpenFOAM/db/dictionary/functionEntries/codeStream/codeStream.C
index 97eeb427679efed201194dbe9337df32c6f6e441..78d33f45537f79ed20f81d345d058abc227c2bf6 100644
--- a/src/OpenFOAM/db/dictionary/functionEntries/codeStream/codeStream.C
+++ b/src/OpenFOAM/db/dictionary/functionEntries/codeStream/codeStream.C
@@ -27,11 +27,11 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "codeStream.H"
-#include "addToMemberFunctionSelectionTable.H"
-#include "StringStream.H"
 #include "dynamicCode.H"
 #include "dynamicCodeContext.H"
+#include "StringStream.H"
 #include "Time.H"
+#include "addToMemberFunctionSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -69,11 +69,7 @@ Foam::dlLibraryTable& Foam::functionEntries::codeStream::libs
     const dictionary& dict
 )
 {
-    const baseIOdictionary& d = static_cast<const baseIOdictionary&>
-    (
-        dict.topDict()
-    );
-    return d.time().libs();
+    return static_cast<const baseIOdictionary&>(dict.topDict()).time().libs();
 }
 
 
@@ -82,28 +78,27 @@ bool Foam::functionEntries::codeStream::doingMasterOnlyReading
     const dictionary& dict
 )
 {
-    const dictionary& topDict = dict.topDict();
+    // Fallback value
+    bool masterOnly = regIOobject::masterOnlyReading;
 
-    if (isA<baseIOdictionary>(topDict))
+    const auto* iodictPtr = isA<baseIOdictionary>(dict.topDict());
+
+    if (iodictPtr)
     {
-        const baseIOdictionary& d = static_cast<const baseIOdictionary&>
-        (
-            topDict
-        );
+        masterOnly = iodictPtr->globalObject();
 
         DebugPout
             << "codeStream : baseIOdictionary:" << dict.name()
-            << " master-only-reading:" << d.globalObject() << endl;
-
-        return d.globalObject();
+            << " master-only-reading:" << masterOnly << endl;
+    }
+    else
+    {
+        DebugPout
+            << "codeStream : not a baseIOdictionary:" << dict.name()
+            << " master-only-reading:" << masterOnly << endl;
     }
 
-    DebugPout
-        << "codeStream : not a baseIOdictionary:" << dict.name()
-        << " master-only-reading:" << regIOobject::masterOnlyReading << endl;
-
-    // Fall back to regIOobject::masterOnlyReading
-    return regIOobject::masterOnlyReading;
+    return masterOnly;
 }
 
 
diff --git a/src/OpenFOAM/db/dictionary/functionEntries/ifeqEntry/ifeqEntry.C b/src/OpenFOAM/db/dictionary/functionEntries/ifeqEntry/ifeqEntry.C
index b98355a03bd77186bf48ed3c474569dccbbef403..0115cf0427dadf73a771e3f3998350d24b417500 100644
--- a/src/OpenFOAM/db/dictionary/functionEntries/ifeqEntry/ifeqEntry.C
+++ b/src/OpenFOAM/db/dictionary/functionEntries/ifeqEntry/ifeqEntry.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2018 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -205,6 +205,10 @@ bool Foam::functionEntries::ifeqEntry::equalToken
             {
                 return equal(t1.floatToken(), t2.floatToken());
             }
+            else if (t2.isLabel())
+            {
+                return t1.floatToken() == t2.labelToken();
+            }
             else if (t2.isScalar())
             {
                 return t1.scalarToken() == t2.scalarToken();
@@ -216,6 +220,10 @@ bool Foam::functionEntries::ifeqEntry::equalToken
             {
                 return equal(t1.doubleToken(), t2.doubleToken());
             }
+            else if (t2.isLabel())
+            {
+                return t1.doubleToken() == t2.labelToken();
+            }
             else if (t2.isScalar())
             {
                 return t1.scalarToken() == t2.scalarToken();
diff --git a/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.C b/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.C
index 7d9fbbc72121a2b6e39b3344924f6f30e43d6e25..978af4adde017f88cea1012fa431038cd38136b4 100644
--- a/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.C
+++ b/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.C
@@ -31,10 +31,13 @@ License
 #include "dynamicCode.H"
 #include "dynamicCodeContext.H"
 #include "dlLibraryTable.H"
+#include "objectRegistry.H"
+#include "IOdictionary.H"
 #include "Pstream.H"
 #include "PstreamReduceOps.H"
 #include "OSspecific.H"
 #include "Ostream.H"
+#include "Time.H"
 #include "regIOobject.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@@ -50,7 +53,6 @@ namespace Foam
 namespace Foam
 {
 
-//! \cond fileScope
 static inline void writeEntryIfPresent
 (
     Ostream& os,
@@ -59,18 +61,24 @@ static inline void writeEntryIfPresent
 )
 {
     const entry* eptr = dict.findEntry(key, keyType::LITERAL);
-
-    if (eptr)
+    if (!eptr)
+    {
+        // Nothing to do
+    }
+    else if (eptr->isDict())
+    {
+        eptr->dict().writeEntry(os);
+    }
+    else
     {
         const tokenList& toks = eptr->stream();
 
-        if (!toks.empty())
+        if (!toks.empty())  // Could also check that it is a string-type
         {
             os.writeEntry(key, toks[0]);
         }
     }
 }
-//! \endcond
 
 } // End namespace Foam
 
@@ -79,6 +87,7 @@ static inline void writeEntryIfPresent
 
 void Foam::codedBase::writeCodeDict(Ostream& os, const dictionary& dict)
 {
+    writeEntryIfPresent(os, dict, "codeContext");
     writeEntryIfPresent(os, dict, "codeInclude");
     writeEntryIfPresent(os, dict, "localCode");
     writeEntryIfPresent(os, dict, "code");
@@ -87,6 +96,36 @@ void Foam::codedBase::writeCodeDict(Ostream& os, const dictionary& dict)
 }
 
 
+const Foam::dictionary&
+Foam::codedBase::codeDict
+(
+    const objectRegistry& obr,
+    const word& dictName
+)
+{
+    IOdictionary* dictptr = obr.getObjectPtr<IOdictionary>(dictName);
+
+    if (!dictptr)
+    {
+        dictptr = new IOdictionary
+        (
+            IOobject
+            (
+                dictName,
+                obr.time().system(),
+                obr,
+                IOobject::MUST_READ_IF_MODIFIED,
+                IOobject::NO_WRITE
+            )
+        );
+
+        obr.store(dictptr);
+    }
+
+    return *dictptr;
+}
+
+
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 void* Foam::codedBase::loadLibrary
diff --git a/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.H b/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.H
index 0892d7ed539e76b5874a309928e6418c28e0966e..aeac411730885d7821fcb5d4066ca4c539d28bf0 100644
--- a/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.H
+++ b/src/OpenFOAM/db/dynamicLibrary/codedBase/codedBase.H
@@ -49,7 +49,6 @@ SourceFiles
 
 #include "dictionary.H"
 #include "dynamicCodeContext.H"
-#include "fileName.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -57,9 +56,9 @@ namespace Foam
 {
 
 // Forward Declarations
-class Ostream;
 class dynamicCode;
 class dlLibraryTable;
+class objectRegistry;
 
 /*---------------------------------------------------------------------------*\
                        Class codedBase Declaration
@@ -104,11 +103,24 @@ class codedBase
 
 protected:
 
+    // Protected Member Functions
+
         //- Write code-dictionary contents
         static void writeCodeDict(Ostream& os, const dictionary& dict);
 
+        //- Return "codeDict" from objectRegistry or read from disk
+        static const dictionary& codeDict
+        (
+            const objectRegistry& obr,
+            const word& dictName = "codeDict"
+        );
 
-    // Protected Member Functions
+
+        //- Access to the dynamic code context
+        dynamicCodeContext& codeContext()
+        {
+            return context_;
+        }
 
         //- Set code context from a dictionary
         void setCodeContext(const dictionary& dict);
@@ -136,16 +148,10 @@ protected:
         //- or use the codeDict() to generate one
         void updateLibrary(const word& name) const;
 
+
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const = 0;
 
-        //- Adapt the context for the current object
-        virtual void prepare
-        (
-            dynamicCode& dynCode,
-            const dynamicCodeContext& context
-        ) const = 0;
-
         // Return a description (type + name) for the output
         virtual string description() const = 0;
 
@@ -155,6 +161,13 @@ protected:
         // Get the dictionary to initialize the codeContext
         virtual const dictionary& codeDict() const = 0;
 
+        //- Adapt the context for the current object
+        virtual void prepare
+        (
+            dynamicCode& dynCode,
+            const dynamicCodeContext& context
+        ) const = 0;
+
 
         //- No copy construct
         codedBase(const codedBase&) = delete;
diff --git a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
index 23941dc0e4184de35d58e70068785c699f3b32d9..ed05c2e11c6a99d5b7fce18c0fe2d5b35cf5dca4 100644
--- a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
+++ b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
@@ -52,10 +52,11 @@ const Foam::word Foam::dynamicCode::codeTemplateEnvName
 const Foam::fileName Foam::dynamicCode::codeTemplateDirName
     = "codeTemplates/dynamicCode";
 
-const char* const Foam::dynamicCode::libTargetRoot =
-    "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib/lib";
+const char* const Foam::dynamicCode::targetLibDir
+    = "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib";
 
-const char* const Foam::dynamicCode::topDirName = "dynamicCode";
+const char* const Foam::dynamicCode::topDirName
+    = "dynamicCode";
 
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
@@ -224,7 +225,8 @@ bool Foam::dynamicCode::createMakeFiles() const
     }
 
     os  << nl
-        << libTargetRoot << codeName_.c_str() << nl;
+        << targetLibDir
+        << "/lib" << codeName_.c_str() << nl;
 
     return true;
 }
diff --git a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.H b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.H
index 438219b505ada462f50c95cb2e02eefa62211cd6..8331e1c232a801dfe236258e35ea494bdb081638 100644
--- a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.H
+++ b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.H
@@ -108,8 +108,8 @@ protected:
 
     // Static Data Members
 
-        //- Root of the LIB target for Make/files
-        static const char* const libTargetRoot;
+        //- Directory for library targets for Make/files
+        static const char* const targetLibDir;
 
         //- Top-level directory name for copy/compiling
         static const char* const topDirName;
diff --git a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.C b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.C
index 0f3402249e2b00197b1a3c6e274c393f30e71230..2a6a25d5aa931f84906e6257aa41d809915ed821 100644
--- a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.C
+++ b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,12 +34,12 @@ License
 
 void Foam::dynamicCodeContext::inplaceExpand
 (
-    string& code,
+    string& str,
     const dictionary& dict
 )
 {
-    stringOps::inplaceTrim(code);
-    stringOps::inplaceExpand(code, dict);
+    stringOps::inplaceTrim(str);
+    stringOps::inplaceExpand(str, dict);
 }
 
 
@@ -47,7 +47,7 @@ unsigned Foam::dynamicCodeContext::addLineDirective
 (
     string& code,
     label lineNum,
-    const fileName& file
+    const string& file
 )
 {
     ++lineNum;  // Change from 0-based to 1-based
@@ -94,72 +94,81 @@ Foam::dynamicCodeContext::dynamicCodeContext(const dictionary& dict)
 
 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
 
-bool Foam::dynamicCodeContext::valid() const
+bool Foam::dynamicCodeContext::valid() const noexcept
 {
     return &(dict_.get()) != &(dictionary::null);
 }
 
 
-void Foam::dynamicCodeContext::setCodeContext(const dictionary& dict)
+bool Foam::dynamicCodeContext::readEntry
+(
+    const word& key,
+    string& str,
+    bool mandatory,
+    bool withLineNum
+)
 {
-    dict_ = std::cref<dictionary>(dict);
-    sha1_.clear();
+    str.clear();
+    sha1_.append("<" + key + ">");
+
+    const dictionary& dict = this->dict();
+    const entry* eptr = dict.findEntry(key, keyType::LITERAL);
+
+    if (!eptr)
+    {
+        if (mandatory)
+        {
+            FatalIOErrorInFunction(dict)
+                << "Entry '" << key << "' not found in dictionary "
+                << dict.name() << nl
+                << exit(FatalIOError);
+        }
+
+        return false;
+    }
 
     // Expand dictionary entries.
     // Removing any leading/trailing whitespace is necessary for compilation
     // options, but is also convenient for includes and code body.
 
-    const entry* eptr;
+    eptr->readEntry(str);
+    dynamicCodeContext::inplaceExpand(str, dict);
+    sha1_.append(str);
 
-    options_.clear();
-    sha1_.append("<codeOptions>");
-    if ((eptr = dict.findEntry("codeOptions", keyType::LITERAL)) != nullptr)
+    if (withLineNum)
     {
-        eptr->readEntry(options_);
-        dynamicCodeContext::inplaceExpand(options_, dict);
-        sha1_.append(options_);
-        // No #line for options (Make/options)
+        addLineDirective(str, eptr->startLineNumber(), dict);
     }
 
-    libs_.clear();
-    sha1_.append("<codeLibs>");
-    if ((eptr = dict.findEntry("codeLibs", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(libs_);
-        dynamicCodeContext::inplaceExpand(libs_, dict);
-        sha1_.append(libs_);
-        // No #line for libs (LIB_LIBS)
-    }
+    return true;
+}
 
-    include_.clear();
-    sha1_.append("<codeInclude>");
-    if ((eptr = dict.findEntry("codeInclude", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(include_);
-        dynamicCodeContext::inplaceExpand(include_, dict);
-        sha1_.append(include_);
-        addLineDirective(include_, eptr->startLineNumber(), dict);
-    }
 
-    code_.clear();
-    sha1_.append("<code>");
-    if ((eptr = dict.findEntry("code", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(code_);
-        dynamicCodeContext::inplaceExpand(code_, dict);
-        sha1_.append(code_);
-        addLineDirective(code_, eptr->startLineNumber(), dict);
-    }
+bool Foam::dynamicCodeContext::readIfPresent
+(
+    const word& key,
+    string& str,
+    bool withLineNum
+)
+{
+    return readEntry(key, str, false, withLineNum);
+}
 
-    localCode_.clear();
-    sha1_.append("<localCode>");
-    if ((eptr = dict.findEntry("localCode", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(localCode_);
-        dynamicCodeContext::inplaceExpand(localCode_, dict);
-        sha1_.append(localCode_);
-        addLineDirective(localCode_, eptr->startLineNumber(), dict);
-    }
+
+void Foam::dynamicCodeContext::setCodeContext(const dictionary& dict)
+{
+    dict_ = std::cref<dictionary>(dict);
+    sha1_.clear();
+
+    // No #line for options (Make/options)
+    readIfPresent("codeOptions", codeOptions_, false);
+
+    // No #line for libs (LIB_LIBS)
+    readIfPresent("codeLibs", codeLibs_, false);
+
+    readIfPresent("codeInclude", codeInclude_);
+    readIfPresent("localCode", localCode_);
+    readIfPresent("code", code_);
 }
 
 
diff --git a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.H b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.H
index 730e2cdb2e2ccf686f89d0baeed9430abb8a25f0..91e444133716d8a57b95aabaeb9efc0800a14766 100644
--- a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.H
+++ b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCodeContext.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -38,9 +38,9 @@ SourceFiles
 #ifndef dynamicCodeContext_H
 #define dynamicCodeContext_H
 
-#include <functional>
 #include "dictionary.H"
 #include "SHA1.H"
+#include <functional>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -61,27 +61,27 @@ class dynamicCodeContext
         //- The SHA1 of the contents
         SHA1 sha1_;
 
-        //- Optional "codeOptions" entry
-        string options_;
-
-        //- Optional "codeLibs" entry
-        string libs_;
+        //- The "codeOptions" entry (optional)
+        string codeOptions_;
 
-        //- Optional "codeInclude" entry
-        string include_;
+        //- The "codeLibs" entry (optional)
+        string codeLibs_;
 
-        //- Optional "code" entry
-        string code_;
+        //- The "codeInclude" entry (optional)
+        string codeInclude_;
 
-        //- Optional "localCode" entry
+        //- The "localCode" entry (optional)
         string localCode_;
 
+        //- The "code" entry (optional)
+        string code_;
+
 
 public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         dynamicCodeContext();
 
         //- Construct from a dictionary
@@ -91,7 +91,7 @@ public:
     // Static Member Functions
 
         //- Cleanup string and expand with dictionary parameters
-        static void inplaceExpand(string& code, const dictionary& dict);
+        static void inplaceExpand(string& str, const dictionary& dict);
 
         //- Prefix a \#line directive to code.
         //  The input lineNum is 0-based.
@@ -104,7 +104,7 @@ public:
         (
             string& code,
             label lineNum,
-            const fileName& file
+            const string& file
         );
 
         //- Prefix a \#line directive to code.
@@ -120,49 +120,49 @@ public:
     // Member Functions
 
         //- Considered valid if not using dictionary::null as the context
-        bool valid() const;
+        bool valid() const noexcept;
 
         //- Set code context from a dictionary
         void setCodeContext(const dictionary& dict);
 
         //- Return the parent dictionary context
-        const dictionary& dict() const
+        const dictionary& dict() const noexcept
         {
             return dict_.get();
         }
 
-        //- Return the code-includes
-        const string& include() const
+        //- The code options (Make/options)
+        const string& options() const noexcept
         {
-            return include_;
+            return codeOptions_;
         }
 
-        //- Return the code-options
-        const string& options() const
+        //- The code libs (LIB_LIBS)
+        const string& libs() const noexcept
         {
-            return options_;
+            return codeLibs_;
         }
 
-        //- Return the code-libs
-        const string& libs() const
+        //- The code includes
+        const string& include() const noexcept
         {
-            return libs_;
+            return codeInclude_;
         }
 
-        //- Return the code
-        const string& code() const
+        //- The local (file-scope) code
+        const string& localCode() const noexcept
         {
-            return code_;
+            return localCode_;
         }
 
-        //- Return the local (file-scope) code
-        const string& localCode() const
+        //- The code
+        const string& code() const noexcept
         {
-            return localCode_;
+            return code_;
         }
 
-        //- Return SHA1 calculated from options, libs, include, code
-        const SHA1& sha1() const
+        //- The SHA1 calculated from options, libs, include, code, etc.
+        const SHA1& sha1() const noexcept
         {
             return sha1_;
         }
@@ -174,14 +174,39 @@ public:
         }
 
 
+    // Reading
+
+        //- Read string entry from context dictionary
+        //- append content to SHA1 hashing and add line number etc.
+        //
+        //  The string is cleared before reading.
+        bool readEntry
+        (
+            const word& key,
+            string& str,
+            bool mandatory = true,
+            bool withLineNum = true
+        );
+
+        //- Read optional string entry from context dictionary,
+        //- append content to SHA1 hashing and add line number etc.
+        //
+        //  The string is cleared before reading.
+        bool readIfPresent
+        (
+            const word& key,
+            string& str,
+            bool withLineNum = true
+        );
+
+
     // Member Operators
 
         //- Cast to dictionary
-        operator const dictionary&() const
+        operator const dictionary&() const noexcept
         {
             return dict_.get();
         }
-
 };
 
 
diff --git a/src/OpenFOAM/db/functionObjects/writeFile/writeFile.C b/src/OpenFOAM/db/functionObjects/writeFile/writeFile.C
index 36cb7cdd0a53198affd7982c12e45f86a5e616d4..51be217fda8cf1d7b88023b01ecbae1a016ac207 100644
--- a/src/OpenFOAM/db/functionObjects/writeFile/writeFile.C
+++ b/src/OpenFOAM/db/functionObjects/writeFile/writeFile.C
@@ -52,7 +52,7 @@ Foam::fileName Foam::functionObjects::writeFile::baseFileDir() const
     // Put in undecomposed case
     // (Note: gives problems for distributed data running)
 
-    fileName baseDir =
+    fileName baseDir
     (
         fileObr_.time().globalPath()
       / functionObject::outputPrefix
@@ -67,7 +67,6 @@ Foam::fileName Foam::functionObjects::writeFile::baseFileDir() const
             baseDir /= mesh.name();
         }
     }
-
     baseDir.clean();  // Remove unneeded ".."
 
     return baseDir;
diff --git a/src/OpenFOAM/db/runTimeSelection/construction/runTimeSelectionTables.H b/src/OpenFOAM/db/runTimeSelection/construction/runTimeSelectionTables.H
index d8bd4719c8264a66d5a9285d1c0069da3d9c1bbd..336927c38f6a62187c90ac919146b7548b64022e 100644
--- a/src/OpenFOAM/db/runTimeSelection/construction/runTimeSelectionTables.H
+++ b/src/OpenFOAM/db/runTimeSelection/construction/runTimeSelectionTables.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,7 +52,7 @@ Description
     typedef autoPtr<baseType> (*argNames##ConstructorPtr)argList;              \
                                                                                \
     /* Construct from argList function table type */                           \
-    typedef HashTable<argNames##ConstructorPtr, word, string::hash>            \
+    typedef HashTable<argNames##ConstructorPtr, word>                          \
         argNames##ConstructorTable;                                            \
                                                                                \
     /* Construct from argList function pointer table pointer */                \
@@ -152,7 +152,7 @@ Description
     typedef autoPtr<baseType> (*argNames##ConstructorPtr)argList;              \
                                                                                \
     /* Construct from argList function table type */                           \
-    typedef HashTable<argNames##ConstructorPtr, word, string::hash>            \
+    typedef HashTable<argNames##ConstructorPtr, word>                          \
         argNames##ConstructorTable;                                            \
                                                                                \
     /* Construct from argList function pointer table pointer */                \
diff --git a/src/OpenFOAM/db/runTimeSelection/memberFunctions/memberFunctionSelectionTables.H b/src/OpenFOAM/db/runTimeSelection/memberFunctions/memberFunctionSelectionTables.H
index 86985540124677efd4db2273522ceb074b322c0d..a92954fbd6a44ed5176522f929ab0607a4632eeb 100644
--- a/src/OpenFOAM/db/runTimeSelection/memberFunctions/memberFunctionSelectionTables.H
+++ b/src/OpenFOAM/db/runTimeSelection/memberFunctions/memberFunctionSelectionTables.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,8 +46,7 @@ Description
     typedef returnType (*memberFunction##argNames##MemberFunctionPtr)argList;  \
                                                                                \
     /* Construct from argList function table type */                           \
-    typedef HashTable                                                          \
-        <memberFunction##argNames##MemberFunctionPtr, word, string::hash>      \
+    typedef HashTable<memberFunction##argNames##MemberFunctionPtr, word>       \
         memberFunction##argNames##MemberFunctionTable;                         \
                                                                                \
     /* Construct from argList function pointer table pointer */                \
diff --git a/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.C b/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.C
index bd94be76eb1a988703cdb37db506d1034593fd42..1b539d37ff6e3b78dbc334d39eba13d82dca2c62 100644
--- a/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.C
+++ b/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,38 +35,41 @@ License
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-const Foam::IOdictionary& Foam::codedFixedValuePointPatchField<Type>::dict()
-const
+Foam::dlLibraryTable& Foam::codedFixedValuePointPatchField<Type>::libs() const
 {
-    const objectRegistry& obr = this->db();
+    return this->db().time().libs();
+}
 
-    const IOdictionary* dictptr = obr.cfindObject<IOdictionary>("codeDict");
-    if (dictptr)
-    {
-        return *dictptr;
-    }
 
-    return obr.store
-    (
-        new IOdictionary
-        (
-            IOobject
-            (
-                "codeDict",
-                this->db().time().system(),
-                this->db(),
-                IOobject::MUST_READ_IF_MODIFIED,
-                IOobject::NO_WRITE
-            )
-        )
-    );
+template<class Type>
+Foam::string Foam::codedFixedValuePointPatchField<Type>::description() const
+{
+    return
+        "patch "
+      + this->patch().name()
+      + " on field "
+      + this->internalField().name();
 }
 
 
 template<class Type>
-Foam::dlLibraryTable& Foam::codedFixedValuePointPatchField<Type>::libs() const
+void Foam::codedFixedValuePointPatchField<Type>::clearRedirect() const
 {
-    return this->db().time().libs();
+    redirectPatchFieldPtr_.reset(nullptr);
+}
+
+
+template<class Type>
+const Foam::dictionary&
+Foam::codedFixedValuePointPatchField<Type>::codeDict() const
+{
+    // Inline "code" or from system/codeDict
+    return
+    (
+        dict_.found("code")
+      ? dict_
+      : codedBase::codeDict(this->db()).subDict(name_)
+    );
 }
 
 
@@ -89,59 +92,28 @@ void Foam::codedFixedValuePointPatchField<Type>::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
     (
         "EXE_INC = -g \\\n"
         "-I$(LIB_SRC)/finiteVolume/lnInclude \\\n"
+        "-I$(LIB_SRC)/meshTools/lnInclude \\\n"
       + context.options()
       + "\n\nLIB_LIBS = \\\n"
         "    -lOpenFOAM \\\n"
         "    -lfiniteVolume \\\n"
+        "    -lmeshTools \\\n"
       + context.libs()
     );
 }
 
 
-template<class Type>
-const Foam::dictionary& Foam::codedFixedValuePointPatchField<Type>::codeDict()
-const
-{
-    // Use system/codeDict or in-line
-    return
-    (
-        dict_.found("code")
-      ? dict_
-      : this->dict().subDict(name_)
-    );
-}
-
-
-template<class Type>
-Foam::string Foam::codedFixedValuePointPatchField<Type>::description() const
-{
-    return
-        "patch "
-      + this->patch().name()
-      + " on field "
-      + this->internalField().name();
-}
-
-
-template<class Type>
-void Foam::codedFixedValuePointPatchField<Type>::clearRedirect() const
-{
-    // Remove instantiation of pointPatchField provided by library
-    redirectPatchFieldPtr_.clear();
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Type>
@@ -153,7 +125,7 @@ Foam::codedFixedValuePointPatchField<Type>::codedFixedValuePointPatchField
 :
     fixedValuePointPatchField<Type>(p, iF),
     codedBase(),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {}
 
 
@@ -187,7 +159,7 @@ Foam::codedFixedValuePointPatchField<Type>::codedFixedValuePointPatchField
     codedBase(),
     dict_(dict),
     name_(dict.getCompat<word>("name", {{"redirectType", 1706}})),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {
     updateLibrary(name_);
 }
diff --git a/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.H b/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.H
index 451361c6c1b0b77555ad1f1d9e42e678c69a1aa7..927bde50bf3af46ff0ada42394476e475dd7aa1a 100644
--- a/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.H
+++ b/src/OpenFOAM/fields/pointPatchFields/derived/codedFixedValue/codedFixedValuePointPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,16 +35,15 @@ Description
     \verbatim
     movingWall
     {
-        type            codedFixedValue;
-        value           uniform 0;
+        type    codedFixedValue;
+        value   uniform 0;
         name    rampedFixedValue;   // name of generated bc
 
         code
         #{
             operator==
             (
-                vector(0,0,1)
-                *min(10, 0.1*this->db().time().value())
+                vector(0,0,1) * min(10, 0.1*this->db().time().value())
             );
         #};
 
@@ -93,9 +92,6 @@ SourceFiles
 namespace Foam
 {
 
-// Forward Declarations
-class IOdictionary;
-
 /*---------------------------------------------------------------------------*\
                Class codedFixedValuePointPatchField Declaration
 \*---------------------------------------------------------------------------*/
@@ -109,7 +105,7 @@ class codedFixedValuePointPatchField
     // Private Data
 
         //- Dictionary contents for the boundary condition
-        mutable dictionary dict_;
+        dictionary dict_;
 
         const word name_;
 
@@ -118,23 +114,21 @@ class codedFixedValuePointPatchField
 
     // Private Member Functions
 
-        const IOdictionary& dict() const;
-
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        // Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        // Clear the ptr to the redirected object
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        // Get the dictionary to initialize the codeContext
+        //- The code dictionary. Inline "code" or from system/codeDict
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
 public:
 
diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C
index fc62b4bd044755187950c5400a70a635c850ff82..4f4f957e441e2bee6cb1347bd28e0b55b46fcde6 100644
--- a/src/OpenFOAM/global/argList/argList.C
+++ b/src/OpenFOAM/global/argList/argList.C
@@ -715,8 +715,7 @@ void Foam::argList::setCasePaths()
 
     if (optIter.found())
     {
-        caseDir = fileName::validate(optIter.val());
-        caseDir.clean();
+        caseDir = fileName::validate(optIter.val());  // includes 'clean'
 
         if (caseDir.empty() || caseDir == ".")
         {
@@ -1193,7 +1192,7 @@ void Foam::argList::parse
                 // Could also check for absolute path, but shouldn't be needed
                 if (adjustOpt)
                 {
-                    source.clean();
+                    source.clean();  // Remove unneeded ".."
                     options_.set("decomposeParDict", source);
                 }
             }
diff --git a/src/OpenFOAM/global/argList/argList.H b/src/OpenFOAM/global/argList/argList.H
index 75ffd9dbef4e70546118aed061336b7fce0d642b..e9d022b545505d058fb525fa8e04f3b25d67b09e 100644
--- a/src/OpenFOAM/global/argList/argList.H
+++ b/src/OpenFOAM/global/argList/argList.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -378,6 +378,7 @@ public:
 
         //- Get a value from the argument at index.
         //  Index 1 is the first (non-option) argument.
+        //  For fileName type, invokes fileName::validate()
         template<class T>
         inline T get(const label index) const;
 
@@ -388,6 +389,7 @@ public:
 
         //- Get a value from the named option
         //  The default template parameter is string (ie, no conversion).
+        //  For fileName type, invokes fileName::validate()
         template<class T=string>
         inline T get(const word& optName) const;
 
diff --git a/src/OpenFOAM/global/argList/argListI.H b/src/OpenFOAM/global/argList/argListI.H
index ed6f1fe59c5f9d8db2dce31ab4b2d0f18d1b13d1..83344cdf2d964fe37f7a8684d6d67ed776b52f89 100644
--- a/src/OpenFOAM/global/argList/argListI.H
+++ b/src/OpenFOAM/global/argList/argListI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -221,7 +221,7 @@ namespace Foam
     template<>
     inline fileName argList::get<Foam::fileName>(const label index) const
     {
-        return args_[index];
+        return fileName::validate(args_[index]);
     }
 
 
@@ -240,7 +240,7 @@ namespace Foam
     template<>
     inline fileName argList::get<Foam::fileName>(const word& optName) const
     {
-        return options_[optName];
+        return fileName::validate(options_[optName]);
     }
 }
 
diff --git a/src/OpenFOAM/meshes/Identifiers/DynamicID/DynamicID.H b/src/OpenFOAM/meshes/Identifiers/DynamicID/DynamicID.H
index 8ddb596f6dc84c8318f6d8720f5e063c6deaccc4..184b6fab2e939c524366d83cc0d7ea54808b1ac5 100644
--- a/src/OpenFOAM/meshes/Identifiers/DynamicID/DynamicID.H
+++ b/src/OpenFOAM/meshes/Identifiers/DynamicID/DynamicID.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -39,7 +39,7 @@ Description
 #ifndef DynamicID_H
 #define DynamicID_H
 
-#include "keyType.H"
+#include "wordRe.H"
 #include "labelList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -47,11 +47,6 @@ Description
 namespace Foam
 {
 
-// Forward declarations
-template<class> class DynamicID;
-template<class ObjectType>
-Ostream& operator<<(Ostream&, const DynamicID<ObjectType>&);
-
 /*---------------------------------------------------------------------------*\
                           Class DynamicID Declaration
 \*---------------------------------------------------------------------------*/
@@ -59,12 +54,12 @@ Ostream& operator<<(Ostream&, const DynamicID<ObjectType>&);
 template<class ObjectType>
 class DynamicID
 {
-    // Private data
+    // Private Data
 
-        //- Zone name
-        keyType key_;
+        //- Selector name
+        wordRe key_;
 
-        //- Zone indices
+        //- Selection indices
         labelList indices_;
 
 
@@ -72,77 +67,89 @@ public:
 
     // Constructors
 
-        //- Construct from name
-        DynamicID(const keyType& key, const ObjectType& obj)
+        //- Construct from selector name and object
+        DynamicID(const wordRe& key, const ObjectType& obj)
         :
             key_(key),
             indices_(obj.indices(key_))
         {}
 
-        //- Construct from Istream
-        DynamicID(Istream& is, const ObjectType& obj)
+        //- Construct from selector name and object
+        DynamicID(wordRe&& key, const ObjectType& obj)
         :
-            key_(is),
+            key_(std::move(key)),
             indices_(obj.indices(key_))
         {}
 
+        //- Construct from selector name and object
+        DynamicID(const word& key, const ObjectType& obj)
+        :
+            DynamicID(wordRe(key), obj)
+        {}
 
-    //- Destructor
-    ~DynamicID() = default;
+        //- Construct from selector name and object
+        DynamicID(const keyType& key, const ObjectType& obj)
+        :
+            DynamicID(wordRe(key), obj)
+        {}
 
+        //- Construct from Istream and object
+        DynamicID(Istream& is, const ObjectType& obj)
+        :
+            DynamicID(wordRe(is), obj)
+        {}
 
-    // Member Functions
 
-        // Access
+    //- Destructor
+    ~DynamicID() = default;
 
-            //- Return name
-            const keyType& name() const
-            {
-                return key_;
-            }
 
-            //- Return indices of matching zones
-            const labelList& indices() const
-            {
-                return indices_;
-            }
+    // Member Functions
 
-            //- Return index of first matching zone
-            label index() const
-            {
-                return indices_.empty() ? -1 : indices_.first();
-            }
+    // Access
 
-            //- Has the zone been found
-            bool active() const
-            {
-                return !indices_.empty();
-            }
+        //- The selector name
+        const wordRe& name() const noexcept
+        {
+            return key_;
+        }
 
+        //- The indices of matching items
+        const labelList& indices() const noexcept
+        {
+            return indices_;
+        }
 
-        // Edit
+        //- The index of the first matching items, -1 if no matches
+        label index() const
+        {
+            return indices_.empty() ? -1 : indices_.first();
+        }
 
-            //- Update
-            void update(const ObjectType& obj)
-            {
-                indices_ = obj.indices(key_);
-            }
+        //- Has the zone been found
+        bool active() const noexcept
+        {
+            return !indices_.empty();
+        }
 
 
-    // IOstream Operators
+    // Edit
 
-        friend Ostream& operator<< <ObjectType>
-        (Ostream&, const DynamicID<ObjectType>&);
+        //- Update
+        void update(const ObjectType& obj)
+        {
+            indices_ = obj.indices(key_);
+        }
 };
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 template<class ObjectType>
-Ostream& operator<<(Ostream& os, const DynamicID<ObjectType>& dynId)
+Ostream& operator<<(Ostream& os, const DynamicID<ObjectType>& obj)
 {
     os  << token::BEGIN_LIST
-        << dynId.name() << token::SPACE << dynId.index()
+        << obj.name() << token::SPACE << obj.index()
         << token::END_LIST;
 
     os.check(FUNCTION_NAME);
diff --git a/src/OpenFOAM/meshes/meshShapes/edge/edge.H b/src/OpenFOAM/meshes/meshShapes/edge/edge.H
index 720788b992e771dd63686c52ec2ac22bd0d42f5d..e029daf60dd1cefbf25d201f46594471dd8bbf20 100644
--- a/src/OpenFOAM/meshes/meshShapes/edge/edge.H
+++ b/src/OpenFOAM/meshes/meshShapes/edge/edge.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -285,32 +285,37 @@ public:
 
     // Hashing
 
-        //- The (commutative) hash-value for edge
-        inline unsigned hashval(unsigned seed=0) const
+        //- The (commutative) hash value for edge, hashes lower value first
+        inline unsigned hash_code(unsigned seed=0) const
         {
-            if (first() < second())
+            Foam::Hash<label> op;
+            if (second() < first())
             {
-                seed = Foam::Hash<label>()(first(), seed);
-                seed = Foam::Hash<label>()(second(), seed);
+                return op(first(), op(second(), seed));
             }
             else
             {
-                seed = Foam::Hash<label>()(second(), seed);
-                seed = Foam::Hash<label>()(first(), seed);
+                return op(second(), op(first(), seed));
             }
-            return seed;
         }
 
-        //- Hashing function class for edge (commutative)
+        //- Hashing functor for edge (commutative)
         //  Also useful for inheritance in sub-classes
-        template<class HashT=Foam::Hash<label>>
-        struct Hash
+        struct hasher
         {
-            inline unsigned operator()(const edge& obj, unsigned seed=0) const
+            unsigned operator()(const edge& obj, unsigned seed=0) const
             {
-                return obj.hashval(seed);
+                return obj.hash_code(seed);
             }
         };
+
+        //- Deprecated(2021-04) hashing functor. Use hasher()
+        // \deprecated(2021-04) - use hasher() functor
+        template<class Unused=bool>
+        struct Hash : edge::hasher
+        {
+            FOAM_DEPRECATED_FOR(2021-04, "hasher()") Hash() {}
+        };
 };
 
 
@@ -322,6 +327,9 @@ template<> struct is_contiguous<edge> : std::true_type {};
 //- Contiguous label data for edge (a pair of labels)
 template<> struct is_contiguous_label<edge> : std::true_type {};
 
+//- Hashing for edge uses commutative (incremental) hash
+template<> struct Hash<edge> : edge::hasher {};
+
 
 // * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
@@ -332,17 +340,6 @@ inline edge reverse(const edge& e)
 }
 
 
-//- Hash specialization for edge, using a commutative (incremental) hash.
-template<>
-struct Hash<edge>
-{
-    inline unsigned operator()(const edge& e, unsigned seed=0) const
-    {
-        return e.hashval(seed);
-    }
-};
-
-
 // * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * * //
 
 //- Compare edges for equal content, ignoring orientation
diff --git a/src/OpenFOAM/meshes/meshShapes/face/face.C b/src/OpenFOAM/meshes/meshShapes/face/face.C
index 868c13df89c502b03eda4a58f246b867c031ad0b..d7fd7c3d07703a185c1b0f52983e9227e46690d3 100644
--- a/src/OpenFOAM/meshes/meshShapes/face/face.C
+++ b/src/OpenFOAM/meshes/meshShapes/face/face.C
@@ -30,7 +30,6 @@ License
 #include "triFace.H"
 #include "triPointRef.H"
 #include "mathematicalConstants.H"
-#include "Swap.H"
 #include "ConstCirculator.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@@ -304,24 +303,18 @@ int Foam::face::compare(const face& a, const face& b)
     // will be circular in the same order (but not necessarily in the
     // same direction or from the same starting point).
 
-    // Trivial reject: faces are different size
-    label sizeA = a.size();
-    label sizeB = b.size();
+    const label sizeA = a.size();
+    const label sizeB = b.size();
 
+    // Trivial reject: faces are different size
     if (sizeA != sizeB || sizeA == 0)
     {
         return 0;
     }
     else if (sizeA == 1)
     {
-        if (a[0] == b[0])
-        {
-            return 1;
-        }
-        else
-        {
-            return 0;
-        }
+        // Trivial: face with a single vertex
+        return (a[0] == b[0] ? 1 : 0);
     }
 
     ConstCirculator<face> aCirc(a);
@@ -410,7 +403,7 @@ bool Foam::face::sameVertices(const face& a, const face& b)
     {
         return false;
     }
-    // Check faces with a single vertex
+    // Trivial: face with a single vertex
     else if (sizeA == 1)
     {
         return (a[0] == b[0]);
@@ -440,6 +433,57 @@ bool Foam::face::sameVertices(const face& a, const face& b)
 }
 
 
+unsigned Foam::face::symmhash_code(const UList<label>& f, unsigned seed)
+{
+    Foam::Hash<label> op;
+
+    label len = f.size();
+
+    if (!len)
+    {
+        // Trivial: zero-sized
+        return 0;
+    }
+    else if (len == 1)
+    {
+        // Trivial: single vertex
+        return op(f[0], seed);
+    }
+
+    // Find location of the min vertex
+    label pivot = 0;
+    for (label i = 1; i < len; ++i)
+    {
+        if (f[pivot] > f[i])
+        {
+            pivot = i;
+        }
+    }
+
+    // Use next lowest value for deciding direction to circulate
+    if (f.fcValue(pivot) < f.rcValue(pivot))
+    {
+        // Forward circulate
+        while (len--)
+        {
+            seed = op(f[pivot], seed);
+            pivot = f.fcIndex(pivot);
+        }
+    }
+    else
+    {
+        // Reverse circulate
+        while (len--)
+        {
+            seed = op(f[pivot], seed);
+            pivot = f.rcIndex(pivot);
+        }
+    }
+
+    return seed;
+}
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 Foam::label Foam::face::collapse()
diff --git a/src/OpenFOAM/meshes/meshShapes/face/face.H b/src/OpenFOAM/meshes/meshShapes/face/face.H
index 8d25990751d911fc07760cde580bba2a98db1e4b..9c5565fcf89b017b581b2c5d5f42d55c603569cd 100644
--- a/src/OpenFOAM/meshes/meshShapes/face/face.H
+++ b/src/OpenFOAM/meshes/meshShapes/face/face.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -421,20 +421,55 @@ public:
 
         //- Return true if the faces have the same vertices
         static bool sameVertices(const face& a, const face& b);
+
+
+    // Hashing
+
+        //- The symmetric hash value for face.
+        //  Starts with lowest vertex value and walks in the direction
+        //  of the next lowest value.
+        static unsigned symmhash_code(const UList<label>& f, unsigned seed=0);
+
+        //- The hash value for face.
+        //  Currently hashes as sequential contiguous data,
+        //  but could/should be commutative
+        inline unsigned hash_code(unsigned seed=0) const
+        {
+            return Foam::Hasher(this->cdata(), this->size_bytes(), seed);
+        }
+
+        //- The symmetric hash value for face.
+        //  Starts with lowest vertex value and walks in the direction
+        //  of the next lowest value.
+        inline unsigned symmhash_code(unsigned seed=0) const
+        {
+            return face::symmhash_code(*this, seed);
+        }
+
+        //- Hashing functor for face
+        struct hasher
+        {
+            unsigned operator()(const face& obj, unsigned seed=0) const
+            {
+                return obj.hash_code(seed);
+            }
+        };
+
+        //- Symmetric hashing functor for face
+        struct symmHasher
+        {
+            unsigned operator()(const face& obj, unsigned seed=0) const
+            {
+                return face::symmhash_code(obj, seed);
+            }
+        };
 };
 
 
 // * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
 
-//- Hash specialization for face
-template<>
-struct Hash<face>
-{
-    inline unsigned operator()(const face& obj, unsigned seed=0) const
-    {
-        return Hasher(obj.cdata(), obj.size()*sizeof(label), seed);
-    }
-};
+//- Hash face as contiguous (non-commutative) list data
+template<> struct Hash<face> : face::hasher {};
 
 
 //- Specialization to offset faces, used in ListListOps::combineOffset
diff --git a/src/OpenFOAM/meshes/meshShapes/triFace/triFace.H b/src/OpenFOAM/meshes/meshShapes/triFace/triFace.H
index af08fc4a48fb6babe8c61b044c078bb9ca0494b5..50fd55fe87a8bc1c63c9305067ecf05c2583d1b7 100644
--- a/src/OpenFOAM/meshes/meshShapes/triFace/triFace.H
+++ b/src/OpenFOAM/meshes/meshShapes/triFace/triFace.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -270,33 +270,35 @@ public:
 
     // Hashing
 
-        //- The (commutative) hash-value for triFace
-        inline unsigned hashval(unsigned seed=0) const
+        //- The (commutative) hash value for triFace
+        inline unsigned hash_code(unsigned seed=0) const
         {
             // Fortunately we don't need this very often
             const uLabel t0((*this)[0]);
             const uLabel t1((*this)[1]);
             const uLabel t2((*this)[2]);
 
-            const uLabel val = (t0*t1*t2 + t0+t1+t2);
+            const uLabel val(t0*t1*t2 + t0+t1+t2);
 
             return Foam::Hash<uLabel>()(val, seed);
         }
 
-        //- Hashing function class for triFace (commutative)
-        //  Also useful for inheritance in sub-classes
-        template<class HashT=Foam::Hash<label>>
-        struct Hash
+        //- Hashing functor for triFace (commutative)
+        struct hasher
         {
-            inline unsigned operator()
-            (
-                const triFace& obj,
-                unsigned seed=0
-            ) const
+            unsigned operator()(const triFace& obj, unsigned seed=0) const
             {
-                return obj.hashval(seed);
+                return obj.hash_code(seed);
             }
         };
+
+        //- Deprecated(2021-04) hashing functor. Use hasher()
+        // \deprecated(2021-04) - use hasher() functor
+        template<class Unused=bool>
+        struct Hash : triFace::hasher
+        {
+            FOAM_DEPRECATED_FOR(2021-04, "hasher()") Hash() {}
+        };
 };
 
 
@@ -308,16 +310,8 @@ template<> struct is_contiguous<triFace> : std::true_type {};
 //- Contiguous label data for triFace
 template<> struct is_contiguous_label<triFace> : std::true_type {};
 
-
-//- Hash specialization for triFace as a commutative hash value.
-template<>
-struct Hash<triFace>
-{
-    inline unsigned operator()(const triFace& obj, unsigned seed=0) const
-    {
-        return obj.hashval(seed);
-    }
-};
+//- Hashing for triFace uses commutative (incremental) hash
+template<> struct Hash<triFace> : triFace::hasher {};
 
 
 //- Specialization to offset faces, used in ListListOps::combineOffset
diff --git a/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.C b/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.C
index f8dff17d4c20c49b948578999e82518c49b6be65..2fee220290ca7e30358e8f60043c03f7b6a129f9 100644
--- a/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.C
+++ b/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -63,11 +63,11 @@ Foam::pointBoundaryMesh::pointBoundaryMesh
 
 Foam::labelList Foam::pointBoundaryMesh::indices
 (
-    const keyType& key,
+    const wordRe& matcher,
     const bool useGroups
 ) const
 {
-    return mesh()().boundaryMesh().indices(key, useGroups);
+    return mesh()().boundaryMesh().indices(matcher, useGroups);
 }
 
 
diff --git a/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.H b/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.H
index a7a852ae092b0c2d513a1a89ce5378c1257965fc..d6a470afcc4255a7d732ce58a1c4f043bac03894 100644
--- a/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.H
+++ b/src/OpenFOAM/meshes/pointMesh/pointBoundaryMesh/pointBoundaryMesh.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -94,14 +94,14 @@ public:
     // Member Functions
 
         //- Return the mesh reference
-        const pointMesh& mesh() const
+        const pointMesh& mesh() const noexcept
         {
             return mesh_;
         }
 
         //- Find patch indices given a name
         //  A no-op (returns empty list) for an empty key
-        labelList indices(const keyType& key, const bool useGroups) const;
+        labelList indices(const wordRe& matcher, const bool useGroups) const;
 
         //- Find patch index given a name
         //  A no-op (returns -1) for an empty patchName
@@ -118,7 +118,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices(const keyType& key, const bool useGroups) const
+        labelList findIndices(const wordRe& key, bool useGroups) const
         {
             return indices(key, useGroups);
         }
diff --git a/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.C b/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.C
index 001b19df7ba4b3955a3b4ef3ec9bb05715faa124..341c984bdcf9fb7b889fe3eca6e961c31a977574 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.C
@@ -604,20 +604,19 @@ Foam::labelRange Foam::polyBoundaryMesh::range(const label patchi) const
 
 Foam::labelList Foam::polyBoundaryMesh::indices
 (
-    const keyType& key,
+    const wordRe& matcher,
     const bool useGroups
 ) const
 {
-    if (key.empty())
+    if (matcher.empty())
     {
         return labelList();
     }
 
     DynamicList<label> patchIndices;
 
-    if (key.isPattern())
+    if (matcher.isPattern())
     {
-        const regExp matcher(key);
         patchIndices = PtrListOps::findMatching(*this, matcher);
 
         // Only examine patch groups if requested and when they exist.
@@ -648,7 +647,6 @@ Foam::labelList Foam::polyBoundaryMesh::indices
         // Literal string.
         // Special version of above for reduced memory footprint
 
-        const word& matcher = key;
         const label patchId = PtrListOps::firstMatching(*this, matcher);
 
         if (patchId >= 0)
@@ -659,7 +657,7 @@ Foam::labelList Foam::polyBoundaryMesh::indices
         // Only examine patch groups if requested and when they exist.
         if (useGroups && !groupPatchIDs().empty())
         {
-            const auto iter = groupPatchIDs().cfind(key);
+            const auto iter = groupPatchIDs().cfind(matcher);
 
             if (iter.found())
             {
@@ -676,24 +674,13 @@ Foam::labelList Foam::polyBoundaryMesh::indices
 }
 
 
-Foam::label Foam::polyBoundaryMesh::findIndex(const keyType& key) const
+Foam::label Foam::polyBoundaryMesh::findIndex(const wordRe& key) const
 {
     if (key.empty())
     {
         return -1;
     }
-    else if (key.isPattern())
-    {
-        // Find as regex
-        const regExp matcher(key);
-        return PtrListOps::firstMatching(*this, matcher);
-    }
-    else
-    {
-        // Find as literal string
-        const word& matcher = key;
-        return PtrListOps::firstMatching(*this, matcher);
-    }
+    return PtrListOps::firstMatching(*this, key);
 }
 
 
diff --git a/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.H b/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.H
index c3d0b0f4b56ec4212a86ffca6c2177fa0230e42e..8f90c0f8f5f0558bde5face2b2b51f1fd344a81e 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.H
+++ b/src/OpenFOAM/meshes/polyMesh/polyBoundaryMesh/polyBoundaryMesh.H
@@ -132,6 +132,7 @@ public:
     //- Destructor
     ~polyBoundaryMesh() = default;
 
+
         //- Clear geometry at this level and at patches
         void clearGeom();
 
@@ -142,7 +143,7 @@ public:
     // Member Functions
 
         //- Return the mesh reference
-        const polyMesh& mesh() const
+        const polyMesh& mesh() const noexcept
         {
             return mesh_;
         }
@@ -200,14 +201,14 @@ public:
         //  A no-op (returns empty list) for an empty key
         labelList indices
         (
-            const keyType& key,
+            const wordRe& matcher,
             const bool useGroups = true
         ) const;
 
 
         //- Return patch index for the first match, return -1 if not found
         //  A no-op (returns -1) for an empty key
-        label findIndex(const keyType& key) const;
+        label findIndex(const wordRe& key) const;
 
         //- Find patch index given a name, return -1 if not found
         //  A no-op (returns -1) for an empty patchName
@@ -296,7 +297,7 @@ public:
         polyPatch& operator[](const word& patchName);
 
 
-    // Ostream operator
+    // Ostream Operator
 
         friend Ostream& operator<<(Ostream& os, const polyBoundaryMesh& pbm);
 
@@ -305,11 +306,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices
-        (
-            const keyType& key,
-            const bool useGroups = true
-        ) const
+        labelList findIndices(const wordRe& key, bool useGroups=true) const
         {
             return this->indices(key, useGroups);
         }
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processorCyclic/processorCyclicPolyPatch.C b/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processorCyclic/processorCyclicPolyPatch.C
index 939f26210abf29a64886b3d58011bd7d0cf973ce..975b078f18678b9258b2b0df413dd39a1e7375c4 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processorCyclic/processorCyclicPolyPatch.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processorCyclic/processorCyclicPolyPatch.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -181,10 +181,10 @@ Foam::labelList Foam::processorCyclicPolyPatch::patchIDs
 {
     return bm.indices
     (
-        keyType
+        wordRe
         (
             "procBoundary.*to.*through" + cyclicPolyPatchName,
-            keyType::REGEX
+            wordRe::REGEX
         )
     );
 }
@@ -203,11 +203,11 @@ int Foam::processorCyclicPolyPatch::tag() const
 
         if (owner())
         {
-            tag_ = string::hash()(cycPatch.name()) % 32768u;
+            tag_ = string::hasher()(cycPatch.name()) % 32768u;
         }
         else
         {
-            tag_ = string::hash()(cycPatch.neighbPatch().name()) % 32768u;
+            tag_ = string::hasher()(cycPatch.neighbPatch().name()) % 32768u;
         }
 
         if (tag_ == Pstream::msgType() || tag_ == -1)
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/derived/wall/wallPolyPatch.C b/src/OpenFOAM/meshes/polyMesh/polyPatches/derived/wall/wallPolyPatch.C
index 5036e075400dd31c513ab1273d74f3b2fad16625..d556ac044d47e88230d37d56ba2dba30e70232e3 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/derived/wall/wallPolyPatch.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/derived/wall/wallPolyPatch.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -53,10 +54,7 @@ Foam::wallPolyPatch::wallPolyPatch
     polyPatch(name, size, start, index, bm, patchType)
 {
     //  wall is not constraint type so add wall group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
@@ -72,10 +70,7 @@ Foam::wallPolyPatch::wallPolyPatch
     polyPatch(name, dict, index, bm, patchType)
 {
     //  wall is not constraint type so add wall group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
index 6db7dc22fcb8cb648992b24ff9cbfe31edaeb668..39ab12285e380ab47643da7d01eac1d4e0f29c7b 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/polyPatch/polyPatch.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -100,14 +100,9 @@ Foam::polyPatch::polyPatch
     faceCellsPtr_(nullptr),
     mePtr_(nullptr)
 {
-    if
-    (
-        patchType != word::null
-     && constraintType(patchType)
-     && !inGroups().found(patchType)
-    )
+    if (!patchType.empty() && constraintType(patchType))
     {
-        inGroups().append(patchType);
+        inGroups().appendUniq(patchType);
     }
 }
 
@@ -161,14 +156,9 @@ Foam::polyPatch::polyPatch
     faceCellsPtr_(nullptr),
     mePtr_(nullptr)
 {
-    if
-    (
-        patchType != word::null
-     && constraintType(patchType)
-     && !inGroups().found(patchType)
-    )
+    if (!patchType.empty() && constraintType(patchType))
     {
-        inGroups().append(patchType);
+        inGroups().appendUniq(patchType);
     }
 }
 
diff --git a/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.C b/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.C
index c2386312c52bf4b33d0d6e4220444e7974df2b9a..2084d5678220353930c59790b001e6d130419072 100644
--- a/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.C
+++ b/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.C
@@ -311,25 +311,14 @@ const
 template<class ZoneType, class MeshType>
 Foam::labelList Foam::ZoneMesh<ZoneType, MeshType>::indices
 (
-    const keyType& key
+    const wordRe& matcher
 ) const
 {
-    if (key.empty())
+    if (matcher.empty())
     {
         return labelList();
     }
-    else if (key.isPattern())
-    {
-        // Match as regex
-        const regExp matcher(key);
-        return PtrListOps::findMatching(*this, matcher);
-    }
-    else
-    {
-        // Compare as literal string
-        const word& matcher = key;
-        return PtrListOps::findMatching(*this, matcher);
-    }
+    return PtrListOps::findMatching(*this, matcher);
 }
 
 
@@ -350,25 +339,14 @@ Foam::labelList Foam::ZoneMesh<ZoneType, MeshType>::indices
 template<class ZoneType, class MeshType>
 Foam::label Foam::ZoneMesh<ZoneType, MeshType>::findIndex
 (
-    const keyType& key
+    const wordRe& key
 ) const
 {
     if (key.empty())
     {
         return -1;
     }
-    else if (key.isPattern())
-    {
-        // Find as regex
-        const regExp matcher(key);
-        return PtrListOps::firstMatching(*this, matcher);
-    }
-    else
-    {
-        // Find as literal string
-        const word& matcher = key;
-        return PtrListOps::firstMatching(*this, matcher);
-    }
+    return PtrListOps::firstMatching(*this, key);
 }
 
 
@@ -378,7 +356,11 @@ Foam::label Foam::ZoneMesh<ZoneType, MeshType>::findIndex
     const wordRes& matcher
 ) const
 {
-    return (matcher.empty() ? -1 : PtrListOps::firstMatching(*this, matcher));
+    if (matcher.empty())
+    {
+        return -1;
+    }
+    return PtrListOps::firstMatching(*this, matcher);
 }
 
 
@@ -495,11 +477,11 @@ Foam::bitSet Foam::ZoneMesh<ZoneType, MeshType>::selection
 template<class ZoneType, class MeshType>
 Foam::bitSet Foam::ZoneMesh<ZoneType, MeshType>::selection
 (
-    const keyType& key
+    const wordRe& matcher
 ) const
 {
-    // key.empty() is handled by indices()
-    return this->selection(this->indices(key));
+    // matcher.empty() is handled by indices()
+    return this->selection(this->indices(matcher));
 }
 
 
diff --git a/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.H b/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.H
index a6dffca158d610783d2ec765c1f431d17f128376..e4c0960a1df3061ac3ee51d80636f0291853d652 100644
--- a/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.H
+++ b/src/OpenFOAM/meshes/polyMesh/zones/ZoneMesh/ZoneMesh.H
@@ -38,10 +38,10 @@ SourceFiles
 #ifndef ZoneMesh_H
 #define ZoneMesh_H
 
-#include "List.H"
 #include "regIOobject.H"
 #include "pointField.H"
 #include "Map.H"
+#include "PtrList.H"
 #include "bitSet.H"
 #include "wordRes.H"
 
@@ -51,7 +51,6 @@ namespace Foam
 {
 
 // Forward Declarations
-
 template<class ZoneType, class MeshType> class ZoneMesh;
 
 template<class ZoneType, class MeshType>
@@ -131,7 +130,7 @@ public:
     // Member Functions
 
         //- Return the mesh reference
-        const MeshType& mesh() const
+        const MeshType& mesh() const noexcept
         {
             return mesh_;
         }
@@ -167,8 +166,8 @@ public:
 
 
         //- Return zone indices for all matches
-        //  A no-op (returns empty list) for an empty key
-        labelList indices(const keyType& key) const;
+        //  A no-op (returns empty list) for an empty matcher
+        labelList indices(const wordRe& matcher) const;
 
         //- Return zone indices for all matches
         //  A no-op (returns empty list) for an empty matcher
@@ -176,7 +175,7 @@ public:
 
         //- Zone index for the first match, return -1 if not found
         //  A no-op (returns -1) for an empty key
-        label findIndex(const keyType& key) const;
+        label findIndex(const wordRe& key) const;
 
         //- Zone index for the first match, return -1 if not found
         //  A no-op (returns -1) for an empty matcher
@@ -205,8 +204,8 @@ public:
         //- specification as a bitSet.
         //  The bitSet is empty (zero-size) if there are no elements matched
         //  anywhere.
-        //  A no-op (returns empty bitSet) for an empty key
-        bitSet selection(const keyType& key) const;
+        //  A no-op (returns empty bitSet) for an empty matcher
+        bitSet selection(const wordRe& matcher) const;
 
         //- Return all elements (cells, faces, points) that match the zone
         //- specification as a bitSet.
@@ -288,7 +287,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices(const keyType& key) const
+        labelList findIndices(const wordRes& key) const
         {
             return indices(key);
         }
diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellEdges.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellEdges.C
index fbb4bd57d92ab90e2ade6af7e64deadbb826bedf..7788c1388f37b00f2c515dff76a608ad8d77d37a 100644
--- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellEdges.C
+++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellEdges.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -77,13 +78,10 @@ void Foam::primitiveMesh::calcCellEdges() const
 
             const labelList& curEdges = fe[facei];
 
-            forAll(curEdges, edgeI)
+            for (const label edgei : curEdges)
             {
-                if (!curCellEdges.found(curEdges[edgeI]))
-                {
-                    // Add the edge
-                    curCellEdges.append(curEdges[edgeI]);
-                }
+                // Add the edge
+                curCellEdges.appendUniq(edgei);
             }
         }
 
@@ -93,13 +91,10 @@ void Foam::primitiveMesh::calcCellEdges() const
 
             const labelList& curEdges = fe[facei];
 
-            forAll(curEdges, edgeI)
+            for (const label edgei : curEdges)
             {
-                if (!curCellEdges.found(curEdges[edgeI]))
-                {
-                    // add the edge
-                    curCellEdges.append(curEdges[edgeI]);
-                }
+                // Add the edge
+                curCellEdges.appendUniq(edgei);
             }
         }
 
diff --git a/src/OpenFOAM/primitives/Pair/Pair.H b/src/OpenFOAM/primitives/Pair/Pair.H
index 7420d1bfc5bd24187838cadd4787518f724d263e..9a9458bc2280d6ca1cf52211493fcc73e5181161 100644
--- a/src/OpenFOAM/primitives/Pair/Pair.H
+++ b/src/OpenFOAM/primitives/Pair/Pair.H
@@ -147,28 +147,21 @@ public:
 
     // Hashing
 
-        //- Symmetrical hashing for Pair data.
-        //  The lower value is hashed first.
-        template<class HashT=Foam::Hash<T>>
-        struct SymmHash
+        //- Symmetric hashing functor for Pair, hashes lower value first
+        //  Regular hasher inherited from FixedList
+        struct symmHasher
         {
-            inline unsigned operator()
-            (
-                const Pair<T>& obj,
-                unsigned seed=0
-            ) const
+            unsigned operator()(const Pair<T>& obj, unsigned seed=0) const
             {
-                if (obj.first() < obj.second())
+                Foam::Hash<T> op;
+                if (obj.second() < obj.first())
                 {
-                    seed = HashT()(obj.first(), seed);
-                    seed = HashT()(obj.second(), seed);
+                    return op(obj.first(), op(obj.second(), seed));
                 }
                 else
                 {
-                    seed = HashT()(obj.second(), seed);
-                    seed = HashT()(obj.first(), seed);
+                    return op(obj.second(), op(obj.first(), seed));
                 }
-                return seed;
             }
         };
 };
@@ -188,28 +181,12 @@ struct is_contiguous_label<Pair<T>> : is_contiguous_label<T> {};
 template<class T>
 struct is_contiguous_scalar<Pair<T>> : is_contiguous_scalar<T> {};
 
-
-// * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
-
-//- Hashing for Pair data, which uses Hasher for contiguous data and
-//- element-wise incrementally hashing otherwise.
+//- Hashing for Pair of data
 template<class T>
-struct Hash<Pair<T>>
-{
-    inline unsigned operator()(const Pair<T>& obj, unsigned seed=0) const
-    {
-        if (is_contiguous<T>::value)
-        {
-            return Hasher(obj.cdata(), obj.size_bytes(), seed);
-        }
-
-        seed = Hash<T>()(obj.first(), seed);
-        seed = Hash<T>()(obj.second(), seed);
+struct Hash<Pair<T>> : Pair<T>::hasher {};
 
-        return seed;
-    }
-};
 
+// * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
 //- Return reverse of a Pair
 template<class T>
diff --git a/src/OpenFOAM/primitives/Pair/labelPairHashes.H b/src/OpenFOAM/primitives/Pair/labelPairHashes.H
index 82c3ead58d3cc8e8977877103ceb4b70882186fe..cd408953585c1a31c035d1c7527163a73ac6e03a 100644
--- a/src/OpenFOAM/primitives/Pair/labelPairHashes.H
+++ b/src/OpenFOAM/primitives/Pair/labelPairHashes.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,19 +52,19 @@ Description
 #ifndef labelPairHashes_H
 #define labelPairHashes_H
 
-#include "HashSet.H"
 #include "labelPair.H"
+#include "HashSet.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
     template<class T>
-    using LabelPairMap = HashTable<T, labelPair, labelPair::Hash<>>;
+    using LabelPairMap = HashTable<T, labelPair, Foam::Hash<labelPair>>;
 
-    typedef HashSet<labelPair, labelPair::Hash<>> labelPairHashSet;
+    typedef HashSet<labelPair, Foam::Hash<labelPair>> labelPairHashSet;
 
-    typedef HashTable<label, labelPair, labelPair::Hash<>> labelPairLookup;
+    typedef HashTable<label, labelPair, Foam::Hash<labelPair>> labelPairLookup;
 }
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/primitives/Pair/wordPairHashes.H b/src/OpenFOAM/primitives/Pair/wordPairHashes.H
new file mode 100644
index 0000000000000000000000000000000000000000..92eaf28a51f5de2a2dcb54a058211629f557595d
--- /dev/null
+++ b/src/OpenFOAM/primitives/Pair/wordPairHashes.H
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2015 OpenFOAM Foundation
+    Copyright (C) 2021 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/>.
+
+Alias
+    Foam::WordPairMap
+    A HashTable to objects of type \<T\> with a wordPair key.
+
+Typedef
+    Foam::wordPairHashSet
+
+Description
+    A HashSet for a wordPair.
+
+Typedef
+    Foam::wordPairHashTable
+
+Description
+    HashTable of wordPair
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef wordPairHashes_H
+#define wordPairHashes_H
+
+#include "wordPair.H"
+#include "HashSet.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    template<class T>
+    using WordPairMap = HashTable<T, wordPair, Foam::Hash<wordPair>>;
+
+    typedef HashSet<wordPair, Foam::Hash<wordPair>> wordPairHashSet;
+
+    typedef HashTable<word, wordPair, Foam::Hash<wordPair>> wordPairHashTable;
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/Tuple2/Tuple2.H b/src/OpenFOAM/primitives/Tuple2/Tuple2.H
index 244eb7da6a8af9b2cd053dd25228f6cb9425fda2..874f34945d8eb1eb53731c91eaf31150fcf1a3df 100644
--- a/src/OpenFOAM/primitives/Tuple2/Tuple2.H
+++ b/src/OpenFOAM/primitives/Tuple2/Tuple2.H
@@ -141,6 +141,29 @@ public:
 };
 
 
+// * * * * * * * * * * * * * * * * * Traits  * * * * * * * * * * * * * * * * //
+
+//- Hashing for Tuple2 data
+template<class T1, class T2>
+struct Hash<Tuple2<T1, T2>>
+{
+    unsigned operator()(const Tuple2<T1, T2>& obj, unsigned seed=0) const
+    {
+        return Hash<T2>()(obj.second(), Hash<T1>()(obj.first(), seed));
+    }
+};
+
+//- Hashing for std::pair data
+template<class T1, class T2>
+struct Hash<std::pair<T1, T2>>
+{
+    unsigned operator()(const std::pair<T1, T2>& obj, unsigned seed=0) const
+    {
+        return Hash<T2>()(obj.second, Hash<T1>()(obj.first, seed));
+    }
+};
+
+
 // * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
 
 //- Return reverse of a Tuple2
diff --git a/src/OpenFOAM/primitives/chars/char/char.H b/src/OpenFOAM/primitives/chars/char/char.H
index c8efbbc39e25202c6af8d6e2711fcb1518267c49..03cb15ddfb84f8a1b7c526b7b86db53cd8cc8092 100644
--- a/src/OpenFOAM/primitives/chars/char/char.H
+++ b/src/OpenFOAM/primitives/chars/char/char.H
@@ -61,15 +61,17 @@ Ostream& operator<<(Ostream& os, const char c);
 //- Write a nul-terminated C-string
 Ostream& operator<<(Ostream& os, const char* str);
 
-//- Test for whitespace
-inline bool isspace(char c)
+//- Test for whitespace (C-locale)
+inline bool isspace(char c) noexcept
 {
     return
     (
-        c == ' '
-     || c == '\n'
-     || c == '\r'
-     || c == '\t'
+        c == ' '    // (0x20)  space (SPC)
+     || c == '\t'   // (0x09)  horizontal tab (TAB)
+     || c == '\n'   // (0x0a)  newline (LF)
+     || c == '\v'   // (0x0b)  vertical tab (VT)
+     || c == '\f'   // (0x0c)  feed (FF)
+     || c == '\r'   // (0x0d)  carriage return (CR)
     );
 }
 
diff --git a/src/OpenFOAM/primitives/enums/Enum.H b/src/OpenFOAM/primitives/enums/Enum.H
index c3a4a012e29af992043a2e6f5d6b0f0e7d150340..8321f612c7a1f0850940d9c6e82ff1420ec90975 100644
--- a/src/OpenFOAM/primitives/enums/Enum.H
+++ b/src/OpenFOAM/primitives/enums/Enum.H
@@ -41,7 +41,6 @@ SourceFiles
 #define Enum_H
 
 #include "wordList.H"
-#include <initializer_list>
 #include <ostream>
 #include <utility>
 
diff --git a/src/OpenFOAM/primitives/hashes/Hash/Hash.H b/src/OpenFOAM/primitives/hashes/Hash/Hash.H
index 6dfc907cb9ed8918c9e0247d6210fa1a1d6ab98a..5329080bf0c56ae4f52255d82c846eb31478d04d 100644
--- a/src/OpenFOAM/primitives/hashes/Hash/Hash.H
+++ b/src/OpenFOAM/primitives/hashes/Hash/Hash.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2012 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,19 +29,22 @@ Class
 
 Description
     Hash function class.
-    The default definition is for primitives, non-primitives used to hash
-    entries on hash tables likely need a specialized version of this class.
+    The default definition is for primitives.
+    Non-primitives used to hash entries on hash tables will likely need
+    a specialized version.
+
+Note
+    The second template parameter (bool) is used for SFINAE overloading,
 
 \*---------------------------------------------------------------------------*/
 
 #ifndef Hash_H
 #define Hash_H
 
-#include "label.H"
-#include "uLabel.H"
 #include "Hasher.H"
-#include "fileName.H"
-#include "wordRe.H"
+#include <cstdint>
+#include <string>
+#include <type_traits>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -52,107 +55,82 @@ namespace Foam
                             Class Hash Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class T>
+template<class T, class SFINAEType=bool>
 struct Hash
 {
-    inline unsigned operator()(const T& obj, unsigned seed=0) const
+    unsigned operator()(const T& obj, unsigned seed=0) const
     {
-        return Hasher(&obj, sizeof(obj), seed);
+        return Foam::Hasher(&obj, sizeof(T), seed);
     }
 };
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-//- Hash specialization for label
-template<>
-struct Hash<Foam::label>
-{
-    //- Incrementally hash a label.
-    //  This will necessarily return a different value than the
-    //  non-incremental version.
-    inline unsigned operator()(const label obj, unsigned seed) const
-    {
-        return Hasher(&obj, sizeof(label), seed);
-    }
-
-    //- Return the unsigned representation of a label.
-    //  This helps if people have relied on the hash value corresponding to
-    //  the natural order.
-    inline unsigned operator()(const label obj) const
-    {
-        return obj;
-    }
-};
-
-
-//- Hash specialization for string
-template<>
-struct Hash<Foam::string>
-{
-    inline unsigned operator()(const string& obj, unsigned seed=0) const
-    {
-        return string::hash()(obj, seed);
+// Specialization for trivial (integer) types
+
+#undef  FOAM_INTHASHER
+#define FOAM_INTHASHER(IntType)                                               \
+    /*! \brief Hashing specialization for IntType */                          \
+    /*! Unseeded (single value) uses natural order, otherwise incremental */  \
+    template<> struct Hash<IntType>                                           \
+    {                                                                         \
+        unsigned operator()(const IntType val) const                          \
+        {                                                                     \
+            return static_cast<unsigned>(val);                                \
+        }                                                                     \
+        unsigned operator()(const IntType val, unsigned seed) const           \
+        {                                                                     \
+            return Foam::Hasher(&val, sizeof(IntType), seed);                 \
+        }                                                                     \
     }
-};
 
+FOAM_INTHASHER(bool);
+FOAM_INTHASHER(char);
+FOAM_INTHASHER(int32_t);
+FOAM_INTHASHER(int64_t);
+FOAM_INTHASHER(uint32_t);
 
-//- Hash specialization for word
-template<>
-struct Hash<Foam::word>
-{
-    inline unsigned operator()(const word& obj, unsigned seed=0) const
-    {
-        return string::hash()(obj, seed);
-    }
-};
+#undef FOAM_INTHASHER
 
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-//- Hash specialization for fileName
+//- Hashing specialization for nullptr. Always 0
 template<>
-struct Hash<Foam::fileName>
+struct Hash<std::nullptr_t>
 {
-    inline unsigned operator()(const fileName& obj, unsigned seed=0) const
+    unsigned operator()(std::nullptr_t, unsigned seed=0) const noexcept
     {
-        return string::hash()(obj, seed);
+        return seed;
     }
 };
 
-
-//- Hash specialization for wordRe
+//- Hashing specialization for pointers, interpret pointer as a integer type
 template<>
-struct Hash<Foam::wordRe>
+struct Hash<void*>
 {
-    inline unsigned operator()(const wordRe& obj, unsigned seed=0) const
+    unsigned operator()(const void* const ptr, unsigned seed=0) const
     {
-        return string::hash()(obj, seed);
+        const uintptr_t addr = uintptr_t(ptr);
+        return Foam::Hasher(&addr, sizeof(addr), seed);
     }
 };
 
 
-//- Hash specialization for keyType
-template<>
-struct Hash<Foam::keyType>
-{
-    inline unsigned operator()(const keyType& obj, unsigned seed=0) const
-    {
-        return string::hash()(obj, seed);
-    }
-};
-
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-//- Hash specialization for pointers, interpret pointer as a integer type.
-template<>
-struct Hash<void*>
+//- Hashing partial specialization for derived string types
+template<class StringType>
+struct Hash
+<
+    StringType,
+    typename std::enable_if
+    <std::is_base_of<std::string, StringType>::value, bool>::type
+>
 {
-    inline unsigned operator()(const void* const& obj, unsigned seed) const
-    {
-        return Hash<intptr_t>()(intptr_t(obj), seed);
-    }
-
-    inline unsigned operator()(const void* const& obj) const
+    unsigned operator()(const std::string& str, unsigned seed=0) const
     {
-        return Hash<intptr_t>()(intptr_t(obj));
+        return Foam::Hasher(str.data(), str.length(), seed);
     }
 };
 
diff --git a/src/OpenFOAM/primitives/hashes/Hash/HashFwd.H b/src/OpenFOAM/primitives/hashes/Hash/HashFwd.H
index e209be86128f42f5037d693c647d7cf6c5cec98c..cd1a9e00d77d1f7d832c27217c6d1558ccbb0a5e 100644
--- a/src/OpenFOAM/primitives/hashes/Hash/HashFwd.H
+++ b/src/OpenFOAM/primitives/hashes/Hash/HashFwd.H
@@ -1,47 +1,14 @@
 /*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2018 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/>.
-
 Description
-    Forward definition for Hash function class and the definition for
-    the Hasher function.
+    Compatibility include.
+    Previous support for forward declarations is no longer needed.
 
 \*---------------------------------------------------------------------------*/
 
 #ifndef HashFwd_H
 #define HashFwd_H
 
-#include "Hasher.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    template<class T> struct Hash;
-}
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+#include "Hash.H"
 
 #endif
 
diff --git a/src/OpenFOAM/primitives/hashes/Hasher/Hasher.C b/src/OpenFOAM/primitives/hashes/Hash/Hasher.C
similarity index 99%
rename from src/OpenFOAM/primitives/hashes/Hasher/Hasher.C
rename to src/OpenFOAM/primitives/hashes/Hash/Hasher.C
index cc8a0af325b7313b33fbc0c1abd79639f2f89e3e..a3bce4fa837e4ae9055c7999e3922446d34e5be3 100644
--- a/src/OpenFOAM/primitives/hashes/Hasher/Hasher.C
+++ b/src/OpenFOAM/primitives/hashes/Hash/Hasher.C
@@ -159,6 +159,114 @@ Description
     }
 
 
+// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
+
+// ----------------------------------------------------------------------------
+//  This works on all machines.  To be useful, it requires
+//  -- that the key be an array of uint32_t's, and
+//  -- that the length be the number of uint32_t's in the key
+//
+//  The function hashword() is identical to hashlittle() on little-endian
+//  machines, and identical to hashbig() on big-endian machines,
+//  except that the length has to be measured in uint32_ts rather than in
+//  bytes.  hashlittle() is more complicated than hashword() only because
+//  hashlittle() has to dance around fitting the key bytes into registers.
+// ----------------------------------------------------------------------------
+unsigned Foam::HasherInt
+(
+    const uint32_t *k,
+    size_t length,
+    unsigned seed
+)
+{
+    uint32_t a, b, c;
+
+    // Set up the internal state
+    a = b = c = 0xdeadbeef + (static_cast<uint32_t>(length) << 2) + seed;
+
+    // handle most of the key
+    while (length > 3)
+    {
+        a += k[0];
+        b += k[1];
+        c += k[2];
+        bitMixer(a,b,c);
+        length -= 3;
+        k += 3;
+    }
+
+    // handle the last 3 uint32_t's
+    switch (length)  // all case statements fall through
+    {
+        case 3 : c += k[2]; [[fallthrough]];
+        case 2 : b += k[1]; [[fallthrough]];
+        case 1 : a += k[0];
+            bitMixerFinal(a,b,c);
+            [[fallthrough]];
+        case 0 :  // case 0: nothing left to add
+            break;
+    }
+
+    return c;
+}
+
+
+// ----------------------------------------------------------------------------
+// hashword2() -- same as hashword(), but take two seeds and return two
+// 32-bit values.  pc and pb must both be non-null, and *pc and *pb must
+// both be initialized with seeds.  If you pass in (*pb)==0, the output
+// (*pc) will be the same as the return value from hashword().
+// ----------------------------------------------------------------------------
+
+// Currently unused
+#if 0
+unsigned Foam::HasherDual
+(
+    const uint32_t *k,
+    size_t length,
+    unsigned& hash1,  // IN: seed OUT: primary hash value
+    unsigned& hash2   // IN: more seed OUT: secondary hash value
+)
+{
+    uint32_t a, b, c;
+
+    // Set up the internal state
+    a = b = c = 0xdeadbeef + (static_cast<uint32_t>(length) << 2) + hash1;
+    c += hash2;
+
+    // handle most of the key
+    while (length > 3)
+    {
+        a += k[0];
+        b += k[1];
+        c += k[2];
+        bitMixer(a,b,c);
+        length -= 3;
+        k += 3;
+    }
+
+    // handle the last 3 uint32_t's
+    switch (length)  // all case statements fall through
+    {
+        case 3 : c += k[2]; [[fallthrough]];
+        case 2 : b += k[1]; [[fallthrough]];
+        case 1 : a += k[0];
+            bitMixerFinal(a,b,c);
+            [[fallthrough]];
+        case 0 :  // case 0: nothing left to add
+            break;
+    }
+
+    // report the result
+    hash1 = c;
+    hash2 = b;
+
+    // return primary hash value
+    return c;
+}
+#endif
+
+
 // * * * * * * * * * * * * * * Static Functions  * * * * * * * * * * * * * * //
 
 // ----------------------------------------------------------------------------
@@ -304,6 +412,7 @@ static unsigned jenkins_hashlittle
     }
     else
     {
+        // need to read the key one byte at a time
         const uint8_t *k = reinterpret_cast<const uint8_t*>(key);
 
         // all but the last block: affect some 32 bits of (a,b,c)
@@ -468,7 +577,6 @@ static unsigned jenkins_hashbig
 
 // * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
 
-
 unsigned Foam::Hasher
 (
     const void *key,
@@ -486,106 +594,4 @@ unsigned Foam::Hasher
 }
 
 
-// ----------------------------------------------------------------------------
-//  This works on all machines.  To be useful, it requires
-//  -- that the key be an array of uint32_t's, and
-//  -- that the length be the number of uint32_t's in the key
-//
-//  The function hashword() is identical to hashlittle() on little-endian
-//  machines, and identical to hashbig() on big-endian machines,
-//  except that the length has to be measured in uint32_ts rather than in
-//  bytes.  hashlittle() is more complicated than hashword() only because
-//  hashlittle() has to dance around fitting the key bytes into registers.
-// ----------------------------------------------------------------------------
-unsigned Foam::HasherInt
-(
-    const uint32_t *k,
-    size_t length,
-    unsigned seed
-)
-{
-    uint32_t a, b, c;
-
-    // Set up the internal state
-    a = b = c = 0xdeadbeef + (static_cast<uint32_t>(length) << 2) + seed;
-
-    // handle most of the key
-    while (length > 3)
-    {
-        a += k[0];
-        b += k[1];
-        c += k[2];
-        bitMixer(a,b,c);
-        length -= 3;
-        k += 3;
-    }
-
-    // handle the last 3 uint32_t's
-    switch (length)  // all case statements fall through
-    {
-        case 3 : c += k[2]; [[fallthrough]];
-        case 2 : b += k[1]; [[fallthrough]];
-        case 1 : a += k[0];
-            bitMixerFinal(a,b,c);
-            [[fallthrough]];
-        case 0 :  // case 0: nothing left to add
-            break;
-    }
-
-    return c;
-}
-
-
-// ----------------------------------------------------------------------------
-// hashword2() -- same as hashword(), but take two seeds and return two
-// 32-bit values.  pc and pb must both be non-null, and *pc and *pb must
-// both be initialized with seeds.  If you pass in (*pb)==0, the output
-// (*pc) will be the same as the return value from hashword().
-// ----------------------------------------------------------------------------
-unsigned Foam::HasherDual
-(
-    const uint32_t *k,
-    size_t length,
-    unsigned& hash1,  // IN: seed OUT: primary hash value
-    unsigned& hash2   // IN: more seed OUT: secondary hash value
-)
-{
-    uint32_t a, b, c;
-
-    // Set up the internal state
-    a = b = c = 0xdeadbeef + (static_cast<uint32_t>(length) << 2) + hash1;
-    c += hash2;
-
-    // handle most of the key
-    while (length > 3)
-    {
-        a += k[0];
-        b += k[1];
-        c += k[2];
-        bitMixer(a,b,c);
-        length -= 3;
-        k += 3;
-    }
-
-    // handle the last 3 uint32_t's
-    switch (length)  // all case statements fall through
-    {
-        case 3 : c += k[2]; [[fallthrough]];
-        case 2 : b += k[1]; [[fallthrough]];
-        case 1 : a += k[0];
-            bitMixerFinal(a,b,c);
-            [[fallthrough]];
-        case 0 :  // case 0: nothing left to add
-            break;
-    }
-
-    // report the result
-    hash1 = c;
-    hash2 = b;
-
-    // return primary hash value
-    return c;
-}
-
-
 // ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/hashes/Hasher/Hasher.H b/src/OpenFOAM/primitives/hashes/Hash/Hasher.H
similarity index 86%
rename from src/OpenFOAM/primitives/hashes/Hasher/Hasher.H
rename to src/OpenFOAM/primitives/hashes/Hash/Hasher.H
index ff08655a32885bca3caa645f11c2ff84cb4c72b6..fb31cdb9f5f4887feaccd05b5ad3f7c5dcd0a636 100644
--- a/src/OpenFOAM/primitives/hashes/Hasher/Hasher.H
+++ b/src/OpenFOAM/primitives/hashes/Hash/Hasher.H
@@ -27,7 +27,7 @@ InNamespace
     Foam
 
 Description
-    Misc. hashing functions, mostly from Bob Jenkins.
+    Miscellaneous hashing functions, mostly from Bob Jenkins.
 
     The Jenkins hashing function(s) is similar in speed to Paul Hsieh's
     SuperFast hash, but is public domain, supports incremental hashing
@@ -61,16 +61,6 @@ namespace Foam
 //  \param[in] seed - the previous hash, or an arbitrary value
 unsigned Hasher(const void* data, size_t len, unsigned seed = 0);
 
-
-//- Hashing of bit-wise internal content of given data object.
-//  For primitives and simple collections of primitives this is reasonable,
-//  but ill-advised for more complex data structures.
-template<class T>
-inline unsigned HasherT(const T& obj, unsigned seed = 0)
-{
-    return Hasher(&obj, sizeof(obj), seed);
-}
-
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/OpenFOAM/primitives/hashes/Hasher/HasherInt.H b/src/OpenFOAM/primitives/hashes/Hash/HasherInt.H
similarity index 99%
rename from src/OpenFOAM/primitives/hashes/Hasher/HasherInt.H
rename to src/OpenFOAM/primitives/hashes/Hash/HasherInt.H
index 725dec250cc1dd72e8b5770459035af4a0926129..7e759ea885545aa1d0765f7c8532ad8d621c9dd7 100644
--- a/src/OpenFOAM/primitives/hashes/Hasher/HasherInt.H
+++ b/src/OpenFOAM/primitives/hashes/Hash/HasherInt.H
@@ -38,7 +38,7 @@ See also
 #define HasherInt_H
 
 #include "Hasher.H"
-#include "int.H"
+#include <cstdint>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
diff --git a/src/OpenFOAM/primitives/spatialVectorAlgebra/SpatialTensor/spatialTensor/spatialTensor.C b/src/OpenFOAM/primitives/spatialVectorAlgebra/SpatialTensor/spatialTensor/spatialTensor.C
index 6b22baad607a7496a8acd7b5164b273017d4d7d2..f106d561f0a66e9fe990fd10d41a1cb996d867f9 100644
--- a/src/OpenFOAM/primitives/spatialVectorAlgebra/SpatialTensor/spatialTensor/spatialTensor.C
+++ b/src/OpenFOAM/primitives/spatialVectorAlgebra/SpatialTensor/spatialTensor/spatialTensor.C
@@ -37,7 +37,7 @@ const char* const Foam::spatialTensor::vsType::componentNames[] =
 {
     "Exx",  "Exy",  "Exz",    "Erxx", "Erxy", "Erxz",
     "Eyx",  "Eyy",  "Eyz",    "Eryx", "Eryy", "Eryz",
-    "Ezx",  "Ezy",  "Ezz",    "Erzx", "Erzy", "Erzz"
+    "Ezx",  "Ezy",  "Ezz",    "Erzx", "Erzy", "Erzz",
 
     "Erxx", "Erxy", "Erxz",   "Exx",  "Exy",  "Exz",
     "Eryx", "Eryy", "Eryz",   "Eyx",  "Eyy",  "Eyz",
diff --git a/src/OpenFOAM/primitives/strings/fileName/fileName.C b/src/OpenFOAM/primitives/strings/fileName/fileName.C
index 64cdaa477f3d57ec51a54c2d431e45d85bbe89bd..09ebe26f3a9e19a6e358f842524e793d7dddb080 100644
--- a/src/OpenFOAM/primitives/strings/fileName/fileName.C
+++ b/src/OpenFOAM/primitives/strings/fileName/fileName.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,72 +52,164 @@ int Foam::fileName::allowSpaceInFileName
 const Foam::fileName Foam::fileName::null;
 
 
-// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
 
-Foam::fileName Foam::fileName::validate
+namespace
+{
+
+// doClean:
+//   - remove duplicate slashes, "/./" and "/../" components.
+//
+// checkValid:
+//   - similar to stripInvalid (but silent)
+//
+// return True if the content changed
+static bool cleanFileName
 (
-    const std::string& s,
-    const bool doClean
+    std::string& str,
+    const bool doClean,
+    const bool checkValid
 )
 {
-    // The logic is very similar to stripInvalid,
-    // but silently removes bad characters
-
-    fileName out;
-    out.resize(s.length());
-
-    std::string::size_type len = 0;
-
-    auto iter = s.cbegin();
+    const auto maxLen = str.length();
+    std::string::size_type nChar = 0;
 
-    #ifdef _WIN32
-    // Preserve UNC \\server-name\...
-    if (s.length() > 2 && s[0] == '\\' && s[1] == '\\')
+    // Preserve UNC \\server\path (windows)
+    // - MS-windows only, but handle for other systems
+    //   since there is no collision with this pattern
+    if (maxLen > 2 && str[0] == '\\' && str[1] == '\\')
     {
-        len += 2;
-        ++iter;
-        ++iter;
+        nChar += 2;
     }
-    #endif
 
     char prev = 0;
-    for (/*nil*/; iter != s.cend(); ++iter)
-    {
-        char c = *iter;
+    auto top = std::string::npos;  // Not yet found
+    bool changed = false;
 
-        // Treat raw backslash like a path separator. There is no "normal"
-        // way for these to be there (except for an OS that uses them), but
-        // could also cause issues when writing strings, shell commands etc.
+    for (auto src = nChar; src < maxLen; /*nil*/)
+    {
+        char c = str[src++];
 
+        // Treat raw backslash like a path separator.
+        // There is no "normal" way for these to be there
+        // (except for an OS that uses them), but can cause issues
+        // when writing strings, shell commands etc.
         if (c == '\\')
         {
             c = '/';
+            str[nChar] = c;
+            changed = true;
+        }
+        else if (checkValid && !Foam::fileName::valid(c))
+        {
+            // Ignore invalid chars
+            // Could explicitly allow space character or rely on
+            // allowSpaceInFileName via fileName::valid()
+            continue;
         }
 
-        // Could explicitly allow space character or rely on
-        // allowSpaceInFileName via fileName::valid()
+        if (c == '/' && top == std::string::npos)
+        {
+            // Top-level slash not previously determined
+            top = (src-1);
+        }
 
-        if (fileName::valid(c))
+        if (doClean && prev == '/')
         {
-            if (doClean && prev == '/' && c == '/')
+            // Repeated '/' - skip it
+            if (c == '/')
             {
-                // Avoid repeated '/';
                 continue;
             }
 
-            // Only track valid chars
-            out[len++] = prev = c;
+            // Could be "/./", "/../" or a trailing "/."
+            if (c == '.')
+            {
+                // Trailing "/." - skip it
+                if (src >= maxLen)
+                {
+                    break;
+                }
+
+                // Peek at the next character
+                const char c1 = str[src];
+
+                // Found "/./" - skip over it
+                if (c1 == '/' || c1 == '\\')
+                {
+                    ++src;
+                    continue;
+                }
+
+                // Trailing "/.." or intermediate "/../"
+                if
+                (
+                    c1 == '.'
+                 &&
+                    (
+                        src+1 >= maxLen
+                     || str[src+1] == '/' || str[src+1] == '\\'
+                    )
+                )
+                {
+                    // Backtrack to find the parent directory
+                    // Minimum of 3 characters:  '/x/../'
+                    // Strip it, provided it is above the top point
+
+                    std::string::size_type parent;
+                    if
+                    (
+                        nChar > 2
+                     && top != std::string::npos
+                     && (parent = str.rfind('/', nChar-2)) != std::string::npos
+                     && parent >= top
+                    )
+                    {
+                        nChar = parent + 1;   // Retain '/' from the parent
+                        src += 2;
+                        continue;
+                    }
+
+                    // Bad resolution, eg 'abc/../../'
+                    // Retain the sequence, but move the top to avoid it being
+                    // considered a valid parent later
+                    top = nChar + 2;
+                }
+            }
         }
+
+        str[nChar++] = prev = c;
     }
 
-    if (doClean && prev == '/' && len > 1)
+    // Remove trailing '/'
+    if (doClean && nChar > 1 && str[nChar-1] == '/')
     {
-        // Avoid trailing '/'
-        --len;
+        --nChar;
     }
 
-    out.resize(len);
+    str.erase(nChar);
+    return changed || (nChar != maxLen);
+}
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+bool Foam::fileName::clean(std::string& str)
+{
+    return cleanFileName(str, true, false);  // clean, checkValid = false
+}
+
 
+Foam::fileName Foam::fileName::validate
+(
+    const std::string& str,
+    const bool doClean
+)
+{
+    fileName out(str, false);           // copy, no stripping
+    cleanFileName(out, doClean, true);  // checkValid = true
     return out;
 }
 
@@ -288,117 +380,19 @@ Foam::fileName& Foam::fileName::toAbsolute()
     {
         fileName& f = *this;
         f = cwd()/f;
-        f.clean();
+        f.clean();  // Remove unneeded ".."
     }
 
     return *this;
 }
 
 
-bool Foam::fileName::clean(std::string& str)
-{
-    // Start with the top slash found - we are never allowed to go above it
-    char prev = '/';
-    auto top = str.find(prev);
-
-    // No slashes - nothing to do
-    if (top == std::string::npos)
-    {
-        return false;
-    }
-
-    // Number of output characters
-    auto nChar = top+1;
-
-    const auto maxLen = str.length();
-
-    for (auto src = nChar; src < maxLen; /*nil*/)
-    {
-        const char c = str[src++];
-
-        if (prev == '/')
-        {
-            // Repeated '/' - skip it
-            if (c == '/')
-            {
-                continue;
-            }
-
-            // Could be "/./", "/../" or a trailing "/."
-            if (c == '.')
-            {
-                // Trailing "/." - skip it
-                if (src >= maxLen)
-                {
-                    break;
-                }
-
-                // Peek at the next character
-                const char c1 = str[src];
-
-                // Found "/./" - skip it
-                if (c1 == '/')
-                {
-                    ++src;
-                    continue;
-                }
-
-                // Trailing "/.." or intermediate "/../"
-                if (c1 == '.' && (src+1 >= maxLen || str[src+1] == '/'))
-                {
-                    string::size_type parent;
-
-                    // Backtrack to find the parent directory
-                    // Minimum of 3 characters:  '/x/../'
-                    // Strip it, provided it is above the top point
-                    if
-                    (
-                        nChar > 2
-                     && (parent = str.rfind('/', nChar-2)) != string::npos
-                     && parent >= top
-                    )
-                    {
-                        nChar = parent + 1;   // Retain '/' from the parent
-                        src += 2;
-                        continue;
-                    }
-
-                    // Bad resolution, eg 'abc/../../'
-                    // Retain the sequence, but move the top to avoid it being
-                    // considered a valid parent later
-                    top = nChar + 2;
-                }
-            }
-        }
-        str[nChar++] = prev = c;
-    }
-
-    // Remove trailing slash
-    if (nChar > 1 && str[nChar-1] == '/')
-    {
-        nChar--;
-    }
-
-    str.resize(nChar);
-
-    return (nChar != maxLen);
-}
-
-
 bool Foam::fileName::clean()
 {
     return fileName::clean(*this);
 }
 
 
-Foam::fileName Foam::fileName::clean() const
-{
-    fileName cleaned(*this);
-    fileName::clean(cleaned);
-    return cleaned;
-}
-
-
 std::string Foam::fileName::nameLessExt(const std::string& str)
 {
     auto beg = str.rfind('/');
@@ -464,12 +458,6 @@ Foam::fileName Foam::fileName::relative
 }
 
 
-bool Foam::fileName::hasExt(const wordRe& ending) const
-{
-    return string::hasExt(ending);
-}
-
-
 Foam::wordList Foam::fileName::components(const char delim) const
 {
     const auto parsed = stringOps::split<string>(*this, delim);
diff --git a/src/OpenFOAM/primitives/strings/fileName/fileName.H b/src/OpenFOAM/primitives/strings/fileName/fileName.H
index a6a137a86942cac65e862ee7956fedd19f94ec46..b00c78e8049cd6cd375cbd6b69813f9bbdb22a41 100644
--- a/src/OpenFOAM/primitives/strings/fileName/fileName.H
+++ b/src/OpenFOAM/primitives/strings/fileName/fileName.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -55,14 +55,13 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
+class fileName;
+class token;
 template<class T> class List;
 template<class T> class UList;
 typedef List<word> wordList;
 
-class wordRe;
-class fileName;
-
 /*---------------------------------------------------------------------------*\
                           Class fileName Declaration
 \*---------------------------------------------------------------------------*/
@@ -100,7 +99,7 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         fileName() = default;
 
         //- Copy construct
@@ -142,13 +141,20 @@ public:
 
     // Member Functions
 
+        //- Inherit all regular string assign() methods
+        using string::assign;
+
+        //- Assign from word or string token.
+        //  \return false if the token was the incorrect type
+        bool assign(const token& tok);
+
         //- Is this character valid for a fileName?
         inline static bool valid(char c);
 
-        //- Construct fileName with no invalid characters, possibly applying
+        //- Construct fileName without invalid characters, possibly applying
         //- other transformations such as changing the path separator,
         //- removing duplicate or trailing slashes, etc.
-        static fileName validate(const std::string& s, const bool doClean=true);
+        static fileName validate(const std::string&, const bool doClean=true);
 
         //- Join two strings with a path separator ('/' by default).
         //  No separator is added if either argument is an empty string or
@@ -168,44 +174,48 @@ public:
         //- Strip invalid characters
         inline void stripInvalid();
 
-        //- Cleanup filename
+        //- Cleanup filename string, possibly applies other transformations
+        //- such as changing the path separator etc.
         //
-        // Removes trailing \c /
-        //   \verbatim
-        //       /                 -->   /
-        //       /abc/             -->   /abc
-        //   \endverbatim
+        // Changes back-slash to forward-slash path separator,
+        // while preserving windows UNC:
+        // \verbatim
+        //     \\server\abc\def  -->   \\server/abc/def
+        // \endverbatim
+        //
+        // Removes trailing slash:
+        // \verbatim
+        //     /                 -->   /
+        //     /abc/             -->   /abc
+        // \endverbatim
         //
-        // Removes repeated slashes
-        //   \verbatim
-        //       /abc////def        -->   /abc/def
-        //   \endverbatim
+        // Removes repeated slashes, but preserves UNC:
+        // \verbatim
+        //     /abc////def          -->   /abc/def
+        //     \\server\abc////def  -->   \\server/abc/def
+        // \endverbatim
         //
-        // Removes \c /./ (current directory)
-        //   \verbatim
-        //       /abc/def/./ghi/.   -->   /abc/def/ghi
-        //       abc/def/./         -->   abc/def
-        //       ./abc/             -->   ./abc
-        //   \endverbatim
+        // Removes \c "/./" (current directory), except for leading one:
+        // \verbatim
+        //     /abc/def/./ghi/.   -->   /abc/def/ghi
+        //     abc/def/./         -->   abc/def
+        //     ./abc/             -->   ./abc
+        // \endverbatim
         //
-        // Removes \c /../ (parent directory)
-        //   \verbatim
-        //       /abc/def/../ghi/jkl/nmo/..   -->   /abc/ghi/jkl
-        //       abc/../def/ghi/../jkl        -->   abc/../def/jkl
-        //   \endverbatim
+        // Removes \c "/../" (parent directory), except for leading one:
+        // \verbatim
+        //     /abc/def/../ghi/jkl/nmo/..   -->   /abc/ghi/jkl
+        //     abc/../def/ghi/../jkl        -->   abc/../def/jkl
+        // \endverbatim
+        // .
         //
         // \return True if the content changed
         static bool clean(std::string& str);
 
-
-        //- Cleanup filename inplace
-        //  \return True if any contents changed
+        //- Cleanup filename (inplace)
+        //  \return True if the content changed
         bool clean();
 
-        //- Cleanup filename
-        //  \return cleaned copy of fileName
-        fileName clean() const;
-
 
     // Interrogation
 
@@ -217,10 +227,13 @@ public:
         //  \param checkGzip add an additional test for a gzip FILE
         Type type(bool followLink=true, bool checkGzip=false) const;
 
-        //- Return true if string starts with a '/'
+        //- Return true if filename starts with a '/' or '\\'
+        //- or (windows-only) with a filesystem-root
         inline static bool isAbsolute(const std::string& str);
 
-        //- Return true if file name is absolute (starts with a '/')
+        //- Return true if filename is absolute,
+        //- which means it starts with a '/' or '\\'
+        //- or (windows-only) with a filesystem-root
         inline bool isAbsolute() const;
 
         //- Convert from relative to absolute
@@ -322,17 +335,11 @@ public:
         //  or when the file name is empty or ended with a '/'.
         inline fileName& ext(const word& ending);
 
-        //- Return true if it has an extension or simply ends with a '.'
-        inline bool hasExt() const;
-
-        //- Return true if the extension is the same as the given ending.
-        inline bool hasExt(const word& ending) const;
-
-        //- Return true if the extension matches the given ending.
-        bool hasExt(const wordRe& ending) const;
+        //- Various checks for extensions
+        using string::hasExt;
 
         //- Remove extension, returning true if string changed.
-        inline bool removeExt();
+        using string::removeExt;
 
 
         //- Return path components as wordList
@@ -389,7 +396,7 @@ public:
         inline fileName& operator=(const char* str);
 
 
-    // Other operators
+    // Other Operators
 
         //- Append a path element with '/' separator.
         //  No '/' separator is added if this or the argument are empty.
diff --git a/src/OpenFOAM/primitives/strings/fileName/fileNameI.H b/src/OpenFOAM/primitives/strings/fileName/fileNameI.H
index 961997331acad23f89841fe8d937209a9b046702..ed1e860e455e0aba2e840f9de6e43d4c2264277f 100644
--- a/src/OpenFOAM/primitives/strings/fileName/fileNameI.H
+++ b/src/OpenFOAM/primitives/strings/fileName/fileNameI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -135,20 +135,22 @@ inline void Foam::fileName::stripInvalid()
 
 inline bool Foam::fileName::isAbsolute(const std::string& str)
 {
-    return
+    return !str.empty() &&
     (
-        (!str.empty() && str.front() == '/')  // ie, str.starts_with('/')
-        #ifdef _WIN32
-        ||
-        (
-            // Eg, d:/path or \\machine/path
-            (str.length() > 2) &&
-            (
-                (str[1] == ':'  && str[2] == '/')
-             || (str[0] == '\\' && str[1] == '\\')
-            )
+        // Starts with '/', but also accept '\\' since it will be
+        // converted to a generic '/' or it is part of a (windows)
+        // UNC '\\server-name\path'
+        // - accept even on non-windows systems
+
+        (str[0] == '/' || str[0] == '\\')
+
+#ifdef _WIN32
+         // Filesytem root - eg, d:/path or d:\path
+     || (
+            (str.length() > 2 && str[1] == ':')
+         && (str[2] == '/' || str[2] == '\\')
         )
-        #endif
+#endif
     );
 }
 
@@ -171,18 +173,6 @@ inline bool Foam::fileName::hasPath() const
 }
 
 
-inline bool Foam::fileName::hasExt() const
-{
-    return string::hasExt();
-}
-
-
-inline bool Foam::fileName::hasExt(const word& ending) const
-{
-    return string::hasExt(ending);
-}
-
-
 inline std::string Foam::fileName::path(const std::string& str)
 {
     const auto i = str.rfind('/');
@@ -256,12 +246,6 @@ inline bool Foam::fileName::removePath()
 }
 
 
-inline bool Foam::fileName::removeExt()
-{
-    return string::removeExt();
-}
-
-
 inline Foam::fileName& Foam::fileName::ext(const word& ending)
 {
     string::ext(ending);
diff --git a/src/OpenFOAM/primitives/strings/fileName/fileNameIO.C b/src/OpenFOAM/primitives/strings/fileName/fileNameIO.C
index 5dbff430f4676008c6c06dabc0a6d0a6baec5949..2fcca95eb509cd8b5857df9dd3f214e657683dad 100644
--- a/src/OpenFOAM/primitives/strings/fileName/fileNameIO.C
+++ b/src/OpenFOAM/primitives/strings/fileName/fileNameIO.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,46 +29,60 @@ License
 #include "fileName.H"
 #include "IOstreams.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::fileName::fileName(Istream& is)
-:
-    string()
 {
     is >> *this;
 }
 
 
-Foam::Istream& Foam::operator>>(Istream& is, fileName& val)
-{
-    token t(is);
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-    if (!t.good())
+bool Foam::fileName::assign(const token& tok)
+{
+    if (tok.isWord())
     {
-        FatalIOErrorInFunction(is)
-            << "Bad token - could not get string"
-            << exit(FatalIOError);
-        is.setBad();
-        return is;
+        // Also accept a plain word as a fileName
+        assign(tok.wordToken());
+        return true;
     }
-
-    if (t.isStringType())
+    else if (tok.isQuotedString())
     {
-        // Also accept a plain word as a fileName
-        val = t.stringToken();
+        assign(tok.stringToken());
+        stripInvalid();  // More stringent for fileName than string
+        return true;
     }
-    else
+
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, fileName& val)
+{
+    token tok(is);
+
+    if (!val.assign(tok))
     {
-        FatalIOErrorInFunction(is)
-            << "Wrong token type - expected string, found "
-            << t.info()
-            << exit(FatalIOError);
+        FatalIOErrorInFunction(is);
+        if (tok.good())
+        {
+            FatalIOError
+                << "Wrong token type - expected string, found "
+                << tok.info();
+        }
+        else
+        {
+            FatalIOError
+                << "Bad token - could not get fileName";
+        }
+        FatalIOError << exit(FatalIOError);
         is.setBad();
         return is;
     }
 
-    val.stripInvalid();
-
     is.check(FUNCTION_NAME);
     return is;
 }
diff --git a/src/OpenFOAM/primitives/strings/keyType/keyType.C b/src/OpenFOAM/primitives/strings/keyType/keyType.C
index 1b9899f03638889d13ba451e77d9ac0e161cd166..6749afe42df6b93514846172f58895b3e63af9bd 100644
--- a/src/OpenFOAM/primitives/strings/keyType/keyType.C
+++ b/src/OpenFOAM/primitives/strings/keyType/keyType.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,7 +28,9 @@ License
 
 #include "keyType.H"
 #include "regExp.H"
+#include "token.H"
 #include "IOstreams.H"
+#include <algorithm>  // For swap
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -38,9 +40,6 @@ const Foam::keyType Foam::keyType::null;
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::keyType::keyType(Istream& is)
-:
-    word(),
-    type_(option::LITERAL)
 {
     is  >> *this;
 }
@@ -48,6 +47,18 @@ Foam::keyType::keyType(Istream& is)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+void Foam::keyType::swap(keyType& rhs)
+{
+    if (this == &rhs)
+    {
+        return;  // Self-swap is a no-op
+    }
+
+    word::swap(static_cast<word&>(rhs));
+    std::swap(type_, rhs.type_);
+}
+
+
 bool Foam::keyType::match(const std::string& text, bool literal) const
 {
     if (!literal && isPattern())
@@ -59,37 +70,40 @@ bool Foam::keyType::match(const std::string& text, bool literal) const
 }
 
 
-// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
-
-Foam::Istream& Foam::operator>>(Istream& is, keyType& val)
+bool Foam::keyType::assign(const token& tok)
 {
-    token t(is);
-
-    if (!t.good())
+    if (tok.isWord())
     {
-        FatalIOErrorInFunction(is)
-            << "Bad token - could not get a word/regex"
-            << exit(FatalIOError);
-        is.setBad();
-        return is;
+        // Assign from word - literal
+        assign(tok.wordToken());
+        uncompile();
+        return true;
     }
-
-    if (t.isWord())
+    else if (tok.isQuotedString())
     {
-        val = t.wordToken();
-        val.setType(keyType::LITERAL);
+        // Assign from quoted string - regular expression
+        assign(tok.stringToken());
+        compile();
+        return true;
     }
-    else if (t.isString())
-    {
-        // Assign from string, treat as regular expression
-        val = t.stringToken();
-        val.setType(keyType::REGEX);
 
-        // Flag empty strings as an error
+    return false;
+}
+
+
+// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, keyType& val)
+{
+    token tok(is);
+
+    if (val.assign(tok))
+    {
         if (val.empty())
         {
+            // Empty strings are an error
             FatalIOErrorInFunction(is)
-                << "Empty word/expression"
+                << "Zero-length regex"
                 << exit(FatalIOError);
             is.setBad();
             return is;
@@ -97,10 +111,19 @@ Foam::Istream& Foam::operator>>(Istream& is, keyType& val)
     }
     else
     {
-        FatalIOErrorInFunction(is)
-            << "Wrong token type - expected word or string, found "
-            << t.info()
-            << exit(FatalIOError);
+        FatalIOErrorInFunction(is);
+        if (tok.good())
+        {
+            FatalIOError
+                << "Wrong token type - expected word or string, found "
+                << tok.info();
+        }
+        else
+        {
+            FatalIOError
+                << "Bad token - could not get keyType";
+        }
+        FatalIOError << exit(FatalIOError);
         is.setBad();
         return is;
     }
diff --git a/src/OpenFOAM/primitives/strings/keyType/keyType.H b/src/OpenFOAM/primitives/strings/keyType/keyType.H
index 7f5b2c87f0640978a2ea5be46ffe7d1c0f10cf19..7ed8fe64b6e288d4d5b04be432d6a04e8002139b 100644
--- a/src/OpenFOAM/primitives/strings/keyType/keyType.H
+++ b/src/OpenFOAM/primitives/strings/keyType/keyType.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,6 +36,7 @@ Description
 
 SourceFiles
     keyType.C
+    keyTypeI.H
 
 \*---------------------------------------------------------------------------*/
 
@@ -43,6 +44,7 @@ SourceFiles
 #define keyType_H
 
 #include "word.H"
+#include "wordRe.H"
 #include "stdFoam.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -51,8 +53,10 @@ namespace Foam
 {
 
 // Forward Declarations
-class Istream;
-class Ostream;
+class keyType;
+class token;
+Istream& operator>>(Istream& is, keyType& val);
+Ostream& operator<<(Ostream& os, const keyType& val);
 
 /*---------------------------------------------------------------------------*\
                            Class keyType Declaration
@@ -90,12 +94,6 @@ private:
         option type_;
 
 
-    // Private Member Functions
-
-        //- No assignment where we cannot determine string/word type
-        void operator=(const std::string&) = delete;
-
-
 public:
 
     // Static Data Members
@@ -106,69 +104,82 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct, empty literal
         inline keyType();
 
-        //- Copy construct, retaining type (literal or regex)
-        inline keyType(const keyType& s);
-
-        //- Copy construct from word, treat as literal.
-        inline keyType(const word& s);
+        //- Copy construct
+        keyType(const keyType&) = default;
 
-        //- Copy construct from string, treat as regular expression.
-        inline keyType(const string& s);
+        //- Move construct
+        keyType(keyType&&) = default;
 
-        //- Construct as copy of character array, treat as literal.
-        inline keyType(const char* s);
+        //- Implicit copy construct from word, treat as LITERAL
+        inline keyType(const word& str);
 
-        //- Copy construct from std::string with specified treatment
-        inline keyType(const std::string& s, option opt);
+        //- Implicit move construct from word, treat as LITERAL
+        inline keyType(word&& str);
 
-        //- Move construct, retaining type (literal or regex)
-        inline keyType(keyType&& s);
+        //- Implicit copy construct from Foam::string, treat as REGEX
+        inline keyType(const string& str);
 
-        //- Move construct from word, treat as literal.
-        inline keyType(word&& s);
+        //- Implicit move construct from Foam::string, treat as REGEX
+        inline keyType(string&& str);
 
-        //- Move construct from string, treat as regular expression.
-        inline keyType(string&& s);
+        //- Copy construct from std::string with specified treatment
+        inline keyType(const std::string& str, option opt);
 
         //- Move construct from std::string with specified treatment
-        inline keyType(std::string&& s, option opt);
+        inline keyType(std::string&& str, option opt);
 
-        //- Construct from Istream
+        //- Implicit construct from character array,
+        //- with specified compile option (default is LITERAL)
+        inline keyType(const char* str, option opt = option::LITERAL);
+
+        //- Construct from Istream by reading a token
         //  Treat as regular expression if surrounded by quotation marks.
         explicit keyType(Istream& is);
 
 
     // Member Functions
 
-        //- Is this character valid for a keyType?
-        //  This is largely identical with what word accepts, but also
-        //  permit brace-brackets, which are valid for some regexs.
-        inline static bool valid(char c);
+        //- Test for valid keyType character?
+        //  Like Foam::word, but with brace-brackets,
+        //  which are valid for some regexs.
+        inline static bool valid(const char c);
 
 
     // Access
 
         //- The keyType is treated as literal, not as pattern.
-        inline bool isLiteral() const;
+        inline bool isLiteral() const noexcept;
 
         //- The keyType is treated as a pattern, not as literal string.
-        inline bool isPattern() const;
+        inline bool isPattern() const noexcept;
 
 
     // Infrastructure
 
-        //- Change the representation
+        //- Inherit all regular string assign() methods
+        using word::assign;
+
+        //- Assign from word or string token.
+        //  Words are treated as literals, strings as regex
+        //  \return false if the token was the incorrect type
+        bool assign(const token& tok);
+
+        //- Change the representation, optionally stripping invalid word
+        //- characters when changing to a literal
         inline void setType(option opt, bool adjust = false);
 
         //- Mark as regular expression
-        inline bool compile();
+        inline bool compile() noexcept;
+
+        //- Mark as literal string
+        inline void uncompile() noexcept;
 
-        //- Mark as literal, instead of a regular expression.
-        //  Optionally strip invalid word characters.
-        inline void uncompile(bool adjust = false);
+        //- Mark as literal string, optionally strip invalid word
+        //- characters when changing to a literal
+        inline void uncompile(bool adjust);
 
 
     // Editing
@@ -177,7 +188,7 @@ public:
         inline void clear();
 
         //- Swap contents. Self-swapping is a no-op.
-        inline void swap(keyType& s);
+        void swap(keyType& rhs);
 
 
     // Matching/Searching
@@ -193,23 +204,25 @@ public:
         //  Allows use as a predicate.
         inline bool operator()(const std::string& text) const;
 
+        //- No assignment where type could be indeterminate
+        void operator=(const std::string&) = delete;
 
         //- Copy assignment, retaining type (literal or regex)
         //  Self-assignment is a no-op.
-        inline void operator=(const keyType& s);
+        inline void operator=(const keyType& str);
 
         //- Move assignment, retaining type (literal or regex)
         //  Self-assignment is a no-op.
-        inline void operator=(keyType&& s);
+        inline void operator=(keyType&& str);
 
-        //- Assign as word, treat as literal
-        inline void operator=(const word& s);
+        //- Assign from word, treat as literal
+        inline void operator=(const word& str);
 
         //- Assign from Foam::string, treat as regular expression
-        inline void operator=(const string& s);
+        inline void operator=(const string& str);
 
-        //- Assign as word, treat as literal
-        inline void operator=(const char* s);
+        //- Assign from character array, treat as literal
+        inline void operator=(const char* str);
 
 
     // Housekeeping
diff --git a/src/OpenFOAM/primitives/strings/keyType/keyTypeI.H b/src/OpenFOAM/primitives/strings/keyType/keyTypeI.H
index cbf710e51709910b48c96c2fa5a34bec80082b66..ca6297e010bb3e796d774e8b0172d88aed3c671f 100644
--- a/src/OpenFOAM/primitives/strings/keyType/keyTypeI.H
+++ b/src/OpenFOAM/primitives/strings/keyType/keyTypeI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,20 +26,12 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include <algorithm>
-
 // * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
 
-inline bool Foam::keyType::valid(char c)
+inline bool Foam::keyType::valid(const char c)
 {
-    return
-    (
-        !isspace(c)
-     && c != '"'   // string quote
-     && c != '\''  // string quote
-     && c != '/'   // path separator
-     && c != ';'   // end statement
-    );
+    // Also accept '{' and '}' (for regex grouping?)
+    return (word::valid(c) || c == '{' || c == '}');
 }
 
 
@@ -52,80 +44,64 @@ inline Foam::keyType::keyType()
 {}
 
 
-inline Foam::keyType::keyType(const keyType& s)
+inline Foam::keyType::keyType(const word& str)
 :
-    word(s, false),
-    type_(s.type_)
+    word(str),
+    type_(option::LITERAL)
 {}
 
 
-inline Foam::keyType::keyType(const word& s)
+inline Foam::keyType::keyType(word&& str)
 :
-    word(s, false),
+    word(std::move(str)),
     type_(option::LITERAL)
 {}
 
 
-inline Foam::keyType::keyType(const string& s)
+inline Foam::keyType::keyType(const string& str)
 :
-    word(s, false),
+    word(str, false),  // No stripping
     type_(option::REGEX)
 {}
 
 
-inline Foam::keyType::keyType(const char* s)
+inline Foam::keyType::keyType(string&& str)
 :
-    word(s, false),
-    type_(option::LITERAL)
+    word(std::move(str), false),  // No stripping
+    type_(option::REGEX)
 {}
 
 
-inline Foam::keyType::keyType(const std::string& s, option opt)
+inline Foam::keyType::keyType(const std::string& str, option opt)
 :
-    word(s, false),
+    word(str, false),  // No stripping
     type_(option(opt & 0x0F))
 {}
 
 
-inline Foam::keyType::keyType(keyType&& s)
-:
-    word(std::move(static_cast<word&>(s)), false),
-    type_(s.type_)
-{
-    s.type_ = option::LITERAL;
-}
-
-
-inline Foam::keyType::keyType(word&& s)
+inline Foam::keyType::keyType(std::string&& str, option opt)
 :
-    word(std::move(s), false),
-    type_(option::LITERAL)
-{}
-
-
-inline Foam::keyType::keyType(string&& s)
-:
-    word(std::move(s), false),
-    type_(option::REGEX)
+    word(std::move(str), false),  // No stripping
+    type_(option(opt & 0x0F))
 {}
 
 
-inline Foam::keyType::keyType(std::string&& s, option opt)
+inline Foam::keyType::keyType(const char* str, option opt)
 :
-    word(std::move(s), false),
+    word(str, false),  // No stripping
     type_(option(opt & 0x0F))
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-inline bool Foam::keyType::isLiteral() const
+inline bool Foam::keyType::isLiteral() const noexcept
 {
-    return (type_ != option::REGEX);
+    return !(type_ & option::REGEX);
 }
 
 
-inline bool Foam::keyType::isPattern() const
+inline bool Foam::keyType::isPattern() const noexcept
 {
     return (type_ & option::REGEX);
 }
@@ -138,7 +114,7 @@ inline void Foam::keyType::setType(option opt, bool adjust)
     if (type_ != opt)
     {
         // Only strip when debug is active (potentially costly operation)
-        if (isPattern() && adjust && word::debug)
+        if (adjust && isPattern() && word::debug)
         {
             string::stripInvalid<word>(*this);
         }
@@ -148,35 +124,35 @@ inline void Foam::keyType::setType(option opt, bool adjust)
 }
 
 
-inline bool Foam::keyType::compile()
+inline bool Foam::keyType::compile() noexcept
 {
     type_ = option::REGEX;
     return true;
 }
 
 
-inline void Foam::keyType::uncompile(bool adjust)
+inline void Foam::keyType::uncompile() noexcept
 {
-    setType(option::LITERAL, adjust);
+    type_ = option::LITERAL;
 }
 
 
-inline void Foam::keyType::clear()
+inline void Foam::keyType::uncompile(bool adjust)
 {
-    word::clear();
+    // Only strip when debug is active (potentially costly operation)
+    if (adjust && isPattern() && word::debug)
+    {
+        string::stripInvalid<word>(*this);
+    }
+
     type_ = option::LITERAL;
 }
 
 
-inline void Foam::keyType::swap(keyType& s)
+inline void Foam::keyType::clear()
 {
-    if (this == &s)
-    {
-        return;  // Self-swap is a no-op
-    }
-
-    word::swap(static_cast<word&>(s));
-    std::swap(type_, s.type_);
+    word::clear();
+    type_ = option::LITERAL;
 }
 
 
@@ -188,48 +164,48 @@ inline bool Foam::keyType::operator()(const std::string& text) const
 }
 
 
-inline void Foam::keyType::operator=(const keyType& s)
+inline void Foam::keyType::operator=(const keyType& str)
 {
-    if (this == &s)
+    if (this == &str)
     {
         return;  // Self-assignment is a no-op
     }
 
-    assign(s); // Bypasses char checking
-    type_ = s.type_;
+    assign(str); // Bypasses char checking
+    type_ = str.type_;
 }
 
 
-inline void Foam::keyType::operator=(keyType&& s)
+inline void Foam::keyType::operator=(keyType&& str)
 {
-    if (this == &s)
+    if (this == &str)
     {
         return;  // Self-assignment is a no-op
     }
 
     clear();
-    swap(s);
+    swap(str);
 }
 
 
-inline void Foam::keyType::operator=(const word& s)
+inline void Foam::keyType::operator=(const char* str)
 {
-    assign(s); // Bypasses char checking
+    assign(str); // Bypasses char checking
     type_ = option::LITERAL;
 }
 
 
-inline void Foam::keyType::operator=(const string& s)
+inline void Foam::keyType::operator=(const word& str)
 {
-    assign(s); // Bypasses char checking
-    type_ = option::REGEX;
+    assign(str); // Bypasses char checking
+    type_ = option::LITERAL;
 }
 
 
-inline void Foam::keyType::operator=(const char* s)
+inline void Foam::keyType::operator=(const string& str)
 {
-    assign(s); // Bypasses char checking
-    type_ = option::LITERAL;
+    assign(str); // Bypasses char checking
+    type_ = option::REGEX;
 }
 
 
diff --git a/src/OpenFOAM/primitives/strings/lists/CStringList.H b/src/OpenFOAM/primitives/strings/lists/CStringList.H
index eb6742a78a4bb3770730bb6b2791be743c6b6dc3..7dff8d3f56a23bd13799d482e96fa36c7a5b98b3 100644
--- a/src/OpenFOAM/primitives/strings/lists/CStringList.H
+++ b/src/OpenFOAM/primitives/strings/lists/CStringList.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -61,7 +61,7 @@ namespace Foam
 {
 
 // Forward Declarations
-template<class String> class SubStrings;
+template<class StringType> class SubStrings;
 
 /*---------------------------------------------------------------------------*\
                          Class CStringList Declaration
@@ -111,7 +111,7 @@ public:
 
     // Constructors
 
-        //- Construct empty, adding content later (via reset).
+        //- Default construct, adding content later (via reset).
         inline CStringList();
 
         //- Copy construct from a list of strings
@@ -190,7 +190,7 @@ public:
         static inline List<StringType> asList(const char * const argv[]);
 
 
-    // Member operators
+    // Member Operators
 
         //- Return element at the given index. No bounds checking.
         inline const char* operator[](int i) const;
diff --git a/src/OpenFOAM/primitives/strings/lists/SubStrings.H b/src/OpenFOAM/primitives/strings/lists/SubStrings.H
index 189865864710563f6c7baa5c26858571031e0e49..79d38b947e08c69237c12201dbbf0190405bf5ee 100644
--- a/src/OpenFOAM/primitives/strings/lists/SubStrings.H
+++ b/src/OpenFOAM/primitives/strings/lists/SubStrings.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,7 +35,7 @@ Description
 #ifndef SubStrings_H
 #define SubStrings_H
 
-#include <regex>
+#include <regex>  // For std::sub_match
 #include <string>
 #include <vector>
 
@@ -48,21 +48,22 @@ namespace Foam
                          Class SubStrings Declaration
 \*---------------------------------------------------------------------------*/
 
-template<class String>
+template<class StringType>
 class SubStrings
 :
-    public std::vector<std::sub_match<typename String::const_iterator>>
+    public std::vector<std::sub_match<typename StringType::const_iterator>>
 {
 public:
 
-    // Typedefs
+    // Types
 
         //- The element type
         using value_type =
-            typename std::sub_match<typename String::const_iterator>;
+            typename std::sub_match<typename StringType::const_iterator>;
 
         //- The const_iterator for the underlying string type
-        using string_iterator = typename String::const_iterator;
+        using string_iterator =
+            typename StringType::const_iterator;
 
 
     // Constructors
@@ -87,12 +88,11 @@ public:
             return len;
         }
 
-
         //- Append sub-string defined by begin/end iterators
         void append
         (
-            const typename String::const_iterator& b,
-            const typename String::const_iterator& e
+            const typename StringType::const_iterator& b,
+            const typename StringType::const_iterator& e
         )
         {
             value_type range;
@@ -103,7 +103,6 @@ public:
             this->push_back(range);
         }
 
-
         //- Const reference to the first element,
         //- for consistency with other OpenFOAM containers
         auto first() const -> decltype(this->front())
@@ -111,7 +110,6 @@ public:
             return this->front();
         }
 
-
         //- Const reference to the last element,
         //- for consistency with other OpenFOAM containers
         auto last() const -> decltype(this->back())
@@ -119,9 +117,8 @@ public:
             return this->back();
         }
 
-
-        //- Get element pos, converted to a string type.
-        String str(size_t pos) const
+        //- Get element at pos, converted to a string type.
+        StringType str(size_t pos) const
         {
             return (*this)[pos].str();
         }
diff --git a/src/OpenFOAM/primitives/strings/regex/regExpCxx.H b/src/OpenFOAM/primitives/strings/regex/regExpCxx.H
index 6bc3735a23eb16fb92b972b36774e553d228ec07..a440485cc67ede3fe8cafc156d510f8e762ea873 100644
--- a/src/OpenFOAM/primitives/strings/regex/regExpCxx.H
+++ b/src/OpenFOAM/primitives/strings/regex/regExpCxx.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -80,6 +80,7 @@ class regExpCxx
         //- Track if input pattern was OK - ie, has a length
         bool ok_;
 
+
     // Private Member Functions
 
         //- Select grammar based on regExpCxx optimisationSwitch
@@ -102,39 +103,67 @@ public:
 
     // Static Member Functions
 
-        //- Test if character appears to be a regular expression meta-character
-        //  \return true if character is one of the following:
+        //- Test if character is a regex meta-character
+        //  \return True if character is one of the following:
         //  - any character: '.' \n
         //  - quantifiers:  '*', '+', '?' \n
         //  - grouping: '(', '|', ')' \n
         //  - range: '[', ']' \n
         //
-        // \note The presence of '{', '}' regex bounds is not considered
-        inline static bool meta(const char c);
+        // \note Regex bounds '{', '}' are not considered
+        inline static bool is_meta(const char c) noexcept;
+
+        //- Test if string contains any (unquoted) meta-characters
+        inline static bool is_meta
+        (
+            const std::string& str,
+            const char quote = '\\'
+        );
+
+
+    // Public Classes
+
+        //- Functor wrapper for testing meta-characters
+        struct meta
+        {
+            //- Test if character is a regex meta-character
+            bool operator()(const char c) const noexcept
+            {
+                return is_meta(c);
+            }
+
+            //- Test string for meta-characters
+            bool operator()(const std::string& s, const char q = '\\') const
+            {
+                return is_meta(s, q);
+            }
+        };
 
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         inline regExpCxx();
 
         //- Copy construct
         inline regExpCxx(const regExpCxx& rgx);
 
         //- Move construct
-        inline regExpCxx(regExpCxx&& rgx);
-
-        //- Construct from character array
-        inline explicit regExpCxx(const char* pattern);
-
-        //- Construct from string
-        inline explicit regExpCxx(const std::string& pattern);
+        inline regExpCxx(regExpCxx&& rgx) noexcept;
 
         //- Construct from character array, optionally ignore case
-        inline regExpCxx(const char* pattern, bool ignoreCase);
+        inline explicit regExpCxx
+        (
+            const char* pattern,
+            const bool ignoreCase = false
+        );
 
         //- Construct from string, optionally ignore case
-        inline regExpCxx(const std::string& pattern, bool ignoreCase);
+        inline explicit regExpCxx
+        (
+            const std::string& pattern,
+            const bool ignoreCase = false
+        );
 
 
     //- Destructor
diff --git a/src/OpenFOAM/primitives/strings/regex/regExpCxxI.H b/src/OpenFOAM/primitives/strings/regex/regExpCxxI.H
index 1c68adb20f2785b367dd4bc35240f3a49924b0b7..90043618ff0b859d51d9b49fb924fcc8a8d2c29c 100644
--- a/src/OpenFOAM/primitives/strings/regex/regExpCxxI.H
+++ b/src/OpenFOAM/primitives/strings/regex/regExpCxxI.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -39,7 +39,7 @@ inline std::regex::flag_type Foam::regExpCxx::syntax()
 }
 
 
-inline bool Foam::regExpCxx::meta(const char c)
+inline bool Foam::regExpCxx::is_meta(const char c) noexcept
 {
     return
     (
@@ -51,6 +51,32 @@ inline bool Foam::regExpCxx::meta(const char c)
 }
 
 
+inline bool Foam::regExpCxx::is_meta
+(
+    const std::string& str,
+    const char quote
+)
+{
+    bool escaped = false;
+    for (const char c : str)
+    {
+        if (quote && c == quote)
+        {
+            escaped = !escaped;  // toggle state
+        }
+        else if (escaped)
+        {
+            escaped = false;
+        }
+        else if (is_meta(c))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline Foam::regExpCxx::regExpCxx()
@@ -67,7 +93,7 @@ inline Foam::regExpCxx::regExpCxx(const regExpCxx& rgx)
 {}
 
 
-inline Foam::regExpCxx::regExpCxx(regExpCxx&& rgx)
+inline Foam::regExpCxx::regExpCxx(regExpCxx&& rgx) noexcept
 :
     re_(std::move(rgx.re_)),
     ok_(rgx.ok_)
@@ -76,25 +102,11 @@ inline Foam::regExpCxx::regExpCxx(regExpCxx&& rgx)
 }
 
 
-inline Foam::regExpCxx::regExpCxx(const char* pattern)
-:
-    re_(),
-    ok_(false)
-{
-    set(pattern, false);
-}
-
-
-inline Foam::regExpCxx::regExpCxx(const std::string& pattern)
-:
-    re_(),
-    ok_(false)
-{
-    set(pattern, false);
-}
-
-
-inline Foam::regExpCxx::regExpCxx(const char* pattern, bool ignoreCase)
+inline Foam::regExpCxx::regExpCxx
+(
+    const char* pattern,
+    const bool ignoreCase
+)
 :
     re_(),
     ok_(false)
@@ -103,7 +115,11 @@ inline Foam::regExpCxx::regExpCxx(const char* pattern, bool ignoreCase)
 }
 
 
-inline Foam::regExpCxx::regExpCxx(const std::string& pattern, bool ignoreCase)
+inline Foam::regExpCxx::regExpCxx
+(
+    const std::string& pattern,
+    const bool ignoreCase
+)
 :
     re_(),
     ok_(false)
@@ -208,15 +224,23 @@ inline bool Foam::regExpCxx::operator()(const std::string& text) const
 
 inline void Foam::regExpCxx::operator=(const regExpCxx& rgx)
 {
-    re_ = rgx.re_;
-    ok_ = rgx.ok_;
+    if (this != &rgx)
+    {
+        // Self-assignment is a no-op
+        re_ = rgx.re_;
+        ok_ = rgx.ok_;
+    }
 }
 
 
 inline void Foam::regExpCxx::operator=(regExpCxx&& rgx)
 {
-    clear();
-    swap(rgx);
+    if (this != &rgx)
+    {
+        // Self-assignment is a no-op
+        clear();
+        swap(rgx);
+    }
 }
 
 
diff --git a/src/OpenFOAM/primitives/strings/string/string.C b/src/OpenFOAM/primitives/strings/string/string.C
index 2c6fe97082a81fddb3520050009963e24a9c3876..04f0b91662633a3d0d06f54919c20621c685e604 100644
--- a/src/OpenFOAM/primitives/strings/string/string.C
+++ b/src/OpenFOAM/primitives/strings/string/string.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -78,34 +78,21 @@ bool Foam::string::ext(const word& ending)
 }
 
 
-bool Foam::string::hasExt(const word& ending) const
+bool Foam::string::hasExt(const wordRe& ending) const
 {
-    auto i = find_ext();
-    if (i == npos)
+    if (ending.isLiteral() || ending.empty())
     {
-        return false;
+        return hasExt(static_cast<const std::string&>(ending));
     }
 
-    ++i; // Compare *after* the dot
-    return
-    (
-        // Lengths must match
-        ((size() - i) == ending.size())
-     && !compare(i, npos, ending)
-    );
-}
-
-
-bool Foam::string::hasExt(const wordRe& ending) const
-{
     const auto i = find_ext();
     if (i == npos)
     {
         return false;
     }
 
-    const std::string end = substr(i+1);  // Compare *after* the dot
-    return ending.match(end);
+    // Regex match - compare *after* the dot
+    return ending.match(substr(i+1));
 }
 
 
diff --git a/src/OpenFOAM/primitives/strings/string/string.H b/src/OpenFOAM/primitives/strings/string/string.H
index 5361ce1c2e4a37b621c2b63cb40c7dfedf966b35..52794f905c66274b037a588721e0d2403e5c4829 100644
--- a/src/OpenFOAM/primitives/strings/string/string.H
+++ b/src/OpenFOAM/primitives/strings/string/string.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -52,9 +52,9 @@ SourceFiles
 
 #include "char.H"
 #include "Hasher.H"
-#include <string>
-#include <cstring>
 #include <cstdlib>
+#include <cstring>
+#include <string>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -122,7 +122,10 @@ protected:
         inline bool hasExt() const;
 
         //- Return true if the extension is the same as the given ending.
-        bool hasExt(const word& ending) const;
+        inline bool hasExt(const char* ending) const;
+
+        //- Return true if the extension is the same as the given ending.
+        inline bool hasExt(const std::string& ending) const;
 
         //- Return true if the extension matches the given ending.
         bool hasExt(const wordRe& ending) const;
@@ -136,6 +139,21 @@ protected:
 
 public:
 
+    // Public Classes
+
+        //- Hashing functor for string and derived string classes
+        struct hasher
+        {
+            unsigned operator()(const std::string& str, unsigned seed=0) const
+            {
+                return Foam::Hasher(str.data(), str.length(), seed);
+            }
+        };
+
+        //- Hashing functor for string and derived string classes
+        struct hash : string::hasher {};
+
+
     // Static Data Members
 
         //- The type name "string"
@@ -148,20 +166,9 @@ public:
         static const string null;
 
 
-    //- Hashing function for string and derived string classes
-    struct hash
-    {
-        inline unsigned operator()
-        (
-            const std::string& str,
-            unsigned seed = 0
-        ) const;
-    };
-
-
     // Constructors
 
-        //- Construct null
+        //- Default construct
         string() = default;
 
         //- Copy construct from std::string
@@ -189,29 +196,16 @@ public:
     // Static Member Functions
 
         //- Does the string contain valid characters only?
-        template<class String>
+        template<class StringType>
         static inline bool valid(const std::string& str);
 
-        //- Does this string contain meta-characters?
-        //  The meta characters can be optionally quoted.
-        template<class String>
-        static inline bool meta(const std::string& str, const char quote='\\');
-
         //- Strip invalid characters from the given string
-        template<class String>
+        template<class StringType>
         static inline bool stripInvalid(std::string& str);
 
         //- Return a valid String from the given string
-        template<class String>
-        static inline String validate(const std::string& str);
-
-        //- Return a String with quoted meta-characters from the given string
-        template<class String>
-        static inline string quotemeta
-        (
-            const std::string& str,
-            const char quote = '\\'
-        );
+        template<class StringType>
+        static inline StringType validate(const std::string& str);
 
 
     // Member Functions
diff --git a/src/OpenFOAM/primitives/strings/string/stringI.H b/src/OpenFOAM/primitives/strings/string/stringI.H
index 51a2046960bd243a0695b7eafba3922adc6da9c8..31a1fbc79a6a3205a12cabb72edf79aa993d16ff 100644
--- a/src/OpenFOAM/primitives/strings/string/stringI.H
+++ b/src/OpenFOAM/primitives/strings/string/stringI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -59,6 +59,26 @@ inline bool Foam::string::hasExt() const
 }
 
 
+inline bool Foam::string::hasExt(const char* ending) const
+{
+    return (ending && string::hasExt(std::string(ending)));
+}
+
+
+inline bool Foam::string::hasExt(const std::string& ending) const
+{
+    const auto len = ending.size();
+    auto i = find_ext();
+    if (i == npos || !len)
+    {
+        return false;
+    }
+
+    ++i; // Compare *after* the dot
+    return ((size() - i) == len) && !compare(i, npos, ending);
+}
+
+
 inline bool Foam::string::removePath()
 {
     const auto i = rfind('/');
@@ -128,12 +148,12 @@ inline Foam::string::string(const size_type len, const char c)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-template<class String>
+template<class StringType>
 inline bool Foam::string::valid(const std::string& str)
 {
     for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
     {
-        if (!String::valid(*iter))
+        if (!StringType::valid(*iter))
         {
             return false;
         }
@@ -143,10 +163,10 @@ inline bool Foam::string::valid(const std::string& str)
 }
 
 
-template<class String>
+template<class StringType>
 inline bool Foam::string::stripInvalid(std::string& str)
 {
-    if (!valid<String>(str))
+    if (!string::valid<StringType>(str))
     {
         size_type nChar = 0;
         iterator outIter = str.begin();
@@ -155,7 +175,7 @@ inline bool Foam::string::stripInvalid(std::string& str)
         {
             const char c = *iter;
 
-            if (String::valid(c))
+            if (StringType::valid(c))
             {
                 *outIter = c;
                 ++outIter;
@@ -172,79 +192,17 @@ inline bool Foam::string::stripInvalid(std::string& str)
 }
 
 
-template<class String>
-inline bool Foam::string::meta(const std::string& str, const char quote)
-{
-    int escaped = 0;
-    for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
-    {
-        const char c = *iter;
-        if (quote && c == quote)
-        {
-            escaped ^= 1;  // toggle state
-        }
-        else if (escaped)
-        {
-            escaped = 0;
-        }
-        else if (String::meta(c))
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-
-template<class String>
-inline Foam::string
-Foam::string::quotemeta(const std::string& str, const char quote)
-{
-    if (!quote)
-    {
-        return str;
-    }
-
-    string sQuoted;
-    sQuoted.reserve(2*str.size());
-
-    int escaped = 0;
-    for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
-    {
-        const char c = *iter;
-        if (c == quote)
-        {
-            escaped ^= 1;  // toggle state
-        }
-        else if (escaped)
-        {
-            escaped = 0;
-        }
-        else if (String::meta(c))
-        {
-            sQuoted += quote;
-        }
-
-        sQuoted += c;
-    }
-
-    sQuoted.shrink_to_fit();
-
-    return sQuoted;
-}
-
-
-template<class String>
-inline String Foam::string::validate(const std::string& str)
+template<class StringType>
+inline StringType Foam::string::validate(const std::string& str)
 {
-    String out;
+    StringType out;
     out.resize(str.size());
 
     size_type len = 0;
     for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
     {
         const char c = *iter;
-        if (String::valid(c))
+        if (StringType::valid(c))
         {
             out[len] = c;
             ++len;
@@ -281,14 +239,4 @@ inline bool Foam::string::operator()(const std::string& text) const
 }
 
 
-inline unsigned Foam::string::hash::operator()
-(
-    const std::string& str,
-    unsigned seed
-) const
-{
-    return Hasher(str.data(), str.size(), seed);
-}
-
-
 // ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/strings/string/stringIO.C b/src/OpenFOAM/primitives/strings/string/stringIO.C
index d7e33c0b3a3c7de96b54782b6e5fb1e99e627699..c89aedc8d36036c9f9e1034974fd3f92f157c5ee 100644
--- a/src/OpenFOAM/primitives/strings/string/stringIO.C
+++ b/src/OpenFOAM/primitives/strings/string/stringIO.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,6 +27,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "string.H"
+#include "token.H"
 #include "IOstreams.H"
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
@@ -41,27 +42,27 @@ Foam::string::string(Istream& is)
 
 Foam::Istream& Foam::operator>>(Istream& is, string& val)
 {
-    token t(is);
+    token tok(is);
 
-    if (!t.good())
+    if (tok.isString())
     {
-        FatalIOErrorInFunction(is)
-            << "Bad token - could not get string"
-            << exit(FatalIOError);
-        is.setBad();
-        return is;
-    }
-
-    if (t.isString())
-    {
-        val = t.stringToken();
+        val = tok.stringToken();
     }
     else
     {
-        FatalIOErrorInFunction(is)
-            << "Wrong token type - expected string, found "
-            << t.info()
-            << exit(FatalIOError);
+        FatalIOErrorInFunction(is);
+        if (tok.good())
+        {
+            FatalIOError
+                << "Wrong token type - expected string, found "
+                << tok.info();
+        }
+        else
+        {
+            FatalIOError
+                << "Bad token - could not get string";
+        }
+        FatalIOError << exit(FatalIOError);
         is.setBad();
         return is;
     }
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
index c634d7d2d9ab4c0427c45669d7786c772caafc2d..be512d45d60bd87f5b8451e9d33869e6e4d2a178 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.C
@@ -719,7 +719,7 @@ std::string::size_type Foam::stringOps::count(const char* s, const char c)
 Foam::string Foam::stringOps::expand
 (
     const std::string& s,
-    const HashTable<string, word, string::hash>& mapping,
+    const HashTable<string>& mapping,
     const char sigil
 )
 {
@@ -732,7 +732,7 @@ Foam::string Foam::stringOps::expand
 void Foam::stringOps::inplaceExpand
 (
     std::string& s,
-    const HashTable<string, word, string::hash>& mapping,
+    const HashTable<string>& mapping,
     const char sigil
 )
 {
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
index b771277c7fed5dc9f3d7e5619c14b0778fcbe1d1..33424ea873b888c10e6bfa9590dacb3583d869a5 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2012 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -77,6 +77,15 @@ namespace stringOps
         return wordRes::matcher(patterns)(text);
     }
 
+    //- Quote any meta-characters in given string
+    template<class StringType, class UnaryPredicate>
+    StringType quotemeta
+    (
+        const StringType& str,
+        const UnaryPredicate& meta,
+        const char quote = '\\'
+    );
+
     //- Expand occurrences of variables according to the mapping
     //- and return the expanded string.
     //
@@ -84,7 +93,7 @@ namespace stringOps
     string expand
     (
         const std::string& s,
-        const HashTable<string, word, string::hash>& mapping,
+        const HashTable<string>& mapping,
         const char sigil = '$'
     );
 
@@ -126,7 +135,7 @@ namespace stringOps
     void inplaceExpand
     (
         std::string& s,
-        const HashTable<string, word, string::hash>& mapping,
+        const HashTable<string>& mapping,
         const char sigil = '$'
     );
 
diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C
index 94bad8fb65f6a5e665a1ecb6fd1a79b2b08f1030..094e19b3804e881bfe8c5c727df74aa65aca8d94 100644
--- a/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C
+++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2018 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,6 +27,45 @@ License
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+template<class StringType, class UnaryPredicate>
+StringType Foam::stringOps::quotemeta
+(
+    const StringType& str,
+    const UnaryPredicate& meta,
+    const char quote
+)
+{
+    if (str.empty() || !quote)
+    {
+        return str;
+    }
+
+    StringType result;
+    result.reserve(1.5*str.size());  // Moderately pessimistic
+
+    bool escaped = false;
+    for (const char c : str)
+    {
+        if (c == quote)
+        {
+            escaped = !escaped;  // toggle state
+        }
+        else if (escaped)
+        {
+            escaped = false;
+        }
+        else if (meta(c))
+        {
+            result += quote;
+        }
+        result += c;
+    }
+
+    result.shrink_to_fit();
+    return result;
+}
+
+
 template<class StringType>
 Foam::SubStrings<StringType> Foam::stringOps::split
 (
diff --git a/src/OpenFOAM/primitives/strings/word/word.C b/src/OpenFOAM/primitives/strings/word/word.C
index 9cc7dc15c5cf79b5da1e8f38fbc07cf5a64c5860..47c9ddd9f788118c58e9ea79fd07ac1e7637b602 100644
--- a/src/OpenFOAM/primitives/strings/word/word.C
+++ b/src/OpenFOAM/primitives/strings/word/word.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -112,7 +112,7 @@ Foam::word Foam::word::validate
 
 Foam::word Foam::word::lessExt() const
 {
-    const size_type i = find_ext();
+    const auto i = find_ext();
 
     if (i == npos)
     {
@@ -136,18 +136,6 @@ Foam::word& Foam::word::ext(const word& ending)
 }
 
 
-bool Foam::word::hasExt(const word& ending) const
-{
-    return string::hasExt(ending);
-}
-
-
-bool Foam::word::hasExt(const wordRe& ending) const
-{
-    return string::hasExt(ending);
-}
-
-
 // * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //
 
 Foam::word Foam::operator&(const word& a, const word& b)
diff --git a/src/OpenFOAM/primitives/strings/word/word.H b/src/OpenFOAM/primitives/strings/word/word.H
index b58f3c50d29a49622bf41b6fc331c50c41eeedd1..d05abd312f3edeaecfed023483691d931a40870a 100644
--- a/src/OpenFOAM/primitives/strings/word/word.H
+++ b/src/OpenFOAM/primitives/strings/word/word.H
@@ -50,11 +50,10 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class word;
-Istream& operator>>(Istream& is, word& w);
-Ostream& operator<<(Ostream& os, const word& w);
-
+Istream& operator>>(Istream& is, word& val);
+Ostream& operator<<(Ostream& os, const word& val);
 
 /*---------------------------------------------------------------------------*\
                            Class word Declaration
@@ -80,7 +79,7 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         word() = default;
 
         //- Copy construct
@@ -169,17 +168,11 @@ public:
         //  or when the file name is empty or ended with a '/'.
         word& ext(const word& ending);
 
-        //- Return true if it has an extension or simply ends with a '.'
-        inline bool hasExt() const;
-
-        //- Return true if the extension is the same as the given ending.
-        bool hasExt(const word& ending) const;
-
-        //- Return true if the extension matches the given ending.
-        bool hasExt(const wordRe& ending) const;
+        //- Various checks for extensions
+        using string::hasExt;
 
         //- Remove extension, returning true if string changed.
-        inline bool removeExt();
+        using string::removeExt;
 
 
     // Member Operators
diff --git a/src/OpenFOAM/primitives/strings/word/wordI.H b/src/OpenFOAM/primitives/strings/word/wordI.H
index 4e93d536c16852e64e1311636a7afe6adc924a3d..bc907c744d7a2aa263f52a5fc3b9784f5793c9c4 100644
--- a/src/OpenFOAM/primitives/strings/word/wordI.H
+++ b/src/OpenFOAM/primitives/strings/word/wordI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,7 +26,6 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include <cctype>
 #include <iostream>  // For std::cerr
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
@@ -57,6 +56,21 @@ inline Foam::word Foam::word::printf
 }
 
 
+inline bool Foam::word::valid(char c)
+{
+    return
+    (
+        !isspace(c)
+     && c != '"'   // string quote
+     && c != '\''  // string quote
+     && c != '/'   // path separator
+     && c != ';'   // end statement
+     && c != '{'   // beg block (eg, subdict)
+     && c != '}'   // end block (eg, subdict)
+    );
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 inline Foam::word::word(const string& s, bool doStrip)
@@ -127,21 +141,6 @@ inline Foam::word::word(const char* s, size_type len, bool doStrip)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-inline bool Foam::word::valid(char c)
-{
-    return
-    (
-        !isspace(c)
-     && c != '"'   // string quote
-     && c != '\''  // string quote
-     && c != '/'   // path separator
-     && c != ';'   // end statement
-     && c != '{'   // beg subdict
-     && c != '}'   // end subdict
-    );
-}
-
-
 inline void Foam::word::stripInvalid()
 {
     // Only strip when debug is active (potentially costly operation)
@@ -162,18 +161,6 @@ inline void Foam::word::stripInvalid()
 }
 
 
-inline bool Foam::word::hasExt() const
-{
-    return string::hasExt();
-}
-
-
-inline bool Foam::word::removeExt()
-{
-    return string::removeExt();
-}
-
-
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 inline Foam::word& Foam::word::operator=(const word& s)
diff --git a/src/OpenFOAM/primitives/strings/word/wordIO.C b/src/OpenFOAM/primitives/strings/word/wordIO.C
index 04ec553e766cccac33642a44797071a1b366c157..03e46193ca69ebb23483e10041003fb67025dd79 100644
--- a/src/OpenFOAM/primitives/strings/word/wordIO.C
+++ b/src/OpenFOAM/primitives/strings/word/wordIO.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,58 +27,59 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "word.H"
+#include "token.H"
 #include "IOstreams.H"
 
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::word::word(Istream& is)
-:
-    string()
 {
     is >> *this;
 }
 
 
+// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+
 Foam::Istream& Foam::operator>>(Istream& is, word& val)
 {
-    token t(is);
-
-    if (!t.good())
-    {
-        FatalIOErrorInFunction(is)
-            << "Bad token - could not get word"
-            << exit(FatalIOError);
-        is.setBad();
-        return is;
-    }
+    token tok(is);
 
-    if (t.isWord())
+    if (tok.isWord())
     {
-        val = t.wordToken();
+        val = tok.wordToken();
     }
-    else if (t.isString())
+    else if (tok.isQuotedString())
     {
         // Try a bit harder and convert string to word
-        val = t.stringToken();
+        val = tok.stringToken();
+        const auto oldLen = val.length();
         string::stripInvalid<word>(val);
 
         // Flag empty strings and bad chars as an error
-        if (val.empty() || val.size() != t.stringToken().size())
+        if (val.empty() || val.length() != oldLen)
         {
             FatalIOErrorInFunction(is)
                 << "Empty word or non-word characters "
-                << t.info()
-                << exit(FatalIOError);
+                << tok.info() << exit(FatalIOError);
             is.setBad();
             return is;
         }
     }
     else
     {
-        FatalIOErrorInFunction(is)
-            << "Wrong token type - expected word, found "
-            << t.info()
-            << exit(FatalIOError);
+        FatalIOErrorInFunction(is);
+        if (tok.good())
+        {
+            FatalIOError
+                << "Wrong token type - expected word, found "
+                << tok.info();
+        }
+        else
+        {
+            FatalIOError
+                << "Bad token - could not get word";
+        }
+        FatalIOError << exit(FatalIOError);
         is.setBad();
         return is;
     }
diff --git a/src/OpenFOAM/primitives/strings/wordRe/wordRe.C b/src/OpenFOAM/primitives/strings/wordRe/wordRe.C
index bf9e9cae67ae3199722cd6fa82fa22f50933244f..9f74c7ac23f0023f441fc93a6e1cb273aabc045a 100644
--- a/src/OpenFOAM/primitives/strings/wordRe/wordRe.C
+++ b/src/OpenFOAM/primitives/strings/wordRe/wordRe.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,8 +27,9 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "wordRe.H"
+#include "keyType.H"
+#include "token.H"
 #include "IOstreams.H"
-#include "InfoProxy.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -37,59 +38,75 @@ const Foam::wordRe Foam::wordRe::null;
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::wordRe::wordRe(Istream& is)
+Foam::wordRe::wordRe(const keyType& str)
 :
-    word(),
-    re_(nullptr)
+    word(str, false)  // No stripping
+{
+    if (str.isPattern())
+    {
+        compile();
+    }
+}
+
+
+Foam::wordRe::wordRe(Istream& is)
 {
     is >> *this;
 }
 
 
-// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::Ostream& Foam::wordRe::info(Ostream& os) const
+bool Foam::wordRe::assign(const token& tok)
 {
-    if (isPattern())
+    if (tok.isWord())
     {
-        os  << "wordRe(regex) " << *this;
+        // Assign from word - literal
+        assign(tok.wordToken());
+        uncompile();
+        return true;
     }
-    else
+    else if (tok.isQuotedString())
     {
-        os  << "wordRe(plain) \"" << *this << '"';
+        // Assign from quoted string - auto-detect regex
+        assign(tok.stringToken());
+        compile(wordRe::DETECT);
+        return true;
     }
 
-    return os;
+    return false;
 }
 
 
-Foam::Istream& Foam::operator>>(Istream& is, wordRe& val)
-{
-    token t(is);
+// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
-    if (!t.good())
+void Foam::wordRe::operator=(const keyType& str)
+{
+    assign(str);
+    if (str.isPattern())
     {
-        FatalIOErrorInFunction(is)
-            << "Bad token - could not get wordRe"
-            << exit(FatalIOError);
-        is.setBad();
-        return is;
+        compile();
     }
-
-    if (t.isWord())
+    else
     {
-        val = t.wordToken();
+        uncompile();
     }
-    else if (t.isString())
-    {
-        // Auto-detects regex
-        val = t.stringToken();
+}
 
-        // Flag empty strings as an error
+
+// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, wordRe& val)
+{
+    token tok(is);
+
+    if (val.assign(tok))
+    {
         if (val.empty())
         {
+            // Empty strings are an error
             FatalIOErrorInFunction(is)
-                << "Empty word/expression"
+                << "Zero-length regex"
                 << exit(FatalIOError);
             is.setBad();
             return is;
@@ -97,10 +114,19 @@ Foam::Istream& Foam::operator>>(Istream& is, wordRe& val)
     }
     else
     {
-        FatalIOErrorInFunction(is)
-            << "Wrong token type - expected word or string, found "
-            << t.info()
-            << exit(FatalIOError);
+        FatalIOErrorInFunction(is);
+        if (tok.good())
+        {
+            FatalIOError
+                << "Wrong token type - expected word or string, found "
+                << tok.info();
+        }
+        else
+        {
+            FatalIOError
+                << "Bad token - could not get wordRe";
+        }
+        FatalIOError << exit(FatalIOError);
         is.setBad();
         return is;
     }
diff --git a/src/OpenFOAM/primitives/strings/wordRe/wordRe.H b/src/OpenFOAM/primitives/strings/wordRe/wordRe.H
index 29a0a762f62f967d771cbc33bd7ef8c248c8ba91..c1467d8d428a16d4f3ed32817d59c4509ea8c015 100644
--- a/src/OpenFOAM/primitives/strings/wordRe/wordRe.H
+++ b/src/OpenFOAM/primitives/strings/wordRe/wordRe.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,10 +42,11 @@ Description
 
 Note
     If the string contents are changed - eg, by the operator+=() or by
-    string::replace(), etc - it will be necessary to use compile() to
-    synchronize the regular expression.
+    string::replace(), etc - it will be necessary to use compile()
+    or uncompile() to synchronize the regular expression.
 
 SourceFiles
+    wordReI.H
     wordRe.C
 
 \*---------------------------------------------------------------------------*/
@@ -55,7 +56,7 @@ SourceFiles
 
 #include "word.H"
 #include "regExp.H"
-#include "keyType.H"
+#include "stdFoam.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -63,8 +64,11 @@ namespace Foam
 {
 
 // Forward Declarations
-class Istream;
-class Ostream;
+class keyType;
+class token;
+class wordRe;
+Istream& operator>>(Istream& is, wordRe& val);
+Ostream& operator<<(Ostream& os, const wordRe& val);
 
 /*---------------------------------------------------------------------------*\
                            Class wordRe Declaration
@@ -99,7 +103,7 @@ public:
             ICASE   = 2, //!< Ignore case in regular expression
             NOCASE  = 2, //!< \deprecated(2018-04) Alias for ICASE
             DETECT  = 4, //!< Detect if the string contains meta-characters
-            UNKNOWN = 4, //!< Unknown content.
+            UNKNOWN = 4, //!< Unknown content (for return value).
             REGEX_ICASE  = (REGEX|ICASE),   //!< Combined REGEX and ICASE
             DETECT_ICASE = (DETECT|ICASE),  //!< Combined DETECT and ICASE
         };
@@ -107,7 +111,7 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct, empty literal
         inline wordRe();
 
         //- Copy construct
@@ -116,75 +120,75 @@ public:
         //- Move construct
         inline wordRe(wordRe&& str);
 
-        //- Construct from keyType, using its compile information
-        inline explicit wordRe(const keyType& str);
-
-        //- Copy from character array, treat as a literal
-        inline explicit wordRe(const char* str);
-
-        //- Copy from std::string, treat as a literal
-        inline explicit wordRe(const std::string& str);
-
-        //- Copy from string, treat as a literal
-        inline explicit wordRe(const string& str);
-
-        //- Copy from word, treat as a literal
-        inline explicit wordRe(const word& str);
-
-        //- Copy from keyType, use specified compile option
-        inline wordRe(const keyType& str, const compOption opt);
-
-        //- Copy from character array, use specified compile option
-        inline wordRe(const char* str, const compOption opt);
-
-        //- Copy from std::string, use specified compile option
-        inline wordRe(const std::string& str, const compOption opt);
-
-        //- Copy from string, use specified compile option
-        inline wordRe(const string& str, const compOption opt);
-
-        //- Copy from word, use specified compile option
-        inline wordRe(const word& str, const compOption opt);
-
-        //- Construct from Istream
-        //  Words are treated as literals, strings with an auto-test
+        //- Implicit copy construct from word, as LITERAL
+        inline wordRe(const word& str);
+
+        //- Implicit move construct from word, as LITERAL
+        inline wordRe(word&& str);
+
+        //- Implicit copy construct from other string-types,
+        //- with specified compile option (default is LITERAL)
+        inline wordRe
+        (
+            const std::string& str,
+            const compOption opt = compOption::LITERAL
+        );
+
+        //- Implicit construct from character array,
+        //- with specified compile option (default is LITERAL)
+        inline wordRe
+        (
+            const char* str,
+            const compOption opt = compOption::LITERAL
+        );
+
+        //- Implicit copy construct from keyType, using its compile type
+        wordRe(const keyType& str);
+
+        //- Construct from Istream by reading a token
+        //  Words are treated as literals, strings with an auto-detect
         explicit wordRe(Istream& is);
 
 
     // Member Functions
 
-        //- Is this a meta character?
-        inline static bool meta(char c);
-
-        //- Is this character valid for a wordRe?
-        //  This is largely identical with what word accepts, but also
-        //  permit brace-brackets, which are valid for some regexs.
-        inline static bool valid(char c);
-
-        //- Test string for regular expression meta characters
-        inline static bool isPattern(const std::string& str);
+        //- Test for valid wordRe character?
+        //  Like Foam::word, but with brace-brackets,
+        //  which are valid for some regexs.
+        inline static bool valid(const char c);
 
 
     // Access
 
         //- The wordRe is treated as literal string, not as pattern.
-        inline bool isLiteral() const;
+        inline bool isLiteral() const noexcept;
 
         //- The wordRe is treated as a pattern, not as literal string.
-        inline bool isPattern() const;
+        inline bool isPattern() const noexcept;
 
 
     // Infrastructure
 
-        //- Compile the regular expression
+        //- Inherit all regular string assign() methods
+        using word::assign;
+
+        //- Assign from word or string token.
+        //  Words are treated as literals, strings with an auto-detect
+        //  \return false if the token was the incorrect type
+        bool assign(const token& tok);
+
+        //- Compile as regular expression
         inline bool compile();
 
+        //- Mark as literal string, remove any regular expression
+        inline void uncompile();
+
         //- Possibly compile the regular expression, with greater control
         inline bool compile(const compOption opt);
 
-        //- Make wordRe a literal again, instead of a regular expression.
-        //  Optionally strip invalid word characters.
-        inline void uncompile(bool adjust = false);
+        //- Mark as literal string, optionally stripping invalid word
+        //- characters when changing to a literal
+        inline void uncompile(bool adjust);
 
 
     // Editing
@@ -209,15 +213,6 @@ public:
         inline bool match(const std::string& text, bool literal=false) const;
 
 
-    // Miscellaneous
-
-        //- Return a string with quoted meta-characters
-        inline string quotemeta() const;
-
-        //- Output some basic info
-        Ostream& info(Ostream& os) const;
-
-
     // Member Operators
 
         //- Perform smart match on text, as per match()
@@ -234,7 +229,7 @@ public:
 
         //- Copy keyType and its type (literal or regex)
         //  Always case sensitive
-        inline void operator=(const keyType& str);
+        void operator=(const keyType& str);
 
         //- Copy string, auto-test for regular expression
         //  Always case sensitive
diff --git a/src/OpenFOAM/primitives/strings/wordRe/wordReI.H b/src/OpenFOAM/primitives/strings/wordRe/wordReI.H
index 9d09b771d0e5d137d039dbda81796cbc0f173f4e..215f5da116c609946f7a63f5834c4f8b3166a7b4 100644
--- a/src/OpenFOAM/primitives/strings/wordRe/wordReI.H
+++ b/src/OpenFOAM/primitives/strings/wordRe/wordReI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,21 +28,10 @@ License
 
 // * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
 
-inline bool Foam::wordRe::meta(char c)
+inline bool Foam::wordRe::valid(const char c)
 {
-    return regExp::meta(c);
-}
-
-
-inline bool Foam::wordRe::valid(char c)
-{
-    return keyType::valid(c);
-}
-
-
-inline bool Foam::wordRe::isPattern(const std::string& str)
-{
-    return string::meta<regExp>(str);
+    // Also accept '{' and '}' (for regex grouping?)
+    return (word::valid(c) || c == '{' || c == '}');
 }
 
 
@@ -57,8 +46,7 @@ inline Foam::wordRe::wordRe()
 
 inline Foam::wordRe::wordRe(const wordRe& str)
 :
-    word(str, false),
-    re_()
+    word(static_cast<const word&>(str))
 {
     if (str.isPattern())
     {
@@ -74,52 +62,23 @@ inline Foam::wordRe::wordRe(wordRe&& str)
 {}
 
 
-inline Foam::wordRe::wordRe(const keyType& str)
-:
-    word(str, false),
-    re_()
-{
-    if (str.isPattern())
-    {
-        compile();
-    }
-}
-
-
-inline Foam::wordRe::wordRe(const char* str)
-:
-    word(str, false),
-    re_()
-{}
-
-
-inline Foam::wordRe::wordRe(const std::string& str)
-:
-    word(str, false),
-    re_()
-{}
-
-
-inline Foam::wordRe::wordRe(const string& str)
+inline Foam::wordRe::wordRe(const word& str)
 :
-    word(str, false),
-    re_()
+    word(str)
 {}
 
 
-inline Foam::wordRe::wordRe(const word& str)
+inline Foam::wordRe::wordRe(word&& str)
 :
-    word(str, false),
-    re_()
+    word(std::move(str))
 {}
 
 
-inline Foam::wordRe::wordRe(const keyType& str, const compOption opt)
+inline Foam::wordRe::wordRe(const std::string& str, const compOption opt)
 :
-    word(str, false),
-    re_()
+    word(str, false)  // No stripping
 {
-    if (str.isPattern())
+    if (opt != wordRe::LITERAL)
     {
         compile(opt);
     }
@@ -128,45 +87,24 @@ inline Foam::wordRe::wordRe(const keyType& str, const compOption opt)
 
 inline Foam::wordRe::wordRe(const char* str, const compOption opt)
 :
-    wordRe(str)
-{
-    compile(opt);
-}
-
-
-inline Foam::wordRe::wordRe(const std::string& str, const compOption opt)
-:
-    wordRe(str)
+    word(str, false)  // No stripping
 {
-    compile(opt);
-}
-
-
-inline Foam::wordRe::wordRe(const string& str, const compOption opt)
-:
-    wordRe(str)
-{
-    compile(opt);
-}
-
-
-inline Foam::wordRe::wordRe(const word& str, const compOption opt)
-:
-    wordRe(str)
-{
-    compile(opt);
+    if (opt != wordRe::LITERAL)
+    {
+        compile(opt);
+    }
 }
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-inline bool Foam::wordRe::isLiteral() const
+inline bool Foam::wordRe::isLiteral() const noexcept
 {
     return !re_.exists();
 }
 
 
-inline bool Foam::wordRe::isPattern() const
+inline bool Foam::wordRe::isPattern() const noexcept
 {
     return re_.exists();
 }
@@ -174,7 +112,7 @@ inline bool Foam::wordRe::isPattern() const
 
 inline bool Foam::wordRe::compile(const compOption opt)
 {
-    if (opt)
+    if (opt != wordRe::LITERAL)
     {
         bool comp = false;
 
@@ -184,7 +122,7 @@ inline bool Foam::wordRe::compile(const compOption opt)
         }
         else if (opt & wordRe::DETECT)
         {
-            comp = string::meta<regExp>(*this) || !string::valid<word>(*this);
+            comp = regExp::is_meta(*this) || !string::valid<word>(*this);
         }
         else if (opt & wordRe::ICASE)
         {
@@ -209,13 +147,20 @@ inline bool Foam::wordRe::compile()
 }
 
 
+inline void Foam::wordRe::uncompile()
+{
+    re_.clear();
+}
+
+
 inline void Foam::wordRe::uncompile(bool adjust)
 {
     // Only strip when debug is active (potentially costly operation)
-    if (re_.clear() && adjust && word::debug)
+    if (adjust && isPattern() && word::debug)
     {
         string::stripInvalid<word>(*this);
     }
+    re_.clear();
 }
 
 
@@ -237,12 +182,6 @@ inline bool Foam::wordRe::match(const std::string& text, bool literal) const
 }
 
 
-inline Foam::string Foam::wordRe::quotemeta() const
-{
-    return string::quotemeta<regExp>(*this);
-}
-
-
 inline void Foam::wordRe::set(const std::string& str, const compOption opt)
 {
     assign(str);
@@ -303,20 +242,6 @@ inline void Foam::wordRe::operator=(const word& str)
 }
 
 
-inline void Foam::wordRe::operator=(const keyType& str)
-{
-    assign(str);
-    if (str.isPattern())
-    {
-        compile();
-    }
-    else
-    {
-        re_.clear();
-    }
-}
-
-
 inline void Foam::wordRe::operator=(const string& str)
 {
     assign(str);
diff --git a/src/OpenFOAM/primitives/strings/wordRes/wordRes.H b/src/OpenFOAM/primitives/strings/wordRes/wordRes.H
index 076e77b47557f3dd3d9fb4971a692a00f0149f60..5b368260ed9247f58127947f8e8b2e0e14d51e41 100644
--- a/src/OpenFOAM/primitives/strings/wordRes/wordRes.H
+++ b/src/OpenFOAM/primitives/strings/wordRes/wordRes.H
@@ -56,21 +56,22 @@ class wordRes
     // Private Methods
 
         //- Smart match as literal or regex, stopping on the first match.
-        inline static bool found_match
+        //  \return index of first match, -1 if not found
+        inline static label first_match
         (
-            const UList<wordRe>& patterns,
+            const UList<wordRe>& selectors,
             const std::string& text,
-            bool literal=false
+            const bool literal=false
         );
 
-        //- Smart match across entire list, returning the match type.
+        //- Smart match across entire list, returning the best match type.
         //  Stops on the first literal match, or continues to examine
         //  if a regex match occurs.
         //  \return wordRe::LITERAL, wordRe::REGEX on match and
         //      wordRe::UNKNOWN otherwise.
         inline static wordRe::compOption found_matched
         (
-            const UList<wordRe>& patterns,
+            const UList<wordRe>& selectors,
             const std::string& text
         );
 
@@ -84,17 +85,14 @@ public:
         {
             const UList<wordRe>& values;
 
-            matcher(const UList<wordRe>& list)
+            matcher(const UList<wordRe>& selectors)
             :
-                values(list)
+                values(selectors)
             {}
 
-            //- Return true if string matches ANY of the regular expressions
+            //- True if text matches ANY of the entries.
             //  Allows use as a predicate.
-            bool operator()(const std::string& text) const
-            {
-                return found_match(values, text);
-            }
+            inline bool operator()(const std::string& text) const;
         };
 
 
@@ -104,7 +102,7 @@ public:
         inline static const wordRes& null();
 
         //- Return a wordRes with duplicate entries filtered out.
-        //  No distinction made between literals or regular expressions.
+        //  No distinction made between literals and regular expressions.
         static wordRes uniq(const UList<wordRe>& input);
 
 
@@ -121,7 +119,7 @@ public:
     // Member Functions
 
         //- Filter out duplicate entries (inplace).
-        //  No distinction made between literals or regular expressions.
+        //  No distinction made between literals and regular expressions.
         void uniq();
 
         //- Smart match as literal or regex, stopping on the first match.
@@ -133,16 +131,16 @@ public:
         //- Smart match in the list of matchers, returning the match type.
         //  It stops if there is a literal match, or continues to examine
         //  other regexs.
-        //  \return LITERAL if a lteral match was found, REGEX if a regex
-        //      match was found and UNKNOWN otherwise.
+        //  \return LITERAL if a lteral match was found,
+        //      REGEX if any regex match was found,
+        //      UNKNOWN otherwise.
         inline wordRe::compOption matched(const std::string& text) const;
 
-        //- Extract list indices for all matches.
+        //- Return list indices for all matches.
         //
         //  \param input  A list of string inputs to match against
         //  \param invert invert the matching logic
-        //  \return The locations (indices) in the input list where match()
-        //      is true
+        //  \return indices of the matches in the input list
         template<class StringType>
         inline labelList matching
         (
diff --git a/src/OpenFOAM/primitives/strings/wordRes/wordResI.H b/src/OpenFOAM/primitives/strings/wordRes/wordResI.H
index a62057aa9500fffe1fb2d3f04b5c9aa1905582d4..a0eb765fd5bd2652b7b26e38328451feacd61fc4 100644
--- a/src/OpenFOAM/primitives/strings/wordRes/wordResI.H
+++ b/src/OpenFOAM/primitives/strings/wordRes/wordResI.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2018 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -33,34 +33,36 @@ inline const Foam::wordRes& Foam::wordRes::null()
 }
 
 
-inline bool Foam::wordRes::found_match
+inline Foam::label Foam::wordRes::first_match
 (
-    const UList<wordRe>& patterns,
+    const UList<wordRe>& selectors,
     const std::string& text,
-    bool literal
+    const bool literal
 )
 {
-    for (const wordRe& select : patterns)
+    label index = 0;
+    for (const wordRe& select : selectors)
     {
         if (select.match(text, literal))
         {
-            return true;
+            return index;
         }
+        ++index;
     }
 
-    return false;
+    return -1;
 }
 
 
 inline Foam::wordRe::compOption Foam::wordRes::found_matched
 (
-    const UList<wordRe>& patterns,
+    const UList<wordRe>& selectors,
     const std::string& text
 )
 {
     auto retval(wordRe::compOption::UNKNOWN);
 
-    for (const wordRe& select : patterns)
+    for (const wordRe& select : selectors)
     {
         if (select.isLiteral())
         {
@@ -69,12 +71,14 @@ inline Foam::wordRe::compOption Foam::wordRes::found_matched
                 return wordRe::compOption::LITERAL;
             }
         }
-        else if (wordRe::compOption::UNKNOWN == retval)
+        else if
+        (
+            // Only match regex once
+            retval == wordRe::compOption::UNKNOWN
+         && select.match(text, false)
+        )
         {
-            if (select.match(text, false))
-            {
-                retval = wordRe::compOption::REGEX;
-            }
+            retval = wordRe::compOption::REGEX;
         }
     }
 
@@ -86,7 +90,7 @@ inline Foam::wordRe::compOption Foam::wordRes::found_matched
 
 inline bool Foam::wordRes::match(const std::string& text, bool literal) const
 {
-    return found_match(*this, text, literal);
+    return (first_match(*this, text, literal) >= 0);
 }
 
 
@@ -127,7 +131,13 @@ inline Foam::labelList Foam::wordRes::matching
 
 inline bool Foam::wordRes::operator()(const std::string& text) const
 {
-    return found_match(*this, text);
+    return (wordRes::first_match(*this, text) >= 0);
+}
+
+
+inline bool Foam::wordRes::matcher::operator()(const std::string& text) const
+{
+    return (wordRes::first_match(values, text) >= 0);
 }
 
 
diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C
index c19ee6bad051393dcc973792008273e5379a8b07..7a5332ce81076cf03d8ced93dbb752d11bcf2dec 100644
--- a/src/Pstream/mpi/UPstream.C
+++ b/src/Pstream/mpi/UPstream.C
@@ -305,12 +305,9 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread)
         if (Pstream::master())
         {
             DynamicList<word> allWorlds(numprocs);
-            for (const auto& world : worlds)
+            for (const word& world : worlds)
             {
-                if (!allWorlds.found(world))
-                {
-                    allWorlds.append(world);
-                }
+                allWorlds.appendUniq(world);
             }
             allWorlds_ = std::move(allWorlds);
 
diff --git a/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C b/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
index f89864304a2147094d854481f14c4f5593224696..5eff990577a5f084b4add7fb2706de21c77fccd6 100644
--- a/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
+++ b/src/dynamicFaMesh/interfaceTrackingFvMesh/interfaceTrackingFvMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2019 Zeljko Tukovic, FSB Zagreb.
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -1101,10 +1101,7 @@ void Foam::interfaceTrackingFvMesh::correctPointDisplacement
 
                 if (index == -1)
                 {
-                    if (!pointSet.found(curPoint))
-                    {
-                        pointSet.insert(curPoint);
-                    }
+                    pointSet.insert(curPoint);
                 }
             }
         }
@@ -1127,10 +1124,7 @@ void Foam::interfaceTrackingFvMesh::correctPointDisplacement
 
                 if (index != -1)
                 {
-                    if (!faceSet.found(curFace))
-                    {
-                        faceSet.insert(curFace);
-                    }
+                    faceSet.insert(curFace);
                 }
             }
 
@@ -1197,10 +1191,7 @@ void Foam::interfaceTrackingFvMesh::correctPointDisplacement
 
                 if (index != -1)
                 {
-                    if (!faceSet.found(curFace))
-                    {
-                        faceSet.insert(curFace);
-                    }
+                    faceSet.insert(curFace);
                 }
             }
 
diff --git a/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.C b/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.C
index f753211b1814c8522945f6fa1d74d435bbd9b085..f8b89e90b3811dc3208ee1982f0bafeb3cca5510 100644
--- a/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.C
+++ b/src/dynamicMesh/fvMeshDistribute/fvMeshDistribute.C
@@ -1196,7 +1196,7 @@ void Foam::fvMeshDistribute::findCouples
 
 
     // Insert all into big map. Find matches
-    HashTable<labelPair, labelPair, labelPair::Hash<>> map(2*sum(nProcFaces));
+    LabelPairMap<labelPair> map(2*sum(nProcFaces));
 
     nProcFaces = 0;
 
diff --git a/src/dynamicMesh/meshCut/cellLooper/geomCellLooper.C b/src/dynamicMesh/meshCut/cellLooper/geomCellLooper.C
index 9a529e961bb3661e4582533a52c73c9e18e4f331..245a15cad90509b720955d9b184578253e08735e 100644
--- a/src/dynamicMesh/meshCut/cellLooper/geomCellLooper.C
+++ b/src/dynamicMesh/meshCut/cellLooper/geomCellLooper.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -290,14 +290,12 @@ bool Foam::geomCellLooper::cut
         // Check distance of endpoints to cutPlane
         //
 
-        if (!checkedPoints.found(e.start()))
+        if (checkedPoints.insert(e.start()))
         {
-            checkedPoints.insert(e.start());
+            const scalar typLen = pointEqualTol_ * minEdgeLen(e.start());
 
-            scalar typStartLen = pointEqualTol_ * minEdgeLen(e.start());
-
-            // Check distance of startPt to plane.
-            if (cutPlane.distance(points[e.start()]) < typStartLen)
+            // Check distance to plane.
+            if (cutPlane.distance(points[e.start()]) < typLen)
             {
                 // Use point.
                 localLoop.append(vertToEVert(e.start()));
@@ -306,14 +304,13 @@ bool Foam::geomCellLooper::cut
                 useStart = true;
             }
         }
-        if (!checkedPoints.found(e.end()))
-        {
-            checkedPoints.insert(e.end());
 
-            scalar typEndLen = pointEqualTol_ * minEdgeLen(e.end());
+        if (checkedPoints.insert(e.end()))
+        {
+            const scalar typLen = pointEqualTol_ * minEdgeLen(e.end());
 
-            // Check distance of endPt to plane.
-            if (cutPlane.distance(points[e.end()]) < typEndLen)
+            // Check distance to plane.
+            if (cutPlane.distance(points[e.end()]) < typLen)
             {
                 // Use point.
                 localLoop.append(vertToEVert(e.end()));
diff --git a/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.C b/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.C
index 96dac89c189d81d43f425a071b12a39148770f36..32243a266767eb7dcfeebbd055a9ebf1065a38b7 100644
--- a/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.C
+++ b/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.C
@@ -47,6 +47,31 @@ namespace Foam
 
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
+Foam::dlLibraryTable& Foam::codedPoints0MotionSolver::libs() const
+{
+    return mesh().time().libs();
+}
+
+
+Foam::string Foam::codedPoints0MotionSolver::description() const
+{
+    return "points0MotionSolver " + name();
+}
+
+
+void Foam::codedPoints0MotionSolver::clearRedirect() const
+{
+    redirectMotionSolverPtr_.reset(nullptr);
+}
+
+
+const Foam::dictionary&
+Foam::codedPoints0MotionSolver::codeDict() const
+{
+    return motionSolver::coeffDict();
+}
+
+
 void Foam::codedPoints0MotionSolver::prepare
 (
     dynamicCode& dynCode,
@@ -62,11 +87,11 @@ void Foam::codedPoints0MotionSolver::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
@@ -87,31 +112,6 @@ void Foam::codedPoints0MotionSolver::prepare
 }
 
 
-Foam::dlLibraryTable& Foam::codedPoints0MotionSolver::libs() const
-{
-    return mesh().time().libs();
-}
-
-
-Foam::string Foam::codedPoints0MotionSolver::description() const
-{
-    return "points0MotionSolver " + name();
-}
-
-
-void Foam::codedPoints0MotionSolver::clearRedirect() const
-{
-    redirectMotionSolverPtr_.clear();
-}
-
-
-const Foam::dictionary&
-Foam::codedPoints0MotionSolver::codeDict() const
-{
-    return coeffDict();
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::codedPoints0MotionSolver::codedPoints0MotionSolver
@@ -121,10 +121,10 @@ Foam::codedPoints0MotionSolver::codedPoints0MotionSolver
 )
 :
     motionSolver(mesh, dict, typeName),
-    codedBase()
+    codedBase(),
+    name_(dict.getCompat<word>("name", {{"redirectType", 1706}})),
+    redirectMotionSolverPtr_(nullptr)
 {
-    dict.readCompat<word>("name", {{"redirectType", 1706}}, name_);
-
     updateLibrary(name_);
     redirectMotionSolver();
 }
@@ -140,7 +140,7 @@ Foam::codedPoints0MotionSolver::redirectMotionSolver() const
         // Get the dictionary for the solver and override the
         // solver name (in case it is not a subdictionary and contains
         // the 'coded' as the motionSolver)
-        dictionary constructDict(coeffDict());
+        dictionary constructDict(motionSolver::coeffDict());
         constructDict.set("solver", name_);
         constructDict.set("motionSolver", name_);
 
diff --git a/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.H b/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.H
index 47afa827fdd83e048f38eeb021ad1152e1f37b5e..e99e5434419b0807e3d5217b396be6de8f3fe9c6 100644
--- a/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.H
+++ b/src/dynamicMesh/motionSolvers/displacement/codedPoints0/codedPoints0MotionSolver.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017-2020 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -106,18 +106,18 @@ protected:
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        //- Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        //- Clear any redirected objects
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
         // The dictionary to initialize the codeContext
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
         //- No copy construct
         codedPoints0MotionSolver(const codedPoints0MotionSolver&) = delete;
diff --git a/src/dynamicMesh/polyMeshAdder/polyMeshAdder.C b/src/dynamicMesh/polyMeshAdder/polyMeshAdder.C
index 0dfaa92d41267a56d5d916cb3907abbdbfc30898..3b4f801a8f23153fbb1a53a96b9d0c64fbed4d69 100644
--- a/src/dynamicMesh/polyMeshAdder/polyMeshAdder.C
+++ b/src/dynamicMesh/polyMeshAdder/polyMeshAdder.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019,2021 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -916,10 +916,7 @@ void Foam::polyMeshAdder::mergePointZones
             else if (pointToZone[allPointi] != zoneI)
             {
                 labelList& pZones = addPointToZones[allPointi];
-                if (!pZones.found(zoneI))
-                {
-                    pZones.append(zoneI);
-                }
+                pZones.appendUniq(zoneI);
             }
         }
     }
@@ -942,10 +939,7 @@ void Foam::polyMeshAdder::mergePointZones
             else if (pointToZone[allPointi] != allZoneI)
             {
                 labelList& pZones = addPointToZones[allPointi];
-                if (!pZones.found(allZoneI))
-                {
-                    pZones.append(allZoneI);
-                }
+                pZones.appendUniq(allZoneI);
             }
         }
     }
@@ -1074,9 +1068,8 @@ void Foam::polyMeshAdder::mergeFaceZones
                     labelList& fZones = addFaceToZones[allFacei];
                     boolList& flipZones = addFaceToFlips[allFacei];
 
-                    if (!fZones.found(zoneI))
+                    if (fZones.appendUniq(zoneI))
                     {
-                        fZones.append(zoneI);
                         flipZones.append(flip0);
                     }
                 }
@@ -1117,9 +1110,8 @@ void Foam::polyMeshAdder::mergeFaceZones
                     labelList& fZones = addFaceToZones[allFacei];
                     boolList& flipZones = addFaceToFlips[allFacei];
 
-                    if (!fZones.found(allZoneI))
+                    if (fZones.appendUniq(allZoneI))
                     {
-                        fZones.append(allZoneI);
                         flipZones.append(flip1);
                     }
                 }
@@ -1239,10 +1231,7 @@ void Foam::polyMeshAdder::mergeCellZones
             else if (cellToZone[cell0] != zoneI)
             {
                 labelList& cZones = addCellToZones[cell0];
-                if (!cZones.found(zoneI))
-                {
-                    cZones.append(zoneI);
-                }
+                cZones.appendUniq(zoneI);
             }
         }
     }
@@ -1264,10 +1253,7 @@ void Foam::polyMeshAdder::mergeCellZones
             else if (cellToZone[allCelli] != allZoneI)
             {
                 labelList& cZones = addCellToZones[allCelli];
-                if (!cZones.found(allZoneI))
-                {
-                    cZones.append(allZoneI);
-                }
+                cZones.appendUniq(allZoneI);
             }
         }
     }
diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/combineFaces.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/combineFaces.C
index 440fccfd228899ab448eb71a0058d2feeeb1d906..02517d8f492c09ee61099ea76076c271f545f62e 100644
--- a/src/dynamicMesh/polyTopoChange/polyTopoChange/combineFaces.C
+++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/combineFaces.C
@@ -267,10 +267,7 @@ bool Foam::combineFaces::faceNeighboursValid
                 }
                 else
                 {
-                    if (!neighbourFaces.found(nbrI))
-                    {
-                        neighbourFaces.append(nbrI);
-                    }
+                    neighbourFaces.appendUniq(nbrI);
                 }
             }
 
diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8/hexRef8.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8/hexRef8.C
index 8d2860adbe4b4639d268db4d5bf356064bffa3aa..f6a9420df9a7caf6be4fe435a213622bcc480d7c 100644
--- a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8/hexRef8.C
+++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8/hexRef8.C
@@ -1849,17 +1849,14 @@ bool Foam::hexRef8::matchHexShape
                         if (iter.found())
                         {
                             labelList& pFaces = iter.val();
-                            if (!pFaces.found(facei))
-                            {
-                                pFaces.append(facei);
-                            }
+                            pFaces.appendUniq(facei);
                         }
                         else
                         {
                             pointFaces.insert
                             (
                                 pointi,
-                                labelList(1, facei)
+                                labelList(one{}, facei)
                             );
                         }
                     }
diff --git a/src/dynamicMesh/slidingInterface/enrichedPatch/enrichedPatchPointPoints.C b/src/dynamicMesh/slidingInterface/enrichedPatch/enrichedPatchPointPoints.C
index bd0736cbe4778263490316cc6ceb7e121c0b310b..3362ec1640dd84ac297e936abd146f00888ce4f1 100644
--- a/src/dynamicMesh/slidingInterface/enrichedPatch/enrichedPatchPointPoints.C
+++ b/src/dynamicMesh/slidingInterface/enrichedPatch/enrichedPatchPointPoints.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -58,19 +58,11 @@ void Foam::enrichedPatch::calcPointPoints() const
 
             // Do next label
             const label next = curFace.nextLabel(pointi);
-
-            if (!curPp.found(next))
-            {
-                curPp.append(next);
-            }
+            curPp.appendUniq(next);
 
             // Do previous label
             const label prev = curFace.prevLabel(pointi);
-
-            if (!curPp.found(prev))
-            {
-                curPp.append(prev);
-            }
+            curPp.appendUniq(prev);
         }
     }
 
diff --git a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
index 3a42e98ce88e06c101991bd71698ce020cabe54d..b436ecf9962bb54bac90a2d2e7d62d3774cc979c 100644
--- a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
+++ b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.C
@@ -157,18 +157,17 @@ Foam::wordList Foam::faBoundaryMesh::types() const
 
 Foam::labelList Foam::faBoundaryMesh::indices
 (
-    const keyType& key,
+    const wordRe& matcher,
     const bool useGroups  // ignored
 ) const
 {
-    if (key.empty())
+    if (matcher.empty())
     {
         return labelList();
     }
 
-    if (key.isPattern())
+    if (matcher.isPattern())
     {
-        const regExp matcher(key);
         return PtrListOps::findMatching(*this, matcher);
     }
     else
@@ -176,7 +175,6 @@ Foam::labelList Foam::faBoundaryMesh::indices
         // Literal string.
         // Special version of above for reduced memory footprint
 
-        const word& matcher = key;
         const label patchId = PtrListOps::firstMatching(*this, matcher);
 
         if (patchId >= 0)
@@ -189,24 +187,13 @@ Foam::labelList Foam::faBoundaryMesh::indices
 }
 
 
-Foam::label Foam::faBoundaryMesh::findIndex(const keyType& key) const
+Foam::label Foam::faBoundaryMesh::findIndex(const wordRe& key) const
 {
     if (key.empty())
     {
         return -1;
     }
-    else if (key.isPattern())
-    {
-        // Find as regex
-        const regExp matcher(key);
-        return PtrListOps::firstMatching(*this, matcher);
-    }
-    else
-    {
-        // Find as literal string
-        const word& matcher = key;
-        return PtrListOps::firstMatching(*this, matcher);
-    }
+    return PtrListOps::firstMatching(*this, key);
 }
 
 
diff --git a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
index 579fdbbbcb97739b11df3013e3084e6bcfba5a88..4c2c69ead5b3f234234dd76bff74e694f1181d32 100644
--- a/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
+++ b/src/finiteArea/faMesh/faBoundaryMesh/faBoundaryMesh.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016-2017 Wikki Ltd
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -135,13 +135,13 @@ public:
             //  \note Matching patchGroups currently not supported
             labelList indices
             (
-                const keyType& key,
+                const wordRe& matcher,
                 const bool useGroups = false  /* ignored */
             ) const;
 
             //- Return patch index for the first match, return -1 if not found
             //  A no-op (returns -1) for an empty key
-            label findIndex(const keyType& key) const;
+            label findIndex(const wordRe& key) const;
 
             //- Find patch index given a name, return -1 if not found
             //  A no-op (returns -1) for an empty name
@@ -175,11 +175,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices
-        (
-            const keyType& key,
-            const bool useGroups = false
-        ) const
+        labelList findIndices(const wordRe& key, bool useGroups=false) const
         {
             return indices(key, useGroups);
         }
diff --git a/src/finiteArea/faMesh/faMeshDemandDrivenData.C b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
index 8e9a50dd09e583e63036076daa45675433e8e865..7a363fd2ab6b9a84d34bc18d6d3b1ac1d85cc234 100644
--- a/src/finiteArea/faMesh/faMeshDemandDrivenData.C
+++ b/src/finiteArea/faMesh/faMeshDemandDrivenData.C
@@ -945,7 +945,7 @@ void Foam::faMesh::calcPointAreaNormals() const
 
         labelList curPointPoints = curPatch.edgeLoops()[0];
 
-        for (int i = 0; i < curPointPoints.size(); ++i)
+        for (label i = 0; i < curPointPoints.size(); ++i)
         {
             vector d1 =
                 points[curPatch.meshPoints()[curPointPoints[i]]]
@@ -1212,60 +1212,35 @@ void Foam::faMesh::calcPointAreaNormalsByQuadricsFit() const
 
         labelHashSet pointSet;
 
-        pointSet.insert(curPoint);
-        for (label i=0; i<curFaces.size(); ++i)
+        for (const label facei : curFaces)
         {
-            const labelList& facePoints = faces[curFaces[i]];
-            for (label j=0; j<facePoints.size(); ++j)
-            {
-                if (!pointSet.found(facePoints[j]))
-                {
-                    pointSet.insert(facePoints[j]);
-                }
-            }
+            const labelList& facePoints = faces[facei];
+            pointSet.insert(facePoints);
         }
         pointSet.erase(curPoint);
         labelList curPoints(pointSet.toc());
 
-        if (curPoints.size() < 5)
+        if (pointSet.size() < 5)
         {
             DebugInfo
                 << "WARNING: Extending point set for fitting." << endl;
 
             labelHashSet faceSet(pointFaces[curPoint]);
             labelList curFaces(faceSet.toc());
-            forAll(curFaces, faceI)
+            for (const label facei : curFaces)
             {
-                const labelList& curFaceFaces =
-                    patch().faceFaces()[curFaces[faceI]];
-
-                forAll(curFaceFaces, fI)
-                {
-                    label curFaceFace = curFaceFaces[fI];
-
-                    if (!faceSet.found(curFaceFace))
-                    {
-                        faceSet.insert(curFaceFace);
-                    }
-                }
+                const labelList& curFaceFaces = patch().faceFaces()[facei];
+                faceSet.insert(curFaceFaces);
             }
             curFaces = faceSet.toc();
 
-            labelHashSet pointSet;
+            pointSet.clear();
 
-            pointSet.insert(curPoint);
-            for (label i=0; i<curFaces.size(); ++i)
+            for (const label facei : curFaces)
             {
-                const labelList& facePoints = faces[curFaces[i]];
-                for (label j=0; j<facePoints.size(); ++j)
-                {
-                    if (!pointSet.found(facePoints[j]))
-                    {
-                        pointSet.insert(facePoints[j]);
-                    }
-                }
+                const labelList& facePoints = faces[facei];
+                pointSet.insert(facePoints);
             }
-
             pointSet.erase(curPoint);
             curPoints = pointSet.toc();
         }
@@ -1372,17 +1347,10 @@ void Foam::faMesh::calcPointAreaNormalsByQuadricsFit() const
 
                 labelHashSet pointSet;
 
-                pointSet.insert(curPoint);
-                for (label i=0; i<curFaces.size(); ++i)
+                for (const label facei : curFaces)
                 {
-                    const labelList& facePoints = faces[curFaces[i]];
-                    for (label j=0; j<facePoints.size(); ++j)
-                    {
-                        if (!pointSet.found(facePoints[j]))
-                        {
-                            pointSet.insert(facePoints[j]);
-                        }
-                    }
+                    const labelList& facePoints = faces[facei];
+                    pointSet.insert(facePoints);
                 }
                 pointSet.erase(curPoint);
                 labelList curPoints = pointSet.toc();
@@ -1456,17 +1424,10 @@ void Foam::faMesh::calcPointAreaNormalsByQuadricsFit() const
 
                 labelHashSet pointSet;
 
-                pointSet.insert(curPoint);
-                for (label i=0; i<curFaces.size(); ++i)
+                for (const label facei : curFaces)
                 {
-                    const labelList& facePoints = faces[curFaces[i]];
-                    for (label j=0; j<facePoints.size(); ++j)
-                    {
-                        if (!pointSet.found(facePoints[j]))
-                        {
-                            pointSet.insert(facePoints[j]);
-                        }
-                    }
+                    const labelList& facePoints = faces[facei];
+                    pointSet.insert(facePoints);
                 }
                 pointSet.erase(curPoint);
                 labelList curPoints = pointSet.toc();
@@ -1653,17 +1614,10 @@ void Foam::faMesh::calcPointAreaNormalsByQuadricsFit() const
                 const labelList curFaces(faceSet.toc());
 
                 labelHashSet pointSet;
-                pointSet.insert(curPoint);
-                for (label i=0; i<curFaces.size(); ++i)
+                for (const label facei : curFaces)
                 {
-                    const labelList& facePoints = faces[curFaces[i]];
-                    for (label j=0; j<facePoints.size(); ++j)
-                    {
-                        if (!pointSet.found(facePoints[j]))
-                        {
-                            pointSet.insert(facePoints[j]);
-                        }
-                    }
+                    const labelList& facePoints = faces[facei];
+                    pointSet.insert(facePoints);
                 }
                 pointSet.erase(curPoint);
                 labelList curPoints = pointSet.toc();
diff --git a/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.C b/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.C
index 716e8aaa7f7f8ffab0054228bd91202419aa7dbe..e189d099e62231af2d65f6342caeefae391d203c 100644
--- a/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.C
+++ b/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2017 OpenFOAM Foundation
-    Copyright (C) 2016 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -99,7 +99,7 @@ Foam::porosityModel::porosityModel
         coordinateSystem::New(mesh, coeffs_, coordinateSystem::typeName_())
     )
 {
-    if (zoneName_ == word::null)
+    if (zoneName_.empty())
     {
         dict.readIfPresent("active", active_);
         dict_.readEntry("cellZone", zoneName_);
diff --git a/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.H b/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.H
index d5b2a64864dcb6798ebcd50b6a992bd2d646d798..229bbfc2c52e6e3c372583539312bdcc71bfd1b5 100644
--- a/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.H
+++ b/src/finiteVolume/cfdTools/general/porosityModel/porosityModel/porosityModel.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2018 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -44,7 +45,6 @@ SourceFiles
 #include "runTimeSelectionTables.H"
 #include "coordinateSystem.H"
 #include "dimensionedVector.H"
-#include "keyType.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -70,7 +70,7 @@ class porosityModel
 
 protected:
 
-    // Protected data
+    // Protected Data
 
         //- Porosity name
         word name_;
@@ -88,7 +88,7 @@ protected:
         bool active_;
 
         //- Name(s) of cell-zone
-        keyType zoneName_;
+        wordRe zoneName_;
 
         //- Cell zone IDs
         labelList cellZoneIDs_;
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.C
index 55e412cf02ef0490a86b84ce2d45b48c6273c9e9..0db1fa9d3911bb79a303ccb33e1b4dbe49ba8422 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.C
+++ b/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,37 +36,41 @@ License
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-const Foam::IOdictionary& Foam::codedFixedValueFvPatchField<Type>::dict() const
+Foam::dlLibraryTable& Foam::codedFixedValueFvPatchField<Type>::libs() const
 {
-    const objectRegistry& obr = this->db();
+    return this->db().time().libs();
+}
 
-    const IOdictionary* dictptr = obr.cfindObject<IOdictionary>("codeDict");
-    if (dictptr)
-    {
-        return *dictptr;
-    }
 
-    return obr.store
-    (
-        new IOdictionary
-        (
-            IOobject
-            (
-                "codeDict",
-                this->db().time().system(),
-                this->db(),
-                IOobject::MUST_READ_IF_MODIFIED,
-                IOobject::NO_WRITE
-            )
-        )
-    );
+template<class Type>
+Foam::string Foam::codedFixedValueFvPatchField<Type>::description() const
+{
+    return
+        "patch "
+      + this->patch().name()
+      + " on field "
+      + this->internalField().name();
 }
 
 
 template<class Type>
-Foam::dlLibraryTable& Foam::codedFixedValueFvPatchField<Type>::libs() const
+void Foam::codedFixedValueFvPatchField<Type>::clearRedirect() const
 {
-    return this->db().time().libs();
+    redirectPatchFieldPtr_.reset(nullptr);
+}
+
+
+template<class Type>
+const Foam::dictionary&
+Foam::codedFixedValueFvPatchField<Type>::codeDict() const
+{
+    // Inline "code" or from system/codeDict
+    return
+    (
+        dict_.found("code")
+      ? dict_
+      : codedBase::codeDict(this->db()).subDict(name_)
+    );
 }
 
 
@@ -89,59 +93,28 @@ void Foam::codedFixedValueFvPatchField<Type>::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
     (
         "EXE_INC = -g \\\n"
         "-I$(LIB_SRC)/finiteVolume/lnInclude \\\n"
+        "-I$(LIB_SRC)/meshTools/lnInclude \\\n"
       + context.options()
       + "\n\nLIB_LIBS = \\\n"
         "    -lOpenFOAM \\\n"
         "    -lfiniteVolume \\\n"
+        "    -lmeshTools \\\n"
       + context.libs()
     );
 }
 
 
-template<class Type>
-const Foam::dictionary& Foam::codedFixedValueFvPatchField<Type>::codeDict()
-const
-{
-    // use system/codeDict or in-line
-    return
-    (
-        dict_.found("code")
-      ? dict_
-      : this->dict().subDict(name_)
-    );
-}
-
-
-template<class Type>
-Foam::string Foam::codedFixedValueFvPatchField<Type>::description() const
-{
-    return
-        "patch "
-      + this->patch().name()
-      + " on field "
-      + this->internalField().name();
-}
-
-
-template<class Type>
-void Foam::codedFixedValueFvPatchField<Type>::clearRedirect() const
-{
-    // remove instantiation of fvPatchField provided by library
-    redirectPatchFieldPtr_.clear();
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Type>
@@ -153,7 +126,7 @@ Foam::codedFixedValueFvPatchField<Type>::codedFixedValueFvPatchField
 :
     fixedValueFvPatchField<Type>(p, iF),
     codedBase(),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {}
 
 
@@ -186,7 +159,7 @@ Foam::codedFixedValueFvPatchField<Type>::codedFixedValueFvPatchField
     codedBase(),
     dict_(dict),
     name_(dict.getCompat<word>("name", {{"redirectType", 1706}})),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {
     updateLibrary(name_);
 }
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.H b/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.H
index 992cc77a08be5b77cefab24b90be2a4b8fb02dc8..9f2c91e0721fc62a28e8f4d9dfbf8733597c7247 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.H
+++ b/src/finiteVolume/fields/fvPatchFields/derived/codedFixedValue/codedFixedValueFvPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -94,9 +94,6 @@ SourceFiles
 namespace Foam
 {
 
-// Forward Declarations
-class IOdictionary;
-
 /*---------------------------------------------------------------------------*\
                  Class codedFixedValueFvPatchField Declaration
 \*---------------------------------------------------------------------------*/
@@ -110,7 +107,7 @@ class codedFixedValueFvPatchField
     // Private Data
 
         //- Dictionary contents for the boundary condition
-        const dictionary dict_;
+        dictionary dict_;
 
         const word name_;
 
@@ -119,23 +116,21 @@ class codedFixedValueFvPatchField
 
     // Private Member Functions
 
-        const IOdictionary& dict() const;
-
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        // Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        // Clear the ptr to the redirected object
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        // Get the dictionary to initialize the codeContext
+        //- The code dictionary. Inline "code" or from system/codeDict
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
 public:
 
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.C
index 96e11f9f4eabd1b20adc47707e48958c2de93cd9..a4440ae03c6a60b5f666d5a2d894a4ec4adf8aac 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.C
+++ b/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.C
@@ -35,37 +35,41 @@ License
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class Type>
-const Foam::IOdictionary& Foam::codedMixedFvPatchField<Type>::dict() const
+Foam::dlLibraryTable& Foam::codedMixedFvPatchField<Type>::libs() const
 {
-    const objectRegistry& obr = this->db();
+    return this->db().time().libs();
+}
 
-    const IOdictionary* dictptr = obr.cfindObject<IOdictionary>("codeDict");
-    if (dictptr)
-    {
-        return *dictptr;
-    }
 
-    return obr.store
-    (
-        new IOdictionary
-        (
-            IOobject
-            (
-                "codeDict",
-                this->db().time().system(),
-                this->db(),
-                IOobject::MUST_READ_IF_MODIFIED,
-                IOobject::NO_WRITE
-            )
-        )
-    );
+template<class Type>
+Foam::string Foam::codedMixedFvPatchField<Type>::description() const
+{
+    return
+        "patch "
+      + this->patch().name()
+      + " on field "
+      + this->internalField().name();
 }
 
 
 template<class Type>
-Foam::dlLibraryTable& Foam::codedMixedFvPatchField<Type>::libs() const
+void Foam::codedMixedFvPatchField<Type>::clearRedirect() const
 {
-    return this->db().time().libs();
+    redirectPatchFieldPtr_.reset(nullptr);
+}
+
+
+template<class Type>
+const Foam::dictionary&
+Foam::codedMixedFvPatchField<Type>::codeDict() const
+{
+    // Inline "code" or from system/codeDict
+    return
+    (
+        dict_.found("code")
+      ? dict_
+      : codedBase::codeDict(this->db()).subDict(name_)
+    );
 }
 
 
@@ -88,59 +92,28 @@ void Foam::codedMixedFvPatchField<Type>::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
     (
         "EXE_INC = -g \\\n"
         "-I$(LIB_SRC)/finiteVolume/lnInclude \\\n"
+        "-I$(LIB_SRC)/meshTools/lnInclude \\\n"
       + context.options()
       + "\n\nLIB_LIBS = \\\n"
         "    -lOpenFOAM \\\n"
         "    -lfiniteVolume \\\n"
+        "    -lmeshTools \\\n"
       + context.libs()
     );
 }
 
 
-template<class Type>
-const Foam::dictionary& Foam::codedMixedFvPatchField<Type>::codeDict()
-const
-{
-    // use system/codeDict or in-line
-    return
-    (
-        dict_.found("code")
-      ? dict_
-      : this->dict().subDict(name_)
-    );
-}
-
-
-template<class Type>
-Foam::string Foam::codedMixedFvPatchField<Type>::description() const
-{
-    return
-        "patch "
-      + this->patch().name()
-      + " on field "
-      + this->internalField().name();
-}
-
-
-template<class Type>
-void Foam::codedMixedFvPatchField<Type>::clearRedirect() const
-{
-    // remove instantiation of fvPatchField provided by library
-    redirectPatchFieldPtr_.clear();
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Type>
@@ -152,7 +125,7 @@ Foam::codedMixedFvPatchField<Type>::codedMixedFvPatchField
 :
     mixedFvPatchField<Type>(p, iF),
     codedBase(),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {}
 
 
@@ -185,7 +158,7 @@ Foam::codedMixedFvPatchField<Type>::codedMixedFvPatchField
     codedBase(),
     dict_(dict),
     name_(dict.getCompat<word>("name", {{"redirectType", 1706}})),
-    redirectPatchFieldPtr_()
+    redirectPatchFieldPtr_(nullptr)
 {
     updateLibrary(name_);
 }
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.H b/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.H
index cf941e0906b6fbb81efb09387056def2c94cccdb..2ca033652ec9b7c10055a86d023a16d83be5f01f 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.H
+++ b/src/finiteVolume/fields/fvPatchFields/derived/codedMixed/codedMixedFvPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -104,11 +104,6 @@ SourceFiles
 namespace Foam
 {
 
-// Forward Declarations
-class dynamicCode;
-class dynamicCodeContext;
-class IOdictionary;
-
 /*---------------------------------------------------------------------------*\
                    Class codedMixedFvPatchField Declaration
 \*---------------------------------------------------------------------------*/
@@ -122,18 +117,13 @@ class codedMixedFvPatchField
     // Private Data
 
         //- Dictionary contents for the boundary condition
-        mutable dictionary dict_;
+        dictionary dict_;
 
         const word name_;
 
         mutable autoPtr<mixedFvPatchField<Type>> redirectPatchFieldPtr_;
 
 
-    // Private Member Functions
-
-        const IOdictionary& dict() const;
-
-
 protected:
 
     // Protected Member Functions
@@ -141,18 +131,18 @@ protected:
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        // Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        // Clear the ptr to the redirected object
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        // Get the dictionary to initialize the codeContext
+        //- The code dictionary. Inline "code" or from system/codeDict
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
 public:
 
diff --git a/src/finiteVolume/functionObjects/fieldSelections/fieldSelection/fieldInfo.H b/src/finiteVolume/functionObjects/fieldSelections/fieldSelection/fieldInfo.H
index b9e282e2c32edb425e944268a486b94cd8d1147b..db79172a3883781b3e3affa0a000453a0c4ceef4 100644
--- a/src/finiteVolume/functionObjects/fieldSelections/fieldSelection/fieldInfo.H
+++ b/src/finiteVolume/functionObjects/fieldSelections/fieldSelection/fieldInfo.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,8 +35,8 @@ Description
 #ifndef functionObjects_fieldInfo_H
 #define functionObjects_fieldInfo_H
 
-#include "wordRe.H"
 #include "label.H"
+#include "wordRes.H"
 #include "Switch.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -46,19 +46,20 @@ namespace Foam
 namespace functionObjects
 {
 
-/*---------------------------------------------------------------------------*\
-                          Class fieldInfo Declaration
-\*---------------------------------------------------------------------------*/
-
+// Forward Declarations
 class fieldInfo;
 Istream& operator>>(Istream&, fieldInfo&);
 Ostream& operator<<(Ostream&, const fieldInfo&);
 
+/*---------------------------------------------------------------------------*\
+                          Class fieldInfo Declaration
+\*---------------------------------------------------------------------------*/
+
 class fieldInfo
 {
-    // Pivate data
+    // Pivate Data
 
-        //- Pattern for the field name
+        //- Pattern for the field name(s)
         wordRe name_;
 
         //- Field component
@@ -72,10 +73,10 @@ public:
 
     // Constructors
 
-        //- Null constructor
+        //- Default construct
         fieldInfo()
         :
-            name_(word::null),
+            name_(),
             component_(-1),
             found_(false)
         {}
@@ -153,6 +154,7 @@ public:
 } // End namespace functionObjects
 } // End namespace Foam
 
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 #endif
 
diff --git a/src/finiteVolume/fvMesh/extendedStencil/faceToCell/globalIndexStencils/CFCFaceToCellStencil.C b/src/finiteVolume/fvMesh/extendedStencil/faceToCell/globalIndexStencils/CFCFaceToCellStencil.C
index 1cd27154f484582d6c1637154125030553155362..878317938688c1c075f98a01928970ac1ec876b8 100644
--- a/src/finiteVolume/fvMesh/extendedStencil/faceToCell/globalIndexStencils/CFCFaceToCellStencil.C
+++ b/src/finiteVolume/fvMesh/extendedStencil/faceToCell/globalIndexStencils/CFCFaceToCellStencil.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -140,10 +141,8 @@ void Foam::CFCFaceToCellStencil::calcCellStencil
         allGlobalFaces.clear();
 
         // My faces first
-        forAll(cFaces, i)
+        for (const label facei : cFaces)
         {
-            label facei = cFaces[i];
-
             if
             (
                 mesh().isInternalFace(facei)
@@ -155,10 +154,8 @@ void Foam::CFCFaceToCellStencil::calcCellStencil
         }
 
         // faces of neighbouring cells second
-        forAll(cFaces, i)
+        for (const label facei : cFaces)
         {
-            label facei = cFaces[i];
-
             if (mesh().isInternalFace(facei))
             {
                 label nbrCelli = own[facei];
@@ -168,23 +165,18 @@ void Foam::CFCFaceToCellStencil::calcCellStencil
                 }
                 const cell& nbrFaces = mesh().cells()[nbrCelli];
 
-                forAll(nbrFaces, j)
+                for (const label nbrFacei : nbrFaces)
                 {
-                    label nbrFacei = nbrFaces[j];
-
                     if
                     (
                         mesh().isInternalFace(nbrFacei)
                      || validBFace[nbrFacei-mesh().nInternalFaces()]
                     )
                     {
-                        label nbrGlobalI = globalNumbering().toGlobal(nbrFacei);
+                        label nbrGlobali = globalNumbering().toGlobal(nbrFacei);
 
-                        // Check if already there. Note:should use hashset?
-                        if (!allGlobalFaces.found(nbrGlobalI))
-                        {
-                            allGlobalFaces.append(nbrGlobalI);
-                        }
+                        // Note:should use hashset?
+                        allGlobalFaces.appendUniq(nbrGlobali);
                     }
                 }
             }
@@ -193,15 +185,10 @@ void Foam::CFCFaceToCellStencil::calcCellStencil
                 const labelList& nbrGlobalFaces =
                     neiGlobal[facei-mesh().nInternalFaces()];
 
-                forAll(nbrGlobalFaces, j)
+                for (const label nbrGlobali : nbrGlobalFaces)
                 {
-                    label nbrGlobalI = nbrGlobalFaces[j];
-
-                    // Check if already there. Note:should use hashset?
-                    if (!allGlobalFaces.found(nbrGlobalI))
-                    {
-                        allGlobalFaces.append(nbrGlobalI);
-                    }
+                    // Note:should use hashset?
+                    allGlobalFaces.appendUniq(nbrGlobali);
                 }
             }
         }
diff --git a/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.C b/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.C
index 0377d7c10b59ce5b0baccebdb291017f175aba12..b3d70e1c34007efd42c3d394db7a62132083fddf 100644
--- a/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.C
+++ b/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -75,11 +75,11 @@ Foam::fvBoundaryMesh::fvBoundaryMesh
 
 Foam::labelList Foam::fvBoundaryMesh::indices
 (
-    const keyType& key,
+    const wordRe& matcher,
     const bool useGroups
 ) const
 {
-    return mesh().boundaryMesh().indices(key, useGroups);
+    return mesh().boundaryMesh().indices(matcher, useGroups);
 }
 
 
diff --git a/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.H b/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.H
index 568e3fbe9aef4e58c88228339f09ec6a13a0aa0e..31c27e34a6a5e79e0945ceec23729a0614696e86 100644
--- a/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.H
+++ b/src/finiteVolume/fvMesh/fvBoundaryMesh/fvBoundaryMesh.H
@@ -110,8 +110,8 @@ public:
         lduInterfacePtrsList interfaces() const;
 
         //- Return patch indices for all matches.
-        //  A no-op (returns empty list) for an empty key
-        labelList indices(const keyType& key, const bool useGroups) const;
+        //  A no-op (returns empty list) for an empty matcher
+        labelList indices(const wordRe& matcher, const bool useGroups) const;
 
         //- Find patch index given a name
         //  A no-op (returns -1) for an empty patchName
@@ -137,7 +137,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices(const keyType& key, const bool useGroups) const
+        labelList findIndices(const wordRe& key, bool useGroups) const
         {
             return indices(key, useGroups);
         }
diff --git a/src/finiteVolume/fvMesh/fvGeometryScheme/averageNeighbour/averageNeighbourFvGeometryScheme.C b/src/finiteVolume/fvMesh/fvGeometryScheme/averageNeighbour/averageNeighbourFvGeometryScheme.C
index 3aa139bb876cfd4a11332e15f40c6b52d60004ad..30fc454607fe00c142e75705e4c9c4f643b0c498 100644
--- a/src/finiteVolume/fvMesh/fvGeometryScheme/averageNeighbour/averageNeighbourFvGeometryScheme.C
+++ b/src/finiteVolume/fvMesh/fvGeometryScheme/averageNeighbour/averageNeighbourFvGeometryScheme.C
@@ -699,13 +699,13 @@ void Foam::averageNeighbourFvGeometryScheme::movePoints()
 
 
             // Write current non-ortho
-            fileName outputDir =
+            fileName outputDir
             (
                 mesh_.time().globalPath()
               / functionObject::outputPrefix
               / mesh_.pointsInstance()
             );
-            outputDir.clean();
+            outputDir.clean();  // Remove unneeded ".."
             writerPtr = surfaceWriter::New
             (
                 "ensight" //"vtk"
diff --git a/src/functionObjects/field/ddt2/ddt2.C b/src/functionObjects/field/ddt2/ddt2.C
index 1e01c761180986b540e0e804e8efc4a5ef3b8ebc..7fa82ad0745c631065188576df327f015e2f4e7f 100644
--- a/src/functionObjects/field/ddt2/ddt2.C
+++ b/src/functionObjects/field/ddt2/ddt2.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,6 +26,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "ddt2.H"
+#include "stringOps.H"
 #include "stringListOps.H"
 #include "volFields.H"
 #include "dictionary.H"
@@ -107,7 +108,7 @@ Foam::functionObjects::ddt2::ddt2
 :
     fvMeshFunctionObject(name, runTime, dict),
     selectFields_(),
-    resultName_(word::null),
+    resultName_(),
     denyField_(),
     results_(),
     mag_(dict.getOrDefault("mag", false))
@@ -150,10 +151,8 @@ bool Foam::functionObjects::ddt2::read(const dictionary& dict)
     {
         denyField_.set
         (
-            string::quotemeta<regExp>
-            (
-                resultName_
-            ).replace("@@", "(.+)")
+            stringOps::quotemeta(resultName_, regExp::meta())
+            .replace("@@", "(.+)")
         );
 
         return true;
diff --git a/src/functionObjects/field/externalCoupled/externalCoupled.C b/src/functionObjects/field/externalCoupled/externalCoupled.C
index 3760669f01fae3fe49e72ae46d45e6168a6eba65..72ef8738748a36e50679ae059494bdd52f86059b 100644
--- a/src/functionObjects/field/externalCoupled/externalCoupled.C
+++ b/src/functionObjects/field/externalCoupled/externalCoupled.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -90,10 +90,10 @@ Foam::fileName Foam::functionObjects::externalCoupled::groupDir
     fileName result
     (
         commsDir
-       /regionGroupName
-       /string::validate<fileName>(groupName)
+      / regionGroupName
+      / word::validate(groupName)
     );
-    result.clean();
+    result.clean();  // Remove unneeded ".."
 
     return result;
 }
@@ -278,7 +278,7 @@ void Foam::functionObjects::externalCoupled::writeGeometry
         (
             mesh.boundaryMesh().patchSet
             (
-                List<wordRe>{groupName}
+                wordRes(one{}, groupName)
             ).sortedToc()
         );
 
diff --git a/src/functionObjects/field/externalCoupled/externalCoupledTemplates.C b/src/functionObjects/field/externalCoupled/externalCoupledTemplates.C
index e3e8a3a2409e26e475bd5498ee4b708ce93eb7eb..34e99f62970ec9c794cdbf4040b165a714823999 100644
--- a/src/functionObjects/field/externalCoupled/externalCoupledTemplates.C
+++ b/src/functionObjects/field/externalCoupled/externalCoupledTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -80,7 +80,10 @@ bool Foam::functionObjects::externalCoupled::readData
     }
 
 
+    const wordRes patchSelection(one{}, groupName);
+
     label nFound = 0;
+
     for (const fvMesh& mesh : meshes)
     {
         const volFieldType* vfptr = mesh.findObject<volFieldType>(fieldName);
@@ -89,7 +92,7 @@ bool Foam::functionObjects::externalCoupled::readData
         {
             continue;
         }
-        nFound++;
+        ++nFound;
 
         typename volFieldType::Boundary& bf =
             const_cast<volFieldType*>(vfptr)->boundaryFieldRef();
@@ -97,10 +100,7 @@ bool Foam::functionObjects::externalCoupled::readData
         // Get the patches
         const labelList patchIDs
         (
-            mesh.boundaryMesh().patchSet
-            (
-                List<wordRe>{groupName}
-            ).sortedToc()
+            mesh.boundaryMesh().patchSet(patchSelection).sortedToc()
         );
 
         // Handle column-wise reading of patch data. Supports most easy types
@@ -261,7 +261,7 @@ bool Foam::functionObjects::externalCoupled::readData
         }
     }
 
-    return nFound > 0;
+    return nFound;
 }
 
 
@@ -352,8 +352,9 @@ bool Foam::functionObjects::externalCoupled::writeData
     }
 
 
-    bool headerDone = false;
+    const wordRes patchSelection(one{}, groupName);
 
+    bool headerDone = false;
     label nFound = 0;
 
     for (const fvMesh& mesh : meshes)
@@ -364,7 +365,7 @@ bool Foam::functionObjects::externalCoupled::writeData
         {
             continue;
         }
-        nFound++;
+        ++nFound;
 
         const typename volFieldType::Boundary& bf =
             vfptr->boundaryField();
@@ -372,10 +373,7 @@ bool Foam::functionObjects::externalCoupled::writeData
         // Get the patches
         const labelList patchIDs
         (
-            mesh.boundaryMesh().patchSet
-            (
-                List<wordRe>{groupName}
-            ).sortedToc()
+            mesh.boundaryMesh().patchSet(patchSelection).sortedToc()
         );
 
         // Handle column-wise writing of patch data. Supports most easy types
@@ -474,7 +472,7 @@ bool Foam::functionObjects::externalCoupled::writeData
         }
     }
 
-    return nFound > 0;
+    return nFound;
 }
 
 
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.C b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
index 1c91d52f3ae2d450ef02e98f6b89f34f34458868..c7ec4df888acc8fd7bc2e5edfde518d8db217482 100644
--- a/src/functionObjects/lagrangian/dataCloud/dataCloud.C
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
@@ -195,7 +195,7 @@ bool Foam::functionObjects::dataCloud::read(const dictionary& dict)
         // Standard postProcessing/ naming
         directory_ = time_.globalPath()/functionObject::outputPrefix/name();
     }
-    directory_.clean();
+    directory_.clean();  // Remove unneeded ".."
 
     return true;
 }
diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
index deba0c5e5fc8a773775fc9665b8d768ed2268f21..b56c72db3c1253736e960d00c59fa86305f07e21 100644
--- a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
+++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
@@ -449,7 +449,7 @@ bool Foam::functionObjects::vtkCloud::read(const dictionary& dict)
         // Standard postProcessing/ naming
         directory_ = time_.globalPath()/functionObject::outputPrefix/name();
     }
-    directory_.clean();
+    directory_.clean();  // Remove unneeded ".."
 
     return true;
 }
diff --git a/src/functionObjects/utilities/abort/abort.C b/src/functionObjects/utilities/abort/abort.C
index 7f0a5603888ad766b075d818c8cb63ae188ac8f4..c8a8e0d531d90d81d14816d9370457018277ec6b 100644
--- a/src/functionObjects/utilities/abort/abort.C
+++ b/src/functionObjects/utilities/abort/abort.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -167,11 +167,10 @@ bool Foam::functionObjects::abort::read(const dictionary& dict)
     if (dict.readIfPresent("file", file_))
     {
         file_.expand();
-
-        if (!file_.isAbsolute() && file_.size())
+        if (!file_.empty() && !file_.isAbsolute())
         {
             file_ = time_.globalPath()/file_;
-            file_.clean();
+            file_.clean();  // Remove unneeded ".."
         }
     }
 
@@ -179,7 +178,7 @@ bool Foam::functionObjects::abort::read(const dictionary& dict)
     if (file_.empty())
     {
         file_ = time_.globalPath()/name();
-        file_.clean();
+        file_.clean();  // Remove unneeded ".."
     }
 
     triggered_ = false;
diff --git a/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.C b/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.C
index 0b58ed947a571702d166f3cde6e8819e9ac882bc..829df548684e5916f6595d7a601f82e931d33535 100644
--- a/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.C
+++ b/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -31,6 +31,7 @@ License
 #include "dictionary.H"
 #include "Time.H"
 #include "dynamicCode.H"
+#include "dynamicCodeContext.H"
 #include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@@ -52,6 +53,31 @@ namespace functionObjects
 
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
+Foam::dlLibraryTable& Foam::functionObjects::codedFunctionObject::libs() const
+{
+    return time_.libs();
+}
+
+
+Foam::string Foam::functionObjects::codedFunctionObject::description() const
+{
+    return "functionObject " + name();
+}
+
+
+void Foam::functionObjects::codedFunctionObject::clearRedirect() const
+{
+    redirectFunctionObjectPtr_.reset(nullptr);
+}
+
+
+const Foam::dictionary&
+Foam::functionObjects::codedFunctionObject::codeDict() const
+{
+    return dict_;
+}
+
+
 void Foam::functionObjects::codedFunctionObject::prepare
 (
     dynamicCode& dynCode,
@@ -72,11 +98,11 @@ void Foam::functionObjects::codedFunctionObject::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
@@ -94,31 +120,6 @@ void Foam::functionObjects::codedFunctionObject::prepare
 }
 
 
-Foam::dlLibraryTable& Foam::functionObjects::codedFunctionObject::libs() const
-{
-    return time_.libs();
-}
-
-
-Foam::string Foam::functionObjects::codedFunctionObject::description() const
-{
-    return "functionObject " + name();
-}
-
-
-void Foam::functionObjects::codedFunctionObject::clearRedirect() const
-{
-    redirectFunctionObjectPtr_.clear();
-}
-
-
-const Foam::dictionary&
-Foam::functionObjects::codedFunctionObject::codeDict() const
-{
-    return dict_;
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::functionObjects::codedFunctionObject::codedFunctionObject
@@ -189,99 +190,15 @@ bool Foam::functionObjects::codedFunctionObject::read(const dictionary& dict)
 
     dict.readCompat<word>("name", {{"redirectType", 1706}}, name_);
 
-    label nKeywords = 0;
-
-    const entry* eptr;
-
-    codeData_.clear();
-    codedBase::append("<codeData>");
-    if ((eptr = dict.findEntry("codeData", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(codeData_);
-        dynamicCodeContext::inplaceExpand(codeData_, dict);
-        codedBase::append(codeData_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeData_,
-            eptr->startLineNumber(),
-            dict.name()
-        );
-
-        ++nKeywords;
-    }
-
-    codeRead_.clear();
-    codedBase::append("<codeRead>");
-    if ((eptr = dict.findEntry("codeRead", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(codeRead_);
-        dynamicCodeContext::inplaceExpand(codeRead_, dict);
-        codedBase::append(codeRead_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeRead_,
-            eptr->startLineNumber(),
-            dict.name()
-        );
-
-        ++nKeywords;
-    }
+    auto& ctx = codedBase::codeContext();
 
-    codeExecute_.clear();
-    codedBase::append("<codeExecute>");
-    if ((eptr = dict.findEntry("codeExecute", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(codeExecute_);
-        dynamicCodeContext::inplaceExpand(codeExecute_, dict);
-        codedBase::append(codeExecute_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeExecute_,
-            eptr->startLineNumber(),
-            dict.name()
-        );
-
-        ++nKeywords;
-    }
-
-    codeWrite_.clear();
-    codedBase::append("<codeWrite>");
-    if ((eptr = dict.findEntry("codeWrite", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(codeWrite_);
-        dynamicCodeContext::inplaceExpand(codeWrite_, dict);
-        codedBase::append(codeWrite_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeWrite_,
-            eptr->startLineNumber(),
-            dict.name()
-        );
-
-        ++nKeywords;
-    }
-
-    codeEnd_.clear();
-    codedBase::append("<codeEnd>");
-    if ((eptr = dict.findEntry("codeEnd", keyType::LITERAL)) != nullptr)
-    {
-        eptr->readEntry(codeEnd_);
-        dynamicCodeContext::inplaceExpand(codeEnd_, dict);
-        codedBase::append(codeEnd_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeEnd_,
-            eptr->startLineNumber(),
-            dict.name()
-        );
-
-        ++nKeywords;
-    }
+    // Get code chunks, no short-circuiting
+    int nKeywords = 0;
+    nKeywords += ctx.readIfPresent("codeData", codeData_);
+    nKeywords += ctx.readIfPresent("codeRead", codeRead_);
+    nKeywords += ctx.readIfPresent("codeExecute", codeExecute_);
+    nKeywords += ctx.readIfPresent("codeWrite", codeWrite_);
+    nKeywords += ctx.readIfPresent("codeEnd", codeEnd_);
 
     if (!nKeywords)
     {
diff --git a/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.H b/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.H
index 8d10c295af5d73b112bde853a895d944ee67d764..0ef530cb9979a21605096da739399c8e74abc8e2 100644
--- a/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.H
+++ b/src/functionObjects/utilities/codedFunctionObject/codedFunctionObject.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -38,7 +38,7 @@ Description
        codeInclude | include files
        codeOptions | include paths; inserted into EXE_INC in Make/options
        codeLibs    | link line; inserted into LIB_LIBS in Make/options
-       codeData    | c++; local member data (null constructed);
+       codeData    | c++; local member data (default constructed);
        localCode   | c++; local static functions;
        codeRead    | c++; upon functionObject::read();
        codeExecute | c++; upon functionObject::execute();
@@ -122,18 +122,18 @@ protected:
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        //- Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        //- Clear any redirected objects
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        //- The dictionary to initialize the codeContext
+        //- The code dictionary
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
         //- No copy construct
         codedFunctionObject(const codedFunctionObject&) = delete;
diff --git a/src/functionObjects/utilities/ensightWrite/ensightWrite.C b/src/functionObjects/utilities/ensightWrite/ensightWrite.C
index 7131e34fc6b475abd2bcf78ef95f0fbfb827aa0a..36a3b53d18592091e8bbb52f7365fb3448842dc7 100644
--- a/src/functionObjects/utilities/ensightWrite/ensightWrite.C
+++ b/src/functionObjects/utilities/ensightWrite/ensightWrite.C
@@ -175,7 +175,7 @@ bool Foam::functionObjects::ensightWrite::read(const dictionary& dict)
         // Standard postProcessing/ naming
         outputDir_ = time_.globalPath()/functionObject::outputPrefix/name();
     }
-    outputDir_.clean();
+    outputDir_.clean();  // Remove unneeded ".."
 
     return true;
 }
diff --git a/src/functionObjects/utilities/vtkWrite/vtkWrite.C b/src/functionObjects/utilities/vtkWrite/vtkWrite.C
index 7e0ca767acecfe940903f68a5fbb94ee8ade7f3c..851f4a99f474ce18f52e780a6581667916633985 100644
--- a/src/functionObjects/utilities/vtkWrite/vtkWrite.C
+++ b/src/functionObjects/utilities/vtkWrite/vtkWrite.C
@@ -236,7 +236,7 @@ bool Foam::functionObjects::vtkWrite::read(const dictionary& dict)
         // Standard postProcessing/ naming
         outputDir_ = time_.globalPath()/functionObject::outputPrefix/name();
     }
-    outputDir_.clean();
+    outputDir_.clean();  // Remove unneeded ".."
 
     return true;
 }
diff --git a/src/fvOptions/sources/derived/rotorDiskSource/bladeModel/bladeModel.C b/src/fvOptions/sources/derived/rotorDiskSource/bladeModel/bladeModel.C
index 7e4ea215fbfbb8aa98f847f758e23a5b1d22cd1d..089329248057a5dd91ae96a34bd4f1fbecee4ebe 100644
--- a/src/fvOptions/sources/derived/rotorDiskSource/bladeModel/bladeModel.C
+++ b/src/fvOptions/sources/derived/rotorDiskSource/bladeModel/bladeModel.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,7 +36,7 @@ License
 
 bool Foam::bladeModel::readFromFile() const
 {
-    return fName_ != fileName::null;
+    return !fName_.empty();
 }
 
 
@@ -96,7 +96,7 @@ Foam::bladeModel::bladeModel(const dictionary& dict)
     radius_(),
     twist_(),
     chord_(),
-    fName_(fileName::null)
+    fName_()
 {
     List<Tuple2<word, vector>> data;
     if (readFromFile())
diff --git a/src/fvOptions/sources/derived/rotorDiskSource/profileModel/profileModel.C b/src/fvOptions/sources/derived/rotorDiskSource/profileModel/profileModel.C
index b47afe831f0f0c78e826e3cd76ca16f62cb1489b..41de57780e3f719303040c12a4920cf794aee55c 100644
--- a/src/fvOptions/sources/derived/rotorDiskSource/profileModel/profileModel.C
+++ b/src/fvOptions/sources/derived/rotorDiskSource/profileModel/profileModel.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,7 +42,7 @@ namespace Foam
 
 bool Foam::profileModel::readFromFile() const
 {
-    return fName_ != fileName::null;
+    return !fName_.empty();
 }
 
 
diff --git a/src/fvOptions/sources/general/codedSource/CodedSource.C b/src/fvOptions/sources/general/codedSource/CodedSource.C
index 07185784871808c937ff585aa41af2a713dbd434..07bb792d99b93a21196ead5d9369bdabf3cd1457 100644
--- a/src/fvOptions/sources/general/codedSource/CodedSource.C
+++ b/src/fvOptions/sources/general/codedSource/CodedSource.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,6 +34,35 @@ License
 
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
+
+template<class Type>
+Foam::dlLibraryTable& Foam::fv::CodedSource<Type>::libs() const
+{
+    return mesh_.time().libs();
+}
+
+
+template<class Type>
+Foam::string Foam::fv::CodedSource<Type>::description() const
+{
+    return "fvOption::" + name_;
+}
+
+
+template<class Type>
+void Foam::fv::CodedSource<Type>::clearRedirect() const
+{
+    redirectFvOptionPtr_.reset(nullptr);
+}
+
+
+template<class Type>
+const Foam::dictionary& Foam::fv::CodedSource<Type>::codeDict() const
+{
+    return coeffs_;
+}
+
+
 template<class Type>
 void Foam::fv::CodedSource<Type>::prepare
 (
@@ -53,17 +82,17 @@ void Foam::fv::CodedSource<Type>::prepare
     dynCode.setFilterVariable("codeAddSup", codeAddSup_);
     dynCode.setFilterVariable("codeConstrain", codeConstrain_);
 
-    // compile filtered C template
-    dynCode.addCompileFile("codedFvOptionTemplate.C");
+    // Compile filtered C template
+    dynCode.addCompileFile(codeTemplateC);
 
-    // copy filtered H template
-    dynCode.addCopyFile("codedFvOptionTemplate.H");
+    // Copy filtered H template
+    dynCode.addCopyFile(codeTemplateH);
 
-    // debugging: make  verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // define Make/options
     dynCode.setMakeOptions
@@ -75,43 +104,15 @@ void Foam::fv::CodedSource<Type>::prepare
         "-I$(LIB_SRC)/sampling/lnInclude \\\n"
       + context.options()
       + "\n\nLIB_LIBS = \\\n"
+        "    -lfiniteVolume \\\n"
         "    -lfvOptions \\\n"
         "    -lmeshTools \\\n"
         "    -lsampling \\\n"
-        "    -lfiniteVolume \\\n"
       + context.libs()
     );
 }
 
 
-template<class Type>
-Foam::dlLibraryTable& Foam::fv::CodedSource<Type>::libs() const
-{
-    return mesh_.time().libs();
-}
-
-
-template<class Type>
-Foam::string Foam::fv::CodedSource<Type>::description() const
-{
-    return "fvOption::" + name_;
-}
-
-
-template<class Type>
-void Foam::fv::CodedSource<Type>::clearRedirect() const
-{
-    redirectFvOptionPtr_.clear();
-}
-
-
-template<class Type>
-const Foam::dictionary& Foam::fv::CodedSource<Type>::codeDict() const
-{
-    return coeffs_;
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Type>
@@ -140,7 +141,7 @@ Foam::fv::option& Foam::fv::CodedSource<Type>::redirectFvOption() const
         constructDict.set("type", name_);
         constructDict.changeKeyword(modelType_ & "Coeffs", name_ & "Coeffs");
 
-        redirectFvOptionPtr_ = option::New
+        redirectFvOptionPtr_ = fv::option::New
         (
             name_,
             constructDict,
diff --git a/src/fvOptions/sources/general/codedSource/CodedSource.H b/src/fvOptions/sources/general/codedSource/CodedSource.H
index 2d488f9433d2fb05c5e487b55f57aaf3bbc55c9f..0da0e711c7c3a3728697f40282aaa34c87676d06 100644
--- a/src/fvOptions/sources/general/codedSource/CodedSource.H
+++ b/src/fvOptions/sources/general/codedSource/CodedSource.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -123,7 +123,7 @@ namespace fv
 template<class Type>
 class CodedSource
 :
-    public cellSetOption,
+    public fv::cellSetOption,
     protected codedBase
 {
 protected:
@@ -136,8 +136,8 @@ protected:
         string codeAddSup_;
         string codeConstrain_;
 
-        //- Underlying functionObject
-        mutable autoPtr<option> redirectFvOptionPtr_;
+        //- Underlying code
+        mutable autoPtr<fv::option> redirectFvOptionPtr_;
 
 
     // Protected Member Functions
@@ -145,21 +145,32 @@ protected:
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        //- Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        //- Clear any redirected objects
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        //- Get the dictionary to initialize the codeContext
+        //- Get the dictionary to initialize the code context
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
 
 public:
 
+    // Static Data Members
+
+        //- Name of the C code template to be used
+        static constexpr const char* const codeTemplateC
+            = "codedFvOptionTemplate.C";
+
+        //- Name of the H code template to be used
+        static constexpr const char* const codeTemplateH
+            = "codedFvOptionTemplate.H";
+
+
     //- Runtime type information
     TypeName("coded");
 
diff --git a/src/fvOptions/sources/general/codedSource/CodedSourceIO.C b/src/fvOptions/sources/general/codedSource/CodedSourceIO.C
index 72c112cd41c725f7cbf2637989e2efc9804cef45..02a1232f437bc0fd32eb4736d1a4b6a15bb1fe22 100644
--- a/src/fvOptions/sources/general/codedSource/CodedSourceIO.C
+++ b/src/fvOptions/sources/general/codedSource/CodedSourceIO.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,69 +41,29 @@ bool Foam::fv::CodedSource<Type>::read(const dictionary& dict)
     }
 
     coeffs_.readEntry("fields", fieldNames_);
-    applied_.setSize(fieldNames_.size(), false);
+    applied_.resize(fieldNames_.size(), false);
 
     dict.readCompat<word>("name", {{"redirectType", 1706}}, name_);
 
-    // Code chunks
 
-    codedBase::append("<codeCorrect>");
-    {
-        const entry& e =
-            coeffs_.lookupEntry("codeCorrect", keyType::LITERAL);
-
-        e.readEntry(codeCorrect_);
-        dynamicCodeContext::inplaceExpand(codeCorrect_, coeffs_);
-
-        codedBase::append(codeCorrect_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeCorrect_,
-            e.startLineNumber(),
-            coeffs_
-        );
-    }
-
-    codedBase::append("<codeAddSup>");
-    {
-        const entry& e =
-            coeffs_.lookupEntry("codeAddSup", keyType::LITERAL);
+    // Code context chunks
 
-        e.readEntry(codeAddSup_);
-        dynamicCodeContext::inplaceExpand(codeAddSup_, coeffs_);
+    auto& ctx = codedBase::codeContext();
 
-        codedBase::append(codeAddSup_);
+    ctx.readEntry("codeCorrect", codeCorrect_);
+    ctx.readEntry("codeAddSup", codeAddSup_);
 
-        dynamicCodeContext::addLineDirective
+    // ctx.readEntry("codeConstrain", codeConstrain_);
+    ctx.readEntry  // Compatibility
+    (
+        coeffs_.lookupEntryCompat
         (
-            codeAddSup_,
-            e.startLineNumber(),
-            coeffs_
-        );
-    }
-
-    codedBase::append("<codeConstrain>");
-    {
-        const entry& e =
-            coeffs_.lookupEntryCompat
-            (
-                "codeConstrain",
-                {{ "codeSetValue", 1812 }}, keyType::LITERAL
-            );
-
-        e.readEntry(codeConstrain_);
-        dynamicCodeContext::inplaceExpand(codeConstrain_, coeffs_);
-
-        codedBase::append(codeConstrain_);
-
-        dynamicCodeContext::addLineDirective
-        (
-            codeConstrain_,
-            e.startLineNumber(),
-            coeffs_
-        );
-    }
+            "codeConstrain",
+            {{ "codeSetValue", 1812 }},
+            keyType::LITERAL
+        ).keyword(),
+        codeConstrain_
+    );
 
     return true;
 }
diff --git a/src/genericPatchFields/Make/files b/src/genericPatchFields/Make/files
index 2210ca7cdc31fa22b285c9a0a83b6988d294ac2f..17ebcb95faab1884eb34b820f719c818c41bab59 100644
--- a/src/genericPatchFields/Make/files
+++ b/src/genericPatchFields/Make/files
@@ -1,6 +1,8 @@
+genericPatchFieldBase/genericPatchFieldBase.C
+
 genericFaPatchField/genericFaPatchFields.C
 genericFvPatchField/genericFvPatchFields.C
-genericPointPatchField/genericPointPatchFields.C
 genericFvsPatchField/genericFvsPatchFields.C
+genericPointPatchField/genericPointPatchFields.C
 
 LIB = $(FOAM_LIBBIN)/libgenericPatchFields
diff --git a/src/genericPatchFields/genericFaPatchField/genericFaPatchField.C b/src/genericPatchFields/genericFaPatchField/genericFaPatchField.C
index fc9f319ccc197df0db52c5a9e5074ba1c2071cde..00bfded5d76761855268f3b418198a9e4fceea05 100644
--- a/src/genericPatchFields/genericFaPatchField/genericFaPatchField.C
+++ b/src/genericPatchFields/genericFaPatchField/genericFaPatchField.C
@@ -38,12 +38,12 @@ Foam::genericFaPatchField<Type>::genericFaPatchField
     const DimensionedField<Type, areaMesh>& iF
 )
 :
-    calculatedFaPatchField<Type>(p, iF)
+    parent_bctype(p, iF)
 {
     FatalErrorInFunction
-        << "Trying to construct an genericFaPatchField on patch "
+        << "Trying to construct genericFaPatchField on patch "
         << this->patch().name()
-        << " of field " << this->internalField().name()
+        << " of field " << this->internalField().name() << nl
         << abort(FatalError);
 }
 
@@ -56,545 +56,86 @@ Foam::genericFaPatchField<Type>::genericFaPatchField
     const dictionary& dict
 )
 :
-    calculatedFaPatchField<Type>(p, iF, dict),
-    actualTypeName_(dict.get<word>("type")),
-    dict_(dict)
+    parent_bctype(p, iF, dict),
+    genericPatchFieldBase(dict)
 {
     const label patchSize = this->size();
+    const word& patchName = this->patch().name();
+    const IOobject& io = this->internalField();
 
     if (!dict.found("value"))
     {
-        FatalIOErrorInFunction(dict)
-            << nl << "    Cannot find 'value' entry"
-            << " on patch " << this->patch().name()
-            << " of field " << this->internalField().name()
-            << " in file " << this->internalField().objectPath() << nl
-            << "    which is required to set the"
-               " values of the generic patch field." << nl
-            << "    (Actual type " << actualTypeName_ << ')' << nl << nl
-            << "    Please add the 'value' entry to the write function"
-               " of the user-defined boundary-condition" << nl
-            << exit(FatalIOError);
+        reportMissingEntry("value", patchName, io);
     }
 
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if
-        (
-            key == "type"
-         || key == "value"
-         || !dEntry.isStream() || dEntry.stream().empty()
-        )
-        {
-            continue;
-        }
-
-
-        ITstream& is = dEntry.stream();
-
-        // Read first token
-        token firstToken(is);
-
-        if (firstToken.isWord("nonuniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isCompound())
-            {
-                if
-                (
-                    fieldToken.isLabel()
-                 && fieldToken.labelToken() == 0
-                )
-                {
-                    scalarFields_.insert(key, autoPtr<scalarField>::New());
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    token following 'nonuniform' "
-                           "is not a compound"
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<scalar>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<scalarField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<scalar>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                scalarFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<vector>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<vectorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<vector>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                vectorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<sphericalTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<sphericalTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<sphericalTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                sphTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<symmTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<symmTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<symmTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                symmTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<tensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<tensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<tensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                tensorFields_.insert(key, fPtr);
-            }
-            else
-            {
-                FatalIOErrorInFunction(dict)
-                    << "\n    compound " << fieldToken.compoundToken()
-                    << " not supported"
-                    << "\n    on patch " << this->patch().name()
-                    << " of field "
-                    << this->internalField().name()
-                    << " in file "
-                    << this->internalField().objectPath() << nl
-                    << exit(FatalIOError);
-            }
-        }
-        else if (firstToken.isWord("uniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isPunctuation())
-            {
-                scalarFields_.insert
-                (
-                    key,
-                    autoPtr<scalarField>::New
-                    (
-                        patchSize,
-                        fieldToken.number()
-                    )
-                );
-            }
-            else
-            {
-                // Read as scalarList.
-                is.putBack(fieldToken);
-
-                scalarList l(is);
-
-                if (l.size() == vector::nComponents)
-                {
-                    vector vs(l[0], l[1], l[2]);
-
-                    vectorFields_.insert
-                    (
-                        key,
-                        autoPtr<vectorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == sphericalTensor::nComponents)
-                {
-                    sphericalTensor vs(l[0]);
-
-                    sphTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<sphericalTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == symmTensor::nComponents)
-                {
-                    symmTensor vs(l[0], l[1], l[2], l[3], l[4], l[5]);
-
-                    symmTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<symmTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == tensor::nComponents)
-                {
-                    tensor vs
-                    (
-                        l[0], l[1], l[2],
-                        l[3], l[4], l[5],
-                        l[6], l[7], l[8]
-                    );
-
-                    tensorFields_.insert
-                    (
-                        key,
-                        autoPtr<tensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    unrecognised native type " << l
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-        }
-    }
+    // Handle "value" separately
+    processGeneric(patchSize, patchName, io, true);
 }
 
 
 template<class Type>
 Foam::genericFaPatchField<Type>::genericFaPatchField
 (
-    const genericFaPatchField<Type>& ptf,
+    const genericFaPatchField<Type>& rhs,
     const faPatch& p,
     const DimensionedField<Type, areaMesh>& iF,
     const faPatchFieldMapper& mapper
 )
 :
-    calculatedFaPatchField<Type>(ptf, p, iF, mapper),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_)
+    parent_bctype(rhs, p, iF, mapper),
+    genericPatchFieldBase(zero{}, rhs)
 {
-    forAllConstIters(ptf.scalarFields_, iter)
-    {
-        scalarFields_.insert
-        (
-            iter.key(),
-            autoPtr<scalarField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.vectorFields_, iter)
-    {
-        vectorFields_.insert
-        (
-            iter.key(),
-            autoPtr<vectorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.sphTensorFields_, iter)
-    {
-        sphTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<sphericalTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.symmTensorFields_, iter)
-    {
-        symmTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<symmTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.tensorFields_, iter)
-    {
-        tensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<tensorField>::New(*iter(), mapper)
-        );
-    }
+    this->mapGeneric(rhs, mapper);
 }
 
 
 template<class Type>
 Foam::genericFaPatchField<Type>::genericFaPatchField
 (
-    const genericFaPatchField<Type>& ptf
-)
-:
-    calculatedFaPatchField<Type>(ptf),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
-{}
-
-
-template<class Type>
-Foam::genericFaPatchField<Type>::genericFaPatchField
-(
-    const genericFaPatchField<Type>& ptf,
+    const genericFaPatchField<Type>& rhs,
     const DimensionedField<Type, areaMesh>& iF
 )
 :
-    calculatedFaPatchField<Type>(ptf, iF),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
+    parent_bctype(rhs, iF),
+    genericPatchFieldBase(rhs)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class Type>
+void Foam::genericFaPatchField<Type>::write(Ostream& os) const
+{
+    // Handle "value" separately
+    genericPatchFieldBase::writeGeneric(os, true);
+    this->writeEntry("value", os);
+}
+
+
 template<class Type>
 void Foam::genericFaPatchField<Type>::autoMap
 (
     const faPatchFieldMapper& m
 )
 {
-    calculatedFaPatchField<Type>::autoMap(m);
-
-    forAllIters(scalarFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
+    parent_bctype::autoMap(m);
+    this->autoMapGeneric(m);
 }
 
 
 template<class Type>
 void Foam::genericFaPatchField<Type>::rmap
 (
-    const faPatchField<Type>& ptf,
+    const faPatchField<Type>& rhs,
     const labelList& addr
 )
 {
-    calculatedFaPatchField<Type>::rmap(ptf, addr);
-
-    const genericFaPatchField<Type>& dptf =
-        refCast<const genericFaPatchField<Type>>(ptf);
-
-    forAllIters(scalarFields_, iter )
-    {
-        const auto iter2 = dptf.scalarFields_.cfind(iter.key());
-
-        if (iter.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        const auto iter2 = dptf.vectorFields_.cfind(iter.key());
-
-        if (iter.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        const auto iter2 = dptf.sphTensorFields_.cfind(iter.key());
-
-        if (iter.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
+    parent_bctype::rmap(rhs, addr);
 
-    forAllIters(symmTensorFields_, iter)
+    const auto* base = isA<genericPatchFieldBase>(rhs);
+    if (base)
     {
-        const auto iter2 = dptf.symmTensorFields_.cfind(iter.key());
-
-        if (iter.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        const auto iter2 = dptf.tensorFields_.find(iter.key());
-
-        if (iter.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
+        this->rmapGeneric(*base, addr);
     }
 }
 
@@ -607,14 +148,14 @@ Foam::genericFaPatchField<Type>::valueInternalCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFaPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFaPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -628,14 +169,14 @@ Foam::genericFaPatchField<Type>::valueBoundaryCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFaPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFaPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -646,92 +187,34 @@ Foam::tmp<Foam::Field<Type>>
 Foam::genericFaPatchField<Type>::gradientInternalCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFaPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFaPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
 
+
 template<class Type>
 Foam::tmp<Foam::Field<Type>>
 Foam::genericFaPatchField<Type>::gradientBoundaryCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFaPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
-
-    return *this;
-}
-
-
-template<class Type>
-const Foam::word& Foam::genericFaPatchField<Type>::actualType() const
-{
-    return actualTypeName_;
-}
-
+        << "Cannot be called for a genericFaPatchField";
 
-template<class Type>
-void Foam::genericFaPatchField<Type>::write(Ostream& os) const
-{
-    os.writeEntry("type", actualTypeName_);
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if (key == "type" || key == "value")
-        {
-            // NB: "type" written first, "value" written last
-            continue;
-        }
-        else if
-        (
-            dEntry.isStream()
-         && dEntry.stream().size()
-         && dEntry.stream()[0].isWord("nonuniform")
-        )
-        {
-            if (scalarFields_.found(key))
-            {
-                scalarFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (vectorFields_.found(key))
-            {
-                vectorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (sphTensorFields_.found(key))
-            {
-                sphTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (symmTensorFields_.found(key))
-            {
-                symmTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (tensorFields_.found(key))
-            {
-                tensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-        }
-        else
-        {
-            dEntry.write(os);
-        }
-    }
-
-    this->writeEntry("value", os);
+    return *this;
 }
 
 
diff --git a/src/genericPatchFields/genericFaPatchField/genericFaPatchField.H b/src/genericPatchFields/genericFaPatchField/genericFaPatchField.H
index b4e05daaeedc214ca0f76f39efd7e6088df357bc..fece0812dad8b00b1cf56eb561ce2ffd7c68126c 100644
--- a/src/genericPatchFields/genericFaPatchField/genericFaPatchField.H
+++ b/src/genericPatchFields/genericFaPatchField/genericFaPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -44,7 +44,7 @@ SourceFiles
 #define genericFaPatchField_H
 
 #include "calculatedFaPatchField.H"
-#include "HashPtrTable.H"
+#include "genericPatchFieldBase.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -58,19 +58,11 @@ namespace Foam
 template<class Type>
 class genericFaPatchField
 :
-    public calculatedFaPatchField<Type>
+    public calculatedFaPatchField<Type>,
+    public genericPatchFieldBase
 {
-    // Private Data
-
-        const word actualTypeName_;
-
-        dictionary dict_;
-
-        HashPtrTable<scalarField> scalarFields_;
-        HashPtrTable<vectorField> vectorFields_;
-        HashPtrTable<sphericalTensorField> sphTensorFields_;
-        HashPtrTable<symmTensorField> symmTensorFields_;
-        HashPtrTable<tensorField> tensorFields_;
+    //- The parent boundary condition type
+    typedef calculatedFaPatchField<Type> parent_bctype;
 
 
 public:
@@ -105,12 +97,6 @@ public:
             const faPatchFieldMapper&
         );
 
-        //- Construct as copy
-        genericFaPatchField
-        (
-            const genericFaPatchField<Type>&
-        );
-
         //- Construct and return a clone
         virtual tmp<faPatchField<Type>> clone() const
         {
@@ -120,6 +106,9 @@ public:
             );
         }
 
+        //- Default construct
+        genericFaPatchField(const genericFaPatchField<Type>&) = default;
+
         //- Construct as copy setting internal field reference
         genericFaPatchField
         (
@@ -142,52 +131,38 @@ public:
 
     // Member Functions
 
-        // Mapping Functions
-
-            //- Map (and resize as needed) from self given a mapping object
-            virtual void autoMap
-            (
-                const faPatchFieldMapper&
-            );
+        //- Write
+        virtual void write(Ostream&) const;
 
-            //- Reverse map the given faPatchField onto this faPatchField
-            virtual void rmap
-            (
-                const faPatchField<Type>&,
-                const labelList&
-            );
 
+    // Mapping Functions
 
-        // Evaluation Functions
+        //- Map (and resize as needed) from self given a mapping object
+        virtual void autoMap(const faPatchFieldMapper&);
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueInternalCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
+        //- Reverse map the given faPatchField onto this faPatchField
+        virtual void rmap
+        (
+            const faPatchField<Type>&,
+            const labelList&
+        );
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueBoundaryCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientInternalCoeffs() const;
+    // Evaluation Functions
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientBoundaryCoeffs() const;
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueInternalCoeffs(const tmp<scalarField>&) const;
 
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueBoundaryCoeffs(const tmp<scalarField>&) const;
 
-        //- Return the actual type
-        const word& actualType() const;
+        //- Fatal
+        tmp<Field<Type>> gradientInternalCoeffs() const;
 
-        //- Write
-        virtual void write(Ostream&) const;
+        //- Fatal
+        tmp<Field<Type>> gradientBoundaryCoeffs() const;
 };
 
 
diff --git a/src/genericPatchFields/genericFvPatchField/genericFvPatchField.C b/src/genericPatchFields/genericFvPatchField/genericFvPatchField.C
index 3ce915e5ec60c21dafea89b83840efd920cd372f..0f72c34600b40662d12e45a2ae7442232ee2c302 100644
--- a/src/genericPatchFields/genericFvPatchField/genericFvPatchField.C
+++ b/src/genericPatchFields/genericFvPatchField/genericFvPatchField.C
@@ -38,7 +38,7 @@ Foam::genericFvPatchField<Type>::genericFvPatchField
     const DimensionedField<Type, volMesh>& iF
 )
 :
-    calculatedFvPatchField<Type>(p, iF)
+    parent_bctype(p, iF)
 {
     FatalErrorInFunction
         << "Trying to construct an genericFvPatchField on patch "
@@ -56,545 +56,86 @@ Foam::genericFvPatchField<Type>::genericFvPatchField
     const dictionary& dict
 )
 :
-    calculatedFvPatchField<Type>(p, iF, dict),
-    actualTypeName_(dict.get<word>("type")),
-    dict_(dict)
+    parent_bctype(p, iF, dict),
+    genericPatchFieldBase(dict)
 {
     const label patchSize = this->size();
+    const word& patchName = this->patch().name();
+    const IOobject& io = this->internalField();
 
     if (!dict.found("value"))
     {
-        FatalIOErrorInFunction(dict)
-            << nl << "    Cannot find 'value' entry"
-            << " on patch " << this->patch().name()
-            << " of field " << this->internalField().name()
-            << " in file " << this->internalField().objectPath() << nl
-            << "    which is required to set the"
-               " values of the generic patch field." << nl
-            << "    (Actual type " << actualTypeName_ << ')' << nl << nl
-            << "    Please add the 'value' entry to the write function"
-               " of the user-defined boundary-condition" << nl
-            << exit(FatalIOError);
+        reportMissingEntry("value", patchName, io);
     }
 
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if
-        (
-            key == "type"
-         || key == "value"
-         || !dEntry.isStream() || dEntry.stream().empty()
-        )
-        {
-            continue;
-        }
-
-
-        ITstream& is = dEntry.stream();
-
-        // Read first token
-        token firstToken(is);
-
-        if (firstToken.isWord("nonuniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isCompound())
-            {
-                if
-                (
-                    fieldToken.isLabel()
-                 && fieldToken.labelToken() == 0
-                )
-                {
-                    scalarFields_.insert(key, autoPtr<scalarField>::New());
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    token following 'nonuniform' "
-                           "is not a compound"
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<scalar>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<scalarField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<scalar>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                scalarFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<vector>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<vectorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<vector>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                vectorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<sphericalTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<sphericalTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<sphericalTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                sphTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<symmTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<symmTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<symmTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                symmTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<tensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<tensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<tensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                tensorFields_.insert(key, fPtr);
-            }
-            else
-            {
-                FatalIOErrorInFunction(dict)
-                    << "\n    compound " << fieldToken.compoundToken()
-                    << " not supported"
-                    << "\n    on patch " << this->patch().name()
-                    << " of field "
-                    << this->internalField().name()
-                    << " in file "
-                    << this->internalField().objectPath() << nl
-                    << exit(FatalIOError);
-            }
-        }
-        else if (firstToken.isWord("uniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isPunctuation())
-            {
-                scalarFields_.insert
-                (
-                    key,
-                    autoPtr<scalarField>::New
-                    (
-                        patchSize,
-                        fieldToken.number()
-                    )
-                );
-            }
-            else
-            {
-                // Read as scalarList.
-                is.putBack(fieldToken);
-
-                scalarList l(is);
-
-                if (l.size() == vector::nComponents)
-                {
-                    vector vs(l[0], l[1], l[2]);
-
-                    vectorFields_.insert
-                    (
-                        key,
-                        autoPtr<vectorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == sphericalTensor::nComponents)
-                {
-                    sphericalTensor vs(l[0]);
-
-                    sphTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<sphericalTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == symmTensor::nComponents)
-                {
-                    symmTensor vs(l[0], l[1], l[2], l[3], l[4], l[5]);
-
-                    symmTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<symmTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == tensor::nComponents)
-                {
-                    tensor vs
-                    (
-                        l[0], l[1], l[2],
-                        l[3], l[4], l[5],
-                        l[6], l[7], l[8]
-                    );
-
-                    tensorFields_.insert
-                    (
-                        key,
-                        autoPtr<tensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    unrecognised native type " << l
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-        }
-    }
+    // Handle "value" separately
+    processGeneric(patchSize, patchName, io, true);
 }
 
 
 template<class Type>
 Foam::genericFvPatchField<Type>::genericFvPatchField
 (
-    const genericFvPatchField<Type>& ptf,
+    const genericFvPatchField<Type>& rhs,
     const fvPatch& p,
     const DimensionedField<Type, volMesh>& iF,
     const fvPatchFieldMapper& mapper
 )
 :
-    calculatedFvPatchField<Type>(ptf, p, iF, mapper),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_)
+    parent_bctype(rhs, p, iF, mapper),
+    genericPatchFieldBase(zero{}, rhs)
 {
-    forAllConstIters(ptf.scalarFields_, iter)
-    {
-        scalarFields_.insert
-        (
-            iter.key(),
-            autoPtr<scalarField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.vectorFields_, iter)
-    {
-        vectorFields_.insert
-        (
-            iter.key(),
-            autoPtr<vectorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.sphTensorFields_, iter)
-    {
-        sphTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<sphericalTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.symmTensorFields_, iter)
-    {
-        symmTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<symmTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.tensorFields_, iter)
-    {
-        tensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<tensorField>::New(*iter(), mapper)
-        );
-    }
+    this->mapGeneric(rhs, mapper);
 }
 
 
 template<class Type>
 Foam::genericFvPatchField<Type>::genericFvPatchField
 (
-    const genericFvPatchField<Type>& ptf
-)
-:
-    calculatedFvPatchField<Type>(ptf),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
-{}
-
-
-template<class Type>
-Foam::genericFvPatchField<Type>::genericFvPatchField
-(
-    const genericFvPatchField<Type>& ptf,
+    const genericFvPatchField<Type>& rhs,
     const DimensionedField<Type, volMesh>& iF
 )
 :
-    calculatedFvPatchField<Type>(ptf, iF),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
+    parent_bctype(rhs, iF),
+    genericPatchFieldBase(rhs)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class Type>
+void Foam::genericFvPatchField<Type>::write(Ostream& os) const
+{
+    // Handle "value" separately
+    genericPatchFieldBase::writeGeneric(os, true);
+    this->writeEntry("value", os);
+}
+
+
 template<class Type>
 void Foam::genericFvPatchField<Type>::autoMap
 (
     const fvPatchFieldMapper& m
 )
 {
-    calculatedFvPatchField<Type>::autoMap(m);
-
-    forAllIters(scalarFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
+    parent_bctype::autoMap(m);
+    this->autoMapGeneric(m);
 }
 
 
 template<class Type>
 void Foam::genericFvPatchField<Type>::rmap
 (
-    const fvPatchField<Type>& ptf,
+    const fvPatchField<Type>& rhs,
     const labelList& addr
 )
 {
-    calculatedFvPatchField<Type>::rmap(ptf, addr);
+    parent_bctype::rmap(rhs, addr);
 
-    const genericFvPatchField<Type>& dptf =
-        refCast<const genericFvPatchField<Type>>(ptf);
-
-    forAllIters(scalarFields_, iter)
-    {
-        const auto iter2 = dptf.scalarFields_.cfind(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        const auto iter2 = dptf.vectorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(sphTensorFields_, iter)
+    const auto* base = isA<genericPatchFieldBase>(rhs);
+    if (base)
     {
-        const auto iter2 = dptf.sphTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        const auto iter2 = dptf.symmTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        const auto iter2 = dptf.tensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
+        this->rmapGeneric(*base, addr);
     }
 }
 
@@ -607,14 +148,14 @@ Foam::genericFvPatchField<Type>::valueInternalCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -628,14 +169,14 @@ Foam::genericFvPatchField<Type>::valueBoundaryCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -646,14 +187,14 @@ Foam::tmp<Foam::Field<Type>>
 Foam::genericFvPatchField<Type>::gradientInternalCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -663,74 +204,16 @@ Foam::tmp<Foam::Field<Type>>
 Foam::genericFvPatchField<Type>::gradientBoundaryCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
-
-    return *this;
-}
+        << "Cannot be called for a genericFvPatchField";
 
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
-template<class Type>
-const Foam::word& Foam::genericFvPatchField<Type>::actualType() const
-{
-    return actualTypeName_;
-}
-
-
-template<class Type>
-void Foam::genericFvPatchField<Type>::write(Ostream& os) const
-{
-    os.writeEntry("type", actualTypeName_);
-
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if (key == "type" || key == "value")
-        {
-            continue;
-        }
-        else if
-        (
-            dEntry.isStream()
-         && dEntry.stream().size()
-         && dEntry.stream()[0].isWord("nonuniform")
-        )
-        {
-            if (scalarFields_.found(key))
-            {
-                scalarFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (vectorFields_.found(key))
-            {
-                vectorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (sphTensorFields_.found(key))
-            {
-                sphTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (symmTensorFields_.found(key))
-            {
-                symmTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (tensorFields_.found(key))
-            {
-                tensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-        }
-        else
-        {
-            dEntry.write(os);
-        }
-    }
-
-    this->writeEntry("value", os);
+    return *this;
 }
 
 
diff --git a/src/genericPatchFields/genericFvPatchField/genericFvPatchField.H b/src/genericPatchFields/genericFvPatchField/genericFvPatchField.H
index 7adff19164733b4aa55a485c38736abbc062ce3f..7c527fe050f86ca8d40c7b17cea0ff746c9c9909 100644
--- a/src/genericPatchFields/genericFvPatchField/genericFvPatchField.H
+++ b/src/genericPatchFields/genericFvPatchField/genericFvPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -44,7 +44,7 @@ SourceFiles
 #define genericFvPatchField_H
 
 #include "calculatedFvPatchField.H"
-#include "HashPtrTable.H"
+#include "genericPatchFieldBase.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -58,19 +58,11 @@ namespace Foam
 template<class Type>
 class genericFvPatchField
 :
-    public calculatedFvPatchField<Type>
+    public calculatedFvPatchField<Type>,
+    public genericPatchFieldBase
 {
-    // Private Data
-
-        const word actualTypeName_;
-
-        dictionary dict_;
-
-        HashPtrTable<scalarField> scalarFields_;
-        HashPtrTable<vectorField> vectorFields_;
-        HashPtrTable<sphericalTensorField> sphTensorFields_;
-        HashPtrTable<symmTensorField> symmTensorFields_;
-        HashPtrTable<tensorField> tensorFields_;
+    //- The parent boundary condition type
+    typedef calculatedFvPatchField<Type> parent_bctype;
 
 
 public:
@@ -105,12 +97,6 @@ public:
             const fvPatchFieldMapper&
         );
 
-        //- Construct as copy
-        genericFvPatchField
-        (
-            const genericFvPatchField<Type>&
-        );
-
         //- Construct and return a clone
         virtual tmp<fvPatchField<Type>> clone() const
         {
@@ -120,6 +106,9 @@ public:
             );
         }
 
+        //- Default copy construct
+        genericFvPatchField(const genericFvPatchField<Type>&) = default;
+
         //- Construct as copy setting internal field reference
         genericFvPatchField
         (
@@ -142,52 +131,38 @@ public:
 
     // Member Functions
 
-        // Mapping Functions
-
-            //- Map (and resize as needed) from self given a mapping object
-            virtual void autoMap
-            (
-                const fvPatchFieldMapper&
-            );
+        //- Write
+        virtual void write(Ostream&) const;
 
-            //- Reverse map the given fvPatchField onto this fvPatchField
-            virtual void rmap
-            (
-                const fvPatchField<Type>&,
-                const labelList&
-            );
 
+    // Mapping Functions
 
-        // Evaluation Functions
+        //- Map (and resize as needed) from self given a mapping object
+        virtual void autoMap(const fvPatchFieldMapper&);
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueInternalCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
+        //- Reverse map the given fvPatchField onto this fvPatchField
+        virtual void rmap
+        (
+            const fvPatchField<Type>&,
+            const labelList&
+        );
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueBoundaryCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientInternalCoeffs() const;
+    // Evaluation Functions
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientBoundaryCoeffs() const;
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueInternalCoeffs(const tmp<scalarField>&) const;
 
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueBoundaryCoeffs(const tmp<scalarField>&) const;
 
-        //- Return the actual type
-        const word& actualType() const;
+        //- Fatal
+        tmp<Field<Type>> gradientInternalCoeffs() const;
 
-        //- Write
-        virtual void write(Ostream&) const;
+        //- Fatal
+        tmp<Field<Type>> gradientBoundaryCoeffs() const;
 };
 
 
diff --git a/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.C b/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.C
index 8b3af677ff6954fc4785ae89b56b7d425ff56b7c..a982f110cd75b306cc74f9ea34a1341bc299bf8c 100644
--- a/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.C
+++ b/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.C
@@ -37,7 +37,7 @@ Foam::genericFvsPatchField<Type>::genericFvsPatchField
     const DimensionedField<Type, surfaceMesh>& iF
 )
 :
-    calculatedFvsPatchField<Type>(p, iF)
+    parent_bctype(p, iF)
 {
     FatalErrorInFunction
         << "Trying to construct an genericFvsPatchField on patch "
@@ -55,545 +55,87 @@ Foam::genericFvsPatchField<Type>::genericFvsPatchField
     const dictionary& dict
 )
 :
-    calculatedFvsPatchField<Type>(p, iF, dict),
-    actualTypeName_(dict.get<word>("type")),
-    dict_(dict)
+    parent_bctype(p, iF, dict),
+    genericPatchFieldBase(dict)
 {
     const label patchSize = this->size();
+    const word& patchName = this->patch().name();
+    const IOobject& io = this->internalField();
 
     if (!dict.found("value"))
     {
-        FatalIOErrorInFunction(dict)
-            << nl << "    Cannot find 'value' entry"
-            << " on patch " << this->patch().name()
-            << " of field " << this->internalField().name()
-            << " in file " << this->internalField().objectPath() << nl
-            << "    which is required to set the"
-               " values of the generic patch field." << nl
-            << "    (Actual type " << actualTypeName_ << ')' << nl << nl
-            << "    Please add the 'value' entry to the write function"
-               " of the user-defined boundary-condition" << nl
-            << exit(FatalIOError);
+        reportMissingEntry("value", patchName, io);
     }
 
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if
-        (
-            key == "type"
-         || key == "value"
-         || !dEntry.isStream() || dEntry.stream().empty()
-        )
-        {
-            continue;
-        }
-
-
-        ITstream& is = dEntry.stream();
-
-        // Read first token
-        token firstToken(is);
-
-        if (firstToken.isWord("nonuniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isCompound())
-            {
-                if
-                (
-                    fieldToken.isLabel()
-                 && fieldToken.labelToken() == 0
-                )
-                {
-                    scalarFields_.insert(key, autoPtr<scalarField>::New());
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    token following 'nonuniform' "
-                           "is not a compound"
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<scalar>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<scalarField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<scalar>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                scalarFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<vector>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<vectorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<vector>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                vectorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<sphericalTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<sphericalTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<sphericalTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                sphTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<symmTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<symmTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<symmTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                symmTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<tensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<tensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<tensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                tensorFields_.insert(key, fPtr);
-            }
-            else
-            {
-                FatalIOErrorInFunction(dict)
-                    << "\n    compound " << fieldToken.compoundToken()
-                    << " not supported"
-                    << "\n    on patch " << this->patch().name()
-                    << " of field "
-                    << this->internalField().name()
-                    << " in file "
-                    << this->internalField().objectPath() << nl
-                    << exit(FatalIOError);
-            }
-        }
-        else if (firstToken.isWord("uniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isPunctuation())
-            {
-                scalarFields_.insert
-                (
-                    key,
-                    autoPtr<scalarField>::New
-                    (
-                        patchSize,
-                        fieldToken.number()
-                    )
-                );
-            }
-            else
-            {
-                // Read as scalarList.
-                is.putBack(fieldToken);
-
-                scalarList l(is);
-
-                if (l.size() == vector::nComponents)
-                {
-                    vector vs(l[0], l[1], l[2]);
-
-                    vectorFields_.insert
-                    (
-                        key,
-                        autoPtr<vectorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == sphericalTensor::nComponents)
-                {
-                    sphericalTensor vs(l[0]);
-
-                    sphTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<sphericalTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == symmTensor::nComponents)
-                {
-                    symmTensor vs(l[0], l[1], l[2], l[3], l[4], l[5]);
-
-                    symmTensorFields_.insert
-                    (
-                        key,
-                        autoPtr<symmTensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else if (l.size() == tensor::nComponents)
-                {
-                    tensor vs
-                    (
-                        l[0], l[1], l[2],
-                        l[3], l[4], l[5],
-                        l[6], l[7], l[8]
-                    );
-
-                    tensorFields_.insert
-                    (
-                        key,
-                        autoPtr<tensorField>::New
-                        (
-                            patchSize,
-                            vs
-                        )
-                    );
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    unrecognised native type " << l
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-        }
-    }
+    // Handle "value" separately
+    processGeneric(patchSize, patchName, io, true);
 }
 
 
 template<class Type>
 Foam::genericFvsPatchField<Type>::genericFvsPatchField
 (
-    const genericFvsPatchField<Type>& ptf,
+    const genericFvsPatchField<Type>& rhs,
     const fvPatch& p,
     const DimensionedField<Type, surfaceMesh>& iF,
     const fvPatchFieldMapper& mapper
 )
 :
-    calculatedFvsPatchField<Type>(ptf, p, iF, mapper),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_)
+    parent_bctype(rhs, p, iF, mapper),
+    genericPatchFieldBase(zero{}, rhs)
 {
-    forAllConstIters(ptf.scalarFields_, iter)
-    {
-        scalarFields_.insert
-        (
-            iter.key(),
-            autoPtr<scalarField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.vectorFields_, iter)
-    {
-        vectorFields_.insert
-        (
-            iter.key(),
-            autoPtr<vectorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.sphTensorFields_, iter)
-    {
-        sphTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<sphericalTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.symmTensorFields_, iter)
-    {
-        symmTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<symmTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.tensorFields_, iter)
-    {
-        tensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<tensorField>::New(*iter(), mapper)
-        );
-    }
+    this->mapGeneric(rhs, mapper);
 }
 
 
 template<class Type>
 Foam::genericFvsPatchField<Type>::genericFvsPatchField
 (
-    const genericFvsPatchField<Type>& ptf
-)
-:
-    calculatedFvsPatchField<Type>(ptf),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
-{}
-
-
-template<class Type>
-Foam::genericFvsPatchField<Type>::genericFvsPatchField
-(
-    const genericFvsPatchField<Type>& ptf,
+    const genericFvsPatchField<Type>& rhs,
     const DimensionedField<Type, surfaceMesh>& iF
 )
 :
-    calculatedFvsPatchField<Type>(ptf, iF),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
+    parent_bctype(rhs, iF),
+    genericPatchFieldBase(rhs)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+
+template<class Type>
+void Foam::genericFvsPatchField<Type>::write(Ostream& os) const
+{
+    // Handle "value" separately
+    genericPatchFieldBase::writeGeneric(os, true);
+    this->writeEntry("value", os);
+}
+
+
 template<class Type>
 void Foam::genericFvsPatchField<Type>::autoMap
 (
     const fvPatchFieldMapper& m
 )
 {
-    calculatedFvsPatchField<Type>::autoMap(m);
-
-    forAllIters(scalarFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
+    parent_bctype::autoMap(m);
+    this->autoMapGeneric(m);
 }
 
 
 template<class Type>
 void Foam::genericFvsPatchField<Type>::rmap
 (
-    const fvsPatchField<Type>& ptf,
+    const fvsPatchField<Type>& rhs,
     const labelList& addr
 )
 {
-    calculatedFvsPatchField<Type>::rmap(ptf, addr);
+    parent_bctype::rmap(rhs, addr);
 
-    const genericFvsPatchField<Type>& dptf =
-        refCast<const genericFvsPatchField<Type>>(ptf);
-
-    forAllIters(scalarFields_, iter)
-    {
-        const auto iter2 = dptf.scalarFields_.cfind(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        const auto iter2 = dptf.vectorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(sphTensorFields_, iter)
+    const auto* base = isA<genericPatchFieldBase>(rhs);
+    if (base)
     {
-        const auto iter2 = dptf.sphTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        const auto iter2 = dptf.symmTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        const auto iter2 = dptf.tensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
+        this->rmapGeneric(*base, addr);
     }
 }
 
@@ -606,14 +148,14 @@ Foam::genericFvsPatchField<Type>::valueInternalCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvsPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvsPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -627,14 +169,14 @@ Foam::genericFvsPatchField<Type>::valueBoundaryCoeffs
 ) const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvsPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvsPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -645,14 +187,14 @@ Foam::tmp<Foam::Field<Type>>
 Foam::genericFvsPatchField<Type>::gradientInternalCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvsPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
+        << "Cannot be called for a genericFvsPatchField";
+
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
     return *this;
 }
@@ -662,75 +204,16 @@ Foam::tmp<Foam::Field<Type>>
 Foam::genericFvsPatchField<Type>::gradientBoundaryCoeffs() const
 {
     FatalErrorInFunction
-        << "cannot be called for a genericFvsPatchField"
-           " (actual type " << actualTypeName_ << ")"
-        << "\n    on patch " << this->patch().name()
-        << " of field " << this->internalField().name()
-        << " in file " << this->internalField().objectPath()
-        << "\n    You are probably trying to solve for a field with a "
-           "generic boundary condition."
-        << abort(FatalError);
-
-    return *this;
-}
+        << "Cannot be called for a genericFvsPatchField";
 
+    genericFatalSolveError
+    (
+        this->patch().name(),
+        this->internalField()
+    );
+    FatalError << abort(FatalError);
 
-template<class Type>
-const Foam::word& Foam::genericFvsPatchField<Type>::actualType() const
-{
-    return actualTypeName_;
-}
-
-
-template<class Type>
-void Foam::genericFvsPatchField<Type>::write(Ostream& os) const
-{
-    os.writeEntry("type", actualTypeName_);
-
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if (key == "type" || key == "value")
-        {
-            // NB: "type" written first, "value" written last
-            continue;
-        }
-        else if
-        (
-            dEntry.isStream()
-         && dEntry.stream().size()
-         && dEntry.stream()[0].isWord("nonuniform")
-        )
-        {
-            if (scalarFields_.found(key))
-            {
-                scalarFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (vectorFields_.found(key))
-            {
-                vectorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (sphTensorFields_.found(key))
-            {
-                sphTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (symmTensorFields_.found(key))
-            {
-                symmTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (tensorFields_.found(key))
-            {
-                tensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-        }
-        else
-        {
-            dEntry.write(os);
-        }
-    }
-
-    this->writeEntry("value", os);
+    return *this;
 }
 
 
diff --git a/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.H b/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.H
index b5d3b942f87126e04a9b85b259960622fcf96986..3fe2a26c57e989661c1c0e8d69a2faa727516dc8 100644
--- a/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.H
+++ b/src/genericPatchFields/genericFvsPatchField/genericFvsPatchField.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -43,7 +43,7 @@ SourceFiles
 #define genericFvsPatchField_H
 
 #include "calculatedFvsPatchField.H"
-#include "HashPtrTable.H"
+#include "genericPatchFieldBase.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -57,19 +57,11 @@ namespace Foam
 template<class Type>
 class genericFvsPatchField
 :
-    public calculatedFvsPatchField<Type>
+    public calculatedFvsPatchField<Type>,
+    public genericPatchFieldBase
 {
-    // Private Data
-
-        const word actualTypeName_;
-
-        dictionary dict_;
-
-        HashPtrTable<scalarField> scalarFields_;
-        HashPtrTable<vectorField> vectorFields_;
-        HashPtrTable<sphericalTensorField> sphTensorFields_;
-        HashPtrTable<symmTensorField> symmTensorFields_;
-        HashPtrTable<tensorField> tensorFields_;
+    //- The parent boundary condition type
+    typedef calculatedFvsPatchField<Type> parent_bctype;
 
 
 public:
@@ -104,12 +96,6 @@ public:
             const fvPatchFieldMapper&
         );
 
-        //- Construct as copy
-        genericFvsPatchField
-        (
-            const genericFvsPatchField<Type>&
-        );
-
         //- Construct and return a clone
         virtual tmp<fvsPatchField<Type>> clone() const
         {
@@ -119,6 +105,9 @@ public:
             );
         }
 
+        //- Default copy construct
+        genericFvsPatchField(const genericFvsPatchField<Type>&) = default;
+
         //- Construct as copy setting internal field reference
         genericFvsPatchField
         (
@@ -141,52 +130,38 @@ public:
 
     // Member Functions
 
-        // Mapping Functions
-
-            //- Map (and resize as needed) from self given a mapping object
-            virtual void autoMap
-            (
-                const fvPatchFieldMapper&
-            );
+        //- Write
+        virtual void write(Ostream&) const;
 
-            //- Reverse map the given fvsPatchField onto this fvsPatchField
-            virtual void rmap
-            (
-                const fvsPatchField<Type>&,
-                const labelList&
-            );
 
+    // Mapping Functions
 
-        // Evaluation Functions
+        //- Map (and resize as needed) from self given a mapping object
+        virtual void autoMap(const fvPatchFieldMapper&);
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueInternalCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
+        //- Reverse map the given faPatchField onto this faPatchField
+        virtual void rmap
+        (
+            const fvsPatchField<Type>&,
+            const labelList&
+        );
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the value of this patchField with given weights
-            virtual tmp<Field<Type>> valueBoundaryCoeffs
-            (
-                const tmp<scalarField>&
-            ) const;
 
-            //- Return the matrix diagonal coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientInternalCoeffs() const;
+    // Evaluation Functions
 
-            //- Return the matrix source coefficients corresponding to the
-            //  evaluation of the gradient of this patchField
-            tmp<Field<Type>> gradientBoundaryCoeffs() const;
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueInternalCoeffs(const tmp<scalarField>&) const;
 
+        //- Fatal
+        virtual tmp<Field<Type>>
+        valueBoundaryCoeffs(const tmp<scalarField>&) const;
 
-        //- Return the actual type
-        const word& actualType() const;
+        //- Fatal
+        tmp<Field<Type>> gradientInternalCoeffs() const;
 
-        //- Write
-        virtual void write(Ostream&) const;
+        //- Fatal
+        tmp<Field<Type>> gradientBoundaryCoeffs() const;
 };
 
 
diff --git a/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.C b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.C
new file mode 100644
index 0000000000000000000000000000000000000000..5108533f98860cdcfa1edf885a37b518cd74c0e5
--- /dev/null
+++ b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.C
@@ -0,0 +1,547 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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 "genericPatchFieldBase.H"
+#include "messageStream.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::genericPatchFieldBase::checkFieldSize
+(
+    const label fieldSize,
+    const label patchSize,
+    const word& patchName,
+    const keyType& key,
+    const IOobject& io
+) const
+{
+    const bool ok = (fieldSize == patchSize);
+
+    if (!ok)
+    {
+        FatalIOErrorInFunction(dict_)
+            << "\n    size of field " << key
+            << " (" << fieldSize << ") != patch size (" << patchSize << ')'
+            << "\n    on patch " << patchName
+            << " of field " << io.name() << " in file "
+            << io.objectPath() << nl
+            << exit(FatalIOError);
+    }
+
+    return ok;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::genericPatchFieldBase::genericPatchFieldBase
+(
+    const dictionary& dict
+)
+:
+    actualTypeName_(dict.get<word>("type")),
+    dict_(dict)
+{}
+
+
+Foam::genericPatchFieldBase::genericPatchFieldBase
+(
+    const Foam::zero,
+    const genericPatchFieldBase& rhs
+)
+:
+    actualTypeName_(rhs.actualTypeName_),
+    dict_(rhs.dict_)
+{}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::genericPatchFieldBase::genericFatalSolveError
+(
+    const word& patchName,
+    const IOobject& io
+) const
+{
+    FatalError
+        << " (actual type " << actualTypeName_ << ')'
+        << "\n    on patch " << patchName
+        << " of field " << io.name() << " in file " << io.objectPath() << nl
+        << nl
+        << "    You are probably trying to solve for a field with a "
+           "generic boundary condition." << nl;
+}
+
+
+void Foam::genericPatchFieldBase::reportMissingEntry
+(
+    const word& entryName,
+    const word& patchName,
+    const IOobject& io
+) const
+{
+    FatalIOErrorInFunction(dict_)
+        << nl
+        << "    Missing required '" << entryName << "' entry"
+        << " on patch " << patchName
+        << " of field " << io.name() << " in file " << io.objectPath() << nl
+        << "    (Actual type " << actualTypeName_ << ')' << nl << nl
+        << "    Please add the '" << entryName << "' entry to the"
+           " write function of the user-defined boundary-condition" << nl
+        << exit(FatalIOError);
+}
+
+
+void Foam::genericPatchFieldBase::processGeneric
+(
+    const label patchSize,
+    const word& patchName,
+    const IOobject& io,
+    const bool separateValue
+)
+{
+    for (const entry& dEntry : dict_)
+    {
+        const keyType& key = dEntry.keyword();
+
+        if (key == "type" || (separateValue && key == "value"))
+        {
+            // "type" and possibly "value" handled differently
+        }
+        else
+        {
+            processEntry(dEntry, patchSize, patchName, io);
+        }
+    }
+}
+
+
+bool Foam::genericPatchFieldBase::processEntry
+(
+    const entry& dEntry,
+    const label patchSize,
+    const word& patchName,
+    const IOobject& io
+)
+{
+    if (!dEntry.isStream())
+    {
+        return false;
+    }
+
+    const keyType& key = dEntry.keyword();
+    ITstream& is = dEntry.stream();
+
+    if (is.empty())
+    {
+        return false;
+    }
+
+    #undef FIELDSIZE_CHECK
+    #define FIELDSIZE_CHECK(fieldLen)  \
+        checkFieldSize(fieldLen, patchSize, patchName, key, io)
+
+
+    // First token
+    token tok(is);
+
+    if (tok.isWord("nonuniform"))
+    {
+        is >> tok;
+
+        if (tok.isLabel(0))
+        {
+            // For v2006 and earlier, could have a plain untyped 0
+            // without a compound type.
+            // Just treat as scalar and hope for the best.
+            scalarFields_.insert(key, autoPtr<scalarField>::New());
+        }
+        else if (!tok.isCompound())
+        {
+            FatalIOErrorInFunction(dict_)
+                << "\n    non-compound token following 'nonuniform'"
+                << "\n    on patch " << patchName << " field "
+                << io.name() << " in file "
+                << io.objectPath() << nl
+                << exit(FatalIOError);
+            return false;
+        }
+        else if
+        (
+            tok.compoundToken().type()
+         == token::Compound<List<scalar>>::typeName
+        )
+        {
+            auto fPtr = autoPtr<scalarField>::New();
+
+            fPtr->transfer
+            (
+                dynamicCast<token::Compound<List<scalar>>>
+                (
+                    tok.transferCompoundToken(is)
+                )
+            );
+
+            if (!FIELDSIZE_CHECK(fPtr->size()))
+            {
+                return false;
+            }
+
+            scalarFields_.insert(key, fPtr);
+        }
+        else if
+        (
+            tok.compoundToken().type()
+         == token::Compound<List<vector>>::typeName
+        )
+        {
+            auto fPtr = autoPtr<vectorField>::New();
+
+            fPtr->transfer
+            (
+                dynamicCast<token::Compound<List<vector>>>
+                (
+                    tok.transferCompoundToken(is)
+                )
+            );
+
+            if (!FIELDSIZE_CHECK(fPtr->size()))
+            {
+                return false;
+            }
+            vectorFields_.insert(key, fPtr);
+        }
+        else if
+        (
+            tok.compoundToken().type()
+         == token::Compound<List<sphericalTensor>>::typeName
+        )
+        {
+            auto fPtr = autoPtr<sphericalTensorField>::New();
+
+            fPtr->transfer
+            (
+                dynamicCast<token::Compound<List<sphericalTensor>>>
+                (
+                    tok.transferCompoundToken(is)
+                )
+            );
+
+            if (!FIELDSIZE_CHECK(fPtr->size()))
+            {
+                return false;
+            }
+
+            sphTensorFields_.insert(key, fPtr);
+        }
+        else if
+        (
+            tok.compoundToken().type()
+         == token::Compound<List<symmTensor>>::typeName
+        )
+        {
+            auto fPtr = autoPtr<symmTensorField>::New();
+
+            fPtr->transfer
+            (
+                dynamicCast<token::Compound<List<symmTensor>>>
+                (
+                    tok.transferCompoundToken(is)
+                )
+            );
+
+            if (!FIELDSIZE_CHECK(fPtr->size()))
+            {
+                return false;
+            }
+
+            symmTensorFields_.insert(key, fPtr);
+        }
+        else if
+        (
+            tok.compoundToken().type()
+         == token::Compound<List<tensor>>::typeName
+        )
+        {
+            auto fPtr = autoPtr<tensorField>::New();
+
+            fPtr->transfer
+            (
+                dynamicCast<token::Compound<List<tensor>>>
+                (
+                    tok.transferCompoundToken(is)
+                )
+            );
+
+            if (!FIELDSIZE_CHECK(fPtr->size()))
+            {
+                return false;
+            }
+
+            tensorFields_.insert(key, fPtr);
+        }
+        else
+        {
+            FatalIOErrorInFunction(dict_)
+                << "\n    unsupported compound " << tok.compoundToken()
+                << "\n    on patch " << patchName << " of field "
+                << io.name() << " in file "
+                << io.objectPath() << nl
+                << exit(FatalIOError);
+            return false;
+        }
+    }
+    else if (tok.isWord("uniform"))
+    {
+        is >> tok;
+
+        if (!tok.isPunctuation())
+        {
+            scalarFields_.insert
+            (
+                key,
+                autoPtr<scalarField>::New(patchSize, tok.number())
+            );
+        }
+        else
+        {
+            // Read vector-space as list of scalars
+            is.putBack(tok);
+
+            scalarList list(is);
+
+            if (list.size() == vector::nComponents)
+            {
+                vector vs(list[0], list[1], list[2]);
+
+                vectorFields_.insert
+                (
+                    key,
+                    autoPtr<vectorField>::New
+                    (
+                        patchSize,
+                        vs
+                    )
+                );
+            }
+            else if (list.size() == sphericalTensor::nComponents)
+            {
+                sphericalTensor vs(list[0]);
+
+                sphTensorFields_.insert
+                (
+                    key,
+                    autoPtr<sphericalTensorField>::New
+                    (
+                        patchSize,
+                        vs
+                    )
+                );
+            }
+            else if (list.size() == symmTensor::nComponents)
+            {
+                symmTensor vs
+                (
+                    list[0], list[1], list[2],
+                    list[3], list[4],
+                    list[5]
+                );
+
+                symmTensorFields_.insert
+                (
+                    key,
+                    autoPtr<symmTensorField>::New
+                    (
+                        patchSize,
+                        vs
+                    )
+                );
+            }
+            else if (list.size() == tensor::nComponents)
+            {
+                tensor vs
+                (
+                    list[0], list[1], list[2],
+                    list[3], list[4], list[5],
+                    list[6], list[7], list[8]
+                );
+
+                tensorFields_.insert
+                (
+                    key,
+                    autoPtr<tensorField>::New
+                    (
+                        patchSize,
+                        vs
+                    )
+                );
+            }
+            else
+            {
+                FatalIOErrorInFunction(dict_)
+                    << "\n    unrecognised native type " << flatOutput(list)
+                    << "\n    on patch " << patchName << " of field "
+                    << io.name() << " in file "
+                    << io.objectPath() << nl
+                    << exit(FatalIOError);
+                return false;
+            }
+        }
+    }
+
+    #undef FIELDSIZE_CHECK
+
+    return true;
+}
+
+
+void Foam::genericPatchFieldBase::putEntry
+(
+    const entry& e,
+    Ostream& os
+) const
+{
+    const keyType& key = e.keyword();
+
+    if
+    (
+        e.isStream()
+     && e.stream().size()
+     && e.stream()[0].isWord("nonuniform")
+    )
+    {
+        if (scalarFields_.found(key))
+        {
+            scalarFields_.cfind(key)()->writeEntry(key, os);
+        }
+        else if (vectorFields_.found(key))
+        {
+            vectorFields_.cfind(key)()->writeEntry(key, os);
+        }
+        else if (sphTensorFields_.found(key))
+        {
+            sphTensorFields_.cfind(key)()->writeEntry(key, os);
+        }
+        else if (symmTensorFields_.found(key))
+        {
+            symmTensorFields_.cfind(key)()->writeEntry(key, os);
+        }
+        else if (tensorFields_.found(key))
+        {
+            tensorFields_.cfind(key)()->writeEntry(key, os);
+        }
+    }
+    else
+    {
+        e.write(os);
+    }
+}
+
+
+void Foam::genericPatchFieldBase::writeGeneric
+(
+    Ostream& os,
+    const bool separateValue
+) const
+{
+    os.writeEntry("type", actualTypeName_);
+
+    for (const entry& dEntry : dict_)
+    {
+        const keyType& key = dEntry.keyword();
+
+        if (key == "type" || (separateValue && key == "value"))
+        {
+            // NB: "type" written first, "value" possibly separately
+        }
+        else
+        {
+            putEntry(dEntry, os);
+        }
+    }
+}
+
+
+void Foam::genericPatchFieldBase::rmapGeneric
+(
+    const genericPatchFieldBase& rhs,
+    const labelList& addr
+)
+{
+    forAllIters(scalarFields_, iter)
+    {
+        const auto iter2 = rhs.scalarFields_.cfind(iter.key());
+
+        if (iter2.found())
+        {
+            (*iter)->rmap(*iter2(), addr);
+        }
+    }
+
+    forAllIters(vectorFields_, iter)
+    {
+        const auto iter2 = rhs.vectorFields_.cfind(iter.key());
+
+        if (iter2.found())
+        {
+            (*iter)->rmap(*iter2(), addr);
+        }
+    }
+
+    forAllIters(sphTensorFields_, iter)
+    {
+        const auto iter2 = rhs.sphTensorFields_.cfind(iter.key());
+
+        if (iter2.found())
+        {
+            (*iter)->rmap(*iter2(), addr);
+        }
+    }
+
+    forAllIters(symmTensorFields_, iter)
+    {
+        const auto iter2 = rhs.symmTensorFields_.cfind(iter.key());
+
+        if (iter2.found())
+        {
+            (*iter)->rmap(*iter2(), addr);
+        }
+    }
+
+    forAllIters(tensorFields_, iter)
+    {
+        const auto iter2 = rhs.tensorFields_.find(iter.key());
+
+        if (iter2.found())
+        {
+            (*iter)->rmap(*iter2(), addr);
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.H b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.H
new file mode 100644
index 0000000000000000000000000000000000000000..f22346c7de1b71e32d258d3c3f1498f5798a23b8
--- /dev/null
+++ b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBase.H
@@ -0,0 +1,194 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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::genericPatchFieldBase
+
+Description
+    Generic infrastructure for reading/writing unknown patch types.
+
+SourceFiles
+    genericPatchFieldBase.C
+    genericPatchFieldBaseTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef genericPatchFieldBase_H
+#define genericPatchFieldBase_H
+
+#include "dictionary.H"
+#include "primitiveFields.H"
+#include "HashPtrTable.H"
+#include "IOobject.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class genericPatchFieldBase Declaration
+\*---------------------------------------------------------------------------*/
+
+class genericPatchFieldBase
+{
+    // Private Member Functions
+
+        bool checkFieldSize
+        (
+            const label fieldSize,
+            const label patchSize,
+            const word& patchName,
+            const keyType& key,
+            const IOobject& io
+        ) const;
+
+
+protected:
+
+    // Protected Data
+
+        //- The non-generic patch name
+        word actualTypeName_;
+
+        dictionary dict_;
+
+        HashPtrTable<scalarField> scalarFields_;
+        HashPtrTable<vectorField> vectorFields_;
+        HashPtrTable<sphericalTensorField> sphTensorFields_;
+        HashPtrTable<symmTensorField> symmTensorFields_;
+        HashPtrTable<tensorField> tensorFields_;
+
+
+    // Protected Member Functions
+
+        //- Add error message to FatalError about solving with
+        //- generic condition
+        void genericFatalSolveError
+        (
+            const word& patchName,
+            const IOobject& io
+        ) const;
+
+        //- FatalError for missing entry
+        void reportMissingEntry
+        (
+            const word& entryName,
+            const word& patchName,
+            const IOobject& io
+        ) const;
+
+        void processGeneric
+        (
+            const label patchSize,
+            const word& patchName,
+            const IOobject& io,
+            const bool separateValue
+        );
+
+        bool processEntry
+        (
+            const entry& dEntry,
+            const label patchSize,
+            const word& patchName,
+            const IOobject& io
+        );
+
+        //- Write a single entry, with lookup of hashed values
+        void putEntry(const entry& e, Ostream& os) const;
+
+        //- Write all generic entries from dictionary,
+        //- optionally treating the "value" entry separately
+        void writeGeneric(Ostream& os, const bool separateValue) const;
+
+        //- Implementation for construct with mapper
+        template<class MapperType>
+        void mapGeneric
+        (
+            const genericPatchFieldBase& rhs,
+            const MapperType& mapper
+        );
+
+        //- Implementation for autoMap of self given a mapping object
+        template<class MapperType>
+        void autoMapGeneric(const MapperType& mapper);
+
+        //- Implementation for reverse map given patch field onto this
+        //- patch field
+        void rmapGeneric
+        (
+            const genericPatchFieldBase& rhs,
+            const labelList& addr
+        );
+
+
+    // Constructors
+
+        //- Partial copy construct. Only copy type and dictionary
+        genericPatchFieldBase(const Foam::zero, const genericPatchFieldBase&);
+
+
+public:
+
+    // Constructors
+
+        //- Default construct, generally not useful.
+        genericPatchFieldBase() = default;
+
+        //- Copy construct
+        genericPatchFieldBase(const genericPatchFieldBase&) = default;
+
+        //- Move construct
+        genericPatchFieldBase(genericPatchFieldBase&&) = default;
+
+        //- Initialize from dictionary
+        explicit genericPatchFieldBase(const dictionary& dict);
+
+
+    // Member Functions
+
+        //- The actual patch type
+        const word& actualType() const noexcept
+        {
+            return actualTypeName_;
+        }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "genericPatchFieldBaseTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBaseTemplates.C b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBaseTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..9b1041ab456f71726428ea16daca5b24f07714f9
--- /dev/null
+++ b/src/genericPatchFields/genericPatchFieldBase/genericPatchFieldBaseTemplates.C
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021 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 "genericPatchFieldBase.H"
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class MapperType>
+void Foam::genericPatchFieldBase::mapGeneric
+(
+    const genericPatchFieldBase& rhs,
+    const MapperType& mapper
+)
+{
+    forAllConstIters(rhs.scalarFields_, iter)
+    {
+        scalarFields_.insert
+        (
+            iter.key(),
+            autoPtr<scalarField>::New(*iter(), mapper)
+        );
+    }
+
+    forAllConstIters(rhs.vectorFields_, iter)
+    {
+        vectorFields_.insert
+        (
+            iter.key(),
+            autoPtr<vectorField>::New(*iter(), mapper)
+        );
+    }
+
+    forAllConstIters(rhs.sphTensorFields_, iter)
+    {
+        sphTensorFields_.insert
+        (
+            iter.key(),
+            autoPtr<sphericalTensorField>::New(*iter(), mapper)
+        );
+    }
+
+    forAllConstIters(rhs.symmTensorFields_, iter)
+    {
+        symmTensorFields_.insert
+        (
+            iter.key(),
+            autoPtr<symmTensorField>::New(*iter(), mapper)
+        );
+    }
+
+    forAllConstIters(rhs.tensorFields_, iter)
+    {
+        tensorFields_.insert
+        (
+            iter.key(),
+            autoPtr<tensorField>::New(*iter(), mapper)
+        );
+    }
+}
+
+
+template<class MapperType>
+void Foam::genericPatchFieldBase::autoMapGeneric
+(
+    const MapperType& mapper
+)
+{
+    forAllIters(scalarFields_, iter)
+    {
+        (*iter)->autoMap(mapper);
+    }
+
+    forAllIters(vectorFields_, iter)
+    {
+        (*iter)->autoMap(mapper);
+    }
+
+    forAllIters(sphTensorFields_, iter)
+    {
+        (*iter)->autoMap(mapper);
+    }
+
+    forAllIters(symmTensorFields_, iter)
+    {
+        (*iter)->autoMap(mapper);
+    }
+
+    forAllIters(tensorFields_, iter)
+    {
+        (*iter)->autoMap(mapper);
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/genericPatchFields/genericPointPatchField/genericPointPatchField.C b/src/genericPatchFields/genericPointPatchField/genericPointPatchField.C
index a4ccaecb18916040a32b275addc51320058284b0..7352d82c5cdc4e78f00cea06c134726592f10ffe 100644
--- a/src/genericPatchFields/genericPointPatchField/genericPointPatchField.C
+++ b/src/genericPatchFields/genericPointPatchField/genericPointPatchField.C
@@ -38,9 +38,13 @@ Foam::genericPointPatchField<Type>::genericPointPatchField
     const DimensionedField<Type, pointMesh>& iF
 )
 :
-    calculatedPointPatchField<Type>(p, iF)
+    parent_bctype(p, iF)
 {
-    NotImplemented;
+    FatalErrorInFunction
+        << "Trying to construct genericPointPatchField on patch "
+        << this->patch().name()
+        << " of field " << this->internalField().name() << nl
+        << abort(FatalError);
 }
 
 
@@ -52,468 +56,77 @@ Foam::genericPointPatchField<Type>::genericPointPatchField
     const dictionary& dict
 )
 :
-    calculatedPointPatchField<Type>(p, iF, dict),
-    actualTypeName_(dict.get<word>("type")),
-    dict_(dict)
+    parent_bctype(p, iF, dict),
+    genericPatchFieldBase(dict)
 {
     const label patchSize = this->size();
+    const word& patchName = this->patch().name();
+    const IOobject& io = this->internalField();
 
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if
-        (
-            key == "type"
-         || !dEntry.isStream() || dEntry.stream().empty()
-        )
-        {
-            continue;
-        }
-
-
-        ITstream& is = dEntry.stream();
-
-        // Read first token
-        token firstToken(is);
-
-        if (firstToken.isWord("nonuniform"))
-        {
-            token fieldToken(is);
-
-            if (!fieldToken.isCompound())
-            {
-                if
-                (
-                    fieldToken.isLabel()
-                 && fieldToken.labelToken() == 0
-                )
-                {
-                    scalarFields_.insert(key, autoPtr<scalarField>::New());
-                }
-                else
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    token following 'nonuniform' "
-                           "is not a compound"
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<scalar>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<scalarField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<scalar>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                scalarFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<vector>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<vectorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<vector>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                vectorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<sphericalTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<sphericalTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<sphericalTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                sphTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<symmTensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<symmTensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<symmTensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                symmTensorFields_.insert(key, fPtr);
-            }
-            else if
-            (
-                fieldToken.compoundToken().type()
-             == token::Compound<List<tensor>>::typeName
-            )
-            {
-                auto fPtr = autoPtr<tensorField>::New();
-
-                fPtr->transfer
-                (
-                    dynamicCast<token::Compound<List<tensor>>>
-                    (
-                        fieldToken.transferCompoundToken(is)
-                    )
-                );
-
-                if (fPtr->size() != patchSize)
-                {
-                    FatalIOErrorInFunction(dict)
-                        << "\n    size of field " << key
-                        << " (" << fPtr->size() << ')'
-                        << " is not the same size as the patch ("
-                        << patchSize << ')'
-                        << "\n    on patch " << this->patch().name()
-                        << " of field "
-                        << this->internalField().name()
-                        << " in file "
-                        << this->internalField().objectPath() << nl
-                        << exit(FatalIOError);
-                }
-
-                tensorFields_.insert(key, fPtr);
-            }
-            else
-            {
-                FatalIOErrorInFunction(dict)
-                    << "\n    compound " << fieldToken.compoundToken()
-                    << " not supported"
-                    << "\n    on patch " << this->patch().name()
-                    << " of field "
-                    << this->internalField().name()
-                    << " in file "
-                    << this->internalField().objectPath() << nl
-                    << exit(FatalIOError);
-            }
-        }
-    }
+    // No separate "value"
+    processGeneric(patchSize, patchName, io, false);
 }
 
 
 template<class Type>
 Foam::genericPointPatchField<Type>::genericPointPatchField
 (
-    const genericPointPatchField<Type>& ptf,
+    const genericPointPatchField<Type>& rhs,
     const pointPatch& p,
     const DimensionedField<Type, pointMesh>& iF,
     const pointPatchFieldMapper& mapper
 )
 :
-    calculatedPointPatchField<Type>(ptf, p, iF, mapper),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_)
+    parent_bctype(rhs, p, iF, mapper),
+    genericPatchFieldBase(zero{}, rhs)
 {
-    forAllConstIters(ptf.scalarFields_, iter)
-    {
-        scalarFields_.insert
-        (
-            iter.key(),
-            autoPtr<scalarField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.vectorFields_, iter)
-    {
-        vectorFields_.insert
-        (
-            iter.key(),
-            autoPtr<vectorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.sphTensorFields_, iter)
-    {
-        sphTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<sphericalTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.symmTensorFields_, iter)
-    {
-        symmTensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<symmTensorField>::New(*iter(), mapper)
-        );
-    }
-
-    forAllConstIters(ptf.tensorFields_, iter)
-    {
-        tensorFields_.insert
-        (
-            iter.key(),
-            autoPtr<tensorField>::New(*iter(), mapper)
-        );
-    }
+    this->mapGeneric(rhs, mapper);
 }
 
 
 template<class Type>
 Foam::genericPointPatchField<Type>::genericPointPatchField
 (
-    const genericPointPatchField<Type>& ptf,
+    const genericPointPatchField<Type>& rhs,
     const DimensionedField<Type, pointMesh>& iF
 )
 :
-    calculatedPointPatchField<Type>(ptf, iF),
-    actualTypeName_(ptf.actualTypeName_),
-    dict_(ptf.dict_),
-    scalarFields_(ptf.scalarFields_),
-    vectorFields_(ptf.vectorFields_),
-    sphTensorFields_(ptf.sphTensorFields_),
-    symmTensorFields_(ptf.symmTensorFields_),
-    tensorFields_(ptf.tensorFields_)
+    parent_bctype(rhs, iF),
+    genericPatchFieldBase(rhs)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class Type>
+void Foam::genericPointPatchField<Type>::write(Ostream& os) const
+{
+    // No separate treatment for "value"
+    genericPatchFieldBase::writeGeneric(os, false);
+}
+
+
 template<class Type>
 void Foam::genericPointPatchField<Type>::autoMap
 (
     const pointPatchFieldMapper& m
 )
 {
-    forAllIters(scalarFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(vectorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        (*iter)->autoMap(m);
-    }
+    this->autoMapGeneric(m);
 }
 
 
 template<class Type>
 void Foam::genericPointPatchField<Type>::rmap
 (
-    const pointPatchField<Type>& ptf,
+    const pointPatchField<Type>& rhs,
     const labelList& addr
 )
 {
-    const genericPointPatchField<Type>& dptf =
-        refCast<const genericPointPatchField<Type>>(ptf);
-
-    forAllIters(scalarFields_, iter)
-    {
-        const auto iter2 = dptf.scalarFields_.cfind(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(vectorFields_, iter)
+    const auto* base = isA<genericPatchFieldBase>(rhs);
+    if (base)
     {
-        const auto iter2 = dptf.vectorFields_.cfind(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(sphTensorFields_, iter)
-    {
-        const auto iter2 = dptf.sphTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(symmTensorFields_, iter)
-    {
-        const auto iter2 = dptf.symmTensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-
-    forAllIters(tensorFields_, iter)
-    {
-        const auto iter2 = dptf.tensorFields_.find(iter.key());
-
-        if (iter2.found())
-        {
-            (*iter)->rmap(*iter2(), addr);
-        }
-    }
-}
-
-
-template<class Type>
-const Foam::word& Foam::genericPointPatchField<Type>::actualType() const
-{
-    return actualTypeName_;
-}
-
-
-template<class Type>
-void Foam::genericPointPatchField<Type>::write(Ostream& os) const
-{
-    os.writeEntry("type", actualTypeName_);
-
-    for (const entry& dEntry : dict_)
-    {
-        const keyType& key = dEntry.keyword();
-
-        if (key == "type")
-        {
-            // NB: "type" written first, no special treatment for "value"
-            continue;
-        }
-        else if
-        (
-            dEntry.isStream()
-         && dEntry.stream().size()
-         && dEntry.stream()[0].isWord("nonuniform")
-        )
-        {
-            if (scalarFields_.found(key))
-            {
-                scalarFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (vectorFields_.found(key))
-            {
-                vectorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (sphTensorFields_.found(key))
-            {
-                sphTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (symmTensorFields_.found(key))
-            {
-                symmTensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-            else if (tensorFields_.found(key))
-            {
-                tensorFields_.cfind(key)()->writeEntry(key, os);
-            }
-        }
-        else
-        {
-            dEntry.write(os);
-        }
+        this->rmapGeneric(*base, addr);
     }
 }
 
diff --git a/src/genericPatchFields/genericPointPatchField/genericPointPatchField.H b/src/genericPatchFields/genericPointPatchField/genericPointPatchField.H
index 065a1751577dbca347b6028104f144fc358ca3f2..59b3be11f4b11bf0fbb24dd091a06a877ecff223 100644
--- a/src/genericPatchFields/genericPointPatchField/genericPointPatchField.H
+++ b/src/genericPatchFields/genericPointPatchField/genericPointPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,7 +40,7 @@ SourceFiles
 #define genericPointPatchField_H
 
 #include "calculatedPointPatchField.H"
-#include "HashPtrTable.H"
+#include "genericPatchFieldBase.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -54,19 +54,11 @@ namespace Foam
 template<class Type>
 class genericPointPatchField
 :
-    public calculatedPointPatchField<Type>
+    public calculatedPointPatchField<Type>,
+    public genericPatchFieldBase
 {
-    // Private Data
-
-        const word actualTypeName_;
-
-        dictionary dict_;
-
-        HashPtrTable<scalarField> scalarFields_;
-        HashPtrTable<vectorField> vectorFields_;
-        HashPtrTable<sphericalTensorField> sphTensorFields_;
-        HashPtrTable<symmTensorField> symmTensorFields_;
-        HashPtrTable<tensorField> tensorFields_;
+    //- The parent boundary condition type
+    typedef calculatedPointPatchField<Type> parent_bctype;
 
 
 public:
@@ -113,6 +105,9 @@ public:
             );
         }
 
+        //- Default copy construct
+        genericPointPatchField(const genericPointPatchField<Type>&) = default;
+
         //- Construct as copy setting internal field reference
         genericPointPatchField
         (
@@ -139,27 +134,21 @@ public:
 
     // Member Functions
 
-        // Mapping Functions
-
-            //- Map (and resize as needed) from self given a mapping object
-            virtual void autoMap
-            (
-                const pointPatchFieldMapper&
-            );
+        //- Write
+        virtual void write(Ostream&) const;
 
-            //- Reverse map the given pointPatchField onto this pointPatchField
-            virtual void rmap
-            (
-                const pointPatchField<Type>&,
-                const labelList&
-            );
 
+    // Mapping Functions
 
-        //- Return the actual type
-        const word& actualType() const;
+        //- Map (and resize as needed) from self given a mapping object
+        virtual void autoMap(const pointPatchFieldMapper&);
 
-        //- Write
-        virtual void write(Ostream&) const;
+        //- Reverse map the given pointPatchField onto this pointPatchField
+        virtual void rmap
+        (
+            const pointPatchField<Type>&,
+            const labelList&
+        );
 };
 
 
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleErosion/ParticleErosion.C b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleErosion/ParticleErosion.C
index 228ee73c2cfc2f4fffacc141159851562dce4761..9958881f04e8945d6e97bca2897b95fc6830a7c9 100644
--- a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleErosion/ParticleErosion.C
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleErosion/ParticleErosion.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -67,15 +67,7 @@ Foam::label Foam::ParticleErosion<CloudType>::applyToPatch
     const label globalPatchi
 ) const
 {
-    forAll(patchIDs_, i)
-    {
-        if (patchIDs_[i] == globalPatchi)
-        {
-            return i;
-        }
-    }
-
-    return -1;
+    return patchIDs_.find(globalPatchi);
 }
 
 
@@ -120,7 +112,7 @@ Foam::ParticleErosion<CloudType>::ParticleErosion
     labelHashSet uniqIds;
     for (const wordRe& re : patchNames)
     {
-        labelList ids = findStrings(re, allPatchNames);
+        labelList ids = findMatchingStrings(re, allPatchNames);
 
         if (ids.empty())
         {
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.C
index a2f5686c78a1dae442912f68d474c576c00d6f55..1645ea93a556113d9d937d615bd7a2ae60bc1199 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,39 +34,13 @@ License
 
 Foam::patchInteractionData::patchInteractionData()
 :
-    interactionTypeName_("unknownInteractionTypeName"),
-    patchName_("unknownPatch"),
-    e_(0.0),
-    mu_(0.0)
+    interactionTypeName_(),
+    patchName_(),
+    e_(0),
+    mu_(0)
 {}
 
 
-// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
-
-const Foam::word& Foam::patchInteractionData::interactionTypeName() const
-{
-    return interactionTypeName_;
-}
-
-
-const Foam::keyType& Foam::patchInteractionData::patchName() const
-{
-    return patchName_;
-}
-
-
-Foam::scalar Foam::patchInteractionData::e() const
-{
-    return e_;
-}
-
-
-Foam::scalar Foam::patchInteractionData::mu() const
-{
-    return mu_;
-}
-
-
 // * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
 
 Foam::Istream& Foam::operator>>
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.H b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.H
index 7d9e818d06740e7f30a8baf955eafb23d98ad606..6dba9eb95fd21bbb607656fd99376641177a5f22 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionData.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,36 +35,31 @@ Description
 #ifndef patchInteractionData_H
 #define patchInteractionData_H
 
-#include "Istream.H"
+#include "wordRe.H"
+#include "scalar.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
-/*---------------------------------------------------------------------------*\
-                    Class patchInteractionData Declaration
-\*---------------------------------------------------------------------------*/
 
-// Forward declaration of classes
+// Forward Declaration
 class patchInteractionData;
+Istream& operator>>(Istream& is, patchInteractionData& pid);
 
-// Forward declaration of friend functions
-Istream& operator>>
-(
-    Istream& is,
-    patchInteractionData& pid
-);
-
+/*---------------------------------------------------------------------------*\
+                    Class patchInteractionData Declaration
+\*---------------------------------------------------------------------------*/
 
 class patchInteractionData
 {
-    // Private data
+    // Private Data
 
         //- Interaction type name
         word interactionTypeName_;
 
-        //- Patch name
-        keyType patchName_;
+        //- Patch name(s)
+        wordRe patchName_;
 
         //- Elasticity coefficient
         scalar e_;
@@ -76,35 +72,41 @@ public:
 
     // Constructor
 
-        //- Construct null
+        //- Default construct
         patchInteractionData();
 
 
-    // Member functions
-
-        // Access
+    // Member Functions
 
-            //- Return const access to the interaction type name
-            const word& interactionTypeName() const;
+        //- The interaction type name
+        const word& interactionTypeName() const noexcept
+        {
+            return interactionTypeName_;
+        }
 
-            //- Return const access to the patch name
-            const keyType& patchName() const;
+        //- The patch name(s)
+        const wordRe& patchName() const noexcept
+        {
+            return patchName_;
+        }
 
-            //- Return const access to the elasticity coefficient
-            scalar e() const;
+        //- The elasticity coefficient
+        scalar e() const noexcept
+        {
+            return e_;
+        }
 
-            //- Return const access to the restitution coefficient
-            scalar mu() const;
+        //- The restitution coefficient
+        scalar mu() const noexcept
+        {
+            return mu_;
+        }
 
 
-        // I-O
+    // IO Operators
 
-            //- Istream operator
-            friend Istream& operator>>
-            (
-                Istream& is,
-                patchInteractionData& pid
-            );
+        //- Istream operator
+        friend Istream& operator>>(Istream& is, patchInteractionData& pid);
 };
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionDataList.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionDataList.C
index 07ade8be9f64c45fdc2d420c547b470a79863659..63ecd277103080daf4229282944d2960237022f8 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionDataList.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/patchInteractionDataList.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -50,14 +51,14 @@ Foam::patchInteractionDataList::patchInteractionDataList
     const List<patchInteractionData>& items = *this;
     forAllReverse(items, i)
     {
-        const keyType& patchName = items[i].patchName();
+        const wordRe& patchName = items[i].patchName();
         labelList ids = bMesh.indices(patchName);
 
         if (ids.empty())
         {
             WarningInFunction
-                << "Cannot find any patch names matching " << patchName
-                << endl;
+                << "Cannot find any patch names matching "
+                << patchName << endl;
         }
 
         patchGroupIDs_[i].transfer(ids);
@@ -65,9 +66,8 @@ Foam::patchInteractionDataList::patchInteractionDataList
 
     // Check that all patches are specified
     DynamicList<word> badPatches;
-    forAll(bMesh, patchi)
+    for (const polyPatch& pp : bMesh)
     {
-        const polyPatch& pp = bMesh[patchi];
         if
         (
             !pp.coupled()
@@ -79,12 +79,13 @@ Foam::patchInteractionDataList::patchInteractionDataList
         }
     }
 
-    if (badPatches.size() > 0)
+    if (!badPatches.empty())
     {
         FatalErrorInFunction
             << "All patches must be specified when employing local patch "
             << "interaction. Please specify data for patches:" << nl
-            << badPatches << nl << exit(FatalError);
+            << badPatches << nl
+            << exit(FatalError);
     }
 }
 
@@ -103,15 +104,11 @@ Foam::patchInteractionDataList::patchInteractionDataList
 
 Foam::label Foam::patchInteractionDataList::applyToPatch(const label id) const
 {
-    forAll(patchGroupIDs_, groupI)
+    forAll(patchGroupIDs_, groupi)
     {
-        const labelList& patchIDs = patchGroupIDs_[groupI];
-        forAll(patchIDs, patchi)
+        if (patchGroupIDs_[groupi].found(id))
         {
-            if (patchIDs[patchi] == id)
-            {
-                return groupI;
-            }
+            return groupi;
         }
     }
 
diff --git a/src/mesh/blockMesh/blockMeshTools/blockMeshTools.C b/src/mesh/blockMesh/blockMeshTools/blockMeshTools.C
index be91dbc67403831649c9c5032152b20eaf9a1e5c..350635bc7cfe2b0d72266dd5931b99c1973ec8c6 100644
--- a/src/mesh/blockMesh/blockMeshTools/blockMeshTools.C
+++ b/src/mesh/blockMesh/blockMeshTools/blockMeshTools.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2016 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,6 +28,29 @@ License
 
 #include "blockMeshTools.H"
 
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+static inline const Foam::entry* resolveLabel(const entry& e, const label val)
+{
+    if (e.isStream())
+    {
+        const tokenList& toks = e.stream();
+
+        if (!toks.empty() && toks[0].isLabel(val))
+        {
+            return &e;
+        }
+    }
+
+    return nullptr;
+}
+
+} // End namespace Foam
+
+
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 void Foam::blockMeshTools::read
@@ -93,21 +117,18 @@ void Foam::blockMeshTools::write
 {
     for (const entry& e : dict)
     {
-        if (e.isStream())
+        const entry* eptr = resolveLabel(e, val);
+        if (eptr)
         {
-            label keyVal(Foam::readLabel(e.stream()));
-            if (keyVal == val)
-            {
-                os << e.keyword();
-                return;
-            }
+            os << eptr->keyword();
+            return;
         }
     }
     os << val;
 }
 
 
-const Foam::keyType& Foam::blockMeshTools::findEntry
+const Foam::entry* Foam::blockMeshTools::findEntry
 (
     const dictionary& dict,
     const label val
@@ -115,17 +136,14 @@ const Foam::keyType& Foam::blockMeshTools::findEntry
 {
     for (const entry& e : dict)
     {
-        if (e.isStream())
+        const entry* eptr = resolveLabel(e, val);
+        if (eptr)
         {
-            label keyVal(Foam::readLabel(e.stream()));
-            if (keyVal == val)
-            {
-                return e.keyword();
-            }
+            return eptr;
         }
     }
 
-    return keyType::null;
+    return nullptr;
 }
 
 
diff --git a/src/mesh/blockMesh/blockMeshTools/blockMeshTools.H b/src/mesh/blockMesh/blockMeshTools/blockMeshTools.H
index e6884fba8374644e96e623bc1763e8690ab3cac0..11e9ef199a9aa9691a88e1cc393f05d76031c84e 100644
--- a/src/mesh/blockMesh/blockMeshTools/blockMeshTools.H
+++ b/src/mesh/blockMesh/blockMeshTools/blockMeshTools.H
@@ -50,11 +50,11 @@ namespace blockMeshTools
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
     //- In-place read with dictionary lookup
-    void read(Istream&, label&, const dictionary&);
+    void read(Istream&, label& val, const dictionary&);
 
     //- In-place read with dictionary lookup
     template<class T>
-    void read(Istream&, List<T>&, const dictionary&);
+    void read(Istream&, List<T>& list, const dictionary&);
 
     //- Return-read with dictionary lookup
     label read(Istream&, const dictionary&);
@@ -64,10 +64,10 @@ namespace blockMeshTools
     List<T> read(Istream& is, const dictionary&);
 
     //- Write with dictionary lookup
-    void write(Ostream&, const label, const dictionary&);
+    void write(Ostream&, const label val, const dictionary&);
 
-    //- Linear search for label entry
-    const keyType& findEntry(const dictionary&, const label);
+    //- Linear search for labelled entry, nullptr if not found
+    const entry* findEntry(const dictionary& dict, const label val);
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
index e8ef067ef1f29cbfc367ceb319a8843b52a4d0c4..5be91fd952cd5df2787bd04c1182db42f6f48be9 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
@@ -2559,10 +2559,12 @@ Foam::label Foam::meshRefinement::findRegions
                 if (Pstream::master())
                 {
                     outputDir =
+                    (
                         mesh.time().globalPath()
                       / functionObject::outputPrefix
-                      / mesh.pointsInstance();
-                    outputDir.clean();
+                      / mesh.pointsInstance()
+                    );
+                    outputDir.clean();  // Remove unneeded ".."
                     mkDir(outputDir);
                 }
 
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
index 0fdf183b78b9dca6c6b6fc7018145aa49e83a22e..2afda90edbeb9b38900109a9744070ff9f674b49 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -55,7 +55,7 @@ SourceFiles
 #include "pointFieldsFwd.H"
 #include "Tuple2.H"
 #include "pointIndexHit.H"
-#include "wordPairHashTable.H"
+#include "wordPairHashes.H"
 #include "surfaceZonesInfo.H"
 #include "volumeType.H"
 #include "DynamicField.H"
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C
index 5606e4f731bb8dacf5cb5dab57f07a505435a49f..04b1f916c9bef56d0c59120bfc4d2788536153ae 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2014 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -5155,7 +5155,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
     {
         // 1. Detect inter-region face and allocate names
 
-        HashTable<word, labelPair, labelPair::Hash<>> zoneIDsToFaceZone;
+        LabelPairMap<word> zoneIDsToFaceZone;
 
         for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
         {
@@ -5208,10 +5208,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify
             << endl;
 
         // From cellZone indices to faceZone index
-        HashTable<label, labelPair, labelPair::Hash<>> fZoneLookup
-        (
-            zonesToFaceZone.size()
-        );
+        LabelPairMap<label> fZoneLookup(zonesToFaceZone.size());
 
         const cellZoneMesh& cellZones = mesh_.cellZones();
 
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C
index 4d34c5c3bb00b01c378870b7da51f4208aaa6002..62b50bb0c5b26ad8dda81dde53ba5e5a17947205 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C
@@ -532,7 +532,7 @@ Foam::layerParameters::layerParameters
 
             const labelHashSet patchIDs
             (
-                boundaryMesh.patchSet(List<wordRe>(1, wordRe(key)))
+                boundaryMesh.patchSet(wordRes(one{}, wordRe(key)))
             );
 
             if (patchIDs.size() == 0)
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/wordPairHashTable.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/wordPairHashTable.H
index 36aa168ea3417f0ac59c5f74194504747d362f28..d5b8b49c18d30cb2212db52b80aafe0b3f659a25 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/wordPairHashTable.H
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/wordPairHashTable.H
@@ -1,57 +1,13 @@
 /*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-    Copyright (C) 2015 OpenFOAM Foundation
--------------------------------------------------------------------------------
-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/>.
-
-Typedef
-    Foam::wordPairHashTable
-
 Description
-    HashTable of Pair<word>
-
-Typedef
-    Foam::wordPairHashTable
-
-Description
-    HashTable of Pair<word>
+    Compatibility include.
 
 \*---------------------------------------------------------------------------*/
 
 #ifndef wordPairHashTable_H
 #define wordPairHashTable_H
 
-#include "Pair.H"
-#include "HashTable.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-    typedef HashTable<word, Pair<word>, FixedList<word, 2>::Hash<>>
-        wordPairHashTable;
-}
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+#include "wordPairHashes.H"
 
 #endif
 
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
index 2bd3c71bc7dd3ce6da6274d7d30e33d1d9fca298..63e68240e77ecce6f15cdc478cc86967df5698f8 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C
@@ -1974,14 +1974,11 @@ void Foam::snappyLayerDriver::getPatchDisplacement
 //                if (faceFnd.found())
 //                {
 //                    labelList& faces = faceFnd();
-//                    if (!faces.found(patchFacei))
-//                    {
-//                        faces.append(patchFacei);
-//                    }
+//                    faces.appendUniq(patchFacei);
 //                }
 //                else
 //                {
-//                    cellToFaces.insert(celli, labelList(1, patchFacei));
+//                    cellToFaces.insert(celli, labelList(one{}, patchFacei));
 //                }
 //            }
 //
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
index 26f1a320cffe7e8d8f991ec49e0eadbead327df7..30827dcf24c67b81a311ea4b6f6c1c9c2227bb05 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2014 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -37,7 +37,7 @@ SourceFiles
 #ifndef snappyRefineDriver_H
 #define snappyRefineDriver_H
 
-#include "wordPairHashTable.H"
+#include "wordPairHashes.H"
 #include "labelList.H"
 #include "scalarField.H"
 #include "Tuple2.H"
@@ -51,7 +51,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class refinementParameters;
 class snapParameters;
 
diff --git a/src/meshTools/PatchFunction1/CodedField/CodedField.C b/src/meshTools/PatchFunction1/CodedField/CodedField.C
index d41b9457777143a2de7d04a66bea5a98c6d51cb8..40c0c4588b219fd789b0b87df057bddc4d701fde 100644
--- a/src/meshTools/PatchFunction1/CodedField/CodedField.C
+++ b/src/meshTools/PatchFunction1/CodedField/CodedField.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,6 +26,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "dynamicCode.H"
+#include "dynamicCodeContext.H"
 
 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
 
@@ -37,6 +38,48 @@ Foam::PatchFunction1Types::CodedField<Type>::libs() const
 }
 
 
+template<class Type>
+Foam::string
+Foam::PatchFunction1Types::CodedField<Type>::description() const
+{
+    return "CodedField " + name_;
+}
+
+
+template<class Type>
+void Foam::PatchFunction1Types::CodedField<Type>::clearRedirect() const
+{
+    redirectFunctionPtr_.reset(nullptr);
+}
+
+
+template<class Type>
+const Foam::dictionary&
+Foam::PatchFunction1Types::CodedField<Type>::codeDict
+(
+    const dictionary& dict
+) const
+{
+    // Use named subdictionary if present to provide the code.
+    // This allows running with multiple PatchFunction1s
+
+    return
+    (
+        dict.found("code")
+      ? dict
+      : dict.subDict(name_)
+    );
+}
+
+
+template<class Type>
+const Foam::dictionary&
+Foam::PatchFunction1Types::CodedField<Type>::codeDict() const
+{
+    return codeDict(dict_);
+}
+
+
 template<class Type>
 void Foam::PatchFunction1Types::CodedField<Type>::prepare
 (
@@ -65,70 +108,28 @@ void Foam::PatchFunction1Types::CodedField<Type>::prepare
     // Copy filtered H template
     dynCode.addCopyFile(codeTemplateH);
 
-    // Debugging: make verbose
-    // dynCode.setFilterVariable("verbose", "true");
-    // DetailInfo
-    //     <<"compile " << name_ << " sha1: "
-    //     << context.sha1() << endl;
+    #ifdef FULLDEBUG
+    dynCode.setFilterVariable("verbose", "true");
+    DetailInfo
+        <<"compile " << name_ << " sha1: " << context.sha1() << endl;
+    #endif
 
     // Define Make/options
     dynCode.setMakeOptions
     (
         "EXE_INC = -g \\\n"
-        "-I$(LIB_SRC)/meshTools/lnInclude \\\n"
         "-I$(LIB_SRC)/finiteVolume/lnInclude \\\n"
+        "-I$(LIB_SRC)/meshTools/lnInclude \\\n"
       + context.options()
       + "\n\nLIB_LIBS = \\\n"
         "    -lOpenFOAM \\\n"
         "    -lfiniteVolume \\\n"
+        "    -lmeshTools \\\n"
       + context.libs()
     );
 }
 
 
-template<class Type>
-const Foam::dictionary&
-Foam::PatchFunction1Types::CodedField<Type>::codeDict
-(
-    const dictionary& dict
-) const
-{
-    // Use named subdictionary if present to provide the code. This allows
-    // running with multiple PatchFunction1s
-
-    return
-    (
-        dict.found("code")
-      ? dict
-      : dict.subDict(name_)
-    );
-}
-
-
-template<class Type>
-const Foam::dictionary&
-Foam::PatchFunction1Types::CodedField<Type>::codeDict() const
-{
-    return codeDict(dict_);
-}
-
-
-template<class Type>
-Foam::string
-Foam::PatchFunction1Types::CodedField<Type>::description() const
-{
-    return "CodedField " + name_;
-}
-
-
-template<class Type>
-void Foam::PatchFunction1Types::CodedField<Type>::clearRedirect() const
-{
-    // remove instantiation of fvPatchField provided by library
-    redirectFunctionPtr_.clear();
-}
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class Type>
diff --git a/src/meshTools/PatchFunction1/CodedField/CodedField.H b/src/meshTools/PatchFunction1/CodedField/CodedField.H
index 6cbdb1c86306c26191b9c3610323367b8b3feccf..d034c5337e3740fe40c42a9f98b7431af1def821 100644
--- a/src/meshTools/PatchFunction1/CodedField/CodedField.H
+++ b/src/meshTools/PatchFunction1/CodedField/CodedField.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -120,21 +120,25 @@ protected:
         //- Mutable access to the loaded dynamic libraries
         virtual dlLibraryTable& libs() const;
 
-        //- Adapt the context for the current object
-        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
-
-        // Return a description (type + name) for the output
+        //- Description (type + name) for the output
         virtual string description() const;
 
-        // Clear the ptr to the redirected object
+        //- Clear redirected object(s)
         virtual void clearRedirect() const;
 
-        // Get the (sub)dictionary to initialize the codeContext
-        virtual const dictionary& codeDict(const dictionary& fullDict) const;
+        // Get the code (sub)dictionary
+        virtual const dictionary& codeDict(const dictionary& dict) const;
 
-        // Get the dictionary to initialize the codeContext
+        // Get the code dictionary
         virtual const dictionary& codeDict() const;
 
+        //- Adapt the context for the current object
+        virtual void prepare(dynamicCode&, const dynamicCodeContext&) const;
+
+
+        //- No copy assignment
+        void operator=(const CodedField<Type>&) = delete;
+
 public:
 
     // Static Data Members
@@ -152,12 +156,6 @@ public:
     TypeName("coded");
 
 
-    // Generated Methods
-
-        //- No copy assignment
-        void operator=(const CodedField<Type>&) = delete;
-
-
     // Constructors
 
         //- Construct from entry name and dictionary
diff --git a/src/meshTools/cellDist/cellDistFuncs.C b/src/meshTools/cellDist/cellDistFuncs.C
index f625707f06581c00ce9ba0611d684324bcf00503..29bb06a81bb1af56d34b07a87221ca5d3d97cee0 100644
--- a/src/meshTools/cellDist/cellDistFuncs.C
+++ b/src/meshTools/cellDist/cellDistFuncs.C
@@ -107,10 +107,7 @@ void Foam::cellDistFuncs::getPointNeighbours
 
     for (const label nbr : faceNeighbours)
     {
-        if (!neighbours.found(nbr))
-        {
-            neighbours.append(nbr);
-        }
+        neighbours.appendUniq(nbr);
     }
 
     // Add all point-only neighbours by linear searching in edge neighbours.
@@ -128,10 +125,7 @@ void Foam::cellDistFuncs::getPointNeighbours
         for (const label facei : pointNbs)
         {
             // Check for facei in edge-neighbours part of neighbours
-            if (!neighbours.found(facei))
-            {
-                neighbours.append(facei);
-            }
+            neighbours.appendUniq(facei);
         }
     }
 
diff --git a/src/meshTools/cellFeatures/cellFeatures.C b/src/meshTools/cellFeatures/cellFeatures.C
index 098907fc87fcfc735c5028eb7a8149e2bdbd6746..3d00718efa6875934d890f6d71f3e3dd01eab72b 100644
--- a/src/meshTools/cellFeatures/cellFeatures.C
+++ b/src/meshTools/cellFeatures/cellFeatures.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -180,16 +180,12 @@ void Foam::cellFeatures::walkSuperFace
     Map<label>& toSuperFace
 ) const
 {
-    if (!toSuperFace.found(facei))
+    if (toSuperFace.insert(facei, superFacei))
     {
-        toSuperFace.insert(facei, superFacei);
-
         const labelList& fEdges = mesh_.faceEdges()[facei];
 
-        forAll(fEdges, fEdgeI)
+        for (const label edgeI : fEdges)
         {
-            label edgeI = fEdges[fEdgeI];
-
             if (!featureEdge_.found(edgeI))
             {
                 label face0;
diff --git a/src/meshTools/coordinate/systems/coordinateSystems.C b/src/meshTools/coordinate/systems/coordinateSystems.C
index d84a32301ee711e3c27aa6701075db33aae711dd..94cab825d4c4f67697f8a8f501ffafbda7c47c19 100644
--- a/src/meshTools/coordinate/systems/coordinateSystems.C
+++ b/src/meshTools/coordinate/systems/coordinateSystems.C
@@ -179,24 +179,13 @@ const Foam::coordinateSystems& Foam::coordinateSystems::New
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::labelList Foam::coordinateSystems::indices(const keyType& key) const
+Foam::labelList Foam::coordinateSystems::indices(const wordRe& key) const
 {
     if (key.empty())
     {
         return labelList();
     }
-    else if (key.isPattern())
-    {
-        // Match as regex
-        const regExp matcher(key);
-        return PtrListOps::findMatching(*this, matcher);
-    }
-    else
-    {
-        // Compare as literal string
-        const word& matcher = key;
-        return PtrListOps::findMatching(*this, matcher);
-    }
+    return PtrListOps::findMatching(*this, key);
 }
 
 
@@ -210,24 +199,13 @@ Foam::labelList Foam::coordinateSystems::indices(const wordRes& matcher) const
 }
 
 
-Foam::label Foam::coordinateSystems::findIndex(const keyType& key) const
+Foam::label Foam::coordinateSystems::findIndex(const wordRe& key) const
 {
     if (key.empty())
     {
         return -1;
     }
-    else if (key.isPattern())
-    {
-        // Find as regex
-        const regExp matcher(key);
-        return PtrListOps::firstMatching(*this, matcher);
-    }
-    else
-    {
-        // Find as literal string
-        const word& matcher = key;
-        return PtrListOps::firstMatching(*this, matcher);
-    }
+    return PtrListOps::firstMatching(*this, key);
 }
 
 
@@ -241,7 +219,7 @@ Foam::label Foam::coordinateSystems::findIndex(const wordRes& matcher) const
 }
 
 
-bool Foam::coordinateSystems::found(const keyType& key) const
+bool Foam::coordinateSystems::found(const wordRe& key) const
 {
     return findIndex(key) != -1;
 }
@@ -250,13 +228,16 @@ bool Foam::coordinateSystems::found(const keyType& key) const
 const Foam::coordinateSystem*
 Foam::coordinateSystems::cfind(const word& name) const
 {
-    const label index = this->findIndex(name);
+    const label index =
+    (
+        name.empty() ? -1 : PtrListOps::firstMatching(*this, name)
+    );
 
     if (coordinateSystem::debug)
     {
         InfoInFunction
             << "Global coordinate system: "
-            << name << "=" << index << endl;
+            << name << '=' << index << endl;
     }
 
     if (index < 0)
@@ -271,9 +252,9 @@ Foam::coordinateSystems::cfind(const word& name) const
 const Foam::coordinateSystem&
 Foam::coordinateSystems::lookup(const word& name) const
 {
-    const label index = this->findIndex(name);
+    const coordinateSystem* ptr = this->cfind(name);
 
-    if (index < 0)
+    if (!ptr)
     {
         FatalErrorInFunction
             << "Could not find coordinate system: " << name << nl
@@ -281,14 +262,8 @@ Foam::coordinateSystems::lookup(const word& name) const
             << flatOutput(names()) << nl << nl
             << exit(FatalError);
     }
-    if (coordinateSystem::debug)
-    {
-        InfoInFunction
-            << "Global coordinate system: "
-            << name << "=" << index << endl;
-    }
 
-    return this->operator[](index);
+    return *ptr;
 }
 
 
@@ -298,35 +273,22 @@ Foam::wordList Foam::coordinateSystems::names() const
 }
 
 
-Foam::wordList Foam::coordinateSystems::names(const keyType& key) const
+Foam::wordList Foam::coordinateSystems::names(const wordRe& key) const
 {
     if (key.empty())
     {
         return wordList();
     }
-    else if (key.isPattern())
-    {
-        // Find as regex
-        const regExp matcher(key);
-        return PtrListOps::names(*this, matcher);
-    }
-    else
-    {
-        // Find as literal string
-        const word& matcher = key;
-        return PtrListOps::names(*this, matcher);
-    }
-}
-
-
-Foam::wordList Foam::coordinateSystems::names(const wordRe& matcher) const
-{
-    return PtrListOps::names(*this, matcher);
+    return PtrListOps::names(*this, key);
 }
 
 
 Foam::wordList Foam::coordinateSystems::names(const wordRes& matcher) const
 {
+    if (matcher.empty())
+    {
+        return wordList();
+    }
     return PtrListOps::names(*this, matcher);
 }
 
diff --git a/src/meshTools/coordinate/systems/coordinateSystems.H b/src/meshTools/coordinate/systems/coordinateSystems.H
index d1af2a795b92722665b3b31defd6f2be81169369..eb81ff231bfea77fe70bcf85ee2ffc1c54a60ad5 100644
--- a/src/meshTools/coordinate/systems/coordinateSystems.H
+++ b/src/meshTools/coordinate/systems/coordinateSystems.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -134,7 +134,7 @@ public:
 
         //- Find and return indices for all matches
         //  A no-op (returns empty list) for an empty key
-        labelList indices(const keyType& key) const;
+        labelList indices(const wordRe& key) const;
 
         //- Find and return indices for all matches
         //  A no-op (returns empty list) for an empty matcher
@@ -142,14 +142,14 @@ public:
 
         //- Find and return index for the first match, return -1 if not found
         //  A no-op (returns -1) for an empty key
-        label findIndex(const keyType& key) const;
+        label findIndex(const wordRe& key) const;
 
         //- Find and return index for the first match, return -1 if not found
         //  A no-op (returns -1) for an empty matcher
         label findIndex(const wordRes& matcher) const;
 
         //- Search if given key exists
-        bool found(const keyType& key) const;
+        bool found(const wordRe& key) const;
 
         //- Return pointer to named coordinateSystem or nullptr on error
         const coordinateSystem* cfind(const word& name) const;
@@ -161,10 +161,7 @@ public:
         wordList names() const;
 
         //- A list of the coordinate-system names satisfying the input matcher
-        wordList names(const keyType& key) const;
-
-        //- A list of the coordinate-system names satisfying the input matcher
-        wordList names(const wordRe& matcher) const;
+        wordList names(const wordRe& key) const;
 
         //- A list of the coordinate-system names satisfying the input matcher
         wordList names(const wordRes& matcher) const;
@@ -193,7 +190,7 @@ public:
 
         //- Identical to the indices() method (AUG-2018)
         FOAM_DEPRECATED_FOR(2018-08, "indices() method")
-        labelList findIndices(const keyType& key) const
+        labelList findIndices(const wordRe& key) const
         {
             return this->indices(key);
         }
diff --git a/src/meshTools/coupling/externalFileCoupler.C b/src/meshTools/coupling/externalFileCoupler.C
index 0cae8f6dcd3e14a58bb0d1cb6795bbbe650dad14..722322dc14901c550ac0cb45b3183c753769d1c3 100644
--- a/src/meshTools/coupling/externalFileCoupler.C
+++ b/src/meshTools/coupling/externalFileCoupler.C
@@ -103,7 +103,7 @@ Foam::externalFileCoupler::externalFileCoupler()
     log(false)
 {
     commsDir_.expand();
-    commsDir_.clean();
+    commsDir_.clean();  // Remove unneeded ".."
 }
 
 
@@ -118,7 +118,7 @@ Foam::externalFileCoupler::externalFileCoupler(const fileName& commsDir)
     log(false)
 {
     commsDir_.expand();
-    commsDir_.clean();
+    commsDir_.clean();  // Remove unneeded ".."
 
     if (Pstream::master())
     {
@@ -158,7 +158,7 @@ bool Foam::externalFileCoupler::readDict(const dictionary& dict)
     {
         dict.readEntry("commsDir", commsDir_);
         commsDir_.expand();
-        commsDir_.clean();
+        commsDir_.clean();  // Remove unneeded ".."
         statusDone_ = dict.getOrDefault<word>("statusDone", "done");
         slaveFirst_ = dict.getOrDefault("initByExternal", false);
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
index 626341fcd584b2ec5c8f34d0d809e039ad9e595d..53baa17294470554ca8430ef36151ac010c4632c 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPatchBase.C
@@ -1760,14 +1760,15 @@ Foam::pointIndexHit Foam::mappedPatchBase::facePoint
 const Foam::objectRegistry& Foam::mappedPatchBase::subRegistry
 (
     const objectRegistry& obr,
-    const fileName& rawFName
+    const fileName& path
 )
 {
-    // Lookup (and create if non existing) a registry using a '/' separated
-    // path.
+    // Lookup (and create if non-existing) a registry using
+    // '/' separated path. Like 'mkdir -p'
 
-    const fileName fName(rawFName.clean());
-    const wordList names(fName.components());
+    fileName cleanedPath(path);
+    cleanedPath.clean();  // Remove unneeded ".."
+    const wordList names(cleanedPath.components());
 
     if (names.empty())
     {
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPolyPatch.C b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPolyPatch.C
index 18d8e7f776440a6634057a5c9ac3689cd21df5f2..933d9a88ed63d502e1731e33aa358c6ae99939c1 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedPolyPatch.C
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedPolyPatch.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -55,10 +56,7 @@ Foam::mappedPolyPatch::mappedPolyPatch
     mappedPatchBase(static_cast<const polyPatch&>(*this))
 {
     //  mapped is not constraint type so add mapped group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
@@ -125,10 +123,7 @@ Foam::mappedPolyPatch::mappedPolyPatch
     mappedPatchBase(*this, dict)
 {
     //  mapped is not constraint type so add mapped group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
diff --git a/src/meshTools/mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C b/src/meshTools/mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C
index 5a22fc5b01faeac013a41d2c25024123531f7a2a..6a8f11745e2d68b09101026d68ff841566e83be6 100644
--- a/src/meshTools/mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C
+++ b/src/meshTools/mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2013 OpenFOAM Foundation
+    Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -61,10 +62,7 @@ Foam::mappedWallPolyPatch::mappedWallPolyPatch
     mappedPatchBase(static_cast<const polyPatch&>(*this))
 {
     //  mapped is not constraint type so add mapped group explicitly
-    if (!inGroups().found(mappedPolyPatch::typeName))
-    {
-        inGroups().append(mappedPolyPatch::typeName);
-    }
+    inGroups().appendUniq(mappedPolyPatch::typeName);
 }
 
 
@@ -131,10 +129,7 @@ Foam::mappedWallPolyPatch::mappedWallPolyPatch
     mappedPatchBase(*this, dict)
 {
     //  mapped is not constraint type so add mapped group explicitly
-    if (!inGroups().found(mappedPolyPatch::typeName))
-    {
-        inGroups().append(mappedPolyPatch::typeName);
-    }
+    inGroups().appendUniq(mappedPolyPatch::typeName);
 }
 
 
diff --git a/src/meshTools/regionSplit/localPointRegion.C b/src/meshTools/regionSplit/localPointRegion.C
index ac5453ba756b3ecfb264ab37548f80e405742e25..5a326c2ae9329ee75f00f88a391580b7668ec94d 100644
--- a/src/meshTools/regionSplit/localPointRegion.C
+++ b/src/meshTools/regionSplit/localPointRegion.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -172,7 +172,7 @@ void Foam::localPointRegion::countPointRegions
                         }
                         else
                         {
-                            label localPointi = meshPointMap_.size();
+                            const label localPointi = meshPointMap_.size();
                             meshPointMap_.insert(pointi, localPointi);
                             labelList regions(2);
                             regions[0] = minPointRegion[pointi];
@@ -180,7 +180,7 @@ void Foam::localPointRegion::countPointRegions
                             pointRegions.append(regions);
                         }
 
-                        label meshFaceMapI = meshFaceMap_.size();
+                        const label meshFaceMapI = meshFaceMap_.size();
                         meshFaceMap_.insert(facei, meshFaceMapI);
                     }
                 }
@@ -372,7 +372,7 @@ void Foam::localPointRegion::calcPointRegions
                         const label pointi = f[fp];
                         auto iter = minPointValue.find(pointi);
 
-                        if (iter == minPointValue.end())
+                        if (!iter.found())
                         {
                             minPointValue.insert(pointi, minRegion[facei][fp]);
                         }
diff --git a/src/overset/oversetPolyPatch/oversetPolyPatch.C b/src/overset/oversetPolyPatch/oversetPolyPatch.C
index 71b9877f2655002af89a496adbd8c97207b61067..cb3893bdf0477fc5f7d36b4855168000c01f584d 100644
--- a/src/overset/oversetPolyPatch/oversetPolyPatch.C
+++ b/src/overset/oversetPolyPatch/oversetPolyPatch.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2017 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -56,10 +56,7 @@ Foam::oversetPolyPatch::oversetPolyPatch
     masterPatchID_(-1)
 {
     //  'overset' is not constraint type so add to group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
@@ -76,10 +73,7 @@ Foam::oversetPolyPatch::oversetPolyPatch
     masterPatchID_(-1)
 {
     //  'overset' is not constraint type so add to group explicitly
-    if (!inGroups().found(typeName))
-    {
-        inGroups().append(typeName);
-    }
+    inGroups().appendUniq(typeName);
 }
 
 
diff --git a/src/phaseSystemModels/multiphaseEuler/multiphaseSystem/multiphaseSystem/multiphaseSystem.H b/src/phaseSystemModels/multiphaseEuler/multiphaseSystem/multiphaseSystem/multiphaseSystem.H
index 18c1cfc628229e53c2371ee9037237b8c60f88bc..632b2bdeb03b1cb873d2ead00a55894fa06a50b0 100644
--- a/src/phaseSystemModels/multiphaseEuler/multiphaseSystem/multiphaseSystem/multiphaseSystem.H
+++ b/src/phaseSystemModels/multiphaseEuler/multiphaseSystem/multiphaseSystem/multiphaseSystem.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -68,50 +68,25 @@ class multiphaseSystem
     public IOdictionary,
     public transportModel
 {
-
 public:
 
+    //- Name pair for the interface
     class interfacePair
     :
         public Pair<word>
     {
     public:
 
-        class symmHash
-        :
-            public Hash<interfacePair>
-        {
-        public:
-
-            symmHash()
-            {}
-
-            label operator()(const interfacePair& key) const
-            {
-                return word::hash()(key.first()) + word::hash()(key.second());
-            }
-        };
-
-        class hash
-        :
-             public Hash<interfacePair>
-        {
-        public:
+        // Ordered hashing (alias)
+        using hash = Pair<word>::hasher;
 
-            hash()
-            {}
-
-            label operator()(const interfacePair& key) const
-            {
-                return word::hash()(key.first(), word::hash()(key.second()));
-            }
-        };
+        // Unordered hashing (alias)
+        using symmHash = Pair<word>::symmHasher;
 
 
         // Constructors
 
-            interfacePair()
-            {}
+            interfacePair() = default;
 
             interfacePair(const word& alpha1Name, const word& alpha2Name)
             :
@@ -132,11 +107,7 @@ public:
                 const interfacePair& b
             )
             {
-                return
-                (
-                    ((a.first() == b.first()) && (a.second() == b.second()))
-                 || ((a.first() == b.second()) && (a.second() == b.first()))
-                );
+                return (0 != Pair<word>::compare(a, b));
             }
 
             friend bool operator!=
diff --git a/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.C b/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.C
index de8bddb6a21407f1d5b4712db91d2d9e35ff55ba..e706954636c26d8dfedfdf221a9801dd5ce90f0a 100644
--- a/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.C
+++ b/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.C
@@ -5,7 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2014-2015 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,35 +42,6 @@ Foam::phasePairKey::phasePairKey
 {}
 
 
-// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
-
-bool Foam::phasePairKey::ordered() const
-{
-    return ordered_;
-}
-
-
-// * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * * //
-
-Foam::label Foam::phasePairKey::hash::operator()
-(
-    const phasePairKey& key
-) const
-{
-    if (key.ordered_)
-    {
-        return
-            word::hash()
-            (
-                key.first(),
-                word::hash()(key.second())
-            );
-    }
-
-    return word::hash()(key.first()) + word::hash()(key.second());
-}
-
-
 // * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * * //
 
 bool Foam::operator==
@@ -78,12 +50,11 @@ bool Foam::operator==
     const phasePairKey& b
 )
 {
-    const auto cmp = Pair<word>::compare(a,b);
-
+    const int cmp = Pair<word>::compare(a, b);
     return
     (
-        (a.ordered_ == b.ordered_)
-     && (a.ordered_ ? (cmp == 1) : cmp)
+        (a.ordered() == b.ordered())
+     && (a.ordered() ? (cmp == 1) : cmp)
     );
 }
 
@@ -102,30 +73,29 @@ bool Foam::operator!=
 
 Foam::Istream& Foam::operator>>(Istream& is, phasePairKey& key)
 {
-    const FixedList<word, 3> temp(is);
+    const FixedList<word, 3> toks(is);
 
-    key.first() = temp[0];
+    key.first() = toks[0];
+    key.second() = toks[2];
+    const word& order = toks[1];
 
-    if (temp[1] == "and")
+    if (order == "to")
     {
-        key.ordered_ = false;
+        key.ordered_ = true;
     }
-    else if (temp[1] == "to")
+    else if (order == "and")
     {
-        key.ordered_ = true;
+        key.ordered_ = false;
     }
     else
     {
         FatalErrorInFunction
-            << "Phase pair type is not recognised. "
-            << temp
-            << "Use (phaseDispersed to phaseContinuous) for an ordered pair, "
-            << "or (phase1 and phase2) for an unordered pair."
+            << "Phase pair type is not recognised. " << toks
+            << "Use (phaseDispersed to phaseContinuous) for an ordered pair,"
+               " or (phase1 and phase2) for an unordered pair.\n"
             << exit(FatalError);
     }
 
-    key.second() = temp[2];
-
     return is;
 }
 
@@ -137,7 +107,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const phasePairKey& key)
     os  << token::BEGIN_LIST
         << key.first()
         << token::SPACE
-        << (key.ordered_ ? "to" : "and")
+        << (key.ordered() ? "to" : "and")
         << token::SPACE
         << key.second()
         << token::END_LIST;
diff --git a/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.H b/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.H
index 904e06fe1e77f30de11495d579cd13e81bd9e8ec..b0ffa6b142e2015957ba4117273cc42f516f0225 100644
--- a/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.H
+++ b/src/phaseSystemModels/multiphaseInter/phasesSystem/phasePair/phasePairKey.H
@@ -5,7 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2014-2016 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,8 +28,15 @@ Class
     Foam::phasePairKey
 
 Description
+    An ordered or unorder pair of phase names.
+    Typically specified as follows.
+    \verbatim
+       (phase1 and phase2)  // unordered
+       (phase1 to  phase2)  // ordered
+    \endverbatim
 
 SourceFiles
+    phasePairKey.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -42,7 +50,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class phasePairKey;
 
 bool operator==(const phasePairKey& a, const phasePairKey& b);
@@ -59,7 +67,7 @@ class phasePairKey
 :
     public Pair<word>
 {
-    // Private data
+    // Private Data
 
         //- Flag to indicate whether ordering is important
         bool ordered_;
@@ -67,20 +75,12 @@ class phasePairKey
 
 public:
 
-        //- Ordered or unordered hashing of word pair
-        struct hash
-        {
-            //- Generate a hash from a phase pair key
-            label operator()(const phasePairKey& key) const;
-        };
-
-
     // Constructors
 
-        //- Construct null
-        phasePairKey() {} // = default
+        //- Default construct
+        phasePairKey() = default;
 
-        //- Construct from names and optional ordering flag
+        //- Construct from names and (optional) ordering flag
         phasePairKey
         (
             const word& name1,
@@ -93,10 +93,13 @@ public:
     virtual ~phasePairKey() = default;
 
 
-    // Access
+    // Member Functions
 
         //- Return the ordered flag
-        bool ordered() const;
+        bool ordered() const noexcept
+        {
+            return ordered_;
+        }
 
 
     // Friend Operators
@@ -112,9 +115,37 @@ public:
 
         //- Write to Ostream
         friend Ostream& operator<<(Ostream& os, const phasePairKey& key);
+
+
+    // Hashing
+
+        //- Hashing functor for phasePairKey
+        struct hasher
+        {
+            unsigned operator()(const phasePairKey& key) const
+            {
+                if (key.ordered())
+                {
+                    return Pair<word>::hasher()(key);
+                }
+                else
+                {
+                    return Pair<word>::symmHasher()(key);
+                }
+            }
+        };
+
+        //- Alternative name for functor
+        using hash = phasePairKey::hasher;
 };
 
 
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Hashing for phasePairKey
+template<> struct Hash<phasePairKey> : phasePairKey::hasher {};
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H b/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
index dff407001d714bc943c8e325f7d0bce2f9d03266..a78a2e18a014ae2c981f2fcd3ebfb179fa7d8e20 100644
--- a/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
+++ b/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
@@ -56,7 +56,7 @@ class hyperbolic
     // Private data
 
         //- Minimum fraction of phases which can be considered continuous
-        HashTable<dimensionedScalar, word, word::hash> minContinuousAlpha_;
+        HashTable<dimensionedScalar> minContinuousAlpha_;
 
         //- Width of the transition
         const dimensionedScalar transitionAlphaScale_;
diff --git a/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H b/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
index 9edf6f177012419ef9da1f86b42030d67ea0cdb0..1488032df46e2048595db726d8ab1d32c74f2a89 100644
--- a/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
+++ b/src/phaseSystemModels/reactingEuler/multiphaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
@@ -56,12 +56,10 @@ class linear
     // Private data
 
         //- Minimum fraction of phases which can be considered fully continuous
-        HashTable<dimensionedScalar, word, word::hash>
-            minFullyContinuousAlpha_;
+        HashTable<dimensionedScalar> minFullyContinuousAlpha_;
 
         //- Minimum fraction of phases which can be considered partly continuous
-        HashTable<dimensionedScalar, word, word::hash>
-            minPartlyContinuousAlpha_;
+        HashTable<dimensionedScalar> minPartlyContinuousAlpha_;
 
 
 public:
diff --git a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.C b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.C
index f452e44cb53fb49a7f908bee20063a97b7ff9ebe..95f8c638c85565e447e2702dc8efd765104f19fd 100644
--- a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.C
+++ b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.C
@@ -5,7 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2014-2018 OpenFOAM Foundation
+    Copyright (C) 2014-2015 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,14 +30,6 @@ License
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::phasePairKey::hash::hash()
-{}
-
-
-Foam::phasePairKey::phasePairKey()
-{}
-
-
 Foam::phasePairKey::phasePairKey
 (
     const word& name1,
@@ -49,45 +42,6 @@ Foam::phasePairKey::phasePairKey
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::phasePairKey::~phasePairKey()
-{}
-
-
-// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
-
-bool Foam::phasePairKey::ordered() const
-{
-    return ordered_;
-}
-
-
-// * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * * //
-
-Foam::label Foam::phasePairKey::hash::operator()
-(
-    const phasePairKey& key
-) const
-{
-    if (key.ordered_)
-    {
-        return
-            word::hash()
-            (
-                key.first(),
-                word::hash()(key.second())
-            );
-    }
-    else
-    {
-        return
-            word::hash()(key.first())
-          + word::hash()(key.second());
-    }
-}
-
-
 // * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * * //
 
 bool Foam::operator==
@@ -96,14 +50,12 @@ bool Foam::operator==
     const phasePairKey& b
 )
 {
-    const label c = Pair<word>::compare(a, b);
-
+    const int cmp = Pair<word>::compare(a, b);
     return
-        (a.ordered_ == b.ordered_)
-     && (
-            (a.ordered_ && (c == 1))
-         || (!a.ordered_ && (c != 0))
-        );
+    (
+        (a.ordered() == b.ordered())
+     && (a.ordered() ? (cmp == 1) : cmp)
+    );
 }
 
 
@@ -121,30 +73,29 @@ bool Foam::operator!=
 
 Foam::Istream& Foam::operator>>(Istream& is, phasePairKey& key)
 {
-    const FixedList<word, 3> temp(is);
+    const FixedList<word, 3> toks(is);
 
-    key.first() = temp[0];
+    key.first() = toks[0];
+    key.second() = toks[2];
+    const word& order = toks[1];
 
-    if (temp[1] == "and")
+    if (order == "in")
     {
-        key.ordered_ = false;
+        key.ordered_ = true;
     }
-    else if (temp[1] == "in")
+    else if (order == "and")
     {
-        key.ordered_ = true;
+        key.ordered_ = false;
     }
     else
     {
         FatalErrorInFunction
-            << "Phase pair type is not recognised. "
-            << temp
-            << "Use (phaseDispersed in phaseContinuous) for an ordered"
-            << "pair, or (phase1 and pase2) for an unordered pair."
+            << "Phase pair type is not recognised. " << toks
+            << "Use (phaseDispersed in phaseContinuous) for an ordered pair,"
+               " or (phase1 and phase2) for an unordered pair.\n"
             << exit(FatalError);
     }
 
-    key.second() = temp[2];
-
     return is;
 }
 
@@ -156,7 +107,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const phasePairKey& key)
     os  << token::BEGIN_LIST
         << key.first()
         << token::SPACE
-        << (key.ordered_ ? "in" : "and")
+        << (key.ordered() ? "in" : "and")
         << token::SPACE
         << key.second()
         << token::END_LIST;
diff --git a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.H b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.H
index d8ffcef89660fd6193b89ed713cc661edf8a0635..5bef3cbea3ea774691e95ae79bba59a4de4e6857 100644
--- a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.H
+++ b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phasePair/phasePairKey.H
@@ -5,7 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2014-2018 OpenFOAM Foundation
+    Copyright (C) 2014-2016 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,8 +28,15 @@ Class
     Foam::phasePairKey
 
 Description
+    An ordered or unorder pair of phase names.
+    Typically specified as follows.
+    \verbatim
+       (phase1 and phase2)  // unordered
+       (phase1 in  phase2)  // ordered
+    \endverbatim
 
 SourceFiles
+    phasePairKey.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -42,16 +50,14 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declaration of friend functions and operators
-
+// Forward Declarations
 class phasePairKey;
 
-bool operator==(const phasePairKey&, const phasePairKey&);
-bool operator!=(const phasePairKey&, const phasePairKey&);
-
-Istream& operator>>(Istream&, phasePairKey&);
-Ostream& operator<<(Ostream&, const phasePairKey&);
+bool operator==(const phasePairKey& a, const phasePairKey& b);
+bool operator!=(const phasePairKey& a, const phasePairKey& b);
 
+Istream& operator>>(Istream& is, phasePairKey& key);
+Ostream& operator<<(Ostream& os, const phasePairKey& key);
 
 /*---------------------------------------------------------------------------*\
                          Class phasePairKey Declaration
@@ -61,30 +67,7 @@ class phasePairKey
 :
     public Pair<word>
 {
-public:
-
-        class hash
-        :
-            public Hash<phasePairKey>
-        {
-        public:
-
-            // Constructors
-
-                // Construct null
-                hash();
-
-
-            // Member operators
-
-                // Generate a hash from a phase pair key
-                label operator()(const phasePairKey& key) const;
-        };
-
-
-private:
-
-    // Private data
+    // Private Data
 
         //- Flag to indicate whether ordering is important
         bool ordered_;
@@ -94,10 +77,10 @@ public:
 
     // Constructors
 
-        //- Construct null
-        phasePairKey();
+        //- Default construct
+        phasePairKey() = default;
 
-        //- Construct from names and the ordering flag
+        //- Construct from names and (optional) ordering flag
         phasePairKey
         (
             const word& name1,
@@ -106,32 +89,63 @@ public:
         );
 
 
-    // Destructor
-    virtual ~phasePairKey();
+    //- Destructor
+    virtual ~phasePairKey() = default;
 
 
-    // Access
+    // Member Functions
 
         //- Return the ordered flag
-        bool ordered() const;
+        bool ordered() const noexcept
+        {
+            return ordered_;
+        }
 
 
     // Friend Operators
 
-        //- Test if keys are equal
+        //- Test for equality
         friend bool operator==(const phasePairKey& a, const phasePairKey& b);
 
-        //- Test if keys are unequal
+        //- Test for inequality
         friend bool operator!=(const phasePairKey& a, const phasePairKey& b);
 
-        //- Read from stdin
+        //- Read from Istream
         friend Istream& operator>>(Istream& is, phasePairKey& key);
 
-        //- Write to stdout
+        //- Write to Ostream
         friend Ostream& operator<<(Ostream& os, const phasePairKey& key);
+
+
+    // Hashing
+
+        //- Hashing functor for phasePairKey
+        struct hasher
+        {
+            unsigned operator()(const phasePairKey& key) const
+            {
+                if (key.ordered())
+                {
+                    return Pair<word>::hasher()(key);
+                }
+                else
+                {
+                    return Pair<word>::symmHasher()(key);
+                }
+            }
+        };
+
+        //- Alternative name for functor
+        using hash = phasePairKey::hasher;
 };
 
 
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Hashing for phasePairKey
+template<> struct Hash<phasePairKey> : phasePairKey::hasher {};
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phaseSystem/phaseSystem.H b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phaseSystem/phaseSystem.H
index 9ee4a19b62a104e0cb7ced818e450d6579ffdea6..7b4549dbb57c03b1aefbfc06f296201069c68edc 100644
--- a/src/phaseSystemModels/reactingEuler/multiphaseSystem/phaseSystem/phaseSystem.H
+++ b/src/phaseSystemModels/reactingEuler/multiphaseSystem/phaseSystem/phaseSystem.H
@@ -99,8 +99,7 @@ protected:
             dictTable;
 
         typedef
-            HashTable<autoPtr<blendingMethod>, word, word::hash>
-            blendingMethodTable;
+            HashTable<autoPtr<blendingMethod>> blendingMethodTable;
 
         typedef
             HashTable
diff --git a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
index 6a7c5ef970aa7515ab0ca4fc77b3d1f1f1dc8f7f..de72fcbfbaac9825c5e661dd6255f370f4c250ab 100644
--- a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
+++ b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/hyperbolic/hyperbolic.H
@@ -56,7 +56,7 @@ class hyperbolic
     // Private data
 
         //- Maximum fraction of phases which can be considered dispersed
-        HashTable<dimensionedScalar, word, word::hash> maxDispersedAlpha_;
+        HashTable<dimensionedScalar> maxDispersedAlpha_;
 
         //- Width of the transition
         const dimensionedScalar transitionAlphaScale_;
diff --git a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
index 34c3075ad28104a653ea2bef456c91a8c3c603f9..6f819dfc3f688c0385e810cfb90df78ed6fcdd8f 100644
--- a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
+++ b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/BlendedInterfacialModel/blendingMethods/linear/linear.H
@@ -56,12 +56,10 @@ class linear
     // Private data
 
         //- Maximum fraction of phases which can be considered fully dispersed
-        HashTable<dimensionedScalar, word, word::hash>
-            maxFullyDispersedAlpha_;
+        HashTable<dimensionedScalar> maxFullyDispersedAlpha_;
 
         //- Maximum fraction of phases which can be considered partly dispersed
-        HashTable<dimensionedScalar, word, word::hash>
-            maxPartlyDispersedAlpha_;
+        HashTable<dimensionedScalar> maxPartlyDispersedAlpha_;
 
 
 public:
diff --git a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.C b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.C
index f22d6709e20b0f210097a777955e55b784515405..95f8c638c85565e447e2702dc8efd765104f19fd 100644
--- a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.C
+++ b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.C
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2014-2015 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,15 +30,6 @@ License
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-
-Foam::phasePairKey::hash::hash()
-{}
-
-
-Foam::phasePairKey::phasePairKey()
-{}
-
-
 Foam::phasePairKey::phasePairKey
 (
     const word& name1,
@@ -50,37 +42,6 @@ Foam::phasePairKey::phasePairKey
 {}
 
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::phasePairKey::~phasePairKey()
-{}
-
-
-// * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * * //
-
-Foam::label Foam::phasePairKey::hash::operator()
-(
-    const phasePairKey& key
-) const
-{
-    if (key.ordered_)
-    {
-        return
-            word::hash()
-            (
-                key.first(),
-                word::hash()(key.second())
-            );
-    }
-    else
-    {
-        return
-            word::hash()(key.first())
-          + word::hash()(key.second());
-    }
-}
-
-
 // * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * * //
 
 bool Foam::operator==
@@ -89,14 +50,12 @@ bool Foam::operator==
     const phasePairKey& b
 )
 {
-    const auto c = Pair<word>::compare(a,b);
-
+    const int cmp = Pair<word>::compare(a, b);
     return
-        (a.ordered_ == b.ordered_)
-      && (
-            (a.ordered_ && (c == 1))
-         || (!a.ordered_ && (c != 0))
-        );
+    (
+        (a.ordered() == b.ordered())
+     && (a.ordered() ? (cmp == 1) : cmp)
+    );
 }
 
 
@@ -114,30 +73,29 @@ bool Foam::operator!=
 
 Foam::Istream& Foam::operator>>(Istream& is, phasePairKey& key)
 {
-    const FixedList<word, 3> temp(is);
+    const FixedList<word, 3> toks(is);
 
-    key.first() = temp[0];
+    key.first() = toks[0];
+    key.second() = toks[2];
+    const word& order = toks[1];
 
-    if (temp[1] == "and")
+    if (order == "in")
     {
-        key.ordered_ = false;
+        key.ordered_ = true;
     }
-    else if (temp[1] == "in")
+    else if (order == "and")
     {
-        key.ordered_ = true;
+        key.ordered_ = false;
     }
     else
     {
-         FatalErrorInFunction
-            << "Phase pair type is not recognised. "
-            << temp
-            << "Use (phaseDispersed in phaseContinuous) for an ordered"
-            << "pair, or (phase1 and pase2) for an unordered pair."
+        FatalErrorInFunction
+            << "Phase pair type is not recognised. " << toks
+            << "Use (phaseDispersed in phaseContinuous) for an ordered pair,"
+               " or (phase1 and phase2) for an unordered pair.\n"
             << exit(FatalError);
     }
 
-    key.second() = temp[2];
-
     return is;
 }
 
@@ -149,7 +107,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const phasePairKey& key)
     os  << token::BEGIN_LIST
         << key.first()
         << token::SPACE
-        << (key.ordered_ ? "in" : "and")
+        << (key.ordered() ? "in" : "and")
         << token::SPACE
         << key.second()
         << token::END_LIST;
diff --git a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.H b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.H
index e102b1c775662d305bddf0626436a378ab517fae..5bef3cbea3ea774691e95ae79bba59a4de4e6857 100644
--- a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.H
+++ b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/phasePair/phasePairKey.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2014-2016 OpenFOAM Foundation
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,8 +28,15 @@ Class
     Foam::phasePairKey
 
 Description
+    An ordered or unorder pair of phase names.
+    Typically specified as follows.
+    \verbatim
+       (phase1 and phase2)  // unordered
+       (phase1 in  phase2)  // ordered
+    \endverbatim
 
 SourceFiles
+    phasePairKey.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -42,7 +50,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class phasePairKey;
 
 bool operator==(const phasePairKey& a, const phasePairKey& b);
@@ -59,59 +67,39 @@ class phasePairKey
 :
     public Pair<word>
 {
-public:
-
-        class hash
-        :
-            public Hash<phasePairKey>
-        {
-        public:
-
-            // Constructors
-
-                // Construct null
-                hash();
-
-
-            // Member operators
-
-                // Generate a hash from a phase pair key
-                label operator()(const phasePairKey& key) const;
-        };
-
-
-private:
-
-    // Private data
+    // Private Data
 
         //- Flag to indicate whether ordering is important
         bool ordered_;
 
-public:
 
+public:
 
     // Constructors
 
-        //- Construct null
-        phasePairKey();
+        //- Default construct
+        phasePairKey() = default;
 
-        //- Construct from names and the ordering flag
+        //- Construct from names and (optional) ordering flag
         phasePairKey
         (
             const word& name1,
             const word& name2,
-            const bool ordered
+            const bool ordered = false
         );
 
 
     //- Destructor
-    virtual ~phasePairKey();
+    virtual ~phasePairKey() = default;
 
 
-    // Access
+    // Member Functions
 
         //- Return the ordered flag
-        bool ordered() const;
+        bool ordered() const noexcept
+        {
+            return ordered_;
+        }
 
 
     // Friend Operators
@@ -127,9 +115,37 @@ public:
 
         //- Write to Ostream
         friend Ostream& operator<<(Ostream& os, const phasePairKey& key);
+
+
+    // Hashing
+
+        //- Hashing functor for phasePairKey
+        struct hasher
+        {
+            unsigned operator()(const phasePairKey& key) const
+            {
+                if (key.ordered())
+                {
+                    return Pair<word>::hasher()(key);
+                }
+                else
+                {
+                    return Pair<word>::symmHasher()(key);
+                }
+            }
+        };
+
+        //- Alternative name for functor
+        using hash = phasePairKey::hasher;
 };
 
 
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Hashing for phasePairKey
+template<> struct Hash<phasePairKey> : phasePairKey::hasher {};
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/twoPhaseSystem/twoPhaseSystem.H b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/twoPhaseSystem/twoPhaseSystem.H
index 4d5db21671d7824d0b3bc5b889dbc2796cf7c730..f8a30db0d7928e78e20f3f62179654f4c8fb4c0c 100644
--- a/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/twoPhaseSystem/twoPhaseSystem.H
+++ b/src/phaseSystemModels/twoPhaseEuler/twoPhaseSystem/twoPhaseSystem/twoPhaseSystem.H
@@ -96,7 +96,7 @@ class twoPhaseSystem
         autoPtr<orderedPhasePair> pair2In1_;
 
         //- Blending methods
-        HashTable<autoPtr<blendingMethod>, word, word::hash> blendingMethods_;
+        HashTable<autoPtr<blendingMethod>> blendingMethods_;
 
         //- Drag model
         autoPtr<BlendedInterfacialModel<dragModel>> drag_;
diff --git a/src/sampling/meshToMesh/calcMethod/mapNearest/mapNearestMethod.C b/src/sampling/meshToMesh/calcMethod/mapNearest/mapNearestMethod.C
index c128db7d591924a2dd5c72b410b1fded752ffb96..679c8c022fba85485ccd65c83b9ad6e8206fd04d 100644
--- a/src/sampling/meshToMesh/calcMethod/mapNearest/mapNearestMethod.C
+++ b/src/sampling/meshToMesh/calcMethod/mapNearest/mapNearestMethod.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2016 OpenFOAM Foundation
-    Copyright (C) 2015 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -290,8 +290,8 @@ Foam::label Foam::mapNearestMethod::findMappedSrcCell
     const List<DynamicList<label>>& tgtToSrc
 ) const
 {
-    DynamicList<label> testCells(10);
-    DynamicList<label> visitedCells(10);
+    DynamicList<label> testCells(16);
+    DynamicList<label> visitedCells(16);
 
     testCells.append(tgtCelli);
 
@@ -312,11 +312,11 @@ Foam::label Foam::mapNearestMethod::findMappedSrcCell
             {
                 const labelList& nbrCells = tgt_.cellCells()[tgtI];
 
-                forAll(nbrCells, i)
+                for (const label nbrCelli : nbrCells)
                 {
-                    if (!visitedCells.found(nbrCells[i]))
+                    if (!visitedCells.found(nbrCelli))
                     {
-                        testCells.append(nbrCells[i]);
+                        testCells.append(nbrCelli);
                     }
                 }
             }
diff --git a/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C b/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
index e2bdc189c04b62576fe7d71a8d67cce47550098f..bf879b9c7052bc1adb577ffae54c07d35673e0db 100644
--- a/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
+++ b/src/sampling/meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2013-2014 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -194,13 +194,9 @@ void Foam::meshToMeshMethod::appendNbrCells
     // filter out cells already visited from cell neighbours
     for (const label nbrCelli : nbrCells)
     {
-        if
-        (
-            !visitedCells.found(nbrCelli)
-         && !nbrCellIDs.found(nbrCelli)
-        )
+        if (!visitedCells.found(nbrCelli))
         {
-            nbrCellIDs.append(nbrCelli);
+            nbrCellIDs.appendUniq(nbrCelli);
         }
     }
 }
diff --git a/src/sampling/meshToMesh/meshToMeshParallelOps.C b/src/sampling/meshToMesh/meshToMeshParallelOps.C
index 8074dba57002921a06aad9a730df3467873dcbfc..b58d7952395cdd0abfac3893317322fcd5c96e45 100644
--- a/src/sampling/meshToMesh/meshToMeshParallelOps.C
+++ b/src/sampling/meshToMesh/meshToMeshParallelOps.C
@@ -611,7 +611,7 @@ void Foam::meshToMesh::distributeAndMergeCells
 
     // Count any coupled faces
     typedef FixedList<label, 3> label3;
-    typedef HashTable<label, label3, label3::Hash<>> procCoupleInfo;
+    typedef HashTable<label, label3> procCoupleInfo;
     procCoupleInfo procFaceToGlobalCell;
 
     forAll(allNbrProcIDs, proci)
diff --git a/src/sampling/probes/probes.C b/src/sampling/probes/probes.C
index bce791da89acf396b0efa19cda3bd028a8e0b5f0..63b0222af6e3d45b241e53df80966ac82da91b10 100644
--- a/src/sampling/probes/probes.C
+++ b/src/sampling/probes/probes.C
@@ -208,14 +208,13 @@ Foam::label Foam::probes::prepare()
         // Put in undecomposed case
         // (Note: gives problems for distributed data running)
 
-        fileName probeDir =
+        fileName probeDir
         (
             mesh_.time().globalPath()
           / functionObject::outputPrefix
           / probeSubDir
           / mesh_.time().timeName()
         );
-
         probeDir.clean();  // Remove unneeded ".."
 
         // ignore known fields, close streams for fields that no longer exist
diff --git a/src/sampling/sampledSet/shortestPath/shortestPathSet.C b/src/sampling/sampledSet/shortestPath/shortestPathSet.C
index cd5f1c85f7b9be46c78813bd84a55e07693d8be9..32ae511e8b623a0af079ef36846e1fcfa68b0b01 100644
--- a/src/sampling/sampledSet/shortestPath/shortestPathSet.C
+++ b/src/sampling/sampledSet/shortestPath/shortestPathSet.C
@@ -1316,13 +1316,13 @@ Foam::shortestPathSet::shortestPathSet
 {
     if (debug)
     {
-        fileName outputDir =
+        fileName outputDir
         (
             mesh.time().globalPath()
           / functionObject::outputPrefix
           / mesh.pointsInstance()
         );
-        outputDir.clean();
+        outputDir.clean();  // Remove unneeded ".."
 
         Info<< "shortestPathSet : Writing blocked faces to "
             << outputDir << endl;
diff --git a/src/sampling/surface/cutting/cuttingSurfaceBaseTemplates.C b/src/sampling/surface/cutting/cuttingSurfaceBaseTemplates.C
index 0f6a3bc6e7e284b2f06a3feaeb36e732f37dcbf4..5fd36c392b9d049fe5d3e4a52b22ba7beb671f30 100644
--- a/src/sampling/surface/cutting/cuttingSurfaceBaseTemplates.C
+++ b/src/sampling/surface/cutting/cuttingSurfaceBaseTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -78,7 +78,7 @@ void Foam::cuttingSurfaceBase::walkCellCuts
     Map<label> endPoints;
 
     // Hash of faces (face points) that are exactly on a cell face
-    HashSet<labelList, labelList::Hash<>> onCellFace;
+    HashSet<labelList> onCellFace;
 
 
     // Failure handling
diff --git a/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinLexer.L b/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinLexer.L
index a117a58a85ffeb34c2f9af28f5877fbb1f09642f..cf3ef8f3f3f46d4345754676f2f56bab980dc466 100644
--- a/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinLexer.L
+++ b/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinLexer.L
@@ -270,8 +270,6 @@ scalar RRreaction = RRcal;
 scalar allCommonT = 1000.0;
 
 word currentElementName;
-label currentElementIndex = 0;
-
 word currentSpecieName;
 label currentSpecieIndex = 0;
 label nSpecieElements = 0;
@@ -332,9 +330,8 @@ bool finishReaction = false;
         currentElementName = word::validate(YYText());
         correctElementName(currentElementName);
 
-        if (!elementIndices_.found(currentElementName))
+        if (elementIndices_.insert(currentElementName, elementNames_.size()))
         {
-            elementIndices_.insert(currentElementName, currentElementIndex++);
             elementNames_.append(currentElementName);
         }
         else
diff --git a/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinReader.C b/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinReader.C
index df310b37cd3dd5795008c59b949aa33e69f1384f..3605b16296e4460620a1b6d310770de2fe1e530c 100644
--- a/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinReader.C
+++ b/src/thermophysicalModels/reactionThermo/chemistryReaders/chemkinReader/chemkinReader.C
@@ -871,7 +871,7 @@ Foam::chemkinReader::chemkinReader
     fileName chemkinFile(thermoDict.get<fileName>("CHEMKINFile"));
     chemkinFile.expand();
 
-    fileName thermoFile = fileName::null;
+    fileName thermoFile;
     thermoDict.readIfPresent("CHEMKINThermoFile", thermoFile);
     thermoFile.expand();
 
@@ -887,7 +887,7 @@ Foam::chemkinReader::chemkinReader
             chemkinFile = relPath/chemkinFile;
         }
 
-        if (thermoFile != fileName::null && !thermoFile.isAbsolute())
+        if (!thermoFile.empty() && !thermoFile.isAbsolute())
         {
             thermoFile = relPath/thermoFile;
         }
diff --git a/src/thermophysicalModels/reactionThermo/chemistryReaders/foamChemistryReader/foamChemistryReader.C b/src/thermophysicalModels/reactionThermo/chemistryReaders/foamChemistryReader/foamChemistryReader.C
index 95163ebe4a61160d78f7e9ba416f65b397e90e53..db068fe1336f746006a70b2d46e7adc934922d49 100644
--- a/src/thermophysicalModels/reactionThermo/chemistryReaders/foamChemistryReader/foamChemistryReader.C
+++ b/src/thermophysicalModels/reactionThermo/chemistryReaders/foamChemistryReader/foamChemistryReader.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -61,9 +61,8 @@ void Foam::foamChemistryReader<ThermoType>::readSpeciesComposition()
 
     for (const word& elemName : elems)
     {
-        if (!elementIndices_.found(elemName))
+        if (elementIndices_.insert(elemName, elementNames_.size()))
         {
-            elementIndices_.insert(elemName, elementNames_.size());
             elementNames_.append(elemName);
         }
         else
diff --git a/tutorials/IO/dictionary/good-if.dict b/tutorials/IO/dictionary/good-if1.dict
similarity index 100%
rename from tutorials/IO/dictionary/good-if.dict
rename to tutorials/IO/dictionary/good-if1.dict
diff --git a/tutorials/IO/dictionary/good-if3.dict b/tutorials/IO/dictionary/good-if3.dict
new file mode 100644
index 0000000000000000000000000000000000000000..c859ba2245b3526577824e8fabe3aab7d9945488
--- /dev/null
+++ b/tutorials/IO/dictionary/good-if3.dict
@@ -0,0 +1,31 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v2012                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      dictionary;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// (label == scalar) ?
+#ifeq 0 0.0
+labelEqScalar true;
+#else
+labelEqScalar false; } // Provoke parse error if we see this branch
+#endif
+
+// (scalar == label) ?
+#ifeq 0.0 0
+scalarEqLabel true;
+#else
+scalarEqLabel false; } // Provoke parse error if we see this branch
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //