From c9d1f741ce392acdab730544e7800c3937b0df12 Mon Sep 17 00:00:00 2001
From: Kutalmis Bercin <kutalmis.bercin@esi-group.com>
Date: Mon, 30 Oct 2023 10:12:15 +0000
Subject: [PATCH] ENH: createROMfields: new reduced-order model utility to
 reconstruct fields

---
 .../miscellaneous/createROMfields/Make/files  |   7 +
 .../createROMfields/Make/options              |   7 +
 .../createROMfields/ROMmodels/DMD/DMD.C       | 178 ++++++++++++++
 .../createROMfields/ROMmodels/DMD/DMD.H       | 232 ++++++++++++++++++
 .../createROMfields/ROMmodels/DMD/DMDImpl.C   | 106 ++++++++
 .../ROMmodels/ROMmodel/ROMmodel.C             |  59 +++++
 .../ROMmodels/ROMmodel/ROMmodel.H             | 163 ++++++++++++
 .../ROMmodels/ROMmodel/ROMmodelNew.C          |  60 +++++
 .../createROMfields/createROMfields.C         | 141 +++++++++++
 .../createROMfields/readFields.H              | 189 ++++++++++++++
 etc/caseDicts/annotated/ROMfieldsDict         |  59 +++++
 11 files changed, 1201 insertions(+)
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/Make/files
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/Make/options
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.C
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.H
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMDImpl.C
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.C
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.H
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodelNew.C
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/createROMfields.C
 create mode 100644 applications/utilities/postProcessing/miscellaneous/createROMfields/readFields.H
 create mode 100644 etc/caseDicts/annotated/ROMfieldsDict

diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/files b/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/files
new file mode 100644
index 00000000000..621a2bb0673
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/files
@@ -0,0 +1,7 @@
+ROMmodels/ROMmodel/ROMmodel.C
+ROMmodels/ROMmodel/ROMmodelNew.C
+ROMmodels/DMD/DMD.C
+
+createROMfields.C
+
+EXE = $(FOAM_APPBIN)/createROMfields
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/options b/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/options
new file mode 100644
index 00000000000..d27c95d033d
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/Make/options
@@ -0,0 +1,7 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lmeshTools
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.C b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.C
new file mode 100644
index 00000000000..5f20cb392ab
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.C
@@ -0,0 +1,178 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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 "DMD.H"
+#include "readFields.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ROMmodels
+{
+    defineTypeNameAndDebug(DMD, 0);
+    addToRunTimeSelectionTable(ROMmodel, DMD, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Implementation
+#include "DMDImpl.C"
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::wordList Foam::ROMmodels::DMD::modeNames(const word& modeType) const
+{
+    wordList modeNames(modes_.size());
+    for (const label modei : modes_)
+    {
+        modeNames[modei] =
+            word
+            (
+                "mode"+modeType+"_"+name(modei)+"_"+fieldName_+"_"+objectName_
+            );
+    }
+    return modeNames;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::ROMmodels::DMD::DMD
+(
+    Time& runTime,
+    fvMesh& mesh,
+    const dictionary& dict,
+    const instantList& times
+)
+:
+    ROMmodel(runTime, mesh, dict, times),
+    fieldName_(),
+    objectName_(),
+    deltaT_(),
+    time_(),
+    startTime_(),
+    modes_(),
+    dims_(),
+    amps_(),
+    evals_()
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+bool Foam::ROMmodels::DMD::read(const dictionary& dict)
+{
+    dict.readEntry("field", fieldName_);
+    dict.readEntry("object", objectName_);
+    dict.readEntry("deltaT", deltaT_);
+    dict.readEntry("time", time_);
+    dict.readIfPresent("startTime", startTime_);
+
+    if (deltaT_ < SMALL || time_ < SMALL || startTime_ < 0)
+    {
+        FatalIOErrorInFunction(dict)
+            << "Out-of-range values for " << nl
+            << tab << "deltaT: " << deltaT_
+            << tab << "time: " << time_ << nl
+            << tab << "startTime: " << startTime_ << nl
+            << exit(FatalIOError);
+    }
+
+    dict.readEntry("modes", modes_);
+
+    if (modes_.empty())
+    {
+        FatalIOErrorInFunction(dict)
+            << "Empty list for the mode indices " << nl
+            << tab << "modes: " << modes_ << nl
+            << exit(FatalIOError);
+    }
+
+    dims_.reset(dict.get<dimensionSet>("dimensions"));
+
+    // Load complex amplitudes and eigenvalues
+    dict.readEntry("amplitudes", amps_);
+    dict.readEntry("eigenvalues", evals_);
+
+    if ((amps_.size() != modes_.size()) || (evals_.size() != modes_.size()))
+    {
+        FatalIOErrorInFunction(dict)
+            << "Inconsistent input sizes for "
+            << tab << "modes: " << modes_.size() << nl
+            << tab << "amplitudes: " << amps_.size() << nl
+            << tab << "eigenvalues: " << evals_.size() << nl
+            << exit(FatalIOError);
+    }
+
+    // Create mode-field names
+    const wordList modeReNames(modeNames(word("Re")));
+    const wordList modeImNames(modeNames(word("Im")));
+
+    // Load mode fields
+    runTime_.setTime(time_, 0);
+
+    readFieldsHandler(mesh_).execute(modeReNames);
+    readFieldsHandler(mesh_).execute(modeImNames);
+
+    return true;
+}
+
+
+bool Foam::ROMmodels::DMD::createAndWrite()
+{
+    do
+    {
+        #undef  doLocalCode
+        #define doLocalCode(InputType)                                      \
+        {                                                                   \
+            createAndWriteImpl<VolumeField<InputType>>();                   \
+            createAndWriteImpl<SurfaceField<InputType>>();                  \
+        }
+
+        doLocalCode(scalar);
+        doLocalCode(vector);
+        doLocalCode(sphericalTensor);
+        doLocalCode(symmTensor);
+        doLocalCode(tensor);
+
+        #undef doInnerLocalCode
+        #undef doLocalCode
+    }
+    while (false);
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.H b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.H
new file mode 100644
index 00000000000..75a49acb7b6
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMD.H
@@ -0,0 +1,232 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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::ROMmodels::DMD
+
+Description
+    Field creation model using the streaming total dynamic mode
+    decomposition method (STDMD).
+
+    The governing equations of the field creation are as follows:
+
+    \f[
+        \mathbf{x}_\tau \approx
+        \tilde{\mathbf{x}_\tau} =
+        \left( \sum_{i=0}^{N-1} \phi_i \alpha_i v_i^\tau \right)_{real}​
+    \f]
+
+    with
+
+    \f[
+        \tau = \frac{t - t_o}{\Delta t}​
+    \f]
+
+    where:
+
+    \vartable
+        \mathbf{x}    | Field snapshot at time t
+        \tilde{\mathbf{x}} | Reconstructed field snapshot at time t (complex)
+        N             | Number of modes
+        i             | Mode index
+        \tau          | Nondimensional time
+        t             | Time [s]
+        t_o           | Start time (of mode decomposition calculations) [s]
+        \Delta t      | Time-step size of mode decomposition [s]
+        \phi          | Mode (complex)
+        \alpha        | Mode amplitude (complex)
+        v             | Mode eigenvalue (complex)
+    \endvartable
+
+    References:
+    \verbatim
+        Governing equations (tag:K):
+            Kiewat, M. (2019).
+            Streaming modal decomposition approaches for vehicle aerodynamics.
+            PhD thesis. Munich: Technical University of Munich.
+            URL:mediatum.ub.tum.de/doc/1482652/1482652.pdf
+    \endverbatim
+
+    Operands:
+    \table
+      Operand      | Type                 | Location
+      input        | {vol,surface}\<Type\>Field | \<time\>/\<inpField\>
+      output file  | -                    | -
+      output field | {vol,surface}\<Type\>Field | \<time\>/\<outField\>
+    \endtable
+
+    where \c \<Type\>=Scalar/Vector/SphericalTensor/SymmTensor/Tensor.
+
+Usage
+    Minimal example by using \c system/ROMfieldsDict:
+    \verbatim
+        // Mandatory entries
+        field           <word>;
+        object          <word>;
+        deltaT          <scalar>;
+        time            <scalar>;
+        modes           <labelList>;
+        amplitudes      <complexList>;
+        eigenvalues     <complexList>;
+
+        // Optional entries
+        startTime       <scalar>;
+        dimensions      <dimensionSet>;
+    \endverbatim
+
+    where the entries mean:
+    \table
+      Property     | Description                        | Type | Reqd  | Deflt
+      field        | Name of reconstructed field        | word | yes   | -
+      object       | Name of operand function object    | word | yes   | -
+      deltaT       | Time-step size of mode decomposition | scalar | yes | -
+      time         | Time instant where modes are located | scalar | yes | -
+      modes        | List of mode indices               | labelList | yes | -
+      amplitudes   | Amplitude coefficients             | complexList | yes | -
+      eigenvalues  | Eigenvalues                        | complexList | yes | -
+      startTime | Start time for mode-information collection | scalar | no | 0
+      dimensions | Dimensions of reconstructed fields | dimensionSet | no | -
+    \endtable
+
+SourceFiles
+    DMD.C
+    DMDImpl.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ROMmodels_DMD_H
+#define ROMmodels_DMD_H
+
+#include "ROMmodels/ROMmodel/ROMmodel.H"
+#include "dimensionSet.H"
+#include "complex.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ROMmodels
+{
+/*---------------------------------------------------------------------------*\
+                            Class DMD Declaration
+\*---------------------------------------------------------------------------*/
+
+class DMD
+:
+    public ROMmodel
+{
+    // Private Typedefs
+
+        typedef List<complex> complexList;
+
+
+    // Private Data
+
+        //- Name of reconstructed field
+        word fieldName_;
+
+        //- Name of operand function object
+        word objectName_;
+
+        //- Time-step size of mode decomposition (not that of the simulation)
+        scalar deltaT_;
+
+        //- Time instant where modes are located
+        scalar time_;
+
+        //- Start time for mode-information collection
+        scalar startTime_;
+
+        //- List of mode indices
+        labelList modes_;
+
+        //- Dimensions of reconstructed fields
+        dimensionSet dims_;
+
+        //- Amplitude coefficients
+        complexList amps_;
+
+        //- Eigenvalues
+        complexList evals_;
+
+
+    // Private Member Functions
+
+        //- Return names of mode fields
+        wordList modeNames(const word& modeType) const;
+
+        //- Implementation for creating and writing fields
+        template<class GeoType>
+        bool createAndWriteImpl() const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("DMD");
+
+
+    // Constructors
+
+        //- Construct from components
+        DMD
+        (
+            Time& runTime,
+            fvMesh& mesh,
+            const dictionary& dict,
+            const instantList& times
+        );
+
+        //- No copy construct
+        DMD(const DMD&) = delete;
+
+        //- No copy assignment
+        void operator=(const DMD&) = delete;
+
+
+    //- Destructor
+    virtual ~DMD() = default;
+
+
+    // Member Functions
+
+        //- Read model settings
+        virtual bool read(const dictionary& dict);
+
+        //- Create and write fields
+        virtual bool createAndWrite();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ROMmodels
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMDImpl.C b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMDImpl.C
new file mode 100644
index 00000000000..dd60c898759
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/DMD/DMDImpl.C
@@ -0,0 +1,106 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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 "volFields.H"
+#include "surfaceFields.H"
+#include "zeroGradientFvPatchFields.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+template<class GeoType>
+bool Foam::ROMmodels::DMD::createAndWriteImpl() const
+{
+    typedef typename GeoType::value_type Type;
+
+    const wordList modeReNames(modeNames(word("Re")));
+    const wordList modeImNames(modeNames(word("Im")));
+
+    for (const label i : modes_)
+    {
+        const auto* modeRePtr = mesh_.cfindObject<GeoType>(modeReNames[i]);
+
+        if (!modeRePtr) return false;
+
+        const auto* modeImPtr = mesh_.cfindObject<GeoType>(modeImNames[i]);
+
+        if (!modeImPtr) return false;
+    }
+
+
+    forAll(times_, timei)
+    {
+        runTime_.setTime(times_[timei], timei);
+
+        Info<< "\nTime = " << runTime_.timeName() << endl;
+
+        // Calculate the eigenvalue exponent corresponding to specified time
+        const scalar k = (times_[timei].value() - startTime_)/deltaT_;
+
+        GeoType reconstructedFld
+        (
+            IOobject
+            (
+                IOobject::scopedName(fieldName_, "reconstructed"),
+                runTime_.timeName(),
+                mesh_,
+                IOobject::NO_READ,
+                IOobject::NO_WRITE,
+                IOobject::NO_REGISTER
+            ),
+            mesh_,
+            dimensioned<Type>(dimless, Zero),
+            fvPatchFieldBase::zeroGradientType()
+        );
+
+        forAll(modes_, i)
+        {
+            const label j = modes_[i];
+            const auto& modeRe = mesh_.lookupObject<GeoType>(modeReNames[j]);
+            const auto& modeIm = mesh_.lookupObject<GeoType>(modeImNames[j]);
+
+            const complex evalk(pow(evals_[i], k));
+
+            // (K:Eq. 84)
+            reconstructedFld +=
+            (
+                (modeRe*amps_[i].Re() - modeIm*amps_[i].Im())*evalk.Re()
+              - (modeRe*amps_[i].Im() + modeIm*amps_[i].Re())*evalk.Im()
+            );
+        }
+
+        reconstructedFld.correctBoundaryConditions();
+
+        reconstructedFld.dimensions().reset(dims_);
+
+        reconstructedFld.write();
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.C b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.C
new file mode 100644
index 00000000000..47f02798364
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.C
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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 "ROMmodel.H"
+#include "Time.H"
+#include "fvMesh.H"
+#include "dictionary.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(ROMmodel, 0);
+    defineRunTimeSelectionTable(ROMmodel, dictionary);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::ROMmodel::ROMmodel
+(
+    Time& runTime,
+    fvMesh& mesh,
+    const dictionary& dict,
+    const instantList& times
+)
+:
+    runTime_(runTime),
+    mesh_(mesh),
+    dict_(dict),
+    times_(times)
+{}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.H b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.H
new file mode 100644
index 00000000000..dd973f67055
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodel.H
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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/>.
+
+Namespace
+    Foam::ROMmodels
+
+Description
+    A namespace for various implementations of
+    reduced-order (ROM) field creation models.
+
+Class
+    Foam::ROMmodel
+
+Description
+    Abstract base class for reduced-order models
+    to handle specific model characteristics.
+
+SourceFiles
+    ROMmodel.C
+    ROMmodelNew.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_ROMmodel_H
+#define Foam_ROMmodel_H
+
+#include "runTimeSelectionTables.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class Time;
+class fvMesh;
+class dictionary;
+class instant;
+typedef class List<instant> instantList;
+
+/*---------------------------------------------------------------------------*\
+                          Class ROMmodel Declaration
+\*---------------------------------------------------------------------------*/
+
+class ROMmodel
+{
+protected:
+
+    // Protected Data
+
+        //- Reference to the Time
+        //  Need non-const access to use setTime
+        Time& runTime_;
+
+        //- Reference to the fvMesh
+        //  Need non-const access to use readFieldsHandler
+        fvMesh& mesh_;
+
+        //- Const reference to the dictionary
+        const dictionary& dict_;
+
+        //- Const reference to field times
+        const instantList& times_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("ROMmodel");
+
+
+    // Declare runtime constructor selection table
+
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            ROMmodel,
+            dictionary,
+            (
+                Time& runTime,
+                fvMesh& mesh,
+                const dictionary& dict,
+                const instantList& times
+            ),
+            (runTime, mesh, dict, times)
+        );
+
+
+    // Selectors
+
+        //- Return a reference to the selected ROMmodel
+        static autoPtr<ROMmodel> New
+        (
+            Time& runTime,
+            fvMesh& mesh,
+            const dictionary& dict,
+            const instantList& times
+        );
+
+
+    // Constructors
+
+        //- Construct from components
+        ROMmodel
+        (
+            Time& runTime,
+            fvMesh& mesh,
+            const dictionary& dict,
+            const instantList& times
+        );
+
+        //- No copy construct
+        ROMmodel(const ROMmodel&) = delete;
+
+        //- No copy assignment
+        void operator=(const ROMmodel&) = delete;
+
+
+    //- Destructor
+    virtual ~ROMmodel() = default;
+
+
+    // Member Functions
+
+        //- Read model settings
+        virtual bool read(const dictionary& dict) = 0;
+
+        //- Create and write fields
+        virtual bool createAndWrite() = 0;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodelNew.C b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodelNew.C
new file mode 100644
index 00000000000..7c09aeed1db
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/ROMmodels/ROMmodel/ROMmodelNew.C
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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 "ROMmodel.H"
+#include "dictionary.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::ROMmodel> Foam::ROMmodel::New
+(
+    Time& runTime,
+    fvMesh& mesh,
+    const dictionary& dict,
+    const instantList& times
+)
+{
+    const word modelType(dict.getWord("ROMmodel"));
+
+    auto* ctorPtr = dictionaryConstructorTable(modelType);
+
+    if (!ctorPtr)
+    {
+        FatalIOErrorInLookup
+        (
+            dict,
+            "ROMmodel",
+            modelType,
+            *dictionaryConstructorTablePtr_
+        ) << exit(FatalIOError);
+    }
+
+    return autoPtr<ROMmodel>(ctorPtr(runTime, mesh, dict, times));
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/createROMfields.C b/applications/utilities/postProcessing/miscellaneous/createROMfields/createROMfields.C
new file mode 100644
index 00000000000..d9e7837ba84
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/createROMfields.C
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2023 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
+    createROMfields
+
+Group
+    grpPostProcessingUtilities
+
+Description
+    Create fields using reduced-order modelling (ROM) data
+    at specific time instants without requiring any CFD computations.
+
+Usage
+    Minimal example by using \c system/ROMfieldsDict:
+    \verbatim
+        // Mandatory entries
+        ROMmodel        <word>;
+
+        // Inherited entries
+        // See DMD.H for the 'DMD' ROMmodel
+        ...
+    \endverbatim
+
+    where the entries mean:
+    \table
+      Property  | Description                    | Type | Reqd | Deflt
+      ROMmodel  | Type of reduced-order model    | word | yes  | -
+    \endtable
+
+    Options for the \c ROMmodel entry:
+    \verbatim
+      DMD    | Streaming total dynamic mode decomposition
+    \endverbatim
+
+    The inherited entries are elaborated in:
+      - \link ROMmodel.H \endlink
+      - \link DMD.H \endlink
+
+Note
+  - The quality of results depends on the capabilities of the underlying
+    reduced-order model, and the quality of the input data.
+  - Warning: Reduced-order modelling is an active research area at the time of
+    writing; therefore, there could be cases whereat oddities can be seen.
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "timeSelector.H"
+#include "fvCFD.H"
+#include "ROMmodels/ROMmodel/ROMmodel.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "Create fields using reduced-order modelling (ROM) data at specific "
+        "time instants without requiring any CFD computations."
+    );
+
+    argList::addOption
+    (
+        "dict",
+        "file",
+        "Alternative dictionary for ROMfieldsDict"
+    );
+
+    // No -constant, no special treatment for 0/
+    timeSelector::addOptions(false);
+
+    // Remove treatments unnecessary for this utility
+    argList::noFunctionObjects();
+    argList::removeOption("noZero");
+    argList::removeOption("world");
+
+
+    #include "addRegionOption.H"
+
+    #include "setRootCase.H"
+    #include "createTime.H"
+
+    const word dictName("ROMfieldsDict");
+    #include "setSystemRunTimeDictionaryIO.H"
+    Info<< "Reading " << dictIO.name() << nl << endl;
+    IOdictionary dict(dictIO);
+
+    instantList times = timeSelector::select0(runTime, args);
+    if (times.empty())
+    {
+        FatalErrorInFunction
+            << "No times selected." << nl
+            << exit(FatalError);
+    }
+
+    #include "createNamedMesh.H"
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+    auto ROMptr = ROMmodel::New(runTime, mesh, dict, times);
+
+    ROMptr->read(dict);
+
+    ROMptr->createAndWrite();
+
+
+    Info<< nl;
+    runTime.printExecutionTime(Info);
+
+    Info<< "End\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/miscellaneous/createROMfields/readFields.H b/applications/utilities/postProcessing/miscellaneous/createROMfields/readFields.H
new file mode 100644
index 00000000000..a1d345d1c8d
--- /dev/null
+++ b/applications/utilities/postProcessing/miscellaneous/createROMfields/readFields.H
@@ -0,0 +1,189 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2021-2022 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::readFieldsHandler
+
+Description
+    A simple field-loader, as per the readFields function object
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_readFields_H
+#define Foam_readFields_H
+
+#include "fvMesh.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "messageStream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class readFieldsHandler Declaration
+\*---------------------------------------------------------------------------*/
+
+class readFieldsHandler
+{
+    // Private Data
+
+        //- Mesh reference
+        fvMesh& mesh_;
+
+        //- Output logging (verbosity)
+        bool log;
+
+
+    // Private Member Functions
+
+        //- Attempt load from io, store on database if successful
+        template<class FieldType>
+        bool loadAndStore(const IOobject& io)
+        {
+            if (io.isHeaderClass<FieldType>())
+            {
+                // Store field on mesh database
+                Log << "    Reading " << io.name()
+                    << " (" << FieldType::typeName << ')' << endl;
+
+                mesh_.objectRegistry::store(new FieldType(io, mesh_));
+                return true;
+            }
+
+            return false;
+        }
+
+        //- Forward to loadAndStore for supported types
+        template<class Type>
+        bool loadField(const IOobject& io)
+        {
+            typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
+            typedef typename VolFieldType::Internal IntVolFieldType;
+            typedef GeometricField<Type, fvsPatchField, surfaceMesh>
+                SurfaceFieldType;
+
+            return
+            (
+                loadAndStore<VolFieldType>(io)
+             || loadAndStore<IntVolFieldType>(io)
+             || loadAndStore<SurfaceFieldType>(io)
+            );
+        }
+
+
+        //- Load all fields
+        label loadFields(const UList<word>& fieldSet_)
+        {
+            label nLoaded = 0;
+
+            for (const word& fieldName : fieldSet_)
+            {
+                // Already loaded?
+                const auto* ptr = mesh_.cfindObject<regIOobject>(fieldName);
+
+                if (ptr)
+                {
+                    ++nLoaded;
+                    DebugInfo
+                        << "readFields : "
+                        << ptr->name() << " (" << ptr->type()
+                            << ") already in database" << endl;
+                    continue;
+                }
+
+                // Load field as necessary
+                IOobject io
+                (
+                    fieldName,
+                    mesh_.time().timeName(),
+                    mesh_.thisDb(),
+                    IOobject::MUST_READ,
+                    IOobject::NO_WRITE
+                );
+
+                const bool ok =
+                (
+                    io.typeHeaderOk<regIOobject>(false)
+                 &&
+                    (
+                        loadField<scalar>(io)
+                     || loadField<vector>(io)
+                     || loadField<sphericalTensor>(io)
+                     || loadField<symmTensor>(io)
+                     || loadField<tensor>(io)
+                    )
+                );
+
+                if (ok)
+                {
+                    ++nLoaded;
+                }
+                else
+                {
+                    DebugInfo
+                        << "readFields : failed to load " << fieldName
+                        << endl;
+                }
+        }
+
+        return nLoaded;
+    }
+
+
+public:
+
+    static const bool debug = false;
+
+
+    // Constructors
+
+        //- Construct
+        explicit readFieldsHandler(fvMesh& mesh, bool verbose=true)
+        :
+            mesh_(mesh),
+            log(verbose)
+        {}
+
+
+    // Member Functions
+
+        bool execute(const UList<word>& fieldNames)
+        {
+            loadFields(fieldNames);
+            return true;
+        }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+#endif
+
+// ************************************************************************* //
diff --git a/etc/caseDicts/annotated/ROMfieldsDict b/etc/caseDicts/annotated/ROMfieldsDict
new file mode 100644
index 00000000000..9d22dbd7b50
--- /dev/null
+++ b/etc/caseDicts/annotated/ROMfieldsDict
@@ -0,0 +1,59 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v2306                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      ROMfieldsDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Mandatory entries
+
+// Type of reduced-order model
+ROMmodel        DMD;
+
+// Name of reconstructed field
+field           U;
+
+// Name of operand function object
+object          stdmd02;
+
+// Time-step size of DMD
+deltaT          0.5;
+
+// Time instant in which modes are located
+time            200;
+
+// List of mode indices
+modes           (0 1 2 3 4 5);
+
+// Amplitude coefficients (complex)
+amplitudes
+(
+    // real     imag
+    // (1e-01   2e-02)
+);
+
+// Eigenvalues (complex)
+eigenvalues
+(
+    // real     imag
+    // (1e-01   2e-02)
+);
+
+// Optional entries
+
+// Start time for mode-information collection
+startTime   10;
+
+// Dimensions of reconstructed fields
+dimensions  [0 1 -1 0 0 0 0];
+
+// ************************************************************************* //
-- 
GitLab