diff --git a/src/finiteVolume/Make/files b/src/finiteVolume/Make/files
index db88df52ae7abd5bd5f9f49e836c00cc74db6c03..e7023dea211fa34aad9980e68153641fa7c9545d 100644
--- a/src/finiteVolume/Make/files
+++ b/src/finiteVolume/Make/files
@@ -425,6 +425,7 @@ $(coupling)/externalFileCoupler.C
 
 solutionControl = $(general)/solutionControl
 $(solutionControl)/solutionControl/solutionControl.C
+$(solutionControl)/loopControl/loopControl.C
 $(solutionControl)/simpleControl/simpleControl.C
 $(solutionControl)/pimpleControl/pimpleControl.C
 $(solutionControl)/pisoControl/pisoControl.C
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/loopControl/fvSolution b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/fvSolution
new file mode 100644
index 0000000000000000000000000000000000000000..19d8b0fe0b132a7cad73c67ba050df55c0c0d164
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/fvSolution
@@ -0,0 +1,47 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  plus                                  |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+SIMPLE
+{
+    energyCoupling
+    {
+        // (Max) number of loops
+        iterations      200;
+
+        // The interval to execute onLoop function-objects
+        interval        0;
+
+        // Convergence criteria to terminate loop
+        convergence
+        {
+            "h"         1e-3;
+        }
+
+        // Names of function objects to fire with execute(int) when looping
+        onLoop          ( );
+
+        // Names of function objects to fire with execute(int) when converged
+        onConverged     ( );
+
+        // Names of function objects to fire with execute(int) when loop ends
+        // without convergence
+        onEnd           ( );
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.C b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.C
new file mode 100644
index 0000000000000000000000000000000000000000..f4531243686f3f9e9513f8745c7c0beffdfddc94
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.C
@@ -0,0 +1,280 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "loopControl.H"
+#include "fvSolution.H"
+#include "wordRes.H"
+#include "solutionControl.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::loopControl::clear()
+{
+    total_ = 0;
+    interval_ = 0;
+
+    convergenceDict_.clear();
+    onLoop_.clear();
+    onConverged_.clear();
+    onEnd_.clear();
+
+    converged_ = false;
+}
+
+
+void Foam::loopControl::read(const dictionary& dict)
+{
+    clear();
+
+    bool enabled = dict.lookupOrDefault("enabled", true);
+
+    if (enabled)
+    {
+        scalar timeStart;
+        if (dict.readIfPresent("timeStart", timeStart))
+        {
+            timeStart = time_.userTimeToTime(timeStart);
+
+            enabled =
+            (
+                enabled
+             && time_.value() >= (timeStart - 0.5*time_.deltaTValue())
+            );
+        }
+
+        scalar timeEnd;
+        if (dict.readIfPresent("timeEnd", timeEnd))
+        {
+            timeEnd = time_.userTimeToTime(timeEnd);
+
+            enabled =
+            (
+                enabled
+             && time_.value() <= (timeEnd + 0.5*time_.deltaTValue())
+            );
+        }
+    }
+
+    if (!enabled)
+    {
+        return;
+    }
+
+    dict.readIfPresent("iterations", total_);
+    dict.readIfPresent("interval", interval_);
+
+    convergenceDict_ = dict.subOrEmptyDict("convergence");
+
+    dict.readIfPresent("onLoop", onLoop_);
+    dict.readIfPresent("onConverged", onConverged_);
+    dict.readIfPresent("onEnd", onEnd_);
+}
+
+
+bool Foam::loopControl::checkConverged() const
+{
+    if (convergenceDict_.empty())
+    {
+        return false;
+    }
+
+    HashTable<const fvMesh*> meshes = time_.lookupClass<const fvMesh>();
+
+    bool achieved = true;
+    bool checked = false; // safety that some checks were indeed performed
+
+    forAllConstIters(meshes, meshIter)
+    {
+        const fvMesh& regionMesh = *(meshIter.object());
+
+        const dictionary& solverDict = regionMesh.solverPerformanceDict();
+
+        forAllConstIters(solverDict, iter)
+        {
+            const entry& dataDictEntry = *iter;
+
+            const word& variableName = dataDictEntry.keyword();
+
+            const scalar absTol =
+                convergenceDict_.lookupOrDefault<scalar>(variableName, -1);
+
+            if (absTol > 0)
+            {
+                // Treat like a SIMPLE control
+
+                Pair<scalar> residuals =
+                    solutionControl::maxResidual
+                    (
+                        regionMesh,
+                        dataDictEntry
+                    );
+
+                checked = true;
+                achieved = achieved && (residuals.first() < absTol);
+            }
+        }
+    }
+
+    return checked && achieved;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::loopControl::loopControl
+(
+    Time& runTime,
+    const label nCycles,
+    const word& loopName
+)
+:
+    subLoopTime(runTime, nCycles),
+    name_(loopName),
+    interval_(0),
+    convergenceDict_(),
+    onLoop_(),
+    onConverged_(),
+    onEnd_(),
+    converged_(false)
+{}
+
+
+Foam::loopControl::loopControl
+(
+    Time& runTime,
+    const dictionary& algorithmDict,
+    const word& dictName
+)
+:
+    loopControl(runTime, 0, dictName)
+{
+    // The loop sub-dictionary
+    const dictionary* dictptr = algorithmDict.subDictPtr(dictName);
+
+    if (dictptr)
+    {
+        // Info<< dictName << *dictptr << endl;
+        read(*dictptr);
+    }
+}
+
+
+Foam::loopControl::loopControl
+(
+    Time& runTime,
+    const word& algorithmName,
+    const word& dictName
+)
+:
+    loopControl(runTime, 0, dictName)
+{
+    fvSolution fvsol(time_);
+
+    // Eg, PIMPLE or SIMPLE from <system/fvSolution>
+    const dictionary* dictptr =
+        fvsol.solutionDict().subDictPtr(algorithmName);
+
+    if (dictptr)
+    {
+        // The loop sub-dictionary
+        dictptr = dictptr->subDictPtr(dictName);
+
+        if (dictptr)
+        {
+            // Info<< dictName << *dictptr << endl;
+            read(*dictptr);
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::loopControl::~loopControl()
+{
+    stop();
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::loopControl::loop()
+{
+    bool active = (index_ < total_);   // as per status()
+
+    if (active)
+    {
+        operator++();
+
+        converged_ = checkConverged();
+
+        if (converged_)
+        {
+            time_.functionObjects().execute(onConverged_, index_);
+            stop();
+            return false;
+        }
+        else if
+        (
+            interval_ && !(index_ % interval_)
+         && !onLoop_.empty()
+        )
+        {
+            time_.functionObjects().execute(onLoop_, index_);
+        }
+    }
+    else if (index_)
+    {
+        // Not active, the loop condition has now exiting on the last subloop
+
+        if (!converged_ && !onEnd_.empty())
+        {
+            time_.functionObjects().execute(onEnd_, index_);
+        }
+    }
+
+    return active;
+}
+
+
+// * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * * //
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const loopControl& ctrl)
+{
+    os << ctrl.name() << ": ";
+    if (ctrl.nCycles() && ctrl.index() <= ctrl.nCycles())
+    {
+        os << ctrl.index() << '/' << ctrl.nCycles();
+    }
+    else
+    {
+        os << "off";
+    }
+
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.H b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.H
new file mode 100644
index 0000000000000000000000000000000000000000..ced72d57e3035d5d644c7d548062d47f65622e51
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/solutionControl/loopControl/loopControl.H
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::loopControl
+
+Description
+    A class for managing arbitrary loops with the ability to invoke
+    function object execution.
+
+Usage
+    Examples of function object specification:
+    \verbatim
+    SIMPLE
+    {
+        energyCoupling
+        {
+            iterations  100;
+            onLoop      ();
+            onConverged ( externalCoupled  "loopThings.*" );
+
+            convergence
+            {
+                "h"     1e-3;
+            }
+        }
+    }
+
+    Where the loop entries comprise:
+    \table
+        Property    | Description           | Required      | Default
+        enabled     | active/deactive loop  | no            | true
+        iteration   | times to loop         | no            | 0
+        timeStart   | begin time for loop activation  | no  | -VGREAT
+        timeEnd     | end time of loop activation     | no  | VGREAT
+        interval    | sub-interval to execute onLoop  | no  | 0
+        onLoop      | function object names to call at executeInterval | no
+        onConverged | function object names to call when converged | no
+        onEnd       | function object names to call when loop ends | no
+        convergence | dictionary of convergence values to check | no
+    \endtable
+
+    The function object names listed by \c onLoop, \c onConverged, \c onEnd
+    must implement an \c execute(int) method.
+    If the time controls \c timeStart or \c timeEnd are used for the loop,
+    these values are only inspected upon creation, not during execution.
+
+SeeAlso
+    fvSolution
+
+SourceFiles
+    loopControl.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef loopControl_H
+#define loopControl_H
+
+#include "subLoopTime.H"
+#include "dictionary.H"
+#include "wordReList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class loopControl Declaration
+\*---------------------------------------------------------------------------*/
+
+class loopControl
+:
+    public subLoopTime
+{
+    // Private Member Functions
+
+        //- Reset
+        void clear();
+
+        //- Read settings from dictionary
+        void read(const dictionary& dict);
+
+        //- Execute specified function names
+        bool checkConverged() const;
+
+        //- Disallow default bitwise copy construct
+        loopControl(const loopControl&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const loopControl&) = delete;
+
+protected:
+
+    // Protected data
+
+        //- Name of the loop control (the lookup dictionary name).
+        word name_;
+
+        //- The interval to execute onLoop function-objects
+        label interval_;
+
+        //- Dictionary for checking convergence (all regions)
+        dictionary convergenceDict_;
+
+        //- Function object names to fire during the loop (at executeInterval)
+        List<wordRe> onLoop_;
+
+        //- Function object names to fire on convergence
+        List<wordRe> onConverged_;
+
+        //- Function object names to fire when the loop exits without
+        //- convergence
+        List<wordRe> onEnd_;
+
+        //- Convergence tests passed
+        bool converged_;
+
+public:
+
+    // Constructors
+
+        //- Construct from time with fixed number of cycles
+        //  \param runTime  the top-level time
+        //  \param nCycles  the number of times to loop
+        //  \param loopName  the name of the loop
+        loopControl
+        (
+            Time& runTime,
+            const label nCycles,
+            const word& dictName = "loop"
+        );
+
+        //- Construct from fvSolution dictionary based on time and the name
+        //- of the controlling algorithm
+        //  \param runTime  the top-level time
+        //  \param algorithmName the name of the fvSolution dictionary,
+        //      typically PIMPLE or SIMPLE
+        //  \param dictName  the name of the control dictionary
+        loopControl
+        (
+            Time& runTime,
+            const word& algorithmName,
+            const word& dictName = "loop"
+        );
+
+        //- Construct from fvSolution dictionary based on time and the name
+        //- of the controlling algorithm
+        //  \param runTime  the top-level time
+        //  \param algorithmDict the fvSolution algorithm dictionary,
+        //      typically PIMPLE or SIMPLE
+        //  \param dictName  the name of the control dictionary
+        loopControl
+        (
+            Time& runTime,
+            const dictionary& algorithmDict,
+            const word& dictName = "loop"
+        );
+
+
+    //- Destructor
+    ~loopControl();
+
+
+    // Member Functions
+
+        //- Name of the loop control
+        inline const word& name() const
+        {
+            return name_;
+        }
+
+        //- The interval to execute onLoop function-objects
+        inline label interval() const
+        {
+            return interval_;
+        }
+
+        //- True if looping is active, increments the index and executes
+        //- the onLoop and onConverged functions.
+        //  Example usage,
+        //  \code
+        //      while (control.loop())
+        //      {
+        //          solve;
+        //      }
+        //  \endcode
+        bool loop();
+
+
+    // IOstream operators
+
+        //- Write name and state (on/off, index/total) to Ostream
+        friend Ostream& operator<<(Ostream& os, const loopControl& ctrl);
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //