From 730233cd15d9e7b34987cdc3fa87feee54145589 Mon Sep 17 00:00:00 2001 From: Kutalmis Bercin Date: Thu, 5 Mar 2020 11:37:45 +0000 Subject: [PATCH] ENH: add new FO Streaming-Total Dynamic Mode Decomposition (STDMD) STDMD (i.e. Streaming Total Dynamic Mode Decomposition) is a variant of a data-driven dimensionality reduction method. STDMD is being used as a mathematical post-processing tool to compute a set of dominant modes out of a given flow (or dataset) each of which is associated with a constant frequency and decay rate, so that dynamic features of a given flow may become interpretable, and tractable. Among other Dynamic Mode Decomposition (DMD) variants, STDMD is presumed to provide the general DMD method capabilities alongside economised and feasible memory and CPU usage. Please refer to the header file documentation for further details. ENH: add new STDMD tutorial, pimpleFoam/laminar/cylinder2D --- src/functionObjects/field/Make/files | 2 + src/functionObjects/field/Make/options | 2 + src/functionObjects/field/STDMD/STDMD.C | 1280 +++++++++++++++++ src/functionObjects/field/STDMD/STDMD.H | 644 +++++++++ .../field/STDMD/STDMDTemplates.C | 141 ++ .../pimpleFoam/laminar/cylinder2D/0.orig/U | 51 + .../pimpleFoam/laminar/cylinder2D/0.orig/p | 51 + .../pimpleFoam/laminar/cylinder2D/Allclean | 12 + .../pimpleFoam/laminar/cylinder2D/Allrun | 25 + .../pimpleFoam/laminar/cylinder2D/Allrun.pre | 20 + .../cylinder2D/constant/transportProperties | 23 + .../cylinder2D/constant/turbulenceProperties | 21 + .../cylinder2D/system/blockMeshDict.coarse | 74 + .../cylinder2D/system/blockMeshDict.main | 277 ++++ .../cylinder2D/system/coarseMesh/fvSchemes | 47 + .../cylinder2D/system/coarseMesh/fvSolution | 27 + .../laminar/cylinder2D/system/controlDict | 229 +++ .../cylinder2D/system/decomposeParDict | 28 + .../laminar/cylinder2D/system/fvSchemes | 56 + .../laminar/cylinder2D/system/fvSolution | 56 + .../laminar/cylinder2D/system/mirrorMeshDict | 27 + .../cylinder2D/system/snappyHexMeshDict | 83 ++ 22 files changed, 3176 insertions(+) create mode 100644 src/functionObjects/field/STDMD/STDMD.C create mode 100644 src/functionObjects/field/STDMD/STDMD.H create mode 100644 src/functionObjects/field/STDMD/STDMDTemplates.C create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/0.orig/U create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/0.orig/p create mode 100755 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/Allclean create mode 100755 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/Allrun create mode 100755 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/Allrun.pre create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/constant/transportProperties create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/constant/turbulenceProperties create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/blockMeshDict.coarse create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/blockMeshDict.main create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/coarseMesh/fvSchemes create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/coarseMesh/fvSolution create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/controlDict create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/decomposeParDict create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/fvSchemes create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/fvSolution create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/mirrorMeshDict create mode 100644 tutorials/incompressible/pimpleFoam/laminar/cylinder2D/system/snappyHexMeshDict 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