mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
Merge branch 'feature-refactoring-dmd' into 'develop'
ENH: DMD: refactor dynamic mode decomposition FO See merge request Development/openfoam!440
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 OpenFOAM Foundation
|
||||
Copyright (C) 2019-2020 OpenCFD Ltd.
|
||||
Copyright (C) 2019-2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -492,9 +492,10 @@ template<class MatrixType>
|
||||
MatrixType Foam::pinv
|
||||
(
|
||||
const MatrixType& A,
|
||||
const scalar tol
|
||||
const scalar tolerance
|
||||
)
|
||||
{
|
||||
scalar tol = tolerance;
|
||||
typedef typename MatrixType::cmptType cmptType;
|
||||
|
||||
if (A.empty())
|
||||
@ -506,7 +507,12 @@ MatrixType Foam::pinv
|
||||
|
||||
if (A.size() == 1)
|
||||
{
|
||||
return MatrixType({1, 1}, cmptType(1.0)/(A(0,0) + cmptType(VSMALL)));
|
||||
if (A(0,0) == cmptType(0))
|
||||
{
|
||||
return MatrixType({1, 1}, cmptType(0));
|
||||
}
|
||||
|
||||
return MatrixType({1, 1}, cmptType(1)/(A(0,0) + cmptType(VSMALL)));
|
||||
}
|
||||
|
||||
QRMatrix<MatrixType> QRM
|
||||
@ -521,27 +527,47 @@ MatrixType Foam::pinv
|
||||
// R1 (KP:p. 648)
|
||||
// Find the first diagonal element index with (almost) zero value in R
|
||||
label firstZeroElemi = 0;
|
||||
label i = 0;
|
||||
while (i < 2)
|
||||
{
|
||||
const List<cmptType> diag(R.diag());
|
||||
|
||||
auto lessT = [&](const cmptType& x) { return mag(x) < tol; };
|
||||
auto lessThan = [=](const cmptType& x) { return tol > mag(x); };
|
||||
|
||||
firstZeroElemi =
|
||||
std::distance
|
||||
(
|
||||
diag.cbegin(),
|
||||
std::find_if(diag.cbegin(), diag.cend(), lessT)
|
||||
std::find_if(diag.cbegin(), diag.cend(), lessThan)
|
||||
);
|
||||
}
|
||||
|
||||
if (firstZeroElemi == 0)
|
||||
{
|
||||
WarningInFunction
|
||||
<< "The largest (magnitude) diagonal element is (almost) zero."
|
||||
<< nl << "Returning a zero matrix."
|
||||
<< endl;
|
||||
if (firstZeroElemi == 0)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
WarningInFunction
|
||||
<< "The largest diagonal element magnitude is nearly zero. "
|
||||
<< "Tightening the tolerance."
|
||||
<< endl;
|
||||
|
||||
return MatrixType(A.sizes(), Zero);
|
||||
tol = 1e-13;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
WarningInFunction
|
||||
<< "The largest diagonal element magnitude is nearly zero. "
|
||||
<< "Returning a zero matrix."
|
||||
<< endl;
|
||||
++i;
|
||||
|
||||
return MatrixType({A.n(), A.m()}, Zero);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude the first (almost) zero diagonal row and the rows below
|
||||
@ -599,4 +625,5 @@ MatrixType Foam::pinv
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 OpenFOAM Foundation
|
||||
Copyright (C) 2019 OpenCFD Ltd.
|
||||
Copyright (C) 2019-2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
234
src/functionObjects/field/DMD/DMD.C
Normal file
234
src/functionObjects/field/DMD/DMD.C
Normal file
@ -0,0 +1,234 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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 "DMDModel.H"
|
||||
#include "addToRunTimeSelectionTable.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace functionObjects
|
||||
{
|
||||
defineTypeNameAndDebug(DMD, 0);
|
||||
addToRunTimeSelectionTable(functionObject, DMD, dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
void Foam::functionObjects::DMD::snapshot()
|
||||
{
|
||||
bool processed = false;
|
||||
processed = processed || getSnapshot<scalar>();
|
||||
processed = processed || getSnapshot<vector>();
|
||||
processed = processed || getSnapshot<sphericalTensor>();
|
||||
processed = processed || getSnapshot<symmTensor>();
|
||||
processed = processed || getSnapshot<tensor>();
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< " functionObjects::" << type() << " " << name() << ":"
|
||||
<< " cannot find required input field during snapshot loading: "
|
||||
<< fieldName_ << nl
|
||||
<< " Do you execute required functionObjects"
|
||||
<< " before executing DMD, e.g. mapFields?"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::DMD::initialise()
|
||||
{
|
||||
const label nComps = DMDModelPtr_->nComponents(fieldName_);
|
||||
|
||||
if (patch_.empty())
|
||||
{
|
||||
nSnap_ = nComps*mesh_.nCells();
|
||||
}
|
||||
else
|
||||
{
|
||||
const label patchi = mesh_.boundaryMesh().findPatchID(patch_);
|
||||
|
||||
if (patchi < 0)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Cannot find patch " << patch_
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
nSnap_ = nComps*(mesh_.C().boundaryField()[patchi]).size();
|
||||
}
|
||||
|
||||
const label nSnapTotal = returnReduce(nSnap_, sumOp<label>());
|
||||
|
||||
if (nSnapTotal <= 0)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< " # Zero-size input field = " << fieldName_ << " #"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
if (nSnap_ > 0)
|
||||
{
|
||||
z_ = RMatrix(2*nSnap_, 1, Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
z_ = RMatrix(1, 1, Zero);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::functionObjects::DMD::DMD
|
||||
(
|
||||
const word& name,
|
||||
const Time& runTime,
|
||||
const dictionary& dict
|
||||
)
|
||||
:
|
||||
fvMeshFunctionObject(name, runTime, dict),
|
||||
DMDModelPtr_(DMDModel::New(mesh_, name, dict)),
|
||||
z_(),
|
||||
fieldName_(dict.get<word>("field")),
|
||||
patch_(dict.getOrDefault<word>("patch", word::null)),
|
||||
nSnap_(0),
|
||||
step_(0)
|
||||
{
|
||||
// Check if computation time-step is varying
|
||||
if (runTime.isAdjustTimeStep())
|
||||
{
|
||||
WarningInFunction
|
||||
<< " # DMD: Available only for fixed time-step computations. #"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// Check if mesh topology is changing
|
||||
if (mesh_.topoChanging())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< " # DMD: Available only for non-changing mesh topology. #"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
read(dict);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::functionObjects::DMD::read(const dictionary& dict)
|
||||
{
|
||||
Info<< type() << " " << name() << ":" << endl;
|
||||
|
||||
if (fvMeshFunctionObject::read(dict) && DMDModelPtr_->read(dict))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::DMD::execute()
|
||||
{
|
||||
Log << type() << " " << name() << " execute:" << endl;
|
||||
|
||||
snapshot();
|
||||
|
||||
if (step_ == 1)
|
||||
{
|
||||
DMDModelPtr_->initialise(z_);
|
||||
}
|
||||
|
||||
if (step_ > 1)
|
||||
{
|
||||
DMDModelPtr_->update(z_);
|
||||
}
|
||||
|
||||
++step_;
|
||||
|
||||
Log << tab << "Execution index = " << step_ << " for field: " << fieldName_
|
||||
<< endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::DMD::write()
|
||||
{
|
||||
if (postProcess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::DMD::end()
|
||||
{
|
||||
if (step_ == 0)
|
||||
{
|
||||
// Avoid double execution of write() when writeControl=onEnd
|
||||
return true;
|
||||
}
|
||||
|
||||
Log << type() << " " << name() << " write:" << endl;
|
||||
|
||||
if (step_ < 2)
|
||||
{
|
||||
WarningInFunction
|
||||
<< " # DMD needs at least three snapshots to produce output #"
|
||||
<< nl
|
||||
<< " # Only " << step_ + 1 << " snapshots are available #"
|
||||
<< nl
|
||||
<< " # Skipping DMD output calculation and write #"
|
||||
<< endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
z_.clear();
|
||||
|
||||
DMDModelPtr_->fit();
|
||||
|
||||
mesh_.time().printExecutionTime(Info);
|
||||
|
||||
// Restart the incremental orthonormal basis update
|
||||
step_ = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
278
src/functionObjects/field/DMD/DMD.H
Normal file
278
src/functionObjects/field/DMD/DMD.H
Normal file
@ -0,0 +1,278 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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::functionObjects::DMD
|
||||
|
||||
Group
|
||||
grpFieldFunctionObjects
|
||||
|
||||
Description
|
||||
Computes a dynamic mode decomposition model on a specified field.
|
||||
|
||||
Dynamic mode decomposition (i.e. DMD) is a data-driven
|
||||
dimensionality reduction method. DMD is being used as a mathematical
|
||||
processing tool to reveal dominant modes out of a given field (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,
|
||||
tractable, and even reproducible without computing simulations.
|
||||
DMD only relies on input data, therefore it is an equation-free approach.
|
||||
|
||||
References:
|
||||
\verbatim
|
||||
DMD characteristics:
|
||||
Brunton S. L. (2018).
|
||||
Dynamic mode decomposition overview.
|
||||
Seattle, Washington: University of Washington.
|
||||
youtu.be/sQvrK8AGCAo (Retrieved:24-04-20)
|
||||
\endverbatim
|
||||
|
||||
Operands:
|
||||
\table
|
||||
Operand | Type | Location
|
||||
input | {vol,surface}\<Type\>Field(s) <!--
|
||||
--> | \<time\>/\<inputField\>(s)
|
||||
output file | dat | postProcessing/\<FO\>/\<time\>/\<file\>(s)
|
||||
output field | volVectorField(s) | \<time\>/\<outputField\>(s)
|
||||
\endtable
|
||||
|
||||
where \c \<Type\>=Scalar/Vector/SphericalTensor/SymmTensor/Tensor.
|
||||
|
||||
Output fields:
|
||||
\verbatim
|
||||
modeRe_<modeIndex>_<field>_<FO> | Real part of a mode field
|
||||
modeIm_<modeIndex>_<field>_<FO> | Imaginary part of a mode field
|
||||
\endverbatim
|
||||
|
||||
Output files:
|
||||
\verbatim
|
||||
dynamics_<field>.dat | Dynamics data for each mode
|
||||
filteredDynamics_<field>.dat | Filtered dynamics data for each mode
|
||||
\endverbatim
|
||||
|
||||
wherein for each mode, the following quantities are output into files:
|
||||
\vartable
|
||||
freq | Frequency
|
||||
mag | Magnitude
|
||||
ampRe | Amplitude coefficients (real part)
|
||||
ampIm | Amplitude coefficients (imaginary part)
|
||||
evalRe | Eigenvalue (real part)
|
||||
evalIm | Eigenvalue (imaginary part)
|
||||
\endvartable
|
||||
|
||||
Usage
|
||||
Minimal example by using \c system/controlDict.functions:
|
||||
\verbatim
|
||||
DMD1
|
||||
{
|
||||
// Mandatory entries (unmodifiable)
|
||||
type DMD;
|
||||
libs (fieldFunctionObjects);
|
||||
DMDModel <DMDModel>;
|
||||
field <inputField>;
|
||||
|
||||
// Optional entries (unmodifiable)
|
||||
patch <patchName>;
|
||||
|
||||
// Mandatory/Optional (inherited) entries
|
||||
...
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
where the entries mean:
|
||||
\table
|
||||
Property | Description | Type | Reqd | Deflt
|
||||
type | Type name: DMD | word | yes | -
|
||||
libs | Library name: fieldFunctionObjects | word | yes | -
|
||||
DMDModel | Name of specified DMD model | word | yes | -
|
||||
field | Name of operand field | word | yes | -
|
||||
patch | Name of operand patch | word | no | null
|
||||
\endtable
|
||||
|
||||
Options for the \c DMDModel entry:
|
||||
\verbatim
|
||||
STDMD | Streaming total dynamic mode decomposition
|
||||
\endverbatim
|
||||
|
||||
The inherited entries are elaborated in:
|
||||
- \link functionObject.H \endlink
|
||||
- \link writeFile.H \endlink
|
||||
|
||||
Minimal example by using the \c postProcess utility:
|
||||
\verbatim
|
||||
<solver> -postProcess -fields '(U p)' -time '10:'
|
||||
\endverbatim
|
||||
|
||||
Note
|
||||
- Warning: DMD is an active research area at the time of writing;
|
||||
therefore, there could be cases whereat oddities can be seen.
|
||||
|
||||
See also
|
||||
- Foam::functionObjects::fvMeshFunctionObject
|
||||
- Foam::functionObjects::writeFile
|
||||
- Foam::DMDModel
|
||||
- Foam::DMDModels::STDMD
|
||||
|
||||
SourceFiles
|
||||
DMD.C
|
||||
DMDTemplates.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef functionObjects_DMD_H
|
||||
#define functionObjects_DMD_H
|
||||
|
||||
#include "fvMeshFunctionObject.H"
|
||||
#include "RectangularMatrix.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class DMDModel;
|
||||
|
||||
namespace functionObjects
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class DMD Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class DMD
|
||||
:
|
||||
public fvMeshFunctionObject
|
||||
{
|
||||
typedef RectangularMatrix<scalar> RMatrix;
|
||||
|
||||
// Private Data
|
||||
|
||||
//- Dynamic mode decomposition model
|
||||
autoPtr<DMDModel> DMDModelPtr_;
|
||||
|
||||
//- Snapshot matrix (effectively a column vector)
|
||||
// Upper half = current-time snapshot slot
|
||||
// Lower half = previous-time snapshot slot
|
||||
// A snapshot is an input dataset to be processed per execution step
|
||||
RMatrix z_;
|
||||
|
||||
//- Name of operand field
|
||||
const word fieldName_;
|
||||
|
||||
//- Name of operand patch
|
||||
const word patch_;
|
||||
|
||||
//- Number of elements in a snapshot
|
||||
label nSnap_;
|
||||
|
||||
//- Current execution-step index of DMD,
|
||||
//- not necessarily that of the simulation
|
||||
label step_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
// Process
|
||||
|
||||
//- Initialise snapshot at the first-execution step
|
||||
// Initialisation at the ctor or read level is not possible
|
||||
// since the operand field is not available in the database
|
||||
void initialise();
|
||||
|
||||
//- Create operand snapshot by using
|
||||
//- current-time and previous-time operand fields
|
||||
void snapshot();
|
||||
|
||||
//- Get operand field based on its base type
|
||||
template<class Type>
|
||||
bool getSnapshot();
|
||||
|
||||
//- Get operand field based on its geometric field type
|
||||
// Move previous-time field into previous-time slot in snapshot
|
||||
// copy new current-time field into current-time slot in snapshot
|
||||
template<class GeoFieldType>
|
||||
bool getSnapshotField();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("DMD");
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from Time and dictionary
|
||||
DMD
|
||||
(
|
||||
const word& name,
|
||||
const Time& runTime,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
//- No copy construct
|
||||
DMD(const DMD&) = delete;
|
||||
|
||||
//- No copy assignment
|
||||
void operator=(const DMD&) = delete;
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~DMD() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Read DMD settings
|
||||
virtual bool read(const dictionary& dict);
|
||||
|
||||
//- Execute DMD
|
||||
virtual bool execute();
|
||||
|
||||
//- Write DMD results
|
||||
virtual bool write();
|
||||
|
||||
//- Write DMD results
|
||||
virtual bool end();
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace functionObjects
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#ifdef NoRepository
|
||||
#include "DMDTemplates.C"
|
||||
#endif
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
78
src/functionObjects/field/DMD/DMDModels/DMDModel/DMDModel.C
Normal file
78
src/functionObjects/field/DMD/DMDModels/DMDModel/DMDModel.C
Normal file
@ -0,0 +1,78 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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 "DMDModel.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
defineTypeNameAndDebug(DMDModel, 0);
|
||||
defineRunTimeSelectionTable(DMDModel, dictionary);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::DMDModel::DMDModel
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
)
|
||||
:
|
||||
writeFile(mesh, name, typeName, dict, false),
|
||||
mesh_(mesh),
|
||||
name_(name)
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
Foam::label Foam::DMDModel::nComponents(const word& fieldName) const
|
||||
{
|
||||
label nComps = 0;
|
||||
bool processed = false;
|
||||
processed = processed || nComponents<scalar>(fieldName, nComps);
|
||||
processed = processed || nComponents<vector>(fieldName, nComps);
|
||||
processed = processed || nComponents<sphericalTensor>(fieldName, nComps);
|
||||
processed = processed || nComponents<symmTensor>(fieldName, nComps);
|
||||
processed = processed || nComponents<tensor>(fieldName, nComps);
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< " # Unknown type of input field during initialisation = "
|
||||
<< fieldName << " #" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
return nComps;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
203
src/functionObjects/field/DMD/DMDModels/DMDModel/DMDModel.H
Normal file
203
src/functionObjects/field/DMD/DMDModels/DMDModel/DMDModel.H
Normal file
@ -0,0 +1,203 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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::DMDModels
|
||||
|
||||
Description
|
||||
A namespace for various dynamic mode
|
||||
decomposition (DMD) model implementations.
|
||||
|
||||
Class
|
||||
Foam::DMDModel
|
||||
|
||||
Description
|
||||
Abstract base class for DMD models to handle DMD
|
||||
characteristics for the \c DMD function object.
|
||||
|
||||
SourceFiles
|
||||
DMDModel.C
|
||||
DMDModelNew.C
|
||||
DMDModelTemplates.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DMDModel_H
|
||||
#define DMDModel_H
|
||||
|
||||
#include "fvMesh.H"
|
||||
#include "dictionary.H"
|
||||
#include "HashSet.H"
|
||||
#include "runTimeSelectionTables.H"
|
||||
#include "writeFile.H"
|
||||
#include "OFstream.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class DMDModel Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class DMDModel
|
||||
:
|
||||
public functionObjects::writeFile
|
||||
{
|
||||
typedef SquareMatrix<scalar> SMatrix;
|
||||
typedef RectangularMatrix<scalar> RMatrix;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Protected Data
|
||||
|
||||
//- Reference to the mesh
|
||||
const fvMesh& mesh_;
|
||||
|
||||
//- Name of operand function object
|
||||
const word name_;
|
||||
|
||||
|
||||
// Protected Member Functions
|
||||
|
||||
// Evaluation
|
||||
|
||||
//- Compute and write mode dynamics
|
||||
virtual bool dynamics() = 0;
|
||||
|
||||
//- Compute and write modes
|
||||
virtual bool modes() = 0;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("DMDModel");
|
||||
|
||||
|
||||
// Declare runtime constructor selection table
|
||||
|
||||
declareRunTimeSelectionTable
|
||||
(
|
||||
autoPtr,
|
||||
DMDModel,
|
||||
dictionary,
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
),
|
||||
(mesh, name, dict)
|
||||
);
|
||||
|
||||
|
||||
// Selectors
|
||||
|
||||
//- Return a reference to the selected DMD model
|
||||
static autoPtr<DMDModel> New
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from components
|
||||
DMDModel
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
//- No copy construct
|
||||
DMDModel(const DMDModel&) = delete;
|
||||
|
||||
//- No copy assignment
|
||||
void operator=(const DMDModel&) = delete;
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~DMDModel() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
// Process
|
||||
|
||||
//- Initialise model data members with a given snapshot
|
||||
virtual bool initialise(const RMatrix& snapshot) = 0;
|
||||
|
||||
//- Update model data members with a given snapshot
|
||||
virtual bool update(const RMatrix& snapshot) = 0;
|
||||
|
||||
//- Compute and write modes and
|
||||
//- mode dynamics of model data members
|
||||
virtual bool fit() = 0;
|
||||
|
||||
//- Compute and write a reconstruction of flow field
|
||||
//- based on given modes and mode dynamics (currently no-op)
|
||||
virtual void reconstruct(const wordList modes)
|
||||
{
|
||||
NotImplemented;
|
||||
}
|
||||
|
||||
|
||||
// Access
|
||||
|
||||
//- Return number of components of the base type of a given field
|
||||
label nComponents(const word& fieldName) const;
|
||||
|
||||
//- Get the number of components of the base type of a given field
|
||||
template<class Type>
|
||||
bool nComponents(const word& fieldName, label& nComps) const;
|
||||
|
||||
|
||||
// IO
|
||||
|
||||
//- Read model settings
|
||||
virtual bool read(const dictionary& dict) = 0;
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#ifdef NoRepository
|
||||
#include "DMDModelTemplates.C"
|
||||
#endif
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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 "DMDModel.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::autoPtr<Foam::DMDModel> Foam::DMDModel::New
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
const word modelType(dict.get<word>("DMDModel"));
|
||||
|
||||
auto cstrIter = dictionaryConstructorTablePtr_->cfind(modelType);
|
||||
|
||||
if (!cstrIter.found())
|
||||
{
|
||||
FatalIOErrorInLookup
|
||||
(
|
||||
dict,
|
||||
"DMDModel",
|
||||
modelType,
|
||||
*dictionaryConstructorTablePtr_
|
||||
) << exit(FatalIOError);
|
||||
}
|
||||
|
||||
return autoPtr<DMDModel>(cstrIter()(mesh, name, dict));
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2021 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"
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
template<class Type>
|
||||
bool Foam::DMDModel::nComponents(const word& fieldName, label& nComps) const
|
||||
{
|
||||
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
|
||||
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
|
||||
|
||||
if (mesh_.foundObject<VolFieldType>(fieldName))
|
||||
{
|
||||
nComps = pTraits<typename VolFieldType::value_type>::nComponents;
|
||||
return true;
|
||||
}
|
||||
else if (mesh_.foundObject<SurfaceFieldType>(fieldName))
|
||||
{
|
||||
nComps = pTraits<typename SurfaceFieldType::value_type>::nComponents;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
988
src/functionObjects/field/DMD/DMDModels/derived/STDMD/STDMD.C
Normal file
988
src/functionObjects/field/DMD/DMDModels/derived/STDMD/STDMD.C
Normal file
@ -0,0 +1,988 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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 "STDMD.H"
|
||||
#include "EigenMatrix.H"
|
||||
#include "QRMatrix.H"
|
||||
#include "addToRunTimeSelectionTable.H"
|
||||
|
||||
using namespace Foam::constant::mathematical;
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace DMDModels
|
||||
{
|
||||
defineTypeNameAndDebug(STDMD, 0);
|
||||
addToRunTimeSelectionTable(DMDModel, STDMD, dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
const Foam::Enum
|
||||
<
|
||||
Foam::DMDModels::STDMD::modeSorterType
|
||||
>
|
||||
Foam::DMDModels::STDMD::modeSorterTypeNames
|
||||
({
|
||||
{ modeSorterType::FIRST_SNAPSHOT, "firstSnapshot" },
|
||||
{ modeSorterType::KIEWAT , "kiewat" },
|
||||
{ modeSorterType::KOU_ZHANG , "kouZhang" }
|
||||
});
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
Foam::scalar Foam::DMDModels::STDMD::L2norm(const RMatrix& z) const
|
||||
{
|
||||
#ifdef FULLDEBUG
|
||||
// Check if the given RectangularMatrix is effectively a column vector
|
||||
if (z.n() != 1)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< " # Input matrix is not a column vector. #"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
#endif
|
||||
const bool noSqrt = true;
|
||||
scalar result = z.columnNorm(0, noSqrt);
|
||||
reduce(result, sumOp<scalar>());
|
||||
|
||||
// Heuristic addition to avoid very small or zero norm
|
||||
return max(SMALL, Foam::sqrt(result));
|
||||
}
|
||||
|
||||
|
||||
Foam::RectangularMatrix<Foam::scalar> Foam::DMDModels::STDMD::orthonormalise
|
||||
(
|
||||
RMatrix ez
|
||||
) const
|
||||
{
|
||||
RMatrix dz(Q_.n(), 1, Zero);
|
||||
|
||||
for (label i = 0; i < nGramSchmidt_; ++i)
|
||||
{
|
||||
dz = Q_ & ez;
|
||||
reduce(dz, sumOp<RMatrix>());
|
||||
ez -= Q_*dz;
|
||||
}
|
||||
|
||||
return ez;
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::expand(const RMatrix& ez, const scalar ezNorm)
|
||||
{
|
||||
Info<< tab << "Expanding orthonormal basis for field: " << fieldName_
|
||||
<< endl;
|
||||
|
||||
// Stack a column "(ez/ezNorm)" to "Q"
|
||||
Q_.resize(Q_.m(), Q_.n() + 1);
|
||||
Q_.subColumn(Q_.n() - 1) = ez/ezNorm;
|
||||
|
||||
// Stack a row (Zero) and column (Zero) to "G"
|
||||
G_.resize(G_.m() + 1);
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::compress()
|
||||
{
|
||||
Info<< tab << "Compressing orthonormal basis for field: " << fieldName_
|
||||
<< endl;
|
||||
|
||||
RMatrix q(1, 1, Zero);
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
const bool symmetric = true;
|
||||
const EigenMatrix<scalar> EM(G_, symmetric);
|
||||
const SquareMatrix<scalar>& EVecs = EM.EVecs();
|
||||
DiagonalMatrix<scalar> EVals(EM.EValsRe());
|
||||
|
||||
// Sort eigenvalues in descending order, and track indices
|
||||
const auto descend = [&](scalar a, scalar b){ return a > b; };
|
||||
const List<label> permutation(EVals.sortPermutation(descend));
|
||||
EVals.applyPermutation(permutation);
|
||||
EVals.resize(EVals.size() - 1);
|
||||
|
||||
// Update "G"
|
||||
G_ = SMatrix(maxRank_, Zero);
|
||||
G_.diag(EVals);
|
||||
|
||||
q.resize(Q_.n(), maxRank_);
|
||||
for (label i = 0; i < maxRank_; ++i)
|
||||
{
|
||||
q.subColumn(i) = EVecs.subColumn(permutation[i]);
|
||||
}
|
||||
}
|
||||
Pstream::scatter(G_);
|
||||
Pstream::scatter(q);
|
||||
|
||||
// Update "Q"
|
||||
Q_ = Q_*q;
|
||||
}
|
||||
|
||||
|
||||
Foam::SquareMatrix<Foam::scalar> Foam::DMDModels::STDMD::
|
||||
reducedKoopmanOperator()
|
||||
{
|
||||
Info<< tab << "Computing Atilde" << endl;
|
||||
|
||||
Info<< tab << "Computing local Rx" << endl;
|
||||
|
||||
RMatrix Rx(1, 1, Zero);
|
||||
{
|
||||
QRMatrix<RMatrix> QRM
|
||||
(
|
||||
Qupper_,
|
||||
QRMatrix<RMatrix>::outputTypes::REDUCED_R,
|
||||
QRMatrix<RMatrix>::colPivoting::FALSE
|
||||
);
|
||||
Rx = QRM.R();
|
||||
}
|
||||
Rx.round();
|
||||
|
||||
// Convenience objects A1, A2, A3 to compute "Atilde"
|
||||
RMatrix A1(1, 1, Zero);
|
||||
|
||||
if (Pstream::parRun())
|
||||
{
|
||||
// Parallel direct tall-skinny QR decomposition
|
||||
// (BGD:Fig. 5) & (DGHL:Fig. 2)
|
||||
// Tests revealed that the distribution of "Q" does not affect
|
||||
// the final outcome of TSQR decomposition up to sign
|
||||
|
||||
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
|
||||
|
||||
const label myProcNo = Pstream::myProcNo();
|
||||
const label procNoInSubset = myProcNo % nAgglomerationProcs_;
|
||||
|
||||
// Send Rx from sender subset neighbours to receiver subset master
|
||||
if (procNoInSubset != 0)
|
||||
{
|
||||
const label procNoSubsetMaster = myProcNo - procNoInSubset;
|
||||
|
||||
UOPstream toSubsetMaster(procNoSubsetMaster, pBufs);
|
||||
toSubsetMaster << Rx;
|
||||
|
||||
Rx.clear();
|
||||
}
|
||||
|
||||
pBufs.finishedSends();
|
||||
|
||||
// Receive Rx by receiver subset masters
|
||||
if (procNoInSubset == 0)
|
||||
{
|
||||
// Accept only subset masters possessing sender subset neighbours
|
||||
if (myProcNo + 1 < Pstream::nProcs())
|
||||
{
|
||||
for
|
||||
(
|
||||
label nbr = myProcNo + 1;
|
||||
(
|
||||
nbr < myProcNo + nAgglomerationProcs_
|
||||
&& nbr < Pstream::nProcs()
|
||||
);
|
||||
++nbr
|
||||
)
|
||||
{
|
||||
RMatrix recvMtrx;
|
||||
|
||||
UIPstream fromNbr(nbr, pBufs);
|
||||
fromNbr >> recvMtrx;
|
||||
|
||||
// Append received Rx to Rx of receiver subset masters
|
||||
if (recvMtrx.size() > 0)
|
||||
{
|
||||
Rx.resize(Rx.m() + recvMtrx.m(), Rx.n());
|
||||
Rx.subMatrix
|
||||
(
|
||||
Rx.m() - recvMtrx.m(),
|
||||
Rx.n() - recvMtrx.n()
|
||||
) = recvMtrx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply interim QR decomposition on Rx of receiver subset masters
|
||||
QRMatrix<RMatrix> QRM
|
||||
(
|
||||
Rx,
|
||||
QRMatrix<RMatrix>::outputTypes::REDUCED_R,
|
||||
QRMatrix<RMatrix>::storeMethods::IN_PLACE,
|
||||
QRMatrix<RMatrix>::colPivoting::FALSE
|
||||
);
|
||||
Rx.round();
|
||||
}
|
||||
|
||||
pBufs.clear();
|
||||
|
||||
// Send interim Rx from subset masters to the master
|
||||
if (procNoInSubset == 0)
|
||||
{
|
||||
if (!Pstream::master())
|
||||
{
|
||||
UOPstream toMaster(Pstream::masterNo(), pBufs);
|
||||
toMaster << Rx;
|
||||
}
|
||||
}
|
||||
|
||||
pBufs.finishedSends();
|
||||
|
||||
// Receive interim Rx by the master, and apply final operations
|
||||
if (Pstream::master())
|
||||
{
|
||||
for
|
||||
(
|
||||
label nbr = Pstream::masterNo() + nAgglomerationProcs_;
|
||||
nbr < Pstream::nProcs();
|
||||
nbr += nAgglomerationProcs_
|
||||
)
|
||||
{
|
||||
RMatrix recvMtrx;
|
||||
|
||||
UIPstream fromSubsetMaster(nbr, pBufs);
|
||||
fromSubsetMaster >> recvMtrx;
|
||||
|
||||
// Append received Rx to Rx of the master
|
||||
if (recvMtrx.size() > 0)
|
||||
{
|
||||
Rx.resize(Rx.m() + recvMtrx.m(), Rx.n());
|
||||
Rx.subMatrix
|
||||
(
|
||||
Rx.m() - recvMtrx.m(),
|
||||
Rx.n() - recvMtrx.n()
|
||||
) = recvMtrx;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< tab << "Computing TSQR" << endl;
|
||||
QRMatrix<RMatrix> QRM
|
||||
(
|
||||
Rx,
|
||||
QRMatrix<RMatrix>::outputTypes::REDUCED_R,
|
||||
QRMatrix<RMatrix>::storeMethods::IN_PLACE,
|
||||
QRMatrix<RMatrix>::colPivoting::FALSE
|
||||
);
|
||||
Rx.round();
|
||||
|
||||
// Rx produced by TSQR is unique up to the sign, hence the revert
|
||||
for (scalar& x : Rx)
|
||||
{
|
||||
x *= -1;
|
||||
}
|
||||
|
||||
Info<< tab << "Computing RxInv" << endl;
|
||||
RxInv_ = pinv(Rx);
|
||||
|
||||
Info<< tab << "Computing A1" << endl;
|
||||
A1 = RxInv_*RMatrix(pinv(Rx*(G_^Rx)));
|
||||
Rx.clear();
|
||||
}
|
||||
Pstream::scatter(RxInv_);
|
||||
Pstream::scatter(A1);
|
||||
|
||||
Info<< tab << "Computing A2" << endl;
|
||||
SMatrix A2(Qupper_ & Qlower_);
|
||||
reduce(A2, sumOp<SMatrix>());
|
||||
Qlower_.clear();
|
||||
|
||||
Info<< tab << "Computing A3" << endl;
|
||||
SMatrix A3(Qupper_ & Qupper_);
|
||||
reduce(A3, sumOp<SMatrix>());
|
||||
|
||||
Info<< tab << "Computing Atilde" << endl;
|
||||
// by optimized matrix chain multiplication
|
||||
// obtained by dynamic programming memoisation
|
||||
return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< tab << "Computing RxInv" << endl;
|
||||
RxInv_ = pinv(Rx);
|
||||
|
||||
Info<< tab << "Computing A1" << endl;
|
||||
A1 = RxInv_*RMatrix(pinv(Rx*(G_^Rx)));
|
||||
Rx.clear();
|
||||
|
||||
Info<< tab << "Computing A2" << endl;
|
||||
SMatrix A2(Qupper_ & Qlower_);
|
||||
Qlower_.clear();
|
||||
|
||||
Info<< tab << "Computing A3" << endl;
|
||||
SMatrix A3(Qupper_ & Qupper_);
|
||||
|
||||
Info<< tab << "Computing Atilde" << endl;
|
||||
return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::eigendecomposition(SMatrix& Atilde)
|
||||
{
|
||||
bool fail = false;
|
||||
|
||||
// Compute eigenvalues, and clip eigenvalues with values < "minEval"
|
||||
if (Pstream::master())
|
||||
{
|
||||
Info<< tab << "Computing eigendecomposition" << endl;
|
||||
|
||||
// Replace "Atilde" by eigenvalues (in-place eigendecomposition)
|
||||
const EigenMatrix<scalar> EM(Atilde);
|
||||
const DiagonalMatrix<scalar>& evalsRe = EM.EValsRe();
|
||||
const DiagonalMatrix<scalar>& evalsIm = EM.EValsIm();
|
||||
|
||||
evals_.resize(evalsRe.size());
|
||||
evecs_ = RCMatrix(EM.complexEVecs());
|
||||
|
||||
// Convert scalar eigenvalue pairs to complex eigenvalues
|
||||
label i = 0;
|
||||
for (auto& eval : evals_)
|
||||
{
|
||||
eval = complex(evalsRe[i], evalsIm[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
Info<< tab << "Filtering eigenvalues" << endl;
|
||||
|
||||
List<complex> cp(evals_.size());
|
||||
auto it =
|
||||
std::copy_if
|
||||
(
|
||||
evals_.cbegin(),
|
||||
evals_.cend(),
|
||||
cp.begin(),
|
||||
[&](const complex& x){ return mag(x) > minEval_; }
|
||||
);
|
||||
cp.resize(std::distance(cp.begin(), it));
|
||||
|
||||
if (cp.size() == 0)
|
||||
{
|
||||
WarningInFunction
|
||||
<< "No eigenvalue with mag(eigenvalue) larger than "
|
||||
<< "minEval = " << minEval_ << " was found." << nl
|
||||
<< " Input field may contain only zero-value elements," << nl
|
||||
<< " e.g. no-slip velocity condition on a given patch." << nl
|
||||
<< " Otherwise, please decrease the value of minEval." << nl
|
||||
<< " Skipping further dynamics/mode computations." << nl
|
||||
<< endl;
|
||||
|
||||
fail = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
evals_ = cp;
|
||||
}
|
||||
}
|
||||
Pstream::scatter(fail);
|
||||
|
||||
if (fail)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Pstream::scatter(evals_);
|
||||
Pstream::scatter(evecs_);
|
||||
|
||||
Atilde.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::frequencies()
|
||||
{
|
||||
if (Pstream::master())
|
||||
{
|
||||
Info<< tab << "Computing frequencies" << endl;
|
||||
|
||||
freqs_.resize(evals_.size());
|
||||
|
||||
// Frequency equation (K:Eq. 81)
|
||||
auto frequencyEquation =
|
||||
[&](const complex& eval)
|
||||
{
|
||||
return Foam::log(max(eval, complex(SMALL))).imag()/(twoPi*dt_);
|
||||
};
|
||||
|
||||
// Compute frequencies
|
||||
std::transform
|
||||
(
|
||||
evals_.cbegin(),
|
||||
evals_.cend(),
|
||||
freqs_.begin(),
|
||||
frequencyEquation
|
||||
);
|
||||
|
||||
Info<< tab << "Computing frequency indices" << endl;
|
||||
|
||||
// Initialise iterator by the first search
|
||||
auto margin = [&](const scalar& x){ return (fMin_ <= x && x < fMax_); };
|
||||
|
||||
auto it = std::find_if(freqs_.cbegin(), freqs_.cend(), margin);
|
||||
|
||||
while (it != freqs_.end())
|
||||
{
|
||||
freqsi_.append(std::distance(freqs_.cbegin(), it));
|
||||
it = std::find_if(std::next(it), freqs_.cend(), margin);
|
||||
}
|
||||
}
|
||||
Pstream::scatter(freqs_);
|
||||
Pstream::scatter(freqsi_);
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::amplitudes()
|
||||
{
|
||||
const IOField<scalar> snapshot0
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"snapshot0_" + name_ + "_" + fieldName_,
|
||||
timeName0_,
|
||||
mesh_,
|
||||
IOobject::MUST_READ,
|
||||
IOobject::NO_WRITE,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
RMatrix snapshot(1, 1, Zero);
|
||||
if (!empty_)
|
||||
{
|
||||
snapshot.resize(Qupper_.m(), 1);
|
||||
std::copy(snapshot0.cbegin(), snapshot0.cend(), snapshot.begin());
|
||||
}
|
||||
|
||||
RMatrix R((RxInv_.T()^Qupper_)*snapshot);
|
||||
reduce(R, sumOp<RMatrix>());
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
Info<< tab << "Computing amplitudes" << endl;
|
||||
|
||||
amps_.resize(R.m());
|
||||
const RCMatrix pEvecs(pinv(evecs_));
|
||||
|
||||
// amps_ = pEvecs*R;
|
||||
for (label i = 0; i < amps_.size(); ++i)
|
||||
{
|
||||
for (label j = 0; j < R.m(); ++j)
|
||||
{
|
||||
amps_[i] += pEvecs(i, j)*R(j, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Pstream::scatter(amps_);
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::magnitudes()
|
||||
{
|
||||
if (Pstream::master())
|
||||
{
|
||||
Info<< tab << "Computing magnitudes" << endl;
|
||||
|
||||
mags_.resize(amps_.size());
|
||||
|
||||
Info<< tab << "Sorting modes with ";
|
||||
|
||||
switch (modeSorter_)
|
||||
{
|
||||
case modeSorterType::FIRST_SNAPSHOT:
|
||||
{
|
||||
Info<< "method of first snapshot" << endl;
|
||||
|
||||
std::transform
|
||||
(
|
||||
amps_.cbegin(),
|
||||
amps_.cend(),
|
||||
mags_.begin(),
|
||||
[&](const complex& val){ return mag(val); }
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case modeSorterType::KIEWAT:
|
||||
{
|
||||
Info<< "modified weighted amplitude scaling method" << endl;
|
||||
|
||||
// Eigendecomposition returns evecs with
|
||||
// the unity norm, hence "modeNorm = 1"
|
||||
const scalar modeNorm = 1;
|
||||
const scalar pr = 1;
|
||||
List<scalar> w(step_);
|
||||
std::iota(w.begin(), w.end(), 1);
|
||||
w = sin(twoPi/step_*(w - 1 - 0.25*step_))*pr + pr;
|
||||
|
||||
forAll(amps_, i)
|
||||
{
|
||||
mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case modeSorterType::KOU_ZHANG:
|
||||
{
|
||||
Info<< "weighted amplitude scaling method" << endl;
|
||||
|
||||
const scalar modeNorm = 1;
|
||||
const List<scalar> w(step_, 1.0);
|
||||
|
||||
forAll(amps_, i)
|
||||
{
|
||||
mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Info<< tab << "Computing magnitude indices" << endl;
|
||||
|
||||
magsi_ = freqsi_;
|
||||
|
||||
auto descend =
|
||||
[&](const label i1, const label i2)
|
||||
{
|
||||
return !(mags_[i1] < mags_[i2]);
|
||||
};
|
||||
|
||||
std::sort(magsi_.begin(), magsi_.end(), descend);
|
||||
}
|
||||
Pstream::scatter(mags_);
|
||||
Pstream::scatter(magsi_);
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::DMDModels::STDMD::sorter
|
||||
(
|
||||
const List<scalar>& weight,
|
||||
const complex& amplitude,
|
||||
const complex& eigenvalue,
|
||||
const scalar modeNorm
|
||||
) const
|
||||
{
|
||||
// Omit eigenvalues with very large or very small mags
|
||||
if (!(mag(eigenvalue) < GREAT && mag(eigenvalue) > VSMALL))
|
||||
{
|
||||
Info<< " Returning zero magnitude for mag(eigenvalue) = "
|
||||
<< mag(eigenvalue) << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Omit eigenvalue-STDMD step combinations that pose a risk of overflow
|
||||
if (mag(eigenvalue)*step_ > sortLimiter_)
|
||||
{
|
||||
Info<< " Returning zero magnitude for"
|
||||
<< " mag(eigenvalue) = " << mag(eigenvalue)
|
||||
<< " current index = " << step_
|
||||
<< " sortLimiter = " << sortLimiter_
|
||||
<< endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
scalar magnitude = 0;
|
||||
|
||||
for (label j = 0; j < step_; ++j)
|
||||
{
|
||||
magnitude += weight[j]*modeNorm*mag(amplitude*pow(eigenvalue, j + 1));
|
||||
}
|
||||
|
||||
return magnitude;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::dynamics()
|
||||
{
|
||||
SMatrix Atilde(reducedKoopmanOperator());
|
||||
G_.clear();
|
||||
|
||||
if(!eigendecomposition(Atilde))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
frequencies();
|
||||
|
||||
amplitudes();
|
||||
|
||||
magnitudes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::modes()
|
||||
{
|
||||
Info<< tab << "Computing modes" << endl;
|
||||
|
||||
bool processed = false;
|
||||
processed = processed || modes<scalar>();
|
||||
processed = processed || modes<vector>();
|
||||
processed = processed || modes<sphericalTensor>();
|
||||
processed = processed || modes<symmTensor>();
|
||||
processed = processed || modes<tensor>();
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::writeToFile(const word& fileName) const
|
||||
{
|
||||
Info<< tab << "Writing objects of dynamics" << endl;
|
||||
|
||||
// Write objects of dynamics
|
||||
{
|
||||
autoPtr<OFstream> osPtr =
|
||||
createFile
|
||||
(
|
||||
fileName + "_" + fieldName_,
|
||||
mesh_.time().timeOutputValue()
|
||||
);
|
||||
OFstream& os = osPtr.ref();
|
||||
|
||||
writeFileHeader(os);
|
||||
|
||||
for (const auto& i : labelRange(0, freqs_.size()))
|
||||
{
|
||||
os << freqs_[i] << tab
|
||||
<< mags_[i] << tab
|
||||
<< amps_[i].real() << tab
|
||||
<< amps_[i].imag() << tab
|
||||
<< evals_[i].real() << tab
|
||||
<< evals_[i].imag() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< tab << "Ends output processing for field: " << fieldName_
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::writeFileHeader(Ostream& os) const
|
||||
{
|
||||
writeHeader(os, "DMD output");
|
||||
writeCommented(os, "Frequency");
|
||||
writeTabbed(os, "Magnitude");
|
||||
writeTabbed(os, "Amplitude (real)");
|
||||
writeTabbed(os, "Amplitude (imag)");
|
||||
writeTabbed(os, "Eigenvalue (real)");
|
||||
writeTabbed(os, "Eigenvalue (imag)");
|
||||
os << endl;
|
||||
}
|
||||
|
||||
|
||||
void Foam::DMDModels::STDMD::filter()
|
||||
{
|
||||
Info<< tab << "Filtering objects of dynamics" << endl;
|
||||
|
||||
// Filter objects according to iMags
|
||||
filterIndexed(evals_, magsi_);
|
||||
filterIndexed(evecs_, magsi_);
|
||||
filterIndexed(freqs_, magsi_);
|
||||
filterIndexed(amps_, magsi_);
|
||||
filterIndexed(mags_, magsi_);
|
||||
|
||||
// Clip objects if need be (assuming objects have the same len)
|
||||
if (freqs_.size() > nModes_)
|
||||
{
|
||||
evals_.resize(nModes_);
|
||||
evecs_.resize(evecs_.m(), nModes_);
|
||||
freqs_.resize(nModes_);
|
||||
amps_.resize(nModes_);
|
||||
mags_.resize(nModes_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::DMDModels::STDMD::STDMD
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
)
|
||||
:
|
||||
DMDModel(mesh, name, dict),
|
||||
modeSorter_
|
||||
(
|
||||
modeSorterTypeNames.getOrDefault
|
||||
(
|
||||
"modeSorter",
|
||||
dict,
|
||||
modeSorterType::FIRST_SNAPSHOT
|
||||
)
|
||||
),
|
||||
Q_(),
|
||||
G_(),
|
||||
Qupper_(1, 1, Zero),
|
||||
Qlower_(1, 1, Zero),
|
||||
RxInv_(1, 1, Zero),
|
||||
evals_(Zero),
|
||||
evecs_(1, 1, Zero),
|
||||
freqs_(Zero),
|
||||
freqsi_(),
|
||||
amps_(Zero),
|
||||
mags_(Zero),
|
||||
magsi_(Zero),
|
||||
fieldName_(dict.get<word>("field")),
|
||||
patch_(dict.getOrDefault<word>("patch", word::null)),
|
||||
timeName0_(),
|
||||
minBasis_(0),
|
||||
minEval_(0),
|
||||
dt_(0),
|
||||
sortLimiter_(500),
|
||||
fMin_(0),
|
||||
fMax_(pTraits<label>::max),
|
||||
nGramSchmidt_(5),
|
||||
maxRank_(pTraits<label>::max),
|
||||
step_(0),
|
||||
nModes_(pTraits<label>::max),
|
||||
nAgglomerationProcs_(20),
|
||||
empty_(false)
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::DMDModels::STDMD::read(const dictionary& dict)
|
||||
{
|
||||
writeToFile_ = dict.getOrDefault<bool>("writeToFile", true);
|
||||
|
||||
modeSorter_ =
|
||||
modeSorterTypeNames.getOrDefault
|
||||
(
|
||||
"modeSorter",
|
||||
dict,
|
||||
modeSorterType::FIRST_SNAPSHOT
|
||||
);
|
||||
|
||||
minBasis_ =
|
||||
dict.getCheckOrDefault<scalar>("minBasis", 1e-8, scalarMinMax::ge(0));
|
||||
|
||||
minEval_ =
|
||||
dict.getCheckOrDefault<scalar>("minEVal", 1e-8, scalarMinMax::ge(0));
|
||||
|
||||
dt_ =
|
||||
dict.getCheckOrDefault<scalar>
|
||||
(
|
||||
"interval",
|
||||
(
|
||||
dict.getCheck<label>("executeInterval", labelMinMax::ge(1))
|
||||
*mesh_.time().deltaT().value()
|
||||
),
|
||||
scalarMinMax::ge(0)
|
||||
);
|
||||
|
||||
sortLimiter_ =
|
||||
dict.getCheckOrDefault<scalar>("sortLimiter", 500, scalarMinMax::ge(1));
|
||||
|
||||
nGramSchmidt_ =
|
||||
dict.getCheckOrDefault<label>("nGramSchmidt", 5, labelMinMax::ge(1));
|
||||
|
||||
maxRank_ =
|
||||
dict.getCheckOrDefault<label>
|
||||
(
|
||||
"maxRank",
|
||||
pTraits<label>::max,
|
||||
labelMinMax::ge(1)
|
||||
);
|
||||
|
||||
nModes_ =
|
||||
dict.getCheckOrDefault<label>
|
||||
(
|
||||
"nModes",
|
||||
pTraits<label>::max,
|
||||
labelMinMax::ge(1)
|
||||
);
|
||||
|
||||
fMin_ = dict.getCheckOrDefault<label>("fMin", 0, labelMinMax::ge(0));
|
||||
|
||||
fMax_ =
|
||||
dict.getCheckOrDefault<label>
|
||||
(
|
||||
"fMax",
|
||||
pTraits<label>::max,
|
||||
labelMinMax::ge(fMin_ + 1)
|
||||
);
|
||||
|
||||
nAgglomerationProcs_ =
|
||||
dict.getCheckOrDefault<label>
|
||||
(
|
||||
"nAgglomerationProcs",
|
||||
20,
|
||||
labelMinMax::ge(1)
|
||||
);
|
||||
|
||||
Info<< tab << "Settings are read for:" << nl
|
||||
<< " field: " << fieldName_ << nl
|
||||
<< " modeSorter: " << modeSorterTypeNames[modeSorter_] << nl
|
||||
<< " nModes: " << nModes_ << nl
|
||||
<< " maxRank: " << maxRank_ << nl
|
||||
<< " nGramSchmidt: " << nGramSchmidt_ << nl
|
||||
<< " fMin: " << fMin_ << nl
|
||||
<< " fMax: " << fMax_ << nl
|
||||
<< " minBasis: " << minBasis_ << nl
|
||||
<< " minEVal: " << minEval_ << nl
|
||||
<< " sortLimiter: " << sortLimiter_ << nl
|
||||
<< " nAgglomerationProcs: " << nAgglomerationProcs_ << nl
|
||||
<< endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::initialise(const RMatrix& z)
|
||||
{
|
||||
const scalar norm = L2norm(z);
|
||||
|
||||
if (mag(norm) > 0)
|
||||
{
|
||||
// First-processed snapshot required by the mode-sorting
|
||||
// algorithms at the final output computations (K:p. 43)
|
||||
{
|
||||
const label nSnap = z.m()/2;
|
||||
|
||||
if (nSnap == 0)
|
||||
{
|
||||
empty_ = true;
|
||||
}
|
||||
|
||||
scalarField snapshot0(nSnap);
|
||||
std::copy(z.cbegin(), z.cbegin() + nSnap, snapshot0.begin());
|
||||
timeName0_ = mesh_.time().timeName();
|
||||
|
||||
IOField<scalar>
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"snapshot0_" + name_ + "_" + fieldName_,
|
||||
timeName0_,
|
||||
mesh_,
|
||||
IOobject::NO_READ,
|
||||
IOobject::NO_WRITE
|
||||
),
|
||||
snapshot0
|
||||
).write();
|
||||
}
|
||||
|
||||
Q_ = z/norm;
|
||||
G_ = SMatrix(1);
|
||||
G_(0,0) = sqr(norm);
|
||||
|
||||
++step_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::update(const RMatrix& z)
|
||||
{
|
||||
{
|
||||
//- Working copy of the augmented snapshot matrix "z"
|
||||
//- being used in the classical Gram-Schmidt process
|
||||
const RMatrix ez(orthonormalise(z));
|
||||
|
||||
// Check basis for "z" and, if necessary, expand "Q" and "G"
|
||||
const scalar ezNorm = L2norm(ez);
|
||||
const scalar zNorm = L2norm(z);
|
||||
|
||||
if (ezNorm/zNorm > minBasis_)
|
||||
{
|
||||
expand(ez, ezNorm);
|
||||
}
|
||||
}
|
||||
|
||||
// Update "G" before the potential orthonormal basis compression
|
||||
{
|
||||
RMatrix zTilde(Q_ & z);
|
||||
reduce(zTilde, sumOp<RMatrix>());
|
||||
|
||||
G_ += SMatrix(zTilde^zTilde);
|
||||
}
|
||||
|
||||
// Compress the orthonormal basis if required
|
||||
if (Q_.n() >= maxRank_)
|
||||
{
|
||||
compress();
|
||||
}
|
||||
|
||||
++step_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::DMDModels::STDMD::fit()
|
||||
{
|
||||
// DMD statistics and mode evaluation (K:Fig. 16)
|
||||
const label nSnap = Q_.m()/2;
|
||||
|
||||
// Move upper and lower halves of "Q" to new containers
|
||||
Qupper_ = RMatrix(Q_.subMatrix(0, 0, max(nSnap, 1)));
|
||||
Qlower_ = RMatrix(Q_.subMatrix(nSnap, 0, max(nSnap, 1)));
|
||||
Q_.clear();
|
||||
|
||||
if (!dynamics())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
modes();
|
||||
|
||||
if (Pstream::master() && writeToFile_)
|
||||
{
|
||||
writeToFile(word("dynamics"));
|
||||
|
||||
filter();
|
||||
|
||||
writeToFile(word("filteredDynamics"));
|
||||
}
|
||||
|
||||
step_ = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
530
src/functionObjects/field/DMD/DMDModels/derived/STDMD/STDMD.H
Normal file
530
src/functionObjects/field/DMD/DMDModels/derived/STDMD/STDMD.H
Normal file
@ -0,0 +1,530 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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::DMDModels::STDMD
|
||||
|
||||
Description
|
||||
Streaming Total Dynamic Mode Decomposition (i.e. \c STDMD)
|
||||
is a variant of dynamic mode decomposition.
|
||||
|
||||
Among other Dynamic Mode Decomposition (DMD) variants, \c STDMD is
|
||||
presumed to provide the general DMD method capabilities alongside
|
||||
economised and feasible memory and CPU usage.
|
||||
|
||||
The code implementation corresponds to Figs. 15-16 of the first citation
|
||||
below, more broadly to Section 2.4.
|
||||
|
||||
References:
|
||||
\verbatim
|
||||
DMD and mode-sorting algorithms (tags:K, HRDC, KZ, HWR):
|
||||
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
|
||||
|
||||
Hemati, M. S., Rowley, C. W.,
|
||||
Deem, E. A., & Cattafesta, L. N. (2017).
|
||||
De-biasing the dynamic mode decomposition
|
||||
for applied Koopman spectral analysis of noisy datasets.
|
||||
Theoretical and Computational Fluid Dynamics, 31(4), 349-368.
|
||||
DOI:10.1007/s00162-017-0432-2
|
||||
|
||||
Kou, J., & Zhang, W. (2017).
|
||||
An improved criterion to select
|
||||
dominant modes from dynamic mode decomposition.
|
||||
European Journal of Mechanics-B/Fluids, 62, 109-129.
|
||||
DOI:10.1016/j.euromechflu.2016.11.015
|
||||
|
||||
Hemati, M. S., Williams, M. O., & Rowley, C. W. (2014).
|
||||
Dynamic mode decomposition for large and streaming datasets.
|
||||
Physics of Fluids, 26(11), 111701.
|
||||
DOI:10.1063/1.4901016
|
||||
|
||||
Parallel classical Gram-Schmidt process (tag:Ka):
|
||||
Katagiri, T. (2003).
|
||||
Performance evaluation of parallel
|
||||
Gram-Schmidt re-orthogonalization methods.
|
||||
In: Palma J. M. L. M., Sousa A. A., Dongarra J., Hernández V. (eds)
|
||||
High Performance Computing for Computational Science — VECPAR 2002.
|
||||
Lecture Notes in Computer Science, vol 2565, p. 302-314.
|
||||
Berlin, Heidelberg: Springer.
|
||||
DOI:10.1007/3-540-36569-9_19
|
||||
|
||||
Parallel direct tall-skinny QR decomposition (tags:BGD, DGHL):
|
||||
Benson, A. R., Gleich, D. F., & Demmel, J. (2013).
|
||||
Direct QR factorizations for
|
||||
tall-and-skinny matrices in MapReduce architectures.
|
||||
2013 IEEE International Conference on Big Data.
|
||||
DOI:10.1109/bigdata.2013.6691583
|
||||
|
||||
Demmel, J., Grigori, L., Hoemmen, M., & Langou, J. (2012).
|
||||
Communication-optimal parallel
|
||||
and sequential QR and LU factorizations.
|
||||
SIAM Journal on Scientific Computing, 34(1), A206-A239.
|
||||
DOI:10.1137/080731992
|
||||
\endverbatim
|
||||
|
||||
Usage
|
||||
Minimal example by using \c system/controlDict.functions:
|
||||
\verbatim
|
||||
DMD1
|
||||
{
|
||||
// Mandatory/Optional entries
|
||||
...
|
||||
|
||||
// Mandatory entries (unmodifiable)
|
||||
DMDModel STDMD;
|
||||
|
||||
// Conditional mandatory entries (runtime modifiable)
|
||||
|
||||
// Option-1
|
||||
interval 5.5;
|
||||
|
||||
// Option-2
|
||||
executeInterval 10;
|
||||
|
||||
// Optional entries (runtime modifiable)
|
||||
modeSorter kiewat;
|
||||
nGramSchmidt 5;
|
||||
maxRank 50;
|
||||
nModes 50;
|
||||
fMin 0;
|
||||
fMax 1000000000;
|
||||
nAgglomerationProcs 20;
|
||||
|
||||
// Optional entries (runtime modifiable, yet not recommended)
|
||||
minBasis 0.00000001;
|
||||
minEVal 0.00000001;
|
||||
sortLimiter 500.0;
|
||||
|
||||
// Mandatory/Optional (inherited) entries
|
||||
...
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
where the entries mean:
|
||||
\table
|
||||
Property | Description | Type | Reqd | Deflt
|
||||
DMDModel | Type: STDMD | word | yes | -
|
||||
interval | STDMD time-step size [s] | scalar | cndtnl <!--
|
||||
--> | executeInterval*(current time-step of the simulation)
|
||||
modeSorter | Mode-sorting algorithm model | word | no <!--
|
||||
--> | firstSnapshot
|
||||
nModes | Number of output modes in input frequency range <!--
|
||||
--> | label | no | GREAT
|
||||
maxRank | Max columns in accumulated matrices | label | no | GREAT
|
||||
nGramSchmidt | Number of Gram-Schmidt iterations | label | no | 5
|
||||
fMin | Min (non-negative) output frequency | label | no | 0
|
||||
fMax | Max output frequency | label | no | GREAT
|
||||
nAgglomerationProcs | Number of processors at each agglomeration <!--
|
||||
--> unit during the computation of reduced Koopman <!--
|
||||
--> operator | label | no | 20
|
||||
minBasis | Orthogonal basis expansion threshold | scalar| no | 1e-8
|
||||
minEVal | Min eigenvalue for below eigenvalues are omitted <!--
|
||||
--> | scalar| no | 1e-8
|
||||
sortLimiter | Max allowable magnitude for <!--
|
||||
--> mag(eigenvalue)*(number of DMD steps) to be used in <!--
|
||||
--> modeSorter={kiewat,kouZhang} to avoid overflow errors <!--
|
||||
--> | scalar | no | 500.0
|
||||
\endtable
|
||||
|
||||
Options for the \c modeSorter entry:
|
||||
\verbatim
|
||||
kiewat | Modified weighted-amplitude scaling method
|
||||
kouZhang | Weighted-amplitude scaling method
|
||||
firstSnapshot | First-snapshot amplitude magnitude method
|
||||
\endverbatim
|
||||
|
||||
Note
|
||||
- To specify the STDMD time-step size (not necessarily equal to the
|
||||
time step of the simulation), entries of either \c interval or
|
||||
\c executeInterval must be available (highest to lowest precedence). While
|
||||
\c interval allows users to directly specify the STDMD time-step size
|
||||
in seconds, in absence of \c interval, for convenience,
|
||||
\c executeInterval allows users to compute the STDMD time-step internally
|
||||
by multiplying itself with the current time-step size of the simulation.
|
||||
- Limitation: Restart is currently not available since intermediate writing
|
||||
of STDMD matrices are not supported.
|
||||
- Limitation: Non-physical input (e.g. full-zero fields) can upset STDMD.
|
||||
- Warning: In \c modeSorter algorithms of \c kiewat and \c kouZhang, a
|
||||
function of \f$power(x,y)\f$ exists where \c x is the magnitude of an
|
||||
eigenvalue, and \c y is the number of STDMD snapshots. This function poses
|
||||
a risk of overflow errors since, for example, if \c x ends up above 1.5 with
|
||||
500 snapshots or more, this function automatically throws the floating point
|
||||
error meaning overflow. Therefore, the domain-range of this function is
|
||||
heuristically constrained by the optional entry \c sortLimiter which skips
|
||||
the evaluation of this function for a given
|
||||
mag(eigenvalue)*(no. of STDMD steps), i.e. x*y, whose magnitude is larger
|
||||
than \c sortLimiter.
|
||||
|
||||
See also
|
||||
- Foam::functionObjects::DMD
|
||||
- Foam::DMDModel
|
||||
|
||||
SourceFiles
|
||||
STDMD.C
|
||||
STDMDTemplates.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DMDModels_STDMD_H
|
||||
#define DMDModels_STDMD_H
|
||||
|
||||
#include "DMDModel.H"
|
||||
#include "SquareMatrix.H"
|
||||
#include "RectangularMatrix.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace DMDModels
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class STDMD Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class STDMD
|
||||
:
|
||||
public DMDModel
|
||||
{
|
||||
typedef SquareMatrix<scalar> SMatrix;
|
||||
typedef RectangularMatrix<scalar> RMatrix;
|
||||
typedef RectangularMatrix<complex> RCMatrix;
|
||||
|
||||
|
||||
// Private Enumerations
|
||||
|
||||
//- Options for the mode-sorting algorithm
|
||||
enum modeSorterType : char
|
||||
{
|
||||
FIRST_SNAPSHOT = 0, //!< "First-snapshot amplitude magnitude method"
|
||||
KIEWAT, //!< "Modified weighted-amplitude scaling method"
|
||||
KOU_ZHANG //!< "Weighted-amplitude scaling method"
|
||||
};
|
||||
|
||||
//- Names for modeSorterType
|
||||
static const Enum<modeSorterType> modeSorterTypeNames;
|
||||
|
||||
|
||||
// Private Data
|
||||
|
||||
//- Mode-sorting algorithm
|
||||
enum modeSorterType modeSorter_;
|
||||
|
||||
//- Accumulated-in-time unitary similarity matrix produced by the
|
||||
//- orthonormal decomposition of the augmented snapshot matrix 'z'
|
||||
// (K:Eq. 60)
|
||||
RMatrix Q_;
|
||||
|
||||
//- Accumulated-in-time (squared) upper triangular matrix produced by
|
||||
//- the orthonormal decomposition of the augmented snapshot matrix 'z'
|
||||
// (K:Eq. 64)
|
||||
SMatrix G_;
|
||||
|
||||
//- Upper half of 'Q'
|
||||
RMatrix Qupper_;
|
||||
|
||||
//- Lower half of 'Q'
|
||||
RMatrix Qlower_;
|
||||
|
||||
//- Moore-Penrose pseudo-inverse of 'R' produced by
|
||||
//- the QR decomposition of the last time-step 'Q'
|
||||
RMatrix RxInv_;
|
||||
|
||||
//- Eigenvalues of modes
|
||||
List<complex> evals_;
|
||||
|
||||
//- Eigenvectors of modes
|
||||
RCMatrix evecs_;
|
||||
|
||||
//- (Non-negative) frequencies of modes
|
||||
List<scalar> freqs_;
|
||||
|
||||
//- Indices of 'freqs' where frequencies are
|
||||
//- non-negative and within ['fMin', 'fMax']
|
||||
DynamicList<label> freqsi_;
|
||||
|
||||
//- Amplitudes of modes
|
||||
List<complex> amps_;
|
||||
|
||||
//- (Descending) magnitudes of (complex) amplitudes of modes
|
||||
List<scalar> mags_;
|
||||
|
||||
//- Indices of 'mags'
|
||||
List<label> magsi_;
|
||||
|
||||
//- Name of operand field
|
||||
const word fieldName_;
|
||||
|
||||
//- Name of operand patch
|
||||
const word patch_;
|
||||
|
||||
//- First-processed snapshot required by the mode-sorting
|
||||
//- algorithms at the final output computations (K:p. 43)
|
||||
word timeName0_;
|
||||
|
||||
//- Min value to execute orthogonal basis expansion of 'Q' and 'G'
|
||||
scalar minBasis_;
|
||||
|
||||
//- Min eigenvalue magnitude below where 'evals' are omitted
|
||||
scalar minEval_;
|
||||
|
||||
//- STDMD time-step size that equals to
|
||||
//- (executeInterval of DMD)*(deltaT of simulation) [s]
|
||||
scalar dt_;
|
||||
|
||||
//- Maximum allowable magnitude for mag(eigenvalue)*(number of
|
||||
//- STDMD steps) to be used in modeSorter={kiewat,kouZhang} to
|
||||
//- avoid overflow errors
|
||||
scalar sortLimiter_;
|
||||
|
||||
//- Min frequency: Output only entries corresponding to freqs >= 'fMin'
|
||||
label fMin_;
|
||||
|
||||
//- Max frequency: Output only entries corresponding to freqs <= 'fMax'
|
||||
label fMax_;
|
||||
|
||||
//- Number of maximum iterations of the classical
|
||||
//- Gram-Schmidt procedure for the orthonormalisation
|
||||
label nGramSchmidt_;
|
||||
|
||||
//- Maximum allowable matrix column for 'Q' and 'G'
|
||||
// 'Q' is assumed to always have full-rank, thus 'Q.n() = rank'
|
||||
label maxRank_;
|
||||
|
||||
//- Current execution-step index of STDMD,
|
||||
//- not necessarily that of the simulation
|
||||
label step_;
|
||||
|
||||
//- Number of output modes within input frequency range
|
||||
//- starting from the most energetic mode
|
||||
label nModes_;
|
||||
|
||||
//- Number of processors at each agglomeration unit
|
||||
//- during the computation of reduced Koopman operator
|
||||
label nAgglomerationProcs_;
|
||||
|
||||
//- (Internal) Flag to tag snapshots which are effectively empty
|
||||
bool empty_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
// Process
|
||||
|
||||
//- Return (parallel) L2-norm of a given column vector
|
||||
scalar L2norm(const RMatrix& z) const;
|
||||
|
||||
//- Execute (parallel) classical Gram-Schmidt
|
||||
//- process to orthonormalise 'ez' (Ka:Fig. 5)
|
||||
RMatrix orthonormalise(RMatrix ez) const;
|
||||
|
||||
//- Expand orthonormal bases 'Q' and 'G' by stacking a column
|
||||
//- '(ez/ezNorm)' to 'Q', and a row (Zero) and column (Zero)
|
||||
//- to 'G' if '(ezNorm/zNorm > minBasis)'
|
||||
void expand(const RMatrix& ez, const scalar ezNorm);
|
||||
|
||||
//- Compress orthonormal basis for 'Q' and 'G' if '(Q.n()>maxRank)'
|
||||
void compress();
|
||||
|
||||
|
||||
// Evaluation
|
||||
|
||||
//- Return reduced Koopman operator 'Atilde' (K:Eq. 78)
|
||||
// Also fills 'RxInv'.
|
||||
// The function was not divided into subsections to ensure
|
||||
// minimal usage of memory, hence the complexity of the function
|
||||
SMatrix reducedKoopmanOperator();
|
||||
|
||||
//- Compute eigenvalues and eigenvectors of 'Atilde'
|
||||
// Remove any eigenvalues whose magnitude is smaller than
|
||||
// 'minEVal' while keeping the order of elements the same
|
||||
bool eigendecomposition(SMatrix& Atilde);
|
||||
|
||||
//- Compute and filter frequencies and its indices (K:Eq. 81)
|
||||
// Indices of 'freqs' where frequencies are
|
||||
// non-negative and within ['fMin', 'fMax']
|
||||
void frequencies();
|
||||
|
||||
//- Compute amplitudes
|
||||
void amplitudes();
|
||||
|
||||
//- Compute magnitudes and ordered magnitude indices
|
||||
// Includes advanced sorting methods using
|
||||
// the chosen weighted amplitude scaling method
|
||||
void magnitudes();
|
||||
|
||||
//- Eigenvalue weighted amplitude scaling (KZ:Eq. 33)
|
||||
//- Modified eigenvalue weighted amplitude scaling (K)
|
||||
scalar sorter
|
||||
(
|
||||
const List<scalar>& weight,
|
||||
const complex& amplitude,
|
||||
const complex& eigenvalue,
|
||||
const scalar modeNorm
|
||||
) const;
|
||||
|
||||
//- Compute and write mode dynamics
|
||||
virtual bool dynamics();
|
||||
|
||||
//- Compute and write modes
|
||||
virtual bool modes();
|
||||
|
||||
//- Select field type for modes
|
||||
template<class Type>
|
||||
bool modes();
|
||||
|
||||
//- Compute modes based on input field type
|
||||
template<class GeoFieldType>
|
||||
bool calcModes();
|
||||
|
||||
//- Compute a mode for a given scalar-based input field
|
||||
template<class GeoFieldType>
|
||||
typename std::enable_if
|
||||
<
|
||||
std::is_same<scalar, typename GeoFieldType::value_type>::value,
|
||||
void
|
||||
>::type calcMode
|
||||
(
|
||||
GeoFieldType& modeRe,
|
||||
GeoFieldType& modeIm,
|
||||
const RMatrix& primitiveMode,
|
||||
const label i
|
||||
);
|
||||
|
||||
//- Compute a mode for a given non-scalar-based input field
|
||||
template<class GeoFieldType>
|
||||
typename std::enable_if
|
||||
<
|
||||
!std::is_same<scalar, typename GeoFieldType::value_type>::value,
|
||||
void
|
||||
>::type calcMode
|
||||
(
|
||||
GeoFieldType& modeRe,
|
||||
GeoFieldType& modeIm,
|
||||
const RMatrix& primitiveMode,
|
||||
const label i
|
||||
);
|
||||
|
||||
|
||||
// IO
|
||||
|
||||
//- Write objects of dynamics
|
||||
void writeToFile(const word& fileName) const;
|
||||
|
||||
// Notifying the compiler that we want both writeToFile functions
|
||||
using Foam::functionObjects::writeFile::writeToFile;
|
||||
|
||||
//- Write file header information
|
||||
void writeFileHeader(Ostream& os) const;
|
||||
|
||||
//- Filter objects of dynamics according to 'freqsi' and 'magsi'
|
||||
void filter();
|
||||
|
||||
//- Return a new List containing elems of List at 'indices'
|
||||
template<class Type>
|
||||
void filterIndexed
|
||||
(
|
||||
List<Type>& lst,
|
||||
const UList<label>& indices
|
||||
);
|
||||
|
||||
//- Return a new Matrix containing columns of Matrix at 'indices'
|
||||
template<class MatrixType>
|
||||
void filterIndexed
|
||||
(
|
||||
MatrixType& lst,
|
||||
const UList<label>& indices
|
||||
);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("STDMD");
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from components
|
||||
STDMD
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
const word& name,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
//- No copy construct
|
||||
STDMD(const STDMD&) = delete;
|
||||
|
||||
//- No copy assignment
|
||||
void operator=(const STDMD&) = delete;
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~STDMD() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
// Evaluation
|
||||
|
||||
//- Initialise 'Q' and 'G' (both require the first two snapshots)
|
||||
virtual bool initialise(const RMatrix& z);
|
||||
|
||||
//- Incremental orthonormal basis update (K:Fig. 15)
|
||||
virtual bool update(const RMatrix& z);
|
||||
|
||||
//- Compute and write modes and
|
||||
//- mode dynamics of model data members
|
||||
virtual bool fit();
|
||||
|
||||
|
||||
// IO
|
||||
|
||||
//- Read STDMD settings
|
||||
virtual bool read(const dictionary& dict);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace DMDModels
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#ifdef NoRepository
|
||||
#include "STDMDTemplates.C"
|
||||
#endif
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,236 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020-2021 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 "fixedValueFvPatchField.H"
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
template<class Type>
|
||||
void Foam::DMDModels::STDMD::filterIndexed
|
||||
(
|
||||
List<Type>& lst,
|
||||
const UList<label>& indices
|
||||
)
|
||||
{
|
||||
// Elements within [a, b]
|
||||
List<Type> lstWithin(indices.size());
|
||||
|
||||
// Copy if frequency of element is within [a, b]
|
||||
label j = 0;
|
||||
for (const auto& i : indices)
|
||||
{
|
||||
lstWithin[j] = lst[i];
|
||||
++j;
|
||||
}
|
||||
lst.transfer(lstWithin);
|
||||
}
|
||||
|
||||
|
||||
template<class MatrixType>
|
||||
void Foam::DMDModels::STDMD::filterIndexed
|
||||
(
|
||||
MatrixType& mat,
|
||||
const UList<label>& indices
|
||||
)
|
||||
{
|
||||
// Elements within [a, b]
|
||||
MatrixType matWithin(labelPair(mat.m(), indices.size()));
|
||||
|
||||
// Copy if frequency of element is within [a, b]
|
||||
label j = 0;
|
||||
for (const auto& i : indices)
|
||||
{
|
||||
matWithin.subColumn(j) = mat.subColumn(i);
|
||||
++j;
|
||||
}
|
||||
mat.transfer(matWithin);
|
||||
}
|
||||
|
||||
|
||||
template<class Type>
|
||||
bool Foam::DMDModels::STDMD::modes()
|
||||
{
|
||||
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
|
||||
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
|
||||
|
||||
if (mesh_.foundObject<VolFieldType>(fieldName_))
|
||||
{
|
||||
return calcModes<VolFieldType>();
|
||||
}
|
||||
else if (mesh_.foundObject<SurfaceFieldType>(fieldName_))
|
||||
{
|
||||
return calcModes<SurfaceFieldType>();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class GeoFieldType>
|
||||
bool Foam::DMDModels::STDMD::calcModes()
|
||||
{
|
||||
typedef typename GeoFieldType::value_type Type;
|
||||
|
||||
// Resize the number of output variables to "nModes" if requested
|
||||
if (magsi_.size() > nModes_)
|
||||
{
|
||||
magsi_.resize(nModes_);
|
||||
}
|
||||
|
||||
// Compute and write modes one by one
|
||||
const RMatrix primitiveMode(Qupper_*RxInv_);
|
||||
Qupper_.clear();
|
||||
RxInv_.clear();
|
||||
|
||||
label modei = 0;
|
||||
for (const label i : magsi_)
|
||||
{
|
||||
GeoFieldType modeRe
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"modeRe_" + std::to_string(modei)
|
||||
+ "_" + fieldName_ + "_" + name_,
|
||||
mesh_.time().timeName(),
|
||||
mesh_,
|
||||
IOobject::NO_READ,
|
||||
IOobject::NO_WRITE,
|
||||
false
|
||||
),
|
||||
mesh_,
|
||||
dimensioned<Type>(dimless, Zero),
|
||||
fixedValueFvPatchField<Type>::typeName
|
||||
);
|
||||
|
||||
GeoFieldType modeIm
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"modeIm_" + std::to_string(modei)
|
||||
+ "_" + fieldName_ + "_" + name_,
|
||||
mesh_.time().timeName(),
|
||||
mesh_,
|
||||
IOobject::NO_READ,
|
||||
IOobject::NO_WRITE,
|
||||
false
|
||||
),
|
||||
mesh_,
|
||||
dimensioned<Type>(dimless, Zero),
|
||||
fixedValueFvPatchField<Type>::typeName
|
||||
);
|
||||
|
||||
if (modeRe.size() != 0 && !empty_)
|
||||
{
|
||||
if (patch_.empty())
|
||||
{
|
||||
auto& inModeRe = modeRe.primitiveFieldRef();
|
||||
auto& inModeIm = modeIm.primitiveFieldRef();
|
||||
|
||||
calcMode(inModeRe, inModeIm, primitiveMode, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
const label patchi = mesh_.boundaryMesh().findPatchID(patch_);
|
||||
|
||||
auto& bfModeRe = modeRe.boundaryFieldRef()[patchi];
|
||||
auto& bfModeIm = modeIm.boundaryFieldRef()[patchi];
|
||||
|
||||
calcMode(bfModeRe, bfModeIm, primitiveMode, i);
|
||||
}
|
||||
}
|
||||
|
||||
modeRe.write();
|
||||
modeIm.write();
|
||||
++modei;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class GeoFieldType>
|
||||
typename std::enable_if
|
||||
<
|
||||
std::is_same<Foam::scalar, typename GeoFieldType::value_type>::value,
|
||||
void
|
||||
>::type Foam::DMDModels::STDMD::calcMode
|
||||
(
|
||||
GeoFieldType& modeRe,
|
||||
GeoFieldType& modeIm,
|
||||
const RMatrix& primitiveMode,
|
||||
const label i
|
||||
)
|
||||
{
|
||||
const label fieldSize = modeRe.size();
|
||||
|
||||
for (label p = 0; p < primitiveMode.m(); ++p)
|
||||
{
|
||||
complex mode(Zero);
|
||||
for (label q = 0; q < evecs_.m(); ++q)
|
||||
{
|
||||
mode += primitiveMode(p, q)*evecs_(q, i);
|
||||
}
|
||||
label p1 = p%fieldSize;
|
||||
modeRe[p1] = mode.real();
|
||||
modeIm[p1] = mode.imag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class GeoFieldType>
|
||||
typename std::enable_if
|
||||
<
|
||||
!std::is_same<Foam::scalar, typename GeoFieldType::value_type>::value,
|
||||
void
|
||||
>::type Foam::DMDModels::STDMD::calcMode
|
||||
(
|
||||
GeoFieldType& modeRe,
|
||||
GeoFieldType& modeIm,
|
||||
const RMatrix& primitiveMode,
|
||||
const label i
|
||||
)
|
||||
{
|
||||
const label fieldSize = modeRe.size();
|
||||
|
||||
for (label p = 0; p < primitiveMode.m(); ++p)
|
||||
{
|
||||
complex mode(Zero);
|
||||
for (label q = 0; q < evecs_.m(); ++q)
|
||||
{
|
||||
mode += primitiveMode(p, q)*evecs_(q, i);
|
||||
}
|
||||
label p1 = p%fieldSize;
|
||||
label p2 = p/fieldSize;
|
||||
modeRe[p1][p2] = mode.real();
|
||||
modeIm[p1][p2] = mode.imag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -5,7 +5,7 @@
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2020 OpenCFD Ltd.
|
||||
Copyright (C) 2020-2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -30,112 +30,83 @@ License
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
template<class GeoFieldType>
|
||||
bool Foam::functionObjects::STDMD::getSnapshot()
|
||||
template<class Type>
|
||||
bool Foam::functionObjects::DMD::getSnapshot()
|
||||
{
|
||||
if (!initialised_)
|
||||
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
|
||||
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
|
||||
|
||||
if (foundObject<VolFieldType>(fieldName_))
|
||||
{
|
||||
init();
|
||||
return getSnapshotField<VolFieldType>();
|
||||
}
|
||||
else if (foundObject<SurfaceFieldType>(fieldName_))
|
||||
{
|
||||
return getSnapshotField<SurfaceFieldType>();
|
||||
}
|
||||
|
||||
// Move previous-time snapshot into previous-time slot in z_
|
||||
// Effectively moves the lower half of z_ to its upper half
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class GeoFieldType>
|
||||
bool Foam::functionObjects::DMD::getSnapshotField()
|
||||
{
|
||||
if (step_ == 0)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
if (z_.size() == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move previous-time snapshot into previous-time slot in "z"
|
||||
// Effectively moves the lower half of "z" to its upper half
|
||||
std::rotate(z_.begin(), z_.begin() + nSnap_, z_.end());
|
||||
|
||||
// Copy new current-time snapshot into current-time slot in z_
|
||||
// Effectively copies the new field elements into the lower half of z_
|
||||
const GeoFieldType& Field = lookupObject<GeoFieldType>(fieldName_);
|
||||
const label nField = Field.size();
|
||||
// Copy new current-time snapshot into current-time slot in "z"
|
||||
// Effectively copies the new field elements into the lower half of "z"
|
||||
const label nComps =
|
||||
pTraits<typename GeoFieldType::value_type>::nComponents;
|
||||
|
||||
for (direction dir = 0; dir < nComps_; ++dir)
|
||||
const GeoFieldType& field = lookupObject<GeoFieldType>(fieldName_);
|
||||
|
||||
if (patch_.empty())
|
||||
{
|
||||
z_.subColumn(0, nSnap_ + dir*nField, nField) = Field.component(dir);
|
||||
const label nField = field.size();
|
||||
|
||||
for (direction dir = 0; dir < nComps; ++dir)
|
||||
{
|
||||
z_.subColumn(0, nSnap_ + dir*nField, nField) = field.component(dir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const label patchi = mesh_.boundaryMesh().findPatchID(patch_);
|
||||
|
||||
if (patchi < 0)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Cannot find patch " << patch_
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
const typename GeoFieldType::Boundary& bf = field.boundaryField();
|
||||
|
||||
const Field<typename GeoFieldType::value_type>& pbf = bf[patchi];
|
||||
|
||||
const label nField = pbf.size();
|
||||
|
||||
for (direction dir = 0; dir < nComps; ++dir)
|
||||
{
|
||||
z_.subColumn(0, nSnap_ + dir*nField, nField) = pbf.component(dir);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class Type>
|
||||
bool Foam::functionObjects::STDMD::getSnapshotType()
|
||||
{
|
||||
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
|
||||
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
|
||||
|
||||
if (foundObject<VolFieldType>(fieldName_))
|
||||
{
|
||||
return getSnapshot<VolFieldType>();
|
||||
}
|
||||
else if (foundObject<SurfaceFieldType>(fieldName_))
|
||||
{
|
||||
return getSnapshot<SurfaceFieldType>();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class Type>
|
||||
bool Foam::functionObjects::STDMD::getComps()
|
||||
{
|
||||
typedef GeometricField<Type, fvPatchField, volMesh> VolFieldType;
|
||||
typedef GeometricField<Type, fvsPatchField, surfaceMesh> SurfaceFieldType;
|
||||
|
||||
if (foundObject<VolFieldType>(fieldName_))
|
||||
{
|
||||
nComps_ = pTraits<typename VolFieldType::value_type>::nComponents;
|
||||
return true;
|
||||
}
|
||||
else if (foundObject<SurfaceFieldType>(fieldName_))
|
||||
{
|
||||
nComps_ = pTraits<typename SurfaceFieldType::value_type>::nComponents;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<class Type>
|
||||
void Foam::functionObjects::STDMD::filterIndexed
|
||||
(
|
||||
List<Type>& lst,
|
||||
const UList<label>& indices
|
||||
)
|
||||
{
|
||||
// Elems within [a, b]
|
||||
List<Type> lstWithin(indices.size());
|
||||
|
||||
// Copy if frequency of elem is within [a, b]
|
||||
label j = 0;
|
||||
for (const auto& i : indices)
|
||||
{
|
||||
lstWithin[j] = lst[i];
|
||||
++j;
|
||||
}
|
||||
lst.transfer(lstWithin);
|
||||
}
|
||||
|
||||
|
||||
template<class MatrixType>
|
||||
void Foam::functionObjects::STDMD::filterIndexed
|
||||
(
|
||||
MatrixType& mat,
|
||||
const UList<label>& indices
|
||||
)
|
||||
{
|
||||
// Elems within [a, b]
|
||||
MatrixType matWithin(labelPair(mat.m(), indices.size()));
|
||||
|
||||
// Copy if frequency of elem is within [a, b]
|
||||
label j = 0;
|
||||
for (const auto& i : indices)
|
||||
{
|
||||
matWithin.subColumn(j) = mat.subColumn(i);
|
||||
++j;
|
||||
}
|
||||
mat.transfer(matWithin);
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -121,6 +121,9 @@ stabilityBlendingFactor/stabilityBlendingFactor.C
|
||||
|
||||
interfaceHeight/interfaceHeight.C
|
||||
|
||||
STDMD/STDMD.C
|
||||
DMD/DMD.C
|
||||
DMD/DMDModels/DMDModel/DMDModel.C
|
||||
DMD/DMDModels/DMDModel/DMDModelNew.C
|
||||
DMD/DMDModels/derived/STDMD/STDMD.C
|
||||
|
||||
LIB = $(FOAM_LIBBIN)/libfieldFunctionObjects
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
EXE_INC = \
|
||||
-I$(LIB_SRC)/finiteVolume/lnInclude \
|
||||
-I$(LIB_SRC)/finiteArea/lnInclude \
|
||||
-I$(LIB_SRC)/fileFormats/lnInclude \
|
||||
-I$(LIB_SRC)/surfMesh/lnInclude \
|
||||
-I$(LIB_SRC)/meshTools/lnInclude \
|
||||
@ -22,6 +23,7 @@ EXE_INC = \
|
||||
|
||||
LIB_LIBS = \
|
||||
-lfiniteVolume \
|
||||
-lfiniteArea \
|
||||
-lfileFormats \
|
||||
-lsurfMesh \
|
||||
-lmeshTools \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,645 +0,0 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::functionObjects::STDMD
|
||||
|
||||
Group
|
||||
grpFieldFunctionObjects
|
||||
|
||||
Description
|
||||
(Beta Release) 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.
|
||||
|
||||
The code implementation corresponds to Figs. 15-16 of the first citation
|
||||
below, more broadly to Section 2.4.
|
||||
|
||||
References:
|
||||
\verbatim
|
||||
STDMD and mode-sorting algorithms (tags:K, HRDC, KZ, HWR):
|
||||
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
|
||||
|
||||
Hemati, M. S., Rowley, C. W.,
|
||||
Deem, E. A., & Cattafesta, L. N. (2017).
|
||||
De-biasing the dynamic mode decomposition
|
||||
for applied Koopman spectral analysis of noisy datasets.
|
||||
Theoretical and Computational Fluid Dynamics, 31(4), 349-368.
|
||||
DOI:10.1007/s00162-017-0432-2
|
||||
|
||||
Kou, J., & Zhang, W. (2017).
|
||||
An improved criterion to select
|
||||
dominant modes from dynamic mode decomposition.
|
||||
European Journal of Mechanics-B/Fluids, 62, 109-129.
|
||||
DOI:10.1016/j.euromechflu.2016.11.015
|
||||
|
||||
Hemati, M. S., Williams, M. O., & Rowley, C. W. (2014).
|
||||
Dynamic mode decomposition for large and streaming datasets.
|
||||
Physics of Fluids, 26(11), 111701.
|
||||
DOI:10.1063/1.4901016
|
||||
|
||||
Parallel classical Gram-Schmidt process (tag:Ka):
|
||||
Katagiri, T. (2003).
|
||||
Performance evaluation of parallel
|
||||
Gram-Schmidt re-orthogonalization methods.
|
||||
In: Palma J. M. L. M., Sousa A. A., Dongarra J., Hernández V. (eds)
|
||||
High Performance Computing for Computational Science — VECPAR 2002.
|
||||
Lecture Notes in Computer Science, vol 2565, p. 302-314.
|
||||
Berlin, Heidelberg: Springer.
|
||||
DOI:10.1007/3-540-36569-9_19
|
||||
|
||||
Parallel direct tall-skinny QR decomposition (tags:BGD, DGHL):
|
||||
Benson, A. R., Gleich, D. F., & Demmel, J. (2013).
|
||||
Direct QR factorizations for
|
||||
tall-and-skinny matrices in MapReduce architectures.
|
||||
2013 IEEE International Conference on Big Data.
|
||||
DOI:10.1109/bigdata.2013.6691583
|
||||
|
||||
Demmel, J., Grigori, L., Hoemmen, M., & Langou, J. (2012).
|
||||
Communication-optimal parallel
|
||||
and sequential QR and LU factorizations.
|
||||
SIAM Journal on Scientific Computing, 34(1), A206-A239.
|
||||
DOI:10.1137/080731992
|
||||
|
||||
DMD properties:
|
||||
Brunton S. L. (2018).
|
||||
Dynamic mode decomposition overview.
|
||||
Seattle, Washington: University of Washington.
|
||||
youtu.be/sQvrK8AGCAo (Retrieved:24-04-20)
|
||||
\endverbatim
|
||||
|
||||
Operands:
|
||||
\table
|
||||
Operand | Type | Location
|
||||
input | {vol,surface}\<Type\>Field <!--
|
||||
--> | $FOAM_CASE/\<time\>/\<inpField\>
|
||||
output file | dat <!--
|
||||
--> | $FOAM_CASE/postProcessing/\<FO\>/\<time\>/\<file\>(s)
|
||||
output field | volScalarField(s) | $FOAM_CASE/\<time\>/\<outField\>(s)
|
||||
\endtable
|
||||
|
||||
where \c \<Type\>=Scalar/Vector/SphericalTensor/SymmTensor/Tensor.
|
||||
|
||||
Output fields:
|
||||
\verbatim
|
||||
modeReal<modeIndex><field> | Real part of a mode field
|
||||
modeImag<modeIndex><field> | Imaginary part of a mode field
|
||||
\endverbatim
|
||||
|
||||
Output files:
|
||||
\verbatim
|
||||
uSTDMD.dat | Unfiltered STDMD output
|
||||
STDMD.dat | Filtered STDMD output
|
||||
\endverbatim
|
||||
|
||||
wherein for each mode, the following quantities are output into files:
|
||||
\verbatim
|
||||
Frequency
|
||||
Magnitude
|
||||
Amplitude (real part)
|
||||
Amplitude (imaginary part)
|
||||
Eigenvalue (real part)
|
||||
Eigenvalue (imaginary part)
|
||||
\endverbatim
|
||||
|
||||
Note
|
||||
Operations on boundary fields, e.g. \c wallShearStress, are currently not
|
||||
available.
|
||||
|
||||
Usage
|
||||
Minimal example by using \c system/controlDict.functions:
|
||||
\verbatim
|
||||
STDMD1
|
||||
{
|
||||
// Mandatory entries (unmodifiable)
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field <inpField>;
|
||||
|
||||
// Conditional mandatory entries (unmodifiable)
|
||||
// either of the below
|
||||
stdmdInterval 5.5;
|
||||
executeInterval 10;
|
||||
|
||||
// Optional entries (unmodifiable)
|
||||
modeSorter kiewat;
|
||||
nModes 50;
|
||||
maxRank 50;
|
||||
nGramSchmidt 5;
|
||||
fMin 0;
|
||||
fMax 1000000000;
|
||||
|
||||
// Optional entries (run-time modifiable, yet not recommended)
|
||||
testEigen false;
|
||||
dumpEigen false;
|
||||
minBasis 0.00000001;
|
||||
minEVal 0.00000001;
|
||||
absTol 0.001;
|
||||
relTol 0.0001;
|
||||
sortLimiter 500;
|
||||
|
||||
// Optional (inherited) entries
|
||||
...
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
where the entries mean:
|
||||
\table
|
||||
Property | Description | Type | Req'd | Dflt
|
||||
type | Type name: STDMD | word | yes | -
|
||||
libs | Library name: fieldFunctionObjects | word | yes | -
|
||||
field | Name of the operand field | word | yes | -
|
||||
stdmdInterval | STDMD time-step size [s] | scalar| conditional | <!--
|
||||
--> executeInterval*(current time-step of the simulation)
|
||||
modeSorter | Mode-sorting algorithm variant | word | no | firstSnap
|
||||
nModes | Number of output modes in input freq range | label | no | GREAT
|
||||
maxRank | Max columns in accumulated matrices | label | no | GREAT
|
||||
nGramSchmidt | Number of Gram-Schmidt iterations | label | no | 5
|
||||
fMin | Min (non-negative) output frequency | label | no | 0
|
||||
fMax | Max output frequency | label | no | GREAT
|
||||
testEigen | Flag to verify eigendecompositions | bool | no | false
|
||||
dumpEigen | Flag to log operands of eigendecomps | bool | no | false
|
||||
minBasis | Orthogonal basis expansion threshold | scalar| no | 1e-8
|
||||
minEVal | Min EVal for below EVals are omitted | scalar| no | 1e-8
|
||||
absTol | Min abs tol in eigendecomposition tests | scalar| no | 1e-4
|
||||
relTol | Relative tol in eigendecomposition tests | scalar| no | 1e-6
|
||||
sortLimiter | Maximum allowable magnitude for <!--
|
||||
--> mag(eigenvalue)*(number of STDMD steps) to be used in <!--
|
||||
--> modeSorter={kiewat,kouZhang} to avoid overflow errors <!--
|
||||
--> | scalar | no | 500.0
|
||||
\endtable
|
||||
|
||||
Options for the \c modeSorter entry:
|
||||
\verbatim
|
||||
kiewat | Modified weighted amplitude scaling method
|
||||
kouZhang | Weighted amplitude scaling method
|
||||
firstSnap | Method of first snapshot amplitude magnitude
|
||||
\endverbatim
|
||||
|
||||
The inherited entries are elaborated in:
|
||||
- \link functionObject.H \endlink
|
||||
- \link writeFile.H \endlink
|
||||
|
||||
Usage by \c postProcess utility is not available.
|
||||
|
||||
Note
|
||||
- To specify the STDMD time-step size (not necessarily equal to the
|
||||
time step of the simulation), entries of either \c stdmdInterval or
|
||||
\c executeInterval must be available (highest to lowest precedence). While
|
||||
\c stdmdInterval allows users to directly specify the STDMD time-step size
|
||||
in seconds, in absence of \c stdmdInterval, for convenience,
|
||||
\c executeInterval allows users to compute the STDMD time-step internally
|
||||
by multiplying itself with the current time-step size of the simulation.
|
||||
- Limitation: Restart is currently not available since intermediate writing
|
||||
of STDMD matrices are not supported.
|
||||
- Limitation: Non-physical input (e.g. full-zero fields) may upset STDMD.
|
||||
- Warning: DMD is an active research area at the time of writing; therefore,
|
||||
there would be cases whereat oddities might be encountered.
|
||||
- Warning: This STDMD release is the \b beta release; therefore,
|
||||
small-to-medium changes in input/output interfaces and internal structures
|
||||
should be expected in the next versions.
|
||||
- Warning: In \c modeSorter algorithms of \c kiewat and \c kouZhang, there
|
||||
exists a function of \f$power(x,y)\f$ where \c x is the magnitude of an
|
||||
eigenvalue, and \c y is the number of STDMD snapshots. This function poses
|
||||
a risk of overflow errors since, for example, if \c x ends up above 1.5 with
|
||||
500 snapshots or more, this function automatically throws the floating point
|
||||
error meaning overflow. Therefore, the domain-range of this function is
|
||||
heuristically constrained by the optional entry \c sortLimiter which skips
|
||||
the evaluation of this function for a given
|
||||
mag(eigenvalue)*(no. of STDMD steps), i.e. x*y, whose magnitude is larger
|
||||
than \c sortLimiter.
|
||||
|
||||
See also
|
||||
- Foam::functionObjects::fvMeshFunctionObject
|
||||
- Foam::functionObjects::writeFile
|
||||
- ExtendedCodeGuide::functionObjects::field::STDMD
|
||||
|
||||
SourceFiles
|
||||
STDMD.C
|
||||
STDMDTemplates.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef functionObjects_STDMD_H
|
||||
#define functionObjects_STDMD_H
|
||||
|
||||
#include "fvMeshFunctionObject.H"
|
||||
#include "writeFile.H"
|
||||
#include "EigenMatrix.H"
|
||||
#include "QRMatrix.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace functionObjects
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class STDMD Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class STDMD
|
||||
:
|
||||
public fvMeshFunctionObject,
|
||||
public writeFile
|
||||
{
|
||||
|
||||
typedef SquareMatrix<scalar> SMatrix;
|
||||
typedef SquareMatrix<complex> SCMatrix;
|
||||
typedef RectangularMatrix<scalar> RMatrix;
|
||||
typedef RectangularMatrix<complex> RCMatrix;
|
||||
|
||||
|
||||
// Private Enumerations
|
||||
|
||||
//- Options for the mode-sorting algorithm
|
||||
enum modeSorterType
|
||||
{
|
||||
KIEWAT, //!< "Modified weighted amplitude scaling method"
|
||||
KOU_ZHANG, //!< "Weighted amplitude scaling method"
|
||||
FIRST_SNAPSHOT //!< "Method of first snapshot amplitude magnitude"
|
||||
};
|
||||
|
||||
//- Names for modeSorterType
|
||||
static const Enum<modeSorterType> modeSorterTypeNames;
|
||||
|
||||
|
||||
// Private Data
|
||||
|
||||
//- Mode-sorting algorithm (default=modeSorterType::FIRST_SNAPSHOT)
|
||||
const enum modeSorterType modeSorter_;
|
||||
|
||||
//- Name of the operand volume or surface field
|
||||
const word fieldName_;
|
||||
|
||||
//- Flag: First execution-step initialisation
|
||||
bool initialised_;
|
||||
|
||||
//- Flag: Verify eigendecompositions by using theoretical relations
|
||||
bool testEigen_;
|
||||
|
||||
//- Flag: Write operands of eigendecompositions to log
|
||||
// To activate it, testEigen=true
|
||||
bool dumpEigen_;
|
||||
|
||||
//- Number of output modes within input frequency range
|
||||
//- starting from the most energetic mode
|
||||
const label nModes_;
|
||||
|
||||
//- Maximum allowable matrix column for Qz_ and Gz_
|
||||
// Qz_ is assumed to always have full-rank, thus Qz_.n() = rank
|
||||
const label maxRank_;
|
||||
|
||||
//- Number of maximum iterations of the classical Gram-Schmidt procedure
|
||||
const label nGramSchmidt_;
|
||||
|
||||
//- Min frequency: Output only entries corresponding to freqs >= fMin
|
||||
const label fMin_;
|
||||
|
||||
//- Max frequency: Output only entries corresponding to freqs <= fMax
|
||||
const label fMax_;
|
||||
|
||||
//- Number of base components of input field, e.g. 3 for vector
|
||||
label nComps_;
|
||||
|
||||
//- Number of elements in a snapshot
|
||||
// A snapshot is an input dataset to be processed per execution-step
|
||||
label nSnap_;
|
||||
|
||||
//- Current execution-step index of STDMD,
|
||||
//- not necessarily that of the simulation
|
||||
label currIndex_;
|
||||
|
||||
//- Maximum allowable magnitude for mag(eigenvalue)*(number of
|
||||
//- STDMD steps) to be used in modeSorter={kiewat,kouZhang} to avoid
|
||||
//- overflow errors
|
||||
scalar sortLimiter_;
|
||||
|
||||
//- Min absolute tolerance being used in eigendecomposition tests
|
||||
scalar absTol_;
|
||||
|
||||
//- Relative tolerance being used in eigendecomposition tests
|
||||
scalar relTol_;
|
||||
|
||||
//- Min value to execute orthogonal basis expansion of Qz_ and Gz_
|
||||
scalar minBasis_;
|
||||
|
||||
//- STDMD time-step size that equals to
|
||||
//- (executeInterval of STDMD)*(deltaT of simulation) [s]
|
||||
scalar dt_;
|
||||
|
||||
//- Min EVal magnitude threshold below where EVals are omitted
|
||||
scalar minMagEVal_;
|
||||
|
||||
//- L2-norm of column vector z_
|
||||
scalar zNorm_;
|
||||
|
||||
//- L2-norm of column vector ez_
|
||||
scalar ezNorm_;
|
||||
|
||||
//- Augmented snapshot matrix (effectively a column vector) (K:Eq. 60)
|
||||
// Upper half z_ = current-time snapshot slot
|
||||
// Lower half z_ = previous-time snapshot slot
|
||||
RMatrix z_;
|
||||
|
||||
//- Working copy of the augmented snapshot matrix z_
|
||||
//- being used in the classical Gram-Schmidt process
|
||||
RMatrix ez_;
|
||||
|
||||
//- First-processed snapshot required by the mode-sorting
|
||||
//- algorithms at the final output computations (K:p. 43)
|
||||
RMatrix X1_;
|
||||
|
||||
//- Accumulated-in-time unitary similarity matrix produced by the
|
||||
//- orthonormal decomposition of the augmented snapshot matrix z_
|
||||
// (K:Eq. 60)
|
||||
RMatrix Qz_;
|
||||
|
||||
//- Accumulated-in-time (squared) upper triangular matrix produced by
|
||||
//- the orthonormal decomposition of the augmented snapshot matrix z_
|
||||
// (K:Eq. 64)
|
||||
SMatrix Gz_;
|
||||
|
||||
//- Upper half of Qz_
|
||||
RMatrix QzUH_;
|
||||
|
||||
//- Lower half of Qz_
|
||||
RMatrix QzLH_;
|
||||
|
||||
//- Moore-Penrose pseudo-inverse of R produced by
|
||||
//- the QR decomposition of the last time-step QzUH_
|
||||
RMatrix RxInv_;
|
||||
|
||||
//- Projected STDMD operator (K:Eq. 78)
|
||||
SMatrix Ap_;
|
||||
|
||||
//- Output eigenvectors
|
||||
RCMatrix oEVecs_;
|
||||
|
||||
//- Output eigenvalues
|
||||
List<complex> oEVals_;
|
||||
|
||||
//- Output amplitudes
|
||||
List<complex> oAmps_;
|
||||
|
||||
//- Output (non-negative) frequencies
|
||||
List<scalar> oFreqs_;
|
||||
|
||||
//- Indices of oFreqs_ where freqs are
|
||||
//- non-negative and within [fMin_, fMax_]
|
||||
DynamicList<label> iFreqs_;
|
||||
|
||||
//- Output (descending) magnitudes of (complex) amplitudes
|
||||
List<scalar> oMags_;
|
||||
|
||||
//- Indices of oMags_
|
||||
List<label> iMags_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
// Process
|
||||
|
||||
//- Move previous-time snapshot into previous-time slot in z_
|
||||
//- and copy new current-time snapshot into current-time slot in z_
|
||||
template<class GeoFieldType>
|
||||
bool getSnapshot();
|
||||
|
||||
//- Get the input field type to be processed by snapshot()
|
||||
template<class Type>
|
||||
bool getSnapshotType();
|
||||
|
||||
//- Get the number of base components of input field
|
||||
template<class GeoFieldType>
|
||||
bool getComps();
|
||||
|
||||
//- Return (parallel) L2-norm of a given column vector
|
||||
scalar parnorm(const RMatrix& colVector) const;
|
||||
|
||||
//- Move the current-time snapshot into the previous-time snapshot in z_
|
||||
//- and copy the new field into the current-time snapshot
|
||||
void snapshot();
|
||||
|
||||
//- Initialise all working matrices at the first execution-step
|
||||
void init();
|
||||
|
||||
//- Initialise Qz_, Gz_ (both require the first two snapshots) and X1_
|
||||
void initBasis();
|
||||
|
||||
//- Execute (parallel) classical Gram-Schmidt
|
||||
//- process to orthonormalise ez_ (Ka:Fig. 5)
|
||||
void GramSchmidt();
|
||||
|
||||
//- Expand orthonormal bases Qz_ and Gz_ by stacking a column
|
||||
//- (ez_/ezNorm_) to Qz_, and a row (Zero) and column (Zero)
|
||||
//- to Gz_ if (minBasis_ < ezNorm_/zNorm_)
|
||||
void expandBasis();
|
||||
|
||||
//- Update Gz_ before the potential orthonormal basis compression
|
||||
void updateGz();
|
||||
|
||||
//- Compress orthonormal basis for Qz_ and Gz_ if (maxRank_ < Qz_.n())
|
||||
void compressBasis();
|
||||
|
||||
|
||||
// Postprocess
|
||||
|
||||
//- Return a new List containing elems of List at indices
|
||||
template<class Type>
|
||||
void filterIndexed
|
||||
(
|
||||
List<Type>& lst,
|
||||
const UList<label>& indices
|
||||
);
|
||||
|
||||
//- Return a new Matrix containing columns of Matrix at indices
|
||||
template<class MatrixType>
|
||||
void filterIndexed
|
||||
(
|
||||
MatrixType& lst,
|
||||
const UList<label>& indices
|
||||
);
|
||||
|
||||
//- Compute global Ap (K:Eq. 78)
|
||||
void calcAp();
|
||||
|
||||
//- Compute eigenvalues and eigenvectors
|
||||
void calcEigen();
|
||||
|
||||
//- Weak-type floating-point comparison
|
||||
// bit.ly/2Trdbgs (Eq. 2), and PEP-485
|
||||
bool close
|
||||
(
|
||||
const scalar s1,
|
||||
const scalar s2,
|
||||
const scalar absTol = 0, //<! comparisons near zero
|
||||
const scalar relTol = 1e-8 //<! e.g. vals the same within 8 decimals
|
||||
) const;
|
||||
|
||||
//- Test real/complex eigenvalues by using
|
||||
//- the theoretical relation: (sum(eigenvalues) - trace(A) ~ 0)
|
||||
void testEigenvalues
|
||||
(
|
||||
const SquareMatrix<scalar>& A,
|
||||
const DiagonalMatrix<scalar>& EValsRe
|
||||
) const;
|
||||
|
||||
//- Test real eigenvectors by using the theoretical relation:
|
||||
//- ((A & EVec - EVal*EVec).norm() ~ 0)
|
||||
void testEigenvectors
|
||||
(
|
||||
const SquareMatrix<scalar>& A,
|
||||
const DiagonalMatrix<scalar>& EValsRe,
|
||||
const SquareMatrix<scalar>& EVecs
|
||||
) const;
|
||||
|
||||
//- Test complex eigenvectors by using the theoretical relation:
|
||||
//- ((A & EVec - EVal*EVec).norm() ~ 0)
|
||||
void testEigenvectors
|
||||
(
|
||||
const SquareMatrix<scalar>& A,
|
||||
const List<complex>& EVals,
|
||||
const RectangularMatrix<complex>& EVecs
|
||||
) const;
|
||||
|
||||
//- Remove any eigenvalues whose magnitude is smaller than
|
||||
//- minMagEVal_ while keeping the order of elements the same
|
||||
void filterEVals();
|
||||
|
||||
//- Compute and filter frequencies (K:Eq. 81)
|
||||
void calcFreqs();
|
||||
|
||||
//- Compute frequency indices
|
||||
// Locate indices where oFreqs_ are
|
||||
// in [fMin_, fMax_], and store them in iFreqs_ indices
|
||||
void calcFreqI();
|
||||
|
||||
//- Compute amplitudes
|
||||
void calcAmps();
|
||||
|
||||
//- Compute magnitudes
|
||||
// Includes advanced sorting methods using
|
||||
// the chosen weighted amplitude scaling method
|
||||
void calcMags();
|
||||
|
||||
//- Compute the ordered magnitude indices
|
||||
void calcMagI();
|
||||
|
||||
//- Compute modes
|
||||
void calcModes();
|
||||
|
||||
//- Eigenvalue weighted amplitude scaling (KZ:Eq. 33)
|
||||
//- Modified eigenvalue weighted amplitude scaling (K)
|
||||
scalar sorter
|
||||
(
|
||||
const List<scalar>& weight,
|
||||
const complex& amplitude,
|
||||
const complex& eval,
|
||||
const scalar modeNorm
|
||||
) const;
|
||||
|
||||
//- Output file header information
|
||||
virtual void writeFileHeader(Ostream& os) const;
|
||||
|
||||
//- Filter objects according to iFreqs_ and iMags_
|
||||
void filterOutput();
|
||||
|
||||
//- Write unfiltered/filtered data
|
||||
void writeOutput(OFstream& os) const;
|
||||
|
||||
//- Compute STDMD output
|
||||
void calcOutput();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("STDMD");
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- No default construct
|
||||
STDMD() = delete;
|
||||
|
||||
//- Construct from Time and dictionary
|
||||
STDMD
|
||||
(
|
||||
const word& name,
|
||||
const Time& runTime,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
//- No copy construct
|
||||
STDMD(const STDMD&) = delete;
|
||||
|
||||
//- No copy assignment
|
||||
void operator=(const STDMD&) = delete;
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~STDMD() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Read STDMD settings
|
||||
virtual bool read(const dictionary&);
|
||||
|
||||
//- Execute STDMD
|
||||
virtual bool execute();
|
||||
|
||||
//- Write STDMD output
|
||||
virtual bool write();
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace functionObjects
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#ifdef NoRepository
|
||||
#include "STDMDTemplates.C"
|
||||
#endif
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -36,7 +36,7 @@ boundaryField
|
||||
type symmetry;
|
||||
}
|
||||
|
||||
"left|right"
|
||||
"(left|right)"
|
||||
{
|
||||
type empty;
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ boundaryField
|
||||
type symmetry;
|
||||
}
|
||||
|
||||
"left|right"
|
||||
"(left|right)"
|
||||
{
|
||||
type empty;
|
||||
}
|
||||
|
||||
@ -13,15 +13,24 @@ runParallel renumberMesh -overwrite
|
||||
|
||||
cp -f system/decomposeParDict system/coarseMesh
|
||||
|
||||
runApplication -s coarseMesh decomposePar -region coarseMesh
|
||||
runApplication -s coarseMesh \
|
||||
decomposePar -region coarseMesh
|
||||
|
||||
runParallel -s coarseMesh renumberMesh -overwrite -region coarseMesh
|
||||
runParallel -s coarseMesh \
|
||||
renumberMesh -overwrite -region coarseMesh
|
||||
|
||||
runParallel $(getApplication)
|
||||
|
||||
runParallel -s main redistributePar -reconstruct -latestTime
|
||||
runParallel -s main \
|
||||
redistributePar -reconstruct -overwrite
|
||||
|
||||
runParallel -s coarseMesh redistributePar -reconstruct \
|
||||
-region coarseMesh -time '50,100,200'
|
||||
runParallel -s coarseMesh \
|
||||
redistributePar -reconstruct -region coarseMesh -time '50,100' -overwrite
|
||||
|
||||
if notTest "$@"
|
||||
then
|
||||
runParallel -s postProcess \
|
||||
$(getApplication) -postProcess -fields '(U p)' -time '10:'
|
||||
fi
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "constant";
|
||||
object transportProperties;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "constant";
|
||||
object turbulenceProperties;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd01
|
||||
{
|
||||
// Mandatory entries (unmodifiable)
|
||||
type DMD;
|
||||
libs (fieldFunctionObjects);
|
||||
DMDModel STDMD;
|
||||
field U;
|
||||
|
||||
// Optional entries (unmodifiable)
|
||||
/* patch <patchName>; */
|
||||
|
||||
// Conditional mandatory entries (runtime modifiable)
|
||||
|
||||
// Option-1
|
||||
/* interval 5.5; */
|
||||
|
||||
// Option-2
|
||||
/* executeInterval 10; */
|
||||
|
||||
// Optional entries (runtime modifiable)
|
||||
/* modeSorter firstSnapshot; */
|
||||
/* nGramSchmidt 5; */
|
||||
/* maxRank 50; */
|
||||
/* nModes 50; */
|
||||
/* fMin 0; */
|
||||
/* fMax 1000000000; */
|
||||
|
||||
// Optional entries (runtime modifiable, yet not recommended)
|
||||
/* minBasis 0.00000001; */
|
||||
/* minEVal 0.00000001; */
|
||||
/* sortLimiter 500.0; */
|
||||
|
||||
|
||||
// Optional (inherited) entries
|
||||
region region0;
|
||||
enabled true;
|
||||
log true;
|
||||
timeStart 10;
|
||||
timeEnd 1000;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
writeInterval 1;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd02
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd03
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd04
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd05
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd06
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd07
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd08
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd09
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd10
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd11
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd12
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd13
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
region coarseMesh;
|
||||
maxRank 5;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd14
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd15
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd16
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
modeSorter kiewat;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd17
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch cylinder;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd18
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch outlet;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd19
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
modeSorter kiewat;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd20
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
modeSorter kiewat;
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd21
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
patch outlet;
|
||||
modeSorter kiewat;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd22
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd23
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
modeSorter kiewat;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd24
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch cylinder;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd25
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch outlet;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd26
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
maxRank 10;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,14 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd27
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
region coarseMesh;
|
||||
maxRank 5;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,14 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd28
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field p;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
modeSorter kiewat;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd29
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd30
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd31
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd32
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd33
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd34
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd35
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd36
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd37
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd38
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd39
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd40
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd41
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd42
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd43
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd44
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field pMean;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd45
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UPrime2Mean;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd46
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UPrime2Mean;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd47
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UPrime2Mean;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd48
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UPrime2Mean;
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd49
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UPrime2Mean;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd50
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd51
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd52
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd53
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd54
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd55
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
maxRank 5;
|
||||
region coarseMesh;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd56
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field UMean_movingAverageWindow;
|
||||
patch outlet;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,11 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd57
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field yPlus;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd58
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field yPlus;
|
||||
patch cylinder;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd59
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field yPlus;
|
||||
patch outlet;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,12 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd60
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field yPlus;
|
||||
maxRank 10;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,13 @@
|
||||
// -*- C++ -*-
|
||||
|
||||
stdmd61
|
||||
{
|
||||
${stdmd01}
|
||||
|
||||
field yPlus;
|
||||
patch cylinder;
|
||||
maxRank 2;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -44,10 +44,6 @@ blocks
|
||||
hex (0 1 2 3 4 5 6 7) ($nx $ny $nz) simpleGrading (1 1 1)
|
||||
);
|
||||
|
||||
edges
|
||||
(
|
||||
);
|
||||
|
||||
boundary
|
||||
(
|
||||
allPatches
|
||||
@ -66,8 +62,5 @@ boundary
|
||||
}
|
||||
);
|
||||
|
||||
mergePatchPairs
|
||||
(
|
||||
);
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
@ -23,6 +23,18 @@ x3 #eval{ 10.0*$R };
|
||||
xOutlet #eval{ 18.6667*$R };
|
||||
xInlet #eval{ -10.125*$R };
|
||||
|
||||
RsinPi8 #eval{ $R*sin(0.125*pi()) };
|
||||
RsinPi8n #eval{ -$RsinPi8 };
|
||||
RcosPi8 #eval{ $R*cos(0.125*pi()) };
|
||||
RcosPi8n #eval{ -$RcosPi8 };
|
||||
RsinPi4 #eval{ $R*sin(0.25*pi()) };
|
||||
|
||||
x2sinPi8 #eval{ $x2*sin(0.125*pi()) };
|
||||
x2sinPi8n #eval{ -$x2sinPi8 };
|
||||
x2cosPi8 #eval{ $x2*cos(0.125*pi()) };
|
||||
x2cosPi8n #eval{ -$x2cosPi8 };
|
||||
x2sinPi4 #eval{ $x2*sin(0.25*pi()) };
|
||||
|
||||
z0 -0.0075;
|
||||
z1 0.0075;
|
||||
nz 1;
|
||||
@ -37,18 +49,15 @@ vertices #codeStream
|
||||
|
||||
code
|
||||
#{
|
||||
// sin(45), cos(45)
|
||||
const scalar sqrt05 = sqrt(0.5);
|
||||
|
||||
pointField points(19);
|
||||
points[0] = point($R, 0, $z0);
|
||||
points[1] = point($x2, 0, $z0);
|
||||
points[2] = point($x3, 0, $z0);
|
||||
points[3] = point($x3, $x2*sqrt05, $z0);
|
||||
points[4] = point($x2*sqrt05, $x2*sqrt05, $z0);
|
||||
points[5] = point($R*sqrt05, $R*sqrt05, $z0);
|
||||
points[3] = point($x3, $x2sinPi4, $z0);
|
||||
points[4] = point($x2sinPi4, $x2sinPi4, $z0);
|
||||
points[5] = point($RsinPi4, $RsinPi4, $z0);
|
||||
points[6] = point($x3, $x3, $z0);
|
||||
points[7] = point($x2*sqrt05, $x3, $z0);
|
||||
points[7] = point($x2sinPi4, $x3, $z0);
|
||||
|
||||
// Mirror +x points to -x side
|
||||
points[11] = point(-points[0].x(), points[0].y(), points[0].z());
|
||||
@ -67,7 +76,7 @@ vertices #codeStream
|
||||
|
||||
// Mirror -z points to +z side
|
||||
label sz = points.size();
|
||||
points.resize(2*sz);
|
||||
points.setSize(2*sz);
|
||||
for (label i = 0; i < sz; ++i)
|
||||
{
|
||||
const point& pt = points[i];
|
||||
@ -77,7 +86,7 @@ vertices #codeStream
|
||||
// Add an inner cylinder
|
||||
sz = points.size();
|
||||
label nAdd = 6;
|
||||
points.resize(sz + nAdd);
|
||||
points.setSize(sz + nAdd);
|
||||
|
||||
// Points within the inner cylinder
|
||||
points[sz] = point(0, 0, $z0);
|
||||
@ -93,7 +102,7 @@ vertices #codeStream
|
||||
|
||||
// Mirror -z points to +z side
|
||||
sz = points.size();
|
||||
points.resize(sz + nAdd);
|
||||
points.setSize(sz + nAdd);
|
||||
for (label i = 0; i < nAdd; ++i)
|
||||
{
|
||||
const point& pt = points[i+sz-nAdd];
|
||||
@ -103,21 +112,21 @@ vertices #codeStream
|
||||
// Add downstream and upstream blocks
|
||||
sz = points.size();
|
||||
nAdd = 6;
|
||||
points.resize(sz + nAdd);
|
||||
points.setSize(sz + nAdd);
|
||||
|
||||
// Points on outlet
|
||||
points[sz] = point($xOutlet, 0, $z0);
|
||||
points[sz + 1] = point($xOutlet, $x3, $z0);
|
||||
points[sz + 4] = point($xOutlet, $x2*sqrt05, $z0);
|
||||
points[sz + 4] = point($xOutlet, $x2sinPi4, $z0);
|
||||
|
||||
// Points on inlet
|
||||
points[sz + 2] = point($xInlet, 0, $z0);
|
||||
points[sz + 3] = point($xInlet, $x3, $z0);
|
||||
points[sz + 5] = point($xInlet, $x2*sqrt05, $z0);
|
||||
points[sz + 5] = point($xInlet, $x2sinPi4, $z0);
|
||||
|
||||
// Mirror -z points to +z side
|
||||
sz = points.size();
|
||||
points.resize(sz + nAdd);
|
||||
points.setSize(sz + nAdd);
|
||||
for (label i = 0; i < nAdd; ++i)
|
||||
{
|
||||
const point& pt = points[i + sz - nAdd];
|
||||
@ -149,22 +158,22 @@ blocks
|
||||
|
||||
edges
|
||||
(
|
||||
arc 0 5 origin (0 0 $z0)
|
||||
arc 5 10 origin (0 0 $z0)
|
||||
arc 1 4 origin (0 0 $z0)
|
||||
arc 4 9 origin (0 0 $z0)
|
||||
arc 19 24 origin (0 0 $z1)
|
||||
arc 24 29 origin (0 0 $z1)
|
||||
arc 20 23 origin (0 0 $z1)
|
||||
arc 23 28 origin (0 0 $z1)
|
||||
arc 11 16 origin (0 0 $z0)
|
||||
arc 16 10 origin (0 0 $z0)
|
||||
arc 12 15 origin (0 0 $z0)
|
||||
arc 15 9 origin (0 0 $z0)
|
||||
arc 30 35 origin (0 0 $z1)
|
||||
arc 35 29 origin (0 0 $z1)
|
||||
arc 31 34 origin (0 0 $z1)
|
||||
arc 34 28 origin (0 0 $z1)
|
||||
arc 0 5 ($RcosPi8 $RsinPi8 $z0)
|
||||
arc 5 10 ($RsinPi8 $RcosPi8 $z0)
|
||||
arc 1 4 ($x2cosPi8 $x2sinPi8 $z0)
|
||||
arc 4 9 ($x2sinPi8 $x2cosPi8 $z0)
|
||||
arc 19 24 ($RcosPi8 $RsinPi8 $z1)
|
||||
arc 24 29 ($RsinPi8 $RcosPi8 $z1)
|
||||
arc 20 23 ($x2cosPi8 $x2sinPi8 $z1)
|
||||
arc 23 28 ($x2sinPi8 $x2cosPi8 $z1)
|
||||
arc 11 16 ($RcosPi8n $RsinPi8 $z0)
|
||||
arc 16 10 ($RsinPi8n $RcosPi8 $z0)
|
||||
arc 12 15 ($x2cosPi8n $x2sinPi8 $z0)
|
||||
arc 15 9 ($x2sinPi8n $x2cosPi8 $z0)
|
||||
arc 30 35 ($RcosPi8n $RsinPi8 $z1)
|
||||
arc 35 29 ($RsinPi8n $RcosPi8 $z1)
|
||||
arc 31 34 ($x2cosPi8n $x2sinPi8 $z1)
|
||||
arc 34 28 ($x2sinPi8n $x2cosPi8 $z1)
|
||||
);
|
||||
|
||||
boundary
|
||||
@ -261,8 +270,5 @@ boundary
|
||||
}
|
||||
);
|
||||
|
||||
mergePatchPairs
|
||||
();
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object fvSchemes;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object fvSolution;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object controlDict;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
@ -23,13 +22,13 @@ startTime 0;
|
||||
|
||||
stopAt endTime;
|
||||
|
||||
endTime 200;
|
||||
endTime 100;
|
||||
|
||||
deltaT 0.05;
|
||||
|
||||
writeControl runTime;
|
||||
|
||||
writeInterval 50;
|
||||
writeInterval 0.5;
|
||||
|
||||
purgeWrite 0;
|
||||
|
||||
@ -46,6 +45,45 @@ runTimeModifiable false;
|
||||
|
||||
functions
|
||||
{
|
||||
fieldAverage1
|
||||
{
|
||||
type fieldAverage;
|
||||
libs (fieldFunctionObjects);
|
||||
|
||||
fields
|
||||
(
|
||||
U
|
||||
{
|
||||
mean on;
|
||||
prime2Mean off;
|
||||
base time;
|
||||
windowType exact;
|
||||
window 0.25;
|
||||
windowName movingAverageWindow;
|
||||
allowRestart no;
|
||||
}
|
||||
|
||||
U
|
||||
{
|
||||
mean on;
|
||||
prime2Mean on;
|
||||
base time;
|
||||
}
|
||||
|
||||
p
|
||||
{
|
||||
mean on;
|
||||
prime2Mean on;
|
||||
base time;
|
||||
}
|
||||
);
|
||||
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 1;
|
||||
writeControl onEnd;
|
||||
}
|
||||
|
||||
mapFields1
|
||||
{
|
||||
type mapFields;
|
||||
@ -55,7 +93,11 @@ functions
|
||||
consistent no;
|
||||
patchMap ();
|
||||
cuttingPatches ();
|
||||
fields ( U p );
|
||||
fields
|
||||
(
|
||||
U
|
||||
p
|
||||
);
|
||||
|
||||
timeStart 10;
|
||||
timeEnd 2000;
|
||||
@ -67,162 +109,98 @@ functions
|
||||
writeInterval 50;
|
||||
}
|
||||
|
||||
STDMD1U
|
||||
mapFields2
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field U;
|
||||
|
||||
// Optional entries
|
||||
modeSorter firstSnap;
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh; // mapFields must be executed before.
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
${mapFields1}
|
||||
fields
|
||||
(
|
||||
UMean
|
||||
pMean
|
||||
UPrime2Mean
|
||||
pPrime2Mean
|
||||
UMean_movingAverageWindow
|
||||
);
|
||||
}
|
||||
|
||||
STDMD1p
|
||||
yPlus1
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field p;
|
||||
|
||||
// Optional entries
|
||||
modeSorter firstSnap;
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
type yPlus;
|
||||
libs (fieldFunctionObjects);
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 1;
|
||||
writeControl writeTime;
|
||||
}
|
||||
|
||||
STDMD2U
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field U;
|
||||
#include "DMDs/stdmd01" // field=U
|
||||
#include "DMDs/stdmd02" // field=U, region=coarseMesh
|
||||
#include "DMDs/stdmd03" // field=U, patch=cylinder
|
||||
#include "DMDs/stdmd04" // field=U, patch=outlet
|
||||
#include "DMDs/stdmd05" // field=U, maxRank=10
|
||||
#include "DMDs/stdmd06" // field=U, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd07" // field=U, patch=outlet, maxRank=2
|
||||
|
||||
// Optional entries
|
||||
modeSorter firstSnap;
|
||||
nModes 2;
|
||||
maxRank 2;
|
||||
nGramSchmidt 5;
|
||||
fMin 0;
|
||||
fMax 1000000000;
|
||||
testEigen true;
|
||||
dumpEigen true;
|
||||
minBasis 0.00000001;
|
||||
minEVal 0.00000001;
|
||||
absTol 0.001;
|
||||
relTol 0.0001;
|
||||
#include "DMDs/stdmd08" // field=p
|
||||
#include "DMDs/stdmd09" // field=p, region=coarseMesh
|
||||
#include "DMDs/stdmd10" // field=p, patch=cylinder
|
||||
#include "DMDs/stdmd11" // field=p, patch=outlet
|
||||
#include "DMDs/stdmd12" // field=p, maxRank=10
|
||||
#include "DMDs/stdmd13" // field=p, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd14" // field=p, patch=outlet, maxRank=2
|
||||
|
||||
// Optional (inherited) entries
|
||||
timeStart 50;
|
||||
timeEnd 2000;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
}
|
||||
#include "DMDs/stdmd15" // field=U, modeSorter=kiewat
|
||||
#include "DMDs/stdmd16" // field=U, region=coarseMesh, modeSorter=kiewat
|
||||
#include "DMDs/stdmd17" // field=U, patch=cylinder, modeSorter=kiewat
|
||||
#include "DMDs/stdmd18" // field=U, patch=outlet, modeSorter=kiewat
|
||||
#include "DMDs/stdmd19" // field=U, maxRank=10, modeSorter=kiewat
|
||||
#include "DMDs/stdmd20" // field=U, region=coarseMesh, maxRank=5, modeSorter=kiewat
|
||||
#include "DMDs/stdmd21" // field=U, patch=outlet, maxRank=2, modeSorter=kiewat
|
||||
|
||||
STDMD3U
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field U;
|
||||
#include "DMDs/stdmd22" // field=p, modeSorter=kiewat
|
||||
#include "DMDs/stdmd23" // field=p, region=coarseMesh, modeSorter=kiewat
|
||||
#include "DMDs/stdmd24" // field=p, patch=cylinder, modeSorter=kiewat
|
||||
#include "DMDs/stdmd25" // field=p, patch=outlet, modeSorter=kiewat
|
||||
#include "DMDs/stdmd26" // field=p, maxRank=10, modeSorter=kiewat
|
||||
#include "DMDs/stdmd27" // field=p, region=coarseMesh, maxRank=5, modeSorter=kiewat
|
||||
#include "DMDs/stdmd28" // field=p, patch=outlet, maxRank=2, modeSorter=kiewat
|
||||
|
||||
// Optional entries
|
||||
modeSorter firstSnap;
|
||||
#include "DMDs/stdmd29" // field=UMean
|
||||
// #include "DMDs/stdmd30" // field=UMean, region=coarseMesh
|
||||
#include "DMDs/stdmd31" // field=UMean, patch=cylinder
|
||||
#include "DMDs/stdmd32" // field=UMean, patch=outlet
|
||||
#include "DMDs/stdmd33" // field=UMean, maxRank=10
|
||||
// #include "DMDs/stdmd34" // field=UMean, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd35" // field=UMean, patch=outlet, maxRank=2
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
timeEnd 150;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl runTime;
|
||||
writeInterval 50;
|
||||
}
|
||||
#include "DMDs/stdmd36" // field=pMean
|
||||
// #include "DMDs/stdmd37" // field=pMean, region=coarseMesh
|
||||
#include "DMDs/stdmd38" // field=pMean, patch=cylinder
|
||||
#include "DMDs/stdmd39" // field=pMean, patch=outlet
|
||||
#include "DMDs/stdmd40" // field=pMean, maxRank=10
|
||||
// #include "DMDs/stdmd41" // field=pMean, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd42" // field=pMean, patch=outlet, maxRank=2
|
||||
|
||||
STDMD4U
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field U;
|
||||
#include "DMDs/stdmd43" // field=UPrime2Mean
|
||||
// #include "DMDs/stdmd44" // field=UPrime2Mean, region=coarseMesh
|
||||
#include "DMDs/stdmd45" // field=UPrime2Mean, patch=cylinder
|
||||
#include "DMDs/stdmd46" // field=UPrime2Mean, patch=outlet
|
||||
#include "DMDs/stdmd47" // field=UPrime2Mean, maxRank=10
|
||||
// #include "DMDs/stdmd48" // field=UPrime2Mean, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd49" // field=UPrime2Mean, patch=outlet, maxRank=2
|
||||
|
||||
// Optional entries
|
||||
modeSorter kiewat;
|
||||
#include "DMDs/stdmd50" // field=UMean_movingAverageWindow
|
||||
// #include "DMDs/stdmd51" // field=UMean_movingAverageWindow, region=coarseMesh
|
||||
#include "DMDs/stdmd52" // field=UMean_movingAverageWindow, patch=cylinder
|
||||
#include "DMDs/stdmd53" // field=UMean_movingAverageWindow, patch=outlet
|
||||
#include "DMDs/stdmd54" // field=UMean_movingAverageWindow, maxRank=10
|
||||
// #include "DMDs/stdmd55" // field=UMean_movingAverageWindow, region=coarseMesh, maxRank=5
|
||||
#include "DMDs/stdmd56" // field=UMean_movingAverageWindow, patch=outlet, maxRank=2
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
}
|
||||
|
||||
STDMD4p
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field p;
|
||||
|
||||
// Optional entries
|
||||
modeSorter kiewat;
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
}
|
||||
|
||||
STDMD5U
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field U;
|
||||
|
||||
// Optional entries
|
||||
modeSorter kouZhang;
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
}
|
||||
|
||||
STDMD5p
|
||||
{
|
||||
// Mandatory entries
|
||||
type STDMD;
|
||||
libs (fieldFunctionObjects);
|
||||
field p;
|
||||
|
||||
// Optional entries
|
||||
modeSorter kouZhang;
|
||||
|
||||
// Optional (inherited) entries
|
||||
region coarseMesh;
|
||||
timeStart 10;
|
||||
executeControl timeStep;
|
||||
executeInterval 10;
|
||||
writeControl onEnd;
|
||||
}
|
||||
#include "DMDs/stdmd57" // field=yPlus
|
||||
#include "DMDs/stdmd58" // field=yPlus, patch=cylinder
|
||||
#include "DMDs/stdmd59" // field=yPlus, patch=outlet
|
||||
#include "DMDs/stdmd60" // field=yPlus, maxRank=10
|
||||
#include "DMDs/stdmd61" // field=yPlus, patch=cylinder, maxRank=2
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -10,18 +10,17 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object decomposeParDict;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
numberOfSubdomains 8;
|
||||
numberOfSubdomains 12;
|
||||
|
||||
method hierarchical;
|
||||
|
||||
coeffs
|
||||
{
|
||||
n (8 1 1);
|
||||
n (4 3 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object fvSchemes;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
@ -28,6 +27,7 @@ gradSchemes
|
||||
divSchemes
|
||||
{
|
||||
default Gauss linear;
|
||||
|
||||
div((nuEff*dev(T(grad(U))))) Gauss linear;
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ FoamFile
|
||||
version 2.0;
|
||||
format ascii;
|
||||
class dictionary;
|
||||
location "system";
|
||||
object fvSolution;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -24,4 +24,5 @@ pointAndNormalDict
|
||||
|
||||
planeTolerance 1e-3;
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
@ -22,7 +22,7 @@ geometry
|
||||
{
|
||||
cylinder
|
||||
{
|
||||
type cylinder;
|
||||
type searchableCylinder;
|
||||
point1 (0 0 -0.00375);
|
||||
point2 (0 0 0.00375);
|
||||
radius 0.06;
|
||||
|
||||
@ -43,7 +43,7 @@ atmAmbientTurbSource1
|
||||
atmAmbientTurbSourceCoeffs
|
||||
{
|
||||
selectionMode all;
|
||||
kAmb 0.001;
|
||||
kAmb 1.0e-04;
|
||||
epsilonAmb 7.208e-08;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user