48 Foam::DMDModels::STDMD::modeSorterType
50 Foam::DMDModels::STDMD::modeSorterTypeNames
52 { modeSorterType::FIRST_SNAPSHOT,
"firstSnapshot" },
53 { modeSorterType::KIEWAT ,
"kiewat" },
54 { modeSorterType::KOU_ZHANG ,
"kouZhang" }
60 Foam::scalar Foam::DMDModels::STDMD::L2norm(
const RMatrix& z)
const
67 <<
" # Input matrix is not a column vector. #"
71 const bool noSqrt =
true;
72 scalar result = z.columnNorm(0, noSqrt);
73 reduce(result, sumOp<scalar>());
85 RMatrix dz(Q_.n(), 1,
Zero);
87 for (label i = 0; i < nGramSchmidt_; ++i)
90 reduce(dz, sumOp<RMatrix>());
98 void Foam::DMDModels::STDMD::expand(
const RMatrix& ez,
const scalar ezNorm)
100 Info<<
tab <<
"Expanding orthonormal basis for field: " << fieldName_
104 Q_.resize(Q_.m(), Q_.n() + 1);
105 Q_.subColumn(Q_.n() - 1) = ez/ezNorm;
108 G_.resize(G_.m() + 1);
112 void Foam::DMDModels::STDMD::compress()
114 Info<<
tab <<
"Compressing orthonormal basis for field: " << fieldName_
117 RMatrix q(1, 1,
Zero);
121 const bool symmetric =
true;
122 const EigenMatrix<scalar> EM(G_, symmetric);
123 const SquareMatrix<scalar>& EVecs = EM.EVecs();
124 DiagonalMatrix<scalar> EVals(EM.EValsRe());
127 const auto descend = [&](scalar a, scalar
b){
return a >
b; };
128 const List<label> permutation(EVals.sortPermutation(descend));
129 EVals.applyPermutation(permutation);
130 EVals.resize(EVals.size() - 1);
133 G_ = SMatrix(maxRank_,
Zero);
136 q.resize(Q_.n(), maxRank_);
137 for (label i = 0; i < maxRank_; ++i)
139 q.subColumn(i) = EVecs.subColumn(permutation[i]);
151 reducedKoopmanOperator()
159 QRMatrix<RMatrix> QRM
162 QRMatrix<RMatrix>::outputTypes::REDUCED_R,
163 QRMatrix<RMatrix>::colPivoting::FALSE
170 RMatrix A1(1, 1,
Zero);
182 const label procNoInSubset = myProcNo % nAgglomerationProcs_;
185 if (procNoInSubset != 0)
187 const label procNoSubsetMaster = myProcNo - procNoInSubset;
189 UOPstream toSubsetMaster(procNoSubsetMaster, pBufs);
190 toSubsetMaster <<
Rx;
195 pBufs.finishedSends();
198 if (procNoInSubset == 0)
205 label nbr = myProcNo + 1;
207 nbr < myProcNo + nAgglomerationProcs_
215 UIPstream fromNbr(nbr, pBufs);
219 if (recvMtrx.size() > 0)
221 Rx.resize(
Rx.m() + recvMtrx.m(),
Rx.n());
224 Rx.m() - recvMtrx.m(),
225 Rx.n() - recvMtrx.n()
232 QRMatrix<RMatrix> QRM
235 QRMatrix<RMatrix>::outputTypes::REDUCED_R,
236 QRMatrix<RMatrix>::storeMethods::IN_PLACE,
237 QRMatrix<RMatrix>::colPivoting::FALSE
245 if (procNoInSubset == 0)
254 pBufs.finishedSends();
263 nbr += nAgglomerationProcs_
268 UIPstream fromSubsetMaster(nbr, pBufs);
269 fromSubsetMaster >> recvMtrx;
272 if (recvMtrx.size() > 0)
274 Rx.resize(
Rx.m() + recvMtrx.m(),
Rx.n());
277 Rx.m() - recvMtrx.m(),
278 Rx.n() - recvMtrx.n()
284 QRMatrix<RMatrix> QRM
287 QRMatrix<RMatrix>::outputTypes::REDUCED_R,
288 QRMatrix<RMatrix>::storeMethods::IN_PLACE,
289 QRMatrix<RMatrix>::colPivoting::FALSE
303 A1 = RxInv_*RMatrix(
pinv(
Rx*(G_^
Rx)));
310 SMatrix A2(Qupper_ & Qlower_);
311 reduce(A2, sumOp<SMatrix>());
315 SMatrix A3(Qupper_ & Qupper_);
316 reduce(A3, sumOp<SMatrix>());
321 return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
329 A1 = RxInv_*RMatrix(
pinv(
Rx*(G_^
Rx)));
333 SMatrix A2(Qupper_ & Qlower_);
337 SMatrix A3(Qupper_ & Qupper_);
340 return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
345 bool Foam::DMDModels::STDMD::eigendecomposition(SMatrix& Atilde)
352 Info<<
tab <<
"Computing eigendecomposition" <<
endl;
355 const EigenMatrix<scalar> EM(Atilde);
356 const DiagonalMatrix<scalar>& evalsRe = EM.EValsRe();
357 const DiagonalMatrix<scalar>& evalsIm = EM.EValsIm();
359 evals_.resize(evalsRe.size());
360 evecs_ = RCMatrix(EM.complexEVecs());
364 for (
auto& eval : evals_)
366 eval =
complex(evalsRe[i], evalsIm[i]);
372 List<complex>
cp(evals_.size());
386 <<
"No eigenvalue with mag(eigenvalue) larger than "
387 <<
"minEval = " << minEval_ <<
" was found." <<
nl
388 <<
" Input field may contain only zero-value elements," <<
nl
389 <<
" e.g. no-slip velocity condition on a given patch." <<
nl
390 <<
" Otherwise, please decrease the value of minEval." <<
nl
391 <<
" Skipping further dynamics/mode computations." <<
nl
417 void Foam::DMDModels::STDMD::frequencies()
423 freqs_.resize(evals_.size());
426 auto frequencyEquation =
441 Info<<
tab <<
"Computing frequency indices" <<
endl;
444 auto margin = [&](
const scalar&
x){
return (fMin_ <=
x &&
x < fMax_); };
446 auto it = std::find_if(freqs_.cbegin(), freqs_.cend(), margin);
448 while (it != freqs_.end())
451 it = std::find_if(std::next(it), freqs_.cend(), margin);
459 void Foam::DMDModels::STDMD::amplitudes()
461 const IOField<scalar> snapshot0
465 "snapshot0_" + name_ +
"_" + fieldName_,
474 RMatrix snapshot(1, 1,
Zero);
477 snapshot.resize(Qupper_.m(), 1);
478 std::copy(snapshot0.cbegin(), snapshot0.cend(), snapshot.begin());
481 RMatrix
R((RxInv_.T()^Qupper_)*snapshot);
489 const RCMatrix pEvecs(
pinv(evecs_));
492 for (label i = 0; i < amps_.size(); ++i)
494 for (label j = 0; j <
R.m(); ++j)
496 amps_[i] += pEvecs(i, j)*
R(j, 0);
504 void Foam::DMDModels::STDMD::magnitudes()
510 mags_.resize(amps_.size());
512 Info<<
tab <<
"Sorting modes with ";
516 case modeSorterType::FIRST_SNAPSHOT:
518 Info<<
"method of first snapshot" <<
endl;
530 case modeSorterType::KIEWAT:
532 Info<<
"modified weighted amplitude scaling method" <<
endl;
536 const scalar modeNorm = 1;
538 List<scalar> w(step_);
539 std::iota(w.begin(), w.end(), 1);
540 w =
sin(
twoPi/step_*(w - 1 - 0.25*step_))*pr + pr;
544 mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
550 case modeSorterType::KOU_ZHANG:
552 Info<<
"weighted amplitude scaling method" <<
endl;
554 const scalar modeNorm = 1;
555 const List<scalar> w(step_, 1.0);
559 mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
569 Info<<
tab <<
"Computing magnitude indices" <<
endl;
574 [&](
const label i1,
const label i2)
576 return !(mags_[i1] < mags_[i2]);
579 std::sort(magsi_.begin(), magsi_.end(), descend);
586 Foam::scalar Foam::DMDModels::STDMD::sorter
588 const List<scalar>& weight,
591 const scalar modeNorm
595 if (!(
mag(eigenvalue) < GREAT &&
mag(eigenvalue) > VSMALL))
597 Info<<
" Returning zero magnitude for mag(eigenvalue) = "
604 if (
mag(eigenvalue)*step_ > sortLimiter_)
606 Info<<
" Returning zero magnitude for"
607 <<
" mag(eigenvalue) = " <<
mag(eigenvalue)
608 <<
" current index = " << step_
609 <<
" sortLimiter = " << sortLimiter_
615 scalar magnitude = 0;
617 for (label j = 0; j < step_; ++j)
619 magnitude += weight[j]*modeNorm*
mag(amplitude*
pow(eigenvalue, j + 1));
626 bool Foam::DMDModels::STDMD::dynamics()
628 SMatrix Atilde(reducedKoopmanOperator());
631 if(!eigendecomposition(Atilde))
646 bool Foam::DMDModels::STDMD::modes()
650 bool processed =
false;
651 processed = processed || modes<scalar>();
652 processed = processed || modes<vector>();
653 processed = processed || modes<sphericalTensor>();
654 processed = processed || modes<symmTensor>();
655 processed = processed || modes<tensor>();
668 Info<<
tab <<
"Writing objects of dynamics" <<
endl;
672 autoPtr<OFstream> osPtr =
675 fileName +
"_" + fieldName_,
676 mesh_.time().timeOutputValue()
678 OFstream&
os = osPtr.ref();
682 for (
const auto& i : labelRange(0, freqs_.size()))
684 os << freqs_[i] <<
tab
686 << amps_[i].real() <<
tab
687 << amps_[i].imag() <<
tab
688 << evals_[i].real() <<
tab
689 << evals_[i].imag() <<
endl;
693 Info<<
tab <<
"Ends output processing for field: " << fieldName_
698 void Foam::DMDModels::STDMD::writeFileHeader(Ostream&
os)
const
701 writeCommented(
os,
"Frequency");
702 writeTabbed(
os,
"Magnitude");
703 writeTabbed(
os,
"Amplitude (real)");
704 writeTabbed(
os,
"Amplitude (imag)");
705 writeTabbed(
os,
"Eigenvalue (real)");
706 writeTabbed(
os,
"Eigenvalue (imag)");
711 void Foam::DMDModels::STDMD::filter()
713 Info<<
tab <<
"Filtering objects of dynamics" <<
endl;
716 filterIndexed(evals_, magsi_);
717 filterIndexed(evecs_, magsi_);
718 filterIndexed(freqs_, magsi_);
719 filterIndexed(amps_, magsi_);
720 filterIndexed(mags_, magsi_);
723 if (freqs_.size() > nModes_)
725 evals_.resize(nModes_);
726 evecs_.resize(evecs_.m(), nModes_);
727 freqs_.resize(nModes_);
728 amps_.resize(nModes_);
729 mags_.resize(nModes_);
746 modeSorterTypeNames.getOrDefault
750 modeSorterType::FIRST_SNAPSHOT
765 fieldName_(
dict.get<
word>(
"field")),
778 nAgglomerationProcs_(20),
787 writeToFile_ =
dict.getOrDefault<
bool>(
"writeToFile",
true);
790 modeSorterTypeNames.getOrDefault
794 modeSorterType::FIRST_SNAPSHOT
804 dict.getCheckOrDefault<scalar>
809 *mesh_.time().deltaT().value()
821 dict.getCheckOrDefault<label>
829 dict.getCheckOrDefault<label>
839 dict.getCheckOrDefault<label>
846 nAgglomerationProcs_ =
847 dict.getCheckOrDefault<label>
849 "nAgglomerationProcs",
854 Info<<
tab <<
"Settings are read for:" <<
nl
855 <<
" field: " << fieldName_ <<
nl
856 <<
" modeSorter: " << modeSorterTypeNames[modeSorter_] <<
nl
857 <<
" nModes: " << nModes_ <<
nl
858 <<
" maxRank: " << maxRank_ <<
nl
859 <<
" nGramSchmidt: " << nGramSchmidt_ <<
nl
860 <<
" fMin: " << fMin_ <<
nl
861 <<
" fMax: " << fMax_ <<
nl
862 <<
" minBasis: " << minBasis_ <<
nl
863 <<
" minEVal: " << minEval_ <<
nl
864 <<
" sortLimiter: " << sortLimiter_ <<
nl
865 <<
" nAgglomerationProcs: " << nAgglomerationProcs_ <<
nl
874 const scalar norm = L2norm(z);
881 const label nSnap = z.
m()/2;
883 mesh_.time().timeName(mesh_.time().startTime().value());
894 "snapshot0_" + name_ +
"_" + fieldName_,
904 std::copy(z.
cbegin(), z.
cbegin() + nSnap, snapshot0.begin());
908 mesh_.time().writeFormat(),
909 mesh_.time().writeCompression()
933 const RMatrix ez(orthonormalise(z));
936 const scalar ezNorm = L2norm(ez);
937 const scalar zNorm = L2norm(z);
939 if (ezNorm/zNorm > minBasis_)
954 if (Q_.n() >= maxRank_)
968 const label nSnap = Q_.m()/2;
971 Qupper_ =
RMatrix(Q_.subMatrix(0, 0,
max(nSnap, 1)));
972 Qlower_ =
RMatrix(Q_.subMatrix(nSnap, 0,
max(nSnap, 1)));
984 writeToFile(
word(
"dynamics"));
988 writeToFile(
word(
"filteredDynamics"));