diff --git a/applications/test/namedDictionary/Make/files b/applications/test/namedDictionary/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..7877feb85bf77568ce84bb30d351cf881c95eff9
--- /dev/null
+++ b/applications/test/namedDictionary/Make/files
@@ -0,0 +1,3 @@
+Test-namedDictionary.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-namedDictionary
diff --git a/applications/test/namedDictionary/Make/options b/applications/test/namedDictionary/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88
--- /dev/null
+++ b/applications/test/namedDictionary/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = */
+/* EXE_LIBS = */
diff --git a/applications/test/namedDictionary/Test-namedDictionary.C b/applications/test/namedDictionary/Test-namedDictionary.C
new file mode 100644
index 0000000000000000000000000000000000000000..fc7e4185473e34ac0ac63ce2b48db21d6ee5a1c2
--- /dev/null
+++ b/applications/test/namedDictionary/Test-namedDictionary.C
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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-namedDictionary
+
+Description
+    Test handling of keyType/dictionary
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "IOstreams.H"
+#include "IOobject.H"
+#include "IFstream.H"
+#include "namedDictionary.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+//  Main program:
+
+int main(int argc, char *argv[])
+{
+    argList::noBanner();
+    argList::noParallel();
+    argList::addArgument("file1 .. fileN");
+    argList args(argc, argv, false, true);
+
+    if (args.size() <= 1)
+    {
+        InfoErr<< "Provide a file or files to test" << nl;
+    }
+    else
+    {
+        for (label argi=1; argi < args.size(); ++argi)
+        {
+            const auto dictFile = args.get<fileName>(argi);
+            IFstream ifs(dictFile);
+
+            dictionary dict(ifs);
+
+            IOobject::writeDivider(Info) << nl;
+
+            for (const entry& dEntry : dict)
+            {
+                if (!dEntry.isStream())
+                {
+                    continue;
+                }
+                Info<< "input: " << dEntry << nl;
+                List<namedDictionary> list(dEntry.stream());
+                Info<< "list: " << list << nl;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/namedDictionary/testDict1 b/applications/test/namedDictionary/testDict1
new file mode 100644
index 0000000000000000000000000000000000000000..99c1960109f8964aaf3fb6331e1bed2b02df3c09
--- /dev/null
+++ b/applications/test/namedDictionary/testDict1
@@ -0,0 +1,83 @@
+/*--------------------------------*- 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;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+entry1
+(
+    value1
+    value2 ;  // spurious trailing ';' is removed
+    this { correct true; }
+
+    { }  // Empty everything == ignore
+
+    { anonymous true; }
+);
+
+
+actions1
+(
+    starting
+    {
+        name    self;
+        type    faceSet;
+        action  new;
+        source  something;
+    }
+
+    {
+        name    self;
+        type    faceSet;
+        action  subset;
+        source  something;
+    }
+);
+
+
+actions2
+(
+    {
+        name    self;
+        type    faceSet;
+        action  new;
+        source  something;
+    }
+
+    {
+        name    self;
+        type    faceSet;
+        action  subset;
+        source  something;
+    }
+);
+
+actions3
+(
+    {
+        name    self;
+        type    faceSet;
+        action  new;
+        source  something;
+    }
+
+    subset
+    {
+        name    self;
+        type    faceSet;
+        action  subset;
+        source  something;
+    }
+);
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/applications/utilities/mesh/manipulation/topoSet/topoSet.C b/applications/utilities/mesh/manipulation/topoSet/topoSet.C
index 162fd3fd1043bf80184cb28a1e723b931bcc5b2f..6e590a82bb05dc9aff1a5dbe0120e413c7574aff 100644
--- a/applications/utilities/mesh/manipulation/topoSet/topoSet.C
+++ b/applications/utilities/mesh/manipulation/topoSet/topoSet.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -47,6 +47,7 @@ Description
 #include "faceZoneSet.H"
 #include "pointZoneSet.H"
 #include "IOdictionary.H"
+#include "namedDictionary.H"
 
 using namespace Foam;
 
@@ -237,7 +238,7 @@ int main(int argc, char *argv[])
     IOdictionary topoSetDict(dictIO);
 
     // Read set construct info from dictionary
-    PtrList<dictionary> actions(topoSetDict.lookup("actions"));
+    List<namedDictionary> actionEntries(topoSetDict.lookup("actions"));
 
     forAll(timeDirs, timeI)
     {
@@ -248,8 +249,13 @@ int main(int argc, char *argv[])
         meshReadUpdate(mesh);
 
         // Execute all actions
-        for (const dictionary& dict : actions)
+        for (const namedDictionary& actionEntry : actionEntries)
         {
+            const dictionary& dict = actionEntry.dict();
+            if (dict.empty())
+            {
+                continue;
+            }
             const word setName(dict.get<word>("name"));
             const word setType(dict.get<word>("type"));
 
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index beeaf6afb416fdbea550b204a3780484418da211..cd25b075df91d1a905601b636067864a7fe3fe2a 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -276,7 +276,9 @@ $(dictionary)/dictionaryIO.C
 $(dictionary)/dictionarySearch.C
 $(dictionary)/dictionaryCompat.C
 
+/* Additional helpers */
 $(dictionary)/dictionaryContent/dictionaryContent.C
+$(dictionary)/namedDictionary/namedDictionary.C
 
 entry = $(dictionary)/entry
 $(entry)/entry.C
diff --git a/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.C b/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.C
new file mode 100644
index 0000000000000000000000000000000000000000..3bbd6608fc28531a2f58f87e9bb465181ff00cd0
--- /dev/null
+++ b/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.C
@@ -0,0 +1,122 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "namedDictionary.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::namedDictionary::namedDictionary()
+:
+    Tuple2<keyType, dictionary>()
+{}
+
+
+Foam::namedDictionary::namedDictionary(Istream& is)
+{
+    is >> *this;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::namedDictionary::clear()
+{
+    first().clear();
+    second().clear();
+}
+
+
+bool Foam::namedDictionary::empty() const noexcept
+{
+    return (first().empty() && second().empty());
+}
+
+
+// * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, namedDictionary& obj)
+{
+    obj.clear();
+
+    // Three possible inputs:
+    // - key
+    // - key { ... }
+    // - { ... }
+
+    // Minor consistency with primitiveEntry, also accept the following:
+    // - key ;
+
+    token tok(is);
+    is.putBack(tok);
+
+    if (!tok.isPunctuation(token::BEGIN_BLOCK))
+    {
+        is >> obj.keyword();
+        is >> tok;
+
+        // Discards possible trailing ';'
+        if (!tok.isPunctuation(token::END_STATEMENT))
+        {
+            is.putBack(tok);
+        }
+    }
+
+    if (tok.isPunctuation(token::BEGIN_BLOCK))
+    {
+        obj.dict().read(is);
+    }
+
+    is.check(FUNCTION_NAME);
+    return is;
+}
+
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const namedDictionary& obj)
+{
+    // Three possible outputs:
+    // - key
+    // - key { ... }
+    // - { ... }
+    // No distinction between a missing and an empty dictionary
+
+    if (obj.keyword().empty() || !obj.dict().empty())
+    {
+        // Never allow empty output.
+        // Otherwise cannot re-read for streaming
+        obj.dict().writeEntry(obj.keyword(), os);
+    }
+    else
+    {
+        os << obj.keyword();
+    }
+
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.H b/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.H
new file mode 100644
index 0000000000000000000000000000000000000000..eb6c18b2c53d51a54a75744b48adf53ddd677ef8
--- /dev/null
+++ b/src/OpenFOAM/db/dictionary/namedDictionary/namedDictionary.H
@@ -0,0 +1,154 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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/>.
+
+Class
+    Foam::namedDictionary
+
+Description
+    A tuple of keyType and dictionary, which can be used when reading
+    named or unnamed dictionary entries or simply a name.
+
+    For example,
+    \verbatim
+    fields
+    (
+        U
+        T { relax  false; }
+    );
+    \endverbatim
+
+    In can also be used in situations where an individual dictionary entry
+    should be read.
+    \verbatim
+    actions
+    (
+        testing { action new; ... }   // An action with a name
+        { action subset;  }           // Unnamed action
+    );
+    \endverbatim
+    Normal dictionary reading would fail for this type of input since the
+    leading 'testing' keyword would cause the entire content to be considered
+    a single dictionary.
+
+Note
+    No distinction currently made between a missing and an empty dictionary.
+
+SourceFiles
+    namedDictionary.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef namedDictionary_H
+#define namedDictionary_H
+
+#include "dictionary.H"
+#include "Tuple2.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class namedDictionary;
+Istream& operator>>(Istream&, namedDictionary&);
+Ostream& operator<<(Ostream&, const namedDictionary&);
+
+/*---------------------------------------------------------------------------*\
+                       Class namedDictionary Declaration
+\*---------------------------------------------------------------------------*/
+
+class namedDictionary
+:
+    public Tuple2<keyType, dictionary>
+{
+public:
+
+    // Constructors
+
+        //- Inherit constructors
+        using Tuple2<keyType, dictionary>::Tuple2;
+
+        //- Default construct
+        namedDictionary();
+
+        //- Construct from Istream
+        explicit namedDictionary(Istream& is);
+
+
+    //- Destructor
+    ~namedDictionary() = default;
+
+
+    // Member Functions
+
+        //- Clear keyword and dictionary
+        void clear();
+
+        //- Empty if both keyword and dictionary are empty
+        bool empty() const noexcept;
+
+        //- Return keyword
+        const keyType& keyword() const noexcept
+        {
+            return first();
+        }
+
+        //- Return non-const access to keyword
+        keyType& keyword() noexcept
+        {
+            return first();
+        }
+
+        //- Read-access to the dictionay
+        const dictionary& dict() const noexcept
+        {
+            return second();
+        }
+
+        //- Write access to the dictionay
+        dictionary& dict() noexcept
+        {
+            return second();
+        }
+
+
+    // IOstream Operators
+
+        friend Istream& operator>>(Istream&, namedDictionary&);
+        friend Ostream& operator<<(Ostream&, const namedDictionary&);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/tutorials/heatTransfer/chtMultiRegionTwoPhaseEulerFoam/solidQuenching2D/system/topoSetDict b/tutorials/heatTransfer/chtMultiRegionTwoPhaseEulerFoam/solidQuenching2D/system/topoSetDict
index 5fdf7ef3900335b21caa77a28558e21d0189b9b7..5dd0e8e80011aab90291ece1623f065ff45357c7 100644
--- a/tutorials/heatTransfer/chtMultiRegionTwoPhaseEulerFoam/solidQuenching2D/system/topoSetDict
+++ b/tutorials/heatTransfer/chtMultiRegionTwoPhaseEulerFoam/solidQuenching2D/system/topoSetDict
@@ -17,16 +17,13 @@ FoamFile
 
 actions
 (
-    // Heater
+    heater
     {
         name    heaterCellSet;
         type    cellSet;
         action  new;
         source  boxToCell;
-        sourceInfo
-        {
-            box (-0.01  29e-3 -1 )(4.77e-3 70e-3 1);
-        }
+        box     (-0.01 29e-3 -1) (4.77e-3 70e-3 1);
     }
 
     {
@@ -34,10 +31,7 @@ actions
         type    cellZoneSet;
         action  new;
         source  setToCellZone;
-        sourceInfo
-        {
-            set heaterCellSet;
-        }
+        set     heaterCellSet;
     }
 
     {
@@ -45,10 +39,7 @@ actions
         type    cellSet;
         action  new;
         source  cellToCell;
-        sourceInfo
-        {
-            set heaterCellSet;
-        }
+        set     heaterCellSet;
     }
 
     {
@@ -62,10 +53,7 @@ actions
         type    cellZoneSet;
         action  new;
         source  setToCellZone;
-        sourceInfo
-        {
-            set bottomWaterCellSet;
-        }
+        set     bottomWaterCellSet;
     }
 );
 
diff --git a/tutorials/multiphase/reactingTwoPhaseEulerFoam/RAS/bubbleColumnPolydisperse/system/topoSetDict b/tutorials/multiphase/reactingTwoPhaseEulerFoam/RAS/bubbleColumnPolydisperse/system/topoSetDict
index 30f3403fcd5ab1c8911fe9892fdf57064bfe3c66..5b56ee3a21c4833a7ec7873cfdd048b5b91de783 100644
--- a/tutorials/multiphase/reactingTwoPhaseEulerFoam/RAS/bubbleColumnPolydisperse/system/topoSetDict
+++ b/tutorials/multiphase/reactingTwoPhaseEulerFoam/RAS/bubbleColumnPolydisperse/system/topoSetDict
@@ -16,29 +16,25 @@ FoamFile
 
 actions
 (
+    pickCells
     {
         name    zone;
         type    cellSet;
         action  new;
         source  boxToCell;
-        sourceInfo
-        {
-           boxes
-           (
-                (0 0.4 0) (0.15 0.5 0.1)
-           );
-        }
+        boxes
+        (
+            (0 0.4 0) (0.15 0.5 0.1)
+        );
     }
 
+    convertToZone
     {
         name    zone;
         type    cellZoneSet;
         action  new;
         source  setToCellZone;
-        sourceInfo
-        {
-           set  zone;
-        }
+        set     zone;
     }
 );