diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files
index 649176d1f4..580d9a3340 100644
--- a/src/functionObjects/field/Make/files
+++ b/src/functionObjects/field/Make/files
@@ -118,4 +118,6 @@ stabilityBlendingFactor/stabilityBlendingFactor.C
interfaceHeight/interfaceHeight.C
+STDMD/STDMD.C
+
LIB = $(FOAM_LIBBIN)/libfieldFunctionObjects
diff --git a/src/functionObjects/field/Make/options b/src/functionObjects/field/Make/options
index c24acfb632..5d3d5f11d6 100644
--- a/src/functionObjects/field/Make/options
+++ b/src/functionObjects/field/Make/options
@@ -1,4 +1,5 @@
EXE_INC = \
+ -I$(LIB_SRC)/OpenFOAM/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/fileFormats/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude \
@@ -22,6 +23,7 @@ EXE_INC = \
-I$(LIB_SRC)/phaseSystemModels/reactingEulerFoam/phaseSystems/lnInclude
LIB_LIBS = \
+ -lOpenFOAM \
-lfiniteVolume \
-lfileFormats \
-lsurfMesh \
diff --git a/src/functionObjects/field/STDMD/STDMD.C b/src/functionObjects/field/STDMD/STDMD.C
new file mode 100644
index 0000000000..d550a4cefb
--- /dev/null
+++ b/src/functionObjects/field/STDMD/STDMD.C
@@ -0,0 +1,1280 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2020 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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "STDMD.H"
+#include "addToRunTimeSelectionTable.H"
+
+using namespace Foam::constant::mathematical;
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+ defineTypeNameAndDebug(STDMD, 0);
+ addToRunTimeSelectionTable(functionObject, STDMD, dictionary);
+}
+}
+
+
+const Foam::Enum
+<
+ Foam::functionObjects::STDMD::modeSorterType
+>
+Foam::functionObjects::STDMD::modeSorterTypeNames
+({
+ { modeSorterType::KIEWAT , "kiewat" },
+ { modeSorterType::KOU_ZHANG , "kouZhang" },
+ { modeSorterType::FIRST_SNAPSHOT, "firstSnap" }
+});
+
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+Foam::scalar Foam::functionObjects::STDMD::parnorm
+(
+ const RMatrix& colVector
+) const
+{
+ #ifdef FULLDEBUG
+ // Check if the given RectangularMatrix is effectively a column vector
+ if (colVector.n() != 1)
+ {
+ FatalErrorInFunction
+ << " # Input matrix is not a column vector. #"
+ << abort(FatalError);
+ }
+ #endif
+ const bool noSqrt = true;
+ scalar result(colVector.columnNorm(0, noSqrt));
+ reduce(result, sumOp());
+
+ // Heuristic addition to avoid very small or zero norm
+ return max(SMALL, Foam::sqrt(result));
+}
+
+
+void Foam::functionObjects::STDMD::snapshot()
+{
+ bool processed = false;
+ processed = processed || getSnapshotType();
+ processed = processed || getSnapshotType();
+ processed = processed || getSnapshotType();
+ processed = processed || getSnapshotType();
+ processed = processed || getSnapshotType();
+
+ if (!processed)
+ {
+ FatalErrorInFunction
+ << " # Unknown type of input field during snapshot loading = "
+ << fieldName_ << " #" << nl
+ << " # Do you execute required functionObjects "
+ << "before executing STDMD, e.g. mapFields?"
+ << abort(FatalError);
+ }
+}
+
+
+void Foam::functionObjects::STDMD::init()
+{
+ bool processed = false;
+ processed = processed || getComps();
+ processed = processed || getComps();
+ processed = processed || getComps();
+ processed = processed || getComps();
+ processed = processed || getComps();
+
+ if (!processed)
+ {
+ FatalErrorInFunction
+ << " # Unknown type of input field during initialisation = "
+ << fieldName_ << " #" << nl
+ << " # Do you execute required functionObjects "
+ << "before executing STDMD, e.g. mapFields?"
+ << abort(FatalError);
+ }
+
+ nSnap_ = nComps_*mesh_.nCells();
+
+ if (nSnap_ <= 0)
+ {
+ FatalErrorInFunction
+ << " # Zero-size input field = " << fieldName_ << " #"
+ << abort(FatalError);
+ }
+
+ currIndex_ = 0;
+ zNorm_ = 0;
+ ezNorm_ = 0;
+
+ z_ = RMatrix(2*nSnap_, 1, Zero);
+ ez_ = z_;
+ X1_ = RMatrix(nSnap_, 1, Zero);
+ Qz_ = z_;
+ Gz_ = SMatrix(1);
+
+ RxInv_.clear();
+ Ap_.clear();
+ oEVecs_.clear();
+ oEVals_.clear();
+ oAmps_.clear();
+ oFreqs_.clear();
+ iFreqs_.clear();
+ oMags_.clear();
+ iMags_.clear();
+
+ initialised_ = true;
+}
+
+
+void Foam::functionObjects::STDMD::initBasis()
+{
+ std::copy(z_.cbegin(), z_.cbegin() + nSnap_, X1_.begin());
+
+ zNorm_ = parnorm(z_);
+ Qz_ = z_/zNorm_;
+ Gz_(0,0) = sqr(zNorm_);
+}
+
+
+void Foam::functionObjects::STDMD::GramSchmidt()
+{
+ ez_ = z_;
+ RMatrix dz(Qz_.n(), 1, Zero);
+
+ for (label i = 0; i < nGramSchmidt_; ++i)
+ {
+ dz = Qz_ & ez_;
+ reduce(dz, sumOp());
+ ez_ -= Qz_*dz;
+ }
+}
+
+
+void Foam::functionObjects::STDMD::expandBasis()
+{
+ Log<< tab << "# " << name() << ":"
+ << " Expanding orthonormal basis for field = " << fieldName_ << " #"
+ << endl;
+
+ // Stack a column (ez_/ezNorm_) to Qz_
+ Qz_.resize(Qz_.m(), Qz_.n() + 1);
+ Qz_.subColumn(Qz_.n() - 1) = ez_/ezNorm_;
+
+ // Stack a row (Zero) and column (Zero) to Gz_
+ Gz_.resize(Gz_.m() + 1);
+}
+
+
+void Foam::functionObjects::STDMD::updateGz()
+{
+ RMatrix zTilde(Qz_ & z_);
+ reduce(zTilde, sumOp());
+
+ const SMatrix zTildes(zTilde^zTilde);
+
+ Gz_ += zTildes;
+}
+
+
+void Foam::functionObjects::STDMD::compressBasis()
+{
+ Log<< tab << "# " << name() << ":"
+ << " Compressing orthonormal basis for field = " << fieldName_ << " #"
+ << endl;
+
+ RMatrix qz;
+
+ if (Pstream::master())
+ {
+ const bool symmetric = true;
+ const EigenMatrix EM(Gz_, symmetric);
+ const SquareMatrix& EVecs = EM.EVecs();
+ DiagonalMatrix EVals(EM.EValsRe());
+
+ if (testEigen_)
+ {
+ testEigenvalues(Gz_, EVals);
+ testEigenvectors(Gz_, EVals, EVecs);
+ }
+
+ // Sort eigenvalues in descending order, and track indices
+ const auto descend = [&](scalar a, scalar b){ return a > b; };
+ const List