diff --git a/applications/utilities/miscellaneous/foamRestoreFields/Make/files b/applications/utilities/miscellaneous/foamRestoreFields/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..cf5d388c247e4ef7e6eda7766873c087e537d401
--- /dev/null
+++ b/applications/utilities/miscellaneous/foamRestoreFields/Make/files
@@ -0,0 +1,3 @@
+foamRestoreFields.C
+
+EXE = $(FOAM_APPBIN)/foamRestoreFields
diff --git a/applications/utilities/miscellaneous/foamRestoreFields/Make/options b/applications/utilities/miscellaneous/foamRestoreFields/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88
--- /dev/null
+++ b/applications/utilities/miscellaneous/foamRestoreFields/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = */
+/* EXE_LIBS = */
diff --git a/applications/utilities/miscellaneous/foamRestoreFields/foamRestoreFields.C b/applications/utilities/miscellaneous/foamRestoreFields/foamRestoreFields.C
new file mode 100644
index 0000000000000000000000000000000000000000..a8205f625b79992cd88808a7f5412bcb403e356c
--- /dev/null
+++ b/applications/utilities/miscellaneous/foamRestoreFields/foamRestoreFields.C
@@ -0,0 +1,399 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Application
+    foamRestoreFields
+
+Group
+    grpMiscUtilities
+
+Description
+    Restore field names by removing the ending.
+    The fields are selected automatically or can be specified as optional
+    command arguments.
+
+    The operation 'mean' renames files ending with 'Mean' and makes
+    a backup of existing names, using the '.orig' ending.
+
+    The operation 'orig' renames files ending with '.orig'.
+
+Usage
+    \b foamRestoreFields [OPTION]
+
+    Options:
+      - \par -method mean | orig
+        The renaming method.
+
+      - \par -processor
+        Use processor directories, taking information from processor0/
+
+      - \par -dry-run
+        Test without actually moving/renaming files.
+
+      - \par -verbose
+        Additional verbosity.
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "profiling.H"
+#include "timeSelector.H"
+#include "Enum.H"
+#include "Time.H"
+
+using namespace Foam;
+
+//- The known and support types of operations
+enum restoreMethod
+{
+    MEAN,
+    ORIG
+};
+
+
+static const Enum<restoreMethod> methodNames
+{
+    { restoreMethod::MEAN, "mean" },
+    { restoreMethod::ORIG, "orig" },
+};
+
+
+static const Enum<restoreMethod> methodEndings
+{
+    { restoreMethod::MEAN, "Mean" },
+    { restoreMethod::ORIG, ".orig" },
+};
+
+
+// Files in given directory at time instant
+wordList getFiles(const fileName& dir, const word& instance)
+{
+    return ListOps::create<word>
+    (
+        Foam::readDir(dir/instance, fileName::FILE),
+        [](const fileName& f){ return f.name(); }
+    );
+}
+
+
+// Command-line options: -dry-run, -verbose
+bool dryrun = false, verbose = false;
+
+
+// Use predefined method to walk the directory and rename the files.
+//
+// If no target names are specified, the existing files are scanned for
+// candidates.
+label restoreFields
+(
+    const restoreMethod method,
+    const fileName& dirName,
+    const wordHashSet& existingFiles,
+    const wordList& targetNames
+)
+{
+    // The file ending to search for.
+    const word ending(methodEndings[method]);
+
+    // The backup ending for existing (if any)
+    word bak;
+
+    switch (method)
+    {
+        case restoreMethod::MEAN:
+            bak = methodEndings[restoreMethod::ORIG];
+            break;
+
+        default:
+            break;
+    }
+
+    wordHashSet targets(targetNames);
+
+    if (targets.empty())
+    {
+        // No target names specified - scan existing files for candidates.
+
+        for (word f : existingFiles)    // Operate on a copy
+        {
+            // Eg, check for "UMean" and save as "U"
+            if (f.removeEnd(ending) && f.size())
+            {
+                targets.insert(f);
+            }
+        }
+    }
+
+    if (dryrun)
+    {
+        Info<< "dry-run: " << dirName << nl;
+    }
+    else if (verbose)
+    {
+        Info<< "directory " << dirName << nl;
+    }
+
+    // Count of files moved, including backups
+    label count = 0;
+
+    for (const word& dst : targets)
+    {
+        const word src(dst + ending);
+
+        if (!existingFiles.found(src))
+        {
+            continue;
+        }
+
+        if (bak.size() && existingFiles.found(dst))
+        {
+            if (dryrun || Foam::mv(dirName/dst, dirName/dst + bak))
+            {
+                Info<< "    mv  " << dst << "  " << word(dst + bak) << nl;
+                ++count;
+            }
+        }
+
+        if (dryrun || Foam::mv(dirName/src, dirName/dst))
+        {
+            Info<< "    mv  " << src << "  " << dst << nl;
+            ++count;
+        }
+    }
+
+    return count;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "Restore field names by removing the ending. The fields are "
+        "selected\n"
+        "automatically or can be specified as optional command arguments.\n"
+    );
+
+    profiling::disable(); // Disable profiling (and its output)
+    argList::noJobInfo();
+    argList::noFunctionObjects();
+    argList::addOption
+    (
+        "method",
+        "name",
+        "The restore method (mean|orig) [MANDATORY]. "
+        "With <mean> renames files ending with 'Mean' "
+        "(with backup of existing as '.orig'). "
+        "With <orig> renames files ending with '.orig'"
+    );
+    argList::addBoolOption
+    (
+        "processor",
+        "In serial mode use times from processor0/ directory, but operate on "
+        "processor\\d+ directories"
+    );
+    argList::addBoolOption
+    (
+        "dry-run",
+        "Report action without moving/renaming"
+    );
+    argList::addBoolOption
+    (
+        "verbose",
+        "Additional verbosity"
+    );
+
+    // Non-mandatory arguments
+    argList::addArgument("fieldName ... fieldName");
+
+    timeSelector::addOptions(true, true);
+    #include "setRootCaseNonMandatoryArgs.H"
+
+    dryrun = args.found("dry-run");
+    verbose = args.found("verbose");
+
+
+    // Construct time
+    // ~~~~~~~~~~~~~~
+
+    restoreMethod method = restoreMethod::ORIG;
+    {
+        word methodName;
+
+        if
+        (
+            args.readIfPresent("method", methodName)
+         && methodNames.found(methodName)
+        )
+        {
+            method = methodNames[methodName];
+        }
+        else
+        {
+            Info<< "Unspecified or unknown method name" << nl
+                << "Valid methods: "
+                << flatOutput(methodNames.sortedToc()) << nl
+                << "... stopping" << nl << nl;
+                return 1;
+        }
+    }
+
+    // Optional base or target field names (eg, 'U', 'T' etc)
+    wordList targetNames;
+    if (args.size() > 1)
+    {
+        targetNames.resize(args.size()-1);
+        wordHashSet uniq;
+
+        for (label argi=1; argi < args.size(); ++argi)
+        {
+            if (uniq.insert(args[argi]))
+            {
+                targetNames[uniq.size()-1] = args[argi];
+            }
+        }
+
+        targetNames.resize(uniq.size());
+
+        if (verbose)
+        {
+            Info<< nl
+                << "using method=" << methodNames[method] << nl
+                << "with fields " << flatOutput(targetNames) << nl;
+        }
+    }
+    else if (verbose)
+    {
+        Info<< nl
+            << "using method=" << methodNames[method] << nl
+            << "autodetect fields" << nl;
+    }
+
+
+    // Get times list from the master processor and subset based on
+    // command-line options
+
+    label nProcs = 0;
+    instantList times;
+
+    if (args.found("processor") && !Pstream::parRun())
+    {
+        // Determine the processor count
+        nProcs = fileHandler().nProcs(args.path());
+
+        if (!nProcs)
+        {
+            FatalErrorInFunction
+                << "No processor* directories found"
+                << exit(FatalError);
+        }
+
+        // Obtain times from "processor0/" only
+        times = timeSelector::select
+        (
+            Time
+            (
+                Time::controlDictName,
+                args.rootPath(),
+                args.caseName()/"processor0"
+            ).times(),
+            args
+        );
+    }
+    else
+    {
+        if (Pstream::master())
+        {
+            times = timeSelector::select
+            (
+                Time(Time::controlDictName, args).times(),
+                args
+            );
+        }
+
+        Pstream::scatter(times);
+    }
+
+
+    if (times.empty())
+    {
+        Info<< "no times selected" << nl;
+    }
+
+    for (const instant& inst : times)
+    {
+        Info<< "\nTime = " << inst.name() << nl;
+
+        label count = 0;
+
+        if (nProcs)
+        {
+            const wordHashSet files
+            (
+                getFiles(args.path()/"processor0", inst.name())
+            );
+
+            for (label proci=0; proci < nProcs; ++proci)
+            {
+                count += restoreFields
+                (
+                    method,
+                    args.path()/("processor" + Foam::name(proci))/inst.name(),
+                    files,
+                    targetNames
+                );
+            }
+        }
+        else
+        {
+            wordList files;
+            if (Pstream::master())
+            {
+                files = getFiles(args.path(), inst.name());
+            }
+            Pstream::scatter(files);
+
+            count += restoreFields
+            (
+                method,
+                args.path()/inst.name(),
+                wordHashSet(files),
+                targetNames
+            );
+        }
+
+        if (dryrun)
+        {
+            Info<< "dry-run: ";
+        }
+        Info<< "moved " << count << " files" << nl;
+    }
+
+    Info<< "\nEnd\n" << endl;
+    return 0;
+}
+
+
+// ************************************************************************* //