STDMD.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2020-2021 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "STDMD.H"
29 #include "EigenMatrix.H"
30 #include "QRMatrix.H"
32 
33 using namespace Foam::constant::mathematical;
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39 namespace DMDModels
40 {
43 }
44 }
45 
46 const Foam::Enum
47 <
48  Foam::DMDModels::STDMD::modeSorterType
49 >
50 Foam::DMDModels::STDMD::modeSorterTypeNames
51 ({
52  { modeSorterType::FIRST_SNAPSHOT, "firstSnapshot" },
53  { modeSorterType::KIEWAT , "kiewat" },
54  { modeSorterType::KOU_ZHANG , "kouZhang" }
55 });
56 
57 
58 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
59 
60 Foam::scalar Foam::DMDModels::STDMD::L2norm(const RMatrix& z) const
61 {
62  #ifdef FULLDEBUG
63  // Check if the given RectangularMatrix is effectively a column vector
64  if (z.n() != 1)
65  {
67  << " # Input matrix is not a column vector. #"
68  << exit(FatalError);
69  }
70  #endif
71  const bool noSqrt = true;
72  scalar result = z.columnNorm(0, noSqrt);
73  reduce(result, sumOp<scalar>());
74 
75  // Heuristic addition to avoid very small or zero norm
76  return max(SMALL, Foam::sqrt(result));
77 }
78 
79 
80 Foam::RectangularMatrix<Foam::scalar> Foam::DMDModels::STDMD::orthonormalise
81 (
82  RMatrix ez
83 ) const
84 {
85  RMatrix dz(Q_.n(), 1, Zero);
86 
87  for (label i = 0; i < nGramSchmidt_; ++i)
88  {
89  dz = Q_ & ez;
90  reduce(dz, sumOp<RMatrix>());
91  ez -= Q_*dz;
92  }
93 
94  return ez;
95 }
96 
97 
98 void Foam::DMDModels::STDMD::expand(const RMatrix& ez, const scalar ezNorm)
99 {
100  Info<< tab << "Expanding orthonormal basis for field: " << fieldName_
101  << endl;
102 
103  // Stack a column "(ez/ezNorm)" to "Q"
104  Q_.resize(Q_.m(), Q_.n() + 1);
105  Q_.subColumn(Q_.n() - 1) = ez/ezNorm;
106 
107  // Stack a row (Zero) and column (Zero) to "G"
108  G_.resize(G_.m() + 1);
109 }
110 
111 
112 void Foam::DMDModels::STDMD::compress()
113 {
114  Info<< tab << "Compressing orthonormal basis for field: " << fieldName_
115  << endl;
116 
117  RMatrix q(1, 1, Zero);
118 
119  if (Pstream::master())
120  {
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());
125 
126  // Sort eigenvalues in descending order, and track indices
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);
131 
132  // Update "G"
133  G_ = SMatrix(maxRank_, Zero);
134  G_.diag(EVals);
135 
136  q.resize(Q_.n(), maxRank_);
137  for (label i = 0; i < maxRank_; ++i)
138  {
139  q.subColumn(i) = EVecs.subColumn(permutation[i]);
140  }
141  }
142  Pstream::scatter(G_);
143  Pstream::scatter(q);
144 
145  // Update "Q"
146  Q_ = Q_*q;
147 }
148 
149 
150 Foam::SquareMatrix<Foam::scalar> Foam::DMDModels::STDMD::
151 reducedKoopmanOperator()
152 {
153  Info<< tab << "Computing Atilde" << endl;
154 
155  Info<< tab << "Computing local Rx" << endl;
156 
157  RMatrix Rx(1, 1, Zero);
158  {
159  QRMatrix<RMatrix> QRM
160  (
161  Qupper_,
162  QRMatrix<RMatrix>::outputTypes::REDUCED_R,
163  QRMatrix<RMatrix>::colPivoting::FALSE
164  );
165  Rx = QRM.R();
166  }
167  Rx.round();
168 
169  // Convenience objects A1, A2, A3 to compute "Atilde"
170  RMatrix A1(1, 1, Zero);
171 
172  if (Pstream::parRun())
173  {
174  // Parallel direct tall-skinny QR decomposition
175  // (BGD:Fig. 5) & (DGHL:Fig. 2)
176  // Tests revealed that the distribution of "Q" does not affect
177  // the final outcome of TSQR decomposition up to sign
178 
179  PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
180 
181  const label myProcNo = Pstream::myProcNo();
182  const label procNoInSubset = myProcNo % nAgglomerationProcs_;
183 
184  // Send Rx from sender subset neighbours to receiver subset master
185  if (procNoInSubset != 0)
186  {
187  const label procNoSubsetMaster = myProcNo - procNoInSubset;
188 
189  UOPstream toSubsetMaster(procNoSubsetMaster, pBufs);
190  toSubsetMaster << Rx;
191 
192  Rx.clear();
193  }
194 
195  pBufs.finishedSends();
196 
197  // Receive Rx by receiver subset masters
198  if (procNoInSubset == 0)
199  {
200  // Accept only subset masters possessing sender subset neighbours
201  if (myProcNo + 1 < Pstream::nProcs())
202  {
203  for
204  (
205  label nbr = myProcNo + 1;
206  (
207  nbr < myProcNo + nAgglomerationProcs_
208  && nbr < Pstream::nProcs()
209  );
210  ++nbr
211  )
212  {
213  RMatrix recvMtrx;
214 
215  UIPstream fromNbr(nbr, pBufs);
216  fromNbr >> recvMtrx;
217 
218  // Append received Rx to Rx of receiver subset masters
219  if (recvMtrx.size() > 0)
220  {
221  Rx.resize(Rx.m() + recvMtrx.m(), Rx.n());
222  Rx.subMatrix
223  (
224  Rx.m() - recvMtrx.m(),
225  Rx.n() - recvMtrx.n()
226  ) = recvMtrx;
227  }
228  }
229  }
230 
231  // Apply interim QR decomposition on Rx of receiver subset masters
232  QRMatrix<RMatrix> QRM
233  (
234  Rx,
235  QRMatrix<RMatrix>::outputTypes::REDUCED_R,
236  QRMatrix<RMatrix>::storeMethods::IN_PLACE,
237  QRMatrix<RMatrix>::colPivoting::FALSE
238  );
239  Rx.round();
240  }
241 
242  pBufs.clear();
243 
244  // Send interim Rx from subset masters to the master
245  if (procNoInSubset == 0)
246  {
247  if (!Pstream::master())
248  {
249  UOPstream toMaster(Pstream::masterNo(), pBufs);
250  toMaster << Rx;
251  }
252  }
253 
254  pBufs.finishedSends();
255 
256  // Receive interim Rx by the master, and apply final operations
257  if (Pstream::master())
258  {
259  for
260  (
261  label nbr = Pstream::masterNo() + nAgglomerationProcs_;
262  nbr < Pstream::nProcs();
263  nbr += nAgglomerationProcs_
264  )
265  {
266  RMatrix recvMtrx;
267 
268  UIPstream fromSubsetMaster(nbr, pBufs);
269  fromSubsetMaster >> recvMtrx;
270 
271  // Append received Rx to Rx of the master
272  if (recvMtrx.size() > 0)
273  {
274  Rx.resize(Rx.m() + recvMtrx.m(), Rx.n());
275  Rx.subMatrix
276  (
277  Rx.m() - recvMtrx.m(),
278  Rx.n() - recvMtrx.n()
279  ) = recvMtrx;
280  }
281  }
282 
283  Info<< tab << "Computing TSQR" << endl;
284  QRMatrix<RMatrix> QRM
285  (
286  Rx,
287  QRMatrix<RMatrix>::outputTypes::REDUCED_R,
288  QRMatrix<RMatrix>::storeMethods::IN_PLACE,
289  QRMatrix<RMatrix>::colPivoting::FALSE
290  );
291  Rx.round();
292 
293  // Rx produced by TSQR is unique up to the sign, hence the revert
294  for (scalar& x : Rx)
295  {
296  x *= -1;
297  }
298 
299  Info<< tab << "Computing RxInv" << endl;
300  RxInv_ = pinv(Rx);
301 
302  Info<< tab << "Computing A1" << endl;
303  A1 = RxInv_*RMatrix(pinv(Rx*(G_^Rx)));
304  Rx.clear();
305  }
306  Pstream::scatter(RxInv_);
307  Pstream::scatter(A1);
308 
309  Info<< tab << "Computing A2" << endl;
310  SMatrix A2(Qupper_ & Qlower_);
311  reduce(A2, sumOp<SMatrix>());
312  Qlower_.clear();
313 
314  Info<< tab << "Computing A3" << endl;
315  SMatrix A3(Qupper_ & Qupper_);
316  reduce(A3, sumOp<SMatrix>());
317 
318  Info<< tab << "Computing Atilde" << endl;
319  // by optimized matrix chain multiplication
320  // obtained by dynamic programming memoisation
321  return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
322  }
323  else
324  {
325  Info<< tab << "Computing RxInv" << endl;
326  RxInv_ = pinv(Rx);
327 
328  Info<< tab << "Computing A1" << endl;
329  A1 = RxInv_*RMatrix(pinv(Rx*(G_^Rx)));
330  Rx.clear();
331 
332  Info<< tab << "Computing A2" << endl;
333  SMatrix A2(Qupper_ & Qlower_);
334  Qlower_.clear();
335 
336  Info<< tab << "Computing A3" << endl;
337  SMatrix A3(Qupper_ & Qupper_);
338 
339  Info<< tab << "Computing Atilde" << endl;
340  return SMatrix(RxInv_ & ((A2*(G_*A3))*A1));
341  }
342 }
343 
344 
345 bool Foam::DMDModels::STDMD::eigendecomposition(SMatrix& Atilde)
346 {
347  bool fail = false;
348 
349  // Compute eigenvalues, and clip eigenvalues with values < "minEval"
350  if (Pstream::master())
351  {
352  Info<< tab << "Computing eigendecomposition" << endl;
353 
354  // Replace "Atilde" by eigenvalues (in-place eigendecomposition)
355  const EigenMatrix<scalar> EM(Atilde);
356  const DiagonalMatrix<scalar>& evalsRe = EM.EValsRe();
357  const DiagonalMatrix<scalar>& evalsIm = EM.EValsIm();
358 
359  evals_.resize(evalsRe.size());
360  evecs_ = RCMatrix(EM.complexEVecs());
361 
362  // Convert scalar eigenvalue pairs to complex eigenvalues
363  label i = 0;
364  for (auto& eval : evals_)
365  {
366  eval = complex(evalsRe[i], evalsIm[i]);
367  ++i;
368  }
369 
370  Info<< tab << "Filtering eigenvalues" << endl;
371 
372  List<complex> cp(evals_.size());
373  auto it =
374  std::copy_if
375  (
376  evals_.cbegin(),
377  evals_.cend(),
378  cp.begin(),
379  [&](const complex& x){ return mag(x) > minEval_; }
380  );
381  cp.resize(std::distance(cp.begin(), it));
382 
383  if (cp.size() == 0)
384  {
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
392  << endl;
393 
394  fail = true;
395  }
396  else
397  {
398  evals_ = cp;
399  }
400  }
401  Pstream::scatter(fail);
402 
403  if (fail)
404  {
405  return false;
406  }
407 
408  Pstream::scatter(evals_);
409  Pstream::scatter(evecs_);
410 
411  Atilde.clear();
412 
413  return true;
414 }
415 
416 
417 void Foam::DMDModels::STDMD::frequencies()
418 {
419  if (Pstream::master())
420  {
421  Info<< tab << "Computing frequencies" << endl;
422 
423  freqs_.resize(evals_.size());
424 
425  // Frequency equation (K:Eq. 81)
426  auto frequencyEquation =
427  [&](const complex& eval)
428  {
429  return Foam::log(max(eval, complex(SMALL))).imag()/(twoPi*dt_);
430  };
431 
432  // Compute frequencies
434  (
435  evals_.cbegin(),
436  evals_.cend(),
437  freqs_.begin(),
438  frequencyEquation
439  );
440 
441  Info<< tab << "Computing frequency indices" << endl;
442 
443  // Initialise iterator by the first search
444  auto margin = [&](const scalar& x){ return (fMin_ <= x && x < fMax_); };
445 
446  auto it = std::find_if(freqs_.cbegin(), freqs_.cend(), margin);
447 
448  while (it != freqs_.end())
449  {
450  freqsi_.append(std::distance(freqs_.cbegin(), it));
451  it = std::find_if(std::next(it), freqs_.cend(), margin);
452  }
453  }
454  Pstream::scatter(freqs_);
455  Pstream::scatter(freqsi_);
456 }
457 
458 
459 void Foam::DMDModels::STDMD::amplitudes()
460 {
461  const IOField<scalar> snapshot0
462  (
463  IOobject
464  (
465  "snapshot0_" + name_ + "_" + fieldName_,
466  timeName0_,
467  mesh_,
470  false
471  )
472  );
473 
474  RMatrix snapshot(1, 1, Zero);
475  if (!empty_)
476  {
477  snapshot.resize(Qupper_.m(), 1);
478  std::copy(snapshot0.cbegin(), snapshot0.cend(), snapshot.begin());
479  }
480 
481  RMatrix R((RxInv_.T()^Qupper_)*snapshot);
482  reduce(R, sumOp<RMatrix>());
483 
484  if (Pstream::master())
485  {
486  Info<< tab << "Computing amplitudes" << endl;
487 
488  amps_.resize(R.m());
489  const RCMatrix pEvecs(pinv(evecs_));
490 
491  // amps_ = pEvecs*R;
492  for (label i = 0; i < amps_.size(); ++i)
493  {
494  for (label j = 0; j < R.m(); ++j)
495  {
496  amps_[i] += pEvecs(i, j)*R(j, 0);
497  }
498  }
499  }
500  Pstream::scatter(amps_);
501 }
502 
503 
504 void Foam::DMDModels::STDMD::magnitudes()
505 {
506  if (Pstream::master())
507  {
508  Info<< tab << "Computing magnitudes" << endl;
509 
510  mags_.resize(amps_.size());
511 
512  Info<< tab << "Sorting modes with ";
513 
514  switch (modeSorter_)
515  {
516  case modeSorterType::FIRST_SNAPSHOT:
517  {
518  Info<< "method of first snapshot" << endl;
519 
521  (
522  amps_.cbegin(),
523  amps_.cend(),
524  mags_.begin(),
525  [&](const complex& val){ return mag(val); }
526  );
527  break;
528  }
529 
530  case modeSorterType::KIEWAT:
531  {
532  Info<< "modified weighted amplitude scaling method" << endl;
533 
534  // Eigendecomposition returns evecs with
535  // the unity norm, hence "modeNorm = 1"
536  const scalar modeNorm = 1;
537  const scalar pr = 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;
541 
542  forAll(amps_, i)
543  {
544  mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
545  }
546 
547  break;
548  }
549 
550  case modeSorterType::KOU_ZHANG:
551  {
552  Info<< "weighted amplitude scaling method" << endl;
553 
554  const scalar modeNorm = 1;
555  const List<scalar> w(step_, 1.0);
556 
557  forAll(amps_, i)
558  {
559  mags_[i] = sorter(w, amps_[i], evals_[i], modeNorm);
560  }
561 
562  break;
563  }
564 
565  default:
566  break;
567  }
568 
569  Info<< tab << "Computing magnitude indices" << endl;
570 
571  magsi_ = freqsi_;
572 
573  auto descend =
574  [&](const label i1, const label i2)
575  {
576  return !(mags_[i1] < mags_[i2]);
577  };
578 
579  std::sort(magsi_.begin(), magsi_.end(), descend);
580  }
581  Pstream::scatter(mags_);
582  Pstream::scatter(magsi_);
583 }
584 
585 
586 Foam::scalar Foam::DMDModels::STDMD::sorter
587 (
588  const List<scalar>& weight,
589  const complex& amplitude,
590  const complex& eigenvalue,
591  const scalar modeNorm
592 ) const
593 {
594  // Omit eigenvalues with very large or very small mags
595  if (!(mag(eigenvalue) < GREAT && mag(eigenvalue) > VSMALL))
596  {
597  Info<< " Returning zero magnitude for mag(eigenvalue) = "
598  << mag(eigenvalue) << endl;
599 
600  return 0;
601  }
602 
603  // Omit eigenvalue-STDMD step combinations that pose a risk of overflow
604  if (mag(eigenvalue)*step_ > sortLimiter_)
605  {
606  Info<< " Returning zero magnitude for"
607  << " mag(eigenvalue) = " << mag(eigenvalue)
608  << " current index = " << step_
609  << " sortLimiter = " << sortLimiter_
610  << endl;
611 
612  return 0;
613  }
614 
615  scalar magnitude = 0;
616 
617  for (label j = 0; j < step_; ++j)
618  {
619  magnitude += weight[j]*modeNorm*mag(amplitude*pow(eigenvalue, j + 1));
620  }
621 
622  return magnitude;
623 }
624 
625 
626 bool Foam::DMDModels::STDMD::dynamics()
627 {
628  SMatrix Atilde(reducedKoopmanOperator());
629  G_.clear();
630 
631  if(!eigendecomposition(Atilde))
632  {
633  return false;
634  }
635 
636  frequencies();
637 
638  amplitudes();
639 
640  magnitudes();
641 
642  return true;
643 }
644 
645 
646 bool Foam::DMDModels::STDMD::modes()
647 {
648  Info<< tab << "Computing modes" << endl;
649 
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>();
656 
657  if (!processed)
658  {
659  return false;
660  }
661 
662  return true;
663 }
664 
665 
666 void Foam::DMDModels::STDMD::writeToFile(const word& fileName) const
667 {
668  Info<< tab << "Writing objects of dynamics" << endl;
669 
670  // Write objects of dynamics
671  {
672  autoPtr<OFstream> osPtr =
673  createFile
674  (
675  fileName + "_" + fieldName_,
676  mesh_.time().timeOutputValue()
677  );
678  OFstream& os = osPtr.ref();
679 
680  writeFileHeader(os);
681 
682  for (const auto& i : labelRange(0, freqs_.size()))
683  {
684  os << freqs_[i] << tab
685  << mags_[i] << tab
686  << amps_[i].real() << tab
687  << amps_[i].imag() << tab
688  << evals_[i].real() << tab
689  << evals_[i].imag() << endl;
690  }
691  }
692 
693  Info<< tab << "Ends output processing for field: " << fieldName_
694  << endl;
695 }
696 
697 
698 void Foam::DMDModels::STDMD::writeFileHeader(Ostream& os) const
699 {
700  writeHeader(os, "DMD output");
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)");
707  os << endl;
708 }
709 
710 
711 void Foam::DMDModels::STDMD::filter()
712 {
713  Info<< tab << "Filtering objects of dynamics" << endl;
714 
715  // Filter objects according to iMags
716  filterIndexed(evals_, magsi_);
717  filterIndexed(evecs_, magsi_);
718  filterIndexed(freqs_, magsi_);
719  filterIndexed(amps_, magsi_);
720  filterIndexed(mags_, magsi_);
721 
722  // Clip objects if need be (assuming objects have the same len)
723  if (freqs_.size() > nModes_)
724  {
725  evals_.resize(nModes_);
726  evecs_.resize(evecs_.m(), nModes_);
727  freqs_.resize(nModes_);
728  amps_.resize(nModes_);
729  mags_.resize(nModes_);
730  }
731 }
732 
733 
734 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
735 
737 (
738  const fvMesh& mesh,
739  const word& name,
740  const dictionary& dict
741 )
742 :
743  DMDModel(mesh, name, dict),
744  modeSorter_
745  (
746  modeSorterTypeNames.getOrDefault
747  (
748  "modeSorter",
749  dict,
750  modeSorterType::FIRST_SNAPSHOT
751  )
752  ),
753  Q_(),
754  G_(),
755  Qupper_(1, 1, Zero),
756  Qlower_(1, 1, Zero),
757  RxInv_(1, 1, Zero),
758  evals_(Zero),
759  evecs_(1, 1, Zero),
760  freqs_(Zero),
761  freqsi_(),
762  amps_(Zero),
763  mags_(Zero),
764  magsi_(Zero),
765  fieldName_(dict.get<word>("field")),
766  patch_(dict.getOrDefault<word>("patch", word::null)),
767  timeName0_(),
768  minBasis_(0),
769  minEval_(0),
770  dt_(0),
771  sortLimiter_(500),
772  fMin_(0),
773  fMax_(pTraits<label>::max),
774  nGramSchmidt_(5),
775  maxRank_(pTraits<label>::max),
776  step_(0),
777  nModes_(pTraits<label>::max),
778  nAgglomerationProcs_(20),
779  empty_(false)
780 {}
781 
782 
783 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
784 
786 {
787  writeToFile_ = dict.getOrDefault<bool>("writeToFile", true);
788 
789  modeSorter_ =
790  modeSorterTypeNames.getOrDefault
791  (
792  "modeSorter",
793  dict,
794  modeSorterType::FIRST_SNAPSHOT
795  );
796 
797  minBasis_ =
798  dict.getCheckOrDefault<scalar>("minBasis", 1e-8, scalarMinMax::ge(0));
799 
800  minEval_ =
801  dict.getCheckOrDefault<scalar>("minEVal", 1e-8, scalarMinMax::ge(0));
802 
803  dt_ =
804  dict.getCheckOrDefault<scalar>
805  (
806  "interval",
807  (
808  dict.getCheck<label>("executeInterval", labelMinMax::ge(1))
809  *mesh_.time().deltaT().value()
810  ),
812  );
813 
814  sortLimiter_ =
815  dict.getCheckOrDefault<scalar>("sortLimiter", 500, scalarMinMax::ge(1));
816 
817  nGramSchmidt_ =
818  dict.getCheckOrDefault<label>("nGramSchmidt", 5, labelMinMax::ge(1));
819 
820  maxRank_ =
821  dict.getCheckOrDefault<label>
822  (
823  "maxRank",
825  labelMinMax::ge(1)
826  );
827 
828  nModes_ =
829  dict.getCheckOrDefault<label>
830  (
831  "nModes",
833  labelMinMax::ge(1)
834  );
835 
836  fMin_ = dict.getCheckOrDefault<label>("fMin", 0, labelMinMax::ge(0));
837 
838  fMax_ =
839  dict.getCheckOrDefault<label>
840  (
841  "fMax",
843  labelMinMax::ge(fMin_ + 1)
844  );
845 
846  nAgglomerationProcs_ =
847  dict.getCheckOrDefault<label>
848  (
849  "nAgglomerationProcs",
850  20,
851  labelMinMax::ge(1)
852  );
853 
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
866  << endl;
867 
868  return true;
869 }
870 
871 
873 {
874  const scalar norm = L2norm(z);
875 
876  if (mag(norm) > 0)
877  {
878  // First-processed snapshot required by the mode-sorting
879  // algorithms at the final output computations (K:p. 43)
880  {
881  const label nSnap = z.m()/2;
882  timeName0_ =
883  mesh_.time().timeName(mesh_.time().startTime().value());
884 
885  if (nSnap == 0)
886  {
887  empty_ = true;
888  }
889 
890  IOField<scalar> snapshot0
891  (
892  IOobject
893  (
894  "snapshot0_" + name_ + "_" + fieldName_,
895  timeName0_,
896  mesh_,
899  false
900  ),
901  nSnap
902  );
903 
904  std::copy(z.cbegin(), z.cbegin() + nSnap, snapshot0.begin());
905 
906  const IOstreamOption streamOpt
907  (
908  mesh_.time().writeFormat(),
909  mesh_.time().writeCompression()
910  );
911 
912  fileHandler().writeObject(snapshot0, streamOpt, true);
913  }
914 
915  Q_ = z/norm;
916  G_ = SMatrix(1);
917  G_(0,0) = sqr(norm);
918 
919  ++step_;
920 
921  return true;
922  }
923 
924  return false;
925 }
926 
927 
929 {
930  {
931  //- Working copy of the augmented snapshot matrix "z"
932  //- being used in the classical Gram-Schmidt process
933  const RMatrix ez(orthonormalise(z));
934 
935  // Check basis for "z" and, if necessary, expand "Q" and "G"
936  const scalar ezNorm = L2norm(ez);
937  const scalar zNorm = L2norm(z);
938 
939  if (ezNorm/zNorm > minBasis_)
940  {
941  expand(ez, ezNorm);
942  }
943  }
944 
945  // Update "G" before the potential orthonormal basis compression
946  {
947  RMatrix zTilde(Q_ & z);
948  reduce(zTilde, sumOp<RMatrix>());
949 
950  G_ += SMatrix(zTilde^zTilde);
951  }
952 
953  // Compress the orthonormal basis if required
954  if (Q_.n() >= maxRank_)
955  {
956  compress();
957  }
958 
959  ++step_;
960 
961  return true;
962 }
963 
964 
966 {
967  // DMD statistics and mode evaluation (K:Fig. 16)
968  const label nSnap = Q_.m()/2;
969 
970  // Move upper and lower halves of "Q" to new containers
971  Qupper_ = RMatrix(Q_.subMatrix(0, 0, max(nSnap, 1)));
972  Qlower_ = RMatrix(Q_.subMatrix(nSnap, 0, max(nSnap, 1)));
973  Q_.clear();
974 
975  if (!dynamics())
976  {
977  return true;
978  }
979 
980  modes();
981 
982  if (Pstream::master() && writeToFile_)
983  {
984  writeToFile(word("dynamics"));
985 
986  filter();
987 
988  writeToFile(word("filteredDynamics"));
989  }
990 
991  step_ = 0;
992 
993  return true;
994 }
995 
996 
997 // ************************************************************************* //
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
Foam::IOobject::NO_WRITE
Definition: IOobject.H:195
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:169
Foam::roots::complex
Definition: Roots.H:57
EigenMatrix.H
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:57
Foam::UPstream::masterNo
static constexpr int masterNo() noexcept
Process index of the master (always 0)
Definition: UPstream.H:451
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::IOField
A primitive field of type <T> with automated input and output.
Definition: foamVtkLagrangianWriter.H:61
STDMD.H
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::sin
dimensionedScalar sin(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:264
Foam::pinv
MatrixType pinv(const MatrixType &A, const scalar tol=1e-5)
Definition: QRMatrix.C:493
QRMatrix.H
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Am I the master process.
Definition: UPstream.H:457
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1485
Foam::constant::mathematical::e
constexpr scalar e(M_E)
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::DMDModels::STDMD::fit
virtual bool fit()
Definition: STDMD.C:965
Foam::writeHeader
static void writeHeader(Ostream &os, const word &fieldName)
Definition: rawSurfaceWriterImpl.C:66
Foam::transform
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:521
Foam::sumOp
Definition: ops.H:213
Foam::Rx
tensor Rx(const scalar &omega)
Rotational transformation tensor about the x-axis by omega radians.
Definition: transform.H:85
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:51
R
#define R(A, B, C, D, E, F, K, M)
Foam::constant::physicoChemical::b
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
Foam::sort
void sort(UList< T > &a)
Definition: UList.C:261
Foam::IOstreamOption
The IOstreamOption is a simple container for options an IOstream can normally have.
Definition: IOstreamOption.H:63
Foam::fileOperation::writeObject
virtual bool writeObject(const regIOobject &io, IOstreamOption streamOpt=IOstreamOption(), const bool valid=true) const
Writes a regIOobject (so header, contents and divider).
Definition: fileOperation.C:751
Foam::Matrix::clear
void clear()
Clear Matrix, i.e. set sizes to zero.
Definition: Matrix.C:256
Foam::pow
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
Definition: dimensionedScalar.C:75
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Foam::RectangularMatrix
A templated (M x N) rectangular matrix of objects of <Type>, containing M*N elements,...
Definition: RectangularMatrix.H:57
Foam::constant::mathematical::twoPi
constexpr scalar twoPi(2 *M_PI)
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::FatalError
error FatalError
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
Foam::DMDModels::STDMD::read
virtual bool read(const dictionary &dict)
Read STDMD settings.
Definition: STDMD.C:785
Foam::log
dimensionedScalar log(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:262
os
OBJstream os(runTime.globalPath()/outputName)
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
addToRunTimeSelectionTable.H
Macros for easy insertion into run-time selection tables.
Foam::DMDModels::STDMD::initialise
virtual bool initialise(const RMatrix &z)
Initialise 'Q' and 'G' (both require the first two snapshots)
Definition: STDMD.C:872
Foam::fvMesh
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:85
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::DMDModel
Abstract base class for DMD models to handle DMD characteristics for the DMD function object.
Definition: DMDModel.H:65
Foam::DMDModels::STDMD::update
virtual bool update(const RMatrix &z)
Incremental orthonormal basis update (K:Fig. 15)
Definition: STDMD.C:928
Foam::distance
scalar distance(const vector &p1, const vector &p2)
Definition: curveTools.C:12
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::SquareMatrix
A templated (N x N) square matrix of objects of <Type>, containing N*N elements, derived from Matrix.
Definition: SquareMatrix.H:63
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:51
Foam::UPstream::commsTypes::nonBlocking
Foam::constant::mathematical
Mathematical constants.
Foam::cp
bool cp(const fileName &src, const fileName &dst, const bool followLink=true)
Copy the source to the destination (recursively if necessary).
Definition: MSwindows.C:802
Foam::tab
constexpr char tab
Definition: Ostream.H:403
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:463
Foam::Matrix::m
label m() const noexcept
The number of rows.
Definition: MatrixI.H:96
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::MinMax::ge
static MinMax< T > ge(const T &minVal)
A semi-infinite range from minVal to the type max.
Definition: MinMaxI.H:31
Foam::UPstream::parRun
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::pTraits
A traits class, which is primarily used for primitives.
Definition: pTraits.H:56
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::word::null
static const word null
An empty word.
Definition: word.H:80
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
Foam::functionObjects::writeFile::writeToFile
virtual bool writeToFile() const
Flag to allow writing to file.
Definition: writeFile.C:253
Foam::Matrix::cbegin
const_iterator cbegin() const noexcept
Return const_iterator to begin traversing a constant Matrix.
Definition: MatrixI.H:551
Foam::DMDModels::STDMD
Streaming Total Dynamic Mode Decomposition (i.e. STDMD) is a variant of dynamic mode decomposition.
Definition: STDMD.H:306
Foam::stringOps::expand
string expand(const std::string &s, const HashTable< string > &mapping, const char sigil='$')
Definition: stringOps.C:718
Foam::IOobject::NO_READ
Definition: IOobject.H:188
Foam::DMDModels::STDMD::STDMD
STDMD(const fvMesh &mesh, const word &name, const dictionary &dict)
Construct from components.
Definition: STDMD.C:737
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::UPstream::nProcs
static label nProcs(const label communicator=worldComm)
Number of processes in parallel run, and 1 for serial run.
Definition: UPstream.H:445
Foam::IOobject::MUST_READ
Definition: IOobject.H:185