40template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
42 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots,
43 PtrList<GeometricField<Type, PatchField, GeoMesh >>& ModesGlobal,
45 label Npar, label NnestedOut)
48 List<PtrList<GeometricField<Type, PatchField, GeoMesh >>>
50 label Nt = snapshots.size() / Npar;
51 SnapMatrixNested.setSize(Nt);
53 for (label i = 0; i < Npar; i++)
55 SnapMatrixNested[i].resize(Nt);
57 for (label j = 0; j < Nt; j++)
59 SnapMatrixNested[i].set(j, snapshots[j + Nt * i].clone());
63 List<PtrList<GeometricField<Type, PatchField, GeoMesh >>> UModesNested;
64 PtrList<GeometricField<Type, PatchField, GeoMesh >> y;
65 UModesNested.setSize(Nt);
67 for (label i = 0; i < Npar; i++)
73 for (label i = 0; i < Npar; i++)
77 for (label j = 0; j < y.size(); j++)
79 ModesGlobal.append(y[j].clone());
85 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& ModesGlobal,
86 word fieldName, label Npar, label NnestedOut);
89 PtrList<volVectorField>& snapshots, PtrList<volVectorField>& ModesGlobal,
90 word fieldName, label Npar, label NnestedOut);
92template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
94 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots,
95 PtrList<GeometricField<Type, PatchField, GeoMesh >>& modes,
96 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
100 constexpr bool check_vol = std::is_same<volMesh, GeoMesh>::value
101 || std::is_same<surfaceMesh, GeoMesh>::value;
102 word PODkey =
"POD_" + fieldName;
103 word PODnorm = para->ITHACAdict->lookupOrDefault<word>(PODkey,
"L2");
104 M_Assert(PODnorm ==
"L2" ||
105 PODnorm ==
"Frobenius",
"The PODnorm can be only L2 or Frobenius");
106 Info <<
"Performing POD for " << fieldName <<
" using the " << PODnorm <<
109 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
111 if (para->eigensolver ==
"spectra" )
115 nmodes = snapshots.size() - 2;
118 M_Assert(nmodes <= snapshots.size() - 2,
119 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
125 nmodes = snapshots.size();
128 M_Assert(nmodes <= snapshots.size(),
129 "The number of requested modes cannot be bigger than the number of Snapshots");
134 label NBC = snapshots[0].boundaryField().size();
135 Eigen::MatrixXd _corMatrix;
141 else if (PODnorm ==
"Frobenius")
146 if (Pstream::parRun())
148 reduce(_corMatrix, sumOp<Eigen::MatrixXd>());
151 Eigen::VectorXd eigenValueseig;
152 Eigen::MatrixXd eigenVectoreig;
153 modes.resize(nmodes);
154 Info <<
"####### Performing the POD using EigenDecomposition " <<
155 fieldName <<
" #######" << endl;
156 label ncv = snapshots.size();
157 Spectra::DenseSymMatProd<double> op(_corMatrix);
158 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEg;
160 if (para->eigensolver ==
"spectra")
162 Spectra::SymEigsSolver<Spectra::DenseSymMatProd<double>> es(op, nmodes, ncv);
163 Info <<
"Using Spectra EigenSolver " << endl;
165 es.compute(Spectra::SortRule::LargestAlge);
166 M_Assert(es.info() == Spectra::CompInfo::Successful,
167 "The Eigenvalue Decomposition did not succeed");
168 eigenVectoreig = es.eigenvectors().real();
169 eigenValueseig = es.eigenvalues().real();
171 else if (para->eigensolver ==
"eigen")
173 Info <<
"Using Eigen EigenSolver " << endl;
174 esEg.compute(_corMatrix);
175 M_Assert(esEg.info() == Eigen::Success,
176 "The Eigenvalue Decomposition did not succeed");
177 eigenVectoreig = esEg.eigenvectors().real().rowwise().reverse().leftCols(
179 eigenValueseig = esEg.eigenvalues().real().array().reverse();
182 if (eigenValueseig.array().minCoeff() < 0)
184 eigenValueseig = eigenValueseig.array() + 2 * abs(
185 eigenValueseig.array().minCoeff());
188 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
194 Eigen::MatrixXd modesEig = (SnapMatrix * eigenVectoreig);
197 Eigen::MatrixXd normFact(nmodes, 1);
199 for (label i = 0; i < nmodes; i++)
203 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * V.asDiagonal() *
204 modesEig.col(i))(0, 0));
206 if constexpr(check_vol)
208 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * V.asDiagonal() *
209 modesEig.col(i))(0, 0));
211 else if constexpr(std::is_same<pointMesh, GeoMesh>::value)
213 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * modesEig.col(i))(0,
217 if (Pstream::parRun())
219 normFact(i, 0) = (modesEig.col(i).transpose() * V.asDiagonal() *
220 modesEig.col(i))(0, 0);
223 else if (PODnorm ==
"Frobenius")
225 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * modesEig.col(i))(0,
228 if (Pstream::parRun())
230 normFact(i, 0) = (modesEig.col(i).transpose() * modesEig.col(i))(0, 0);
235 if (Pstream::parRun())
237 reduce(normFact, sumOp<Eigen::MatrixXd>());
240 if (Pstream::parRun())
242 normFact = normFact.cwiseSqrt();
245 List<Eigen::MatrixXd> modesEigBC;
246 modesEigBC.resize(NBC);
248 for (label i = 0; i < NBC; i++)
250 modesEigBC[i] = (SnapMatrixBC[i] * eigenVectoreig);
253 Info << endl <<
"####### Normalized Eigenvalues of " << snapshots[0].name()
254 <<
" #######" << endl << normFact << endl << endl;
256 for (label i = 0; i < nmodes; i++)
258 modesEig.col(i) = modesEig.col(i).array() / normFact(i, 0);
260 for (label j = 0; j < NBC; j++)
262 modesEigBC[j].col(i) = modesEigBC[j].col(i).array() / normFact(i, 0);
266 for (label i = 0; i < modes.size(); i++)
268 GeometricField<Type, PatchField, GeoMesh> tmp2(snapshots[0].name(),
270 Eigen::VectorXd vec = modesEig.col(i);
273 for (label k = 0; k < NBC; k++)
278 modes.set(i, tmp2.clone());
281 eigenValueseig = eigenValueseig / eigenValueseig.sum();
282 Eigen::VectorXd cumEigenValues(eigenValueseig);
284 for (label j = 1; j < cumEigenValues.size(); ++j)
286 cumEigenValues(j) += cumEigenValues(j - 1);
289 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
295 snapshots[0].name());
302 Eigen::saveMarketVector(eigenValueseig,
303 "./ITHACAoutput/POD/Eigenvalues_" + snapshots[0].name(), para->precision,
305 Eigen::saveMarketVector(cumEigenValues,
306 "./ITHACAoutput/POD/CumEigenvalues_" + snapshots[0].name(), para->precision,
311 Info <<
"Reading the existing modes" << endl;
316 "./ITHACAoutput/supremizer/");
326 PtrList<volVectorField>& snapshots, PtrList<volVectorField>& modes,
327 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
331 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& modes,
332 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
336 PtrList<surfaceScalarField>& snapshots, PtrList<surfaceScalarField>& modes,
337 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
340 PtrList<pointVectorField>& snapshots, PtrList<pointVectorField>& modes,
341 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
344template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
346 GeometricField<Type, PatchField, GeoMesh>& templateField,
348 PtrList<GeometricField<Type, PatchField, GeoMesh >>& modes,
355 autoPtr<GeometricField<Type, PatchField, GeoMesh >> meanField)
359 word PODkey =
"POD_" + fieldName;
360 word PODnorm = para->ITHACAdict->lookupOrDefault<word>(PODkey,
"L2");
362 M_Assert(PODnorm ==
"L2" || PODnorm ==
"Frobenius",
363 "The PODnorm can be only L2 or Frobenius");
364 Info <<
"Performing memory efficient POD for " << fieldName
365 <<
" using the " << PODnorm <<
" norm" << endl;
367 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
370 fileName rootPath(
".");
371 Foam::Time runTime2(Foam::Time::controlDictName, rootPath, snapshotsPath);
372 label nSnaps = runTime2.times().size() - 2;
373 Info <<
"Found " << nSnaps <<
" time directories" << endl;
379 <<
"Error: No time directories found in " << snapshotsPath
384 if (para->eigensolver ==
"spectra")
391 M_Assert(nmodes <= nSnaps - 2,
392 "The number of requested modes cannot be bigger than the number of snapshots - 2");
401 M_Assert(nmodes <= nSnaps,
402 "The number of requested modes cannot be bigger than the number of snapshots");
406 Eigen::MatrixXd _corMatrix(nSnaps, nSnaps);
407 _corMatrix.setZero();
408 List<Eigen::MatrixXd> SnapMatrixBC;
409 label NBC = templateField.boundaryField().size();
410 SnapMatrixBC.resize(NBC);
413 for (label i = 0; i < NBC; i++)
415 SnapMatrixBC[i].resize(templateField.boundaryField()[i].size(), nSnaps);
419 for (label i = 0; i < nSnaps; i++)
422 GeometricField<Type, PatchField, GeoMesh> snapI =
434 for (label k = 0; k < NBC; k++)
436 SnapMatrixBC[k].col(i) = snapIBC[k];
440 for (label j = i; j < nSnaps; j++)
442 GeometricField<Type, PatchField, GeoMesh> snapJ =
454 _corMatrix(i, j) = computeInnerProduct(snapI, snapJ);
458 _corMatrix(i, j) = computeFrobeniusInnerProduct(snapI, snapJ);
464 _corMatrix(j, i) = _corMatrix(i, j);
468 Info <<
"Processed snapshot " << i + 1 <<
" of " << nSnaps << endl;
472 if (Pstream::parRun())
474 reduce(_corMatrix, sumOp<Eigen::MatrixXd>());
478 Eigen::VectorXd eigenValues;
479 Eigen::MatrixXd eigenVectors;
480 Info <<
"####### Performing the POD using EigenDecomposition " <<
481 fieldName <<
" #######" << endl;
483 if (para->eigensolver ==
"spectra")
486 Info <<
"Using Spectra EigenSolver " << endl;
487 Spectra::DenseSymMatProd<double> op(_corMatrix);
488 Spectra::SymEigsSolver<Spectra::DenseSymMatProd<double >>
489 solver(op, nmodes, nSnaps);
491 solver.compute(Spectra::SortRule::LargestAlge);
492 M_Assert(solver.info() == Spectra::CompInfo::Successful,
493 "Eigenvalue decomposition failed");
494 eigenVectors = solver.eigenvectors().real();
495 eigenValues = solver.eigenvalues().real();
497 else if (para->eigensolver ==
"eigen")
500 Info <<
"Using Eigen EigenSolver " << endl;
501 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(_corMatrix);
502 M_Assert(solver.info() == Eigen::Success,
503 "Eigenvalue decomposition failed");
504 eigenVectors = solver.eigenvectors().real().rowwise().reverse().leftCols(
506 eigenValues = solver.eigenvalues().real().array().reverse();
510 if (eigenValues.array().minCoeff() < 0)
512 eigenValues = eigenValues.array() + 2 * abs(
513 eigenValues.array().minCoeff());
516 Info <<
"####### End of the POD for " << fieldName <<
" #######" << endl;
518 modes.resize(nmodes);
520 GeometricField<Type, PatchField, GeoMesh> firstSnap =
523 for (label i = 0; i < nmodes; i++)
526 GeometricField<Type, PatchField, GeoMesh> modeI
530 templateField.name(),
531 templateField.time().timeName(),
532 templateField.mesh(),
536 templateField.mesh(),
537 dimensioned<Type>(
"zero", templateField.dimensions(), Zero),
538 firstSnap.boundaryField().types()
542 for (label j = 0; j < nSnaps; j++)
544 GeometricField<Type, PatchField, GeoMesh> snapJ =
546 modeI += snapJ * eigenVectors(j, i);
554 normFactor = computeInnerProduct(modeI, modeI);
558 normFactor = computeFrobeniusInnerProduct(modeI, modeI);
561 if (Pstream::parRun())
563 reduce(normFactor, sumOp<scalar>());
566 normFactor = Foam::sqrt(normFactor);
568 modeI *= dimensionedScalar(
"normFactor", dimless, 1.0 / normFactor);
571 for (label k = 0; k < NBC; k++)
573 Eigen::VectorXd bcValues = SnapMatrixBC[k] * eigenVectors.col(i);
574 bcValues = bcValues / normFactor;
580 modeI.correctBoundaryConditions();
583 modes.set(i, modeI.clone());
584 Info <<
"Constructed mode " << i + 1 <<
" of " << nmodes << endl;
598 eigenValues = eigenValues / eigenValues.sum();
599 Eigen::VectorXd cumEigenValues = eigenValues;
601 for (label j = 1; j < cumEigenValues.size(); ++j)
603 cumEigenValues(j) += cumEigenValues(j - 1);
607 Eigen::saveMarketVector(eigenValues,
608 "./ITHACAoutput/POD/Eigenvalues_" + fieldName, para->precision, para->outytpe);
609 Eigen::saveMarketVector(cumEigenValues,
610 "./ITHACAoutput/POD/CumEigenvalues_" + fieldName, para->precision,
616 Info <<
"Reading existing modes" << endl;
621 "./ITHACAoutput/supremizer/");
632 GeometricField<scalar, fvPatchField, volMesh>&,
634 PtrList<GeometricField<scalar, fvPatchField, volMesh >>&,
641 autoPtr<GeometricField<scalar, fvPatchField, volMesh >>
647 GeometricField<vector, fvPatchField, volMesh>&,
649 PtrList<GeometricField<vector, fvPatchField, volMesh >>&,
656 autoPtr<GeometricField<vector, fvPatchField, volMesh >>
659template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
661 GeometricField<Type, PatchField, GeoMesh>& templateField,
663 autoPtr<GeometricField<Type, PatchField, GeoMesh >>& meanField,
667 fileName rootPath(
".");
668 Foam::Time runTime2(Foam::Time::controlDictName, rootPath, snapshotsPath);
669 label nSnaps = runTime2.times().size() - 2;
670 Info <<
"Found " << nSnaps <<
" time directories" << endl;
675 Info <<
"Computing the mean of snapshots" << endl;
677 *meanField = templateField * 0.;
679 for (
int i = 0; i < nSnaps; i++)
682 GeometricField<Type, PatchField, GeoMesh> snapI =
688 *meanField *= 1. / nSnaps;
693 Info <<
"Reading the mean of snapshots" << endl;
700 GeometricField<scalar, fvPatchField, volMesh>&,
702 autoPtr<GeometricField<scalar, fvPatchField, volMesh >>&,
708 GeometricField<vector, fvPatchField, volMesh>&,
710 autoPtr<GeometricField<vector, fvPatchField, volMesh >>&,
714template<
class Type,
template<
class>
class PatchField,
class GeoMesh >
716 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots,
717 PtrList<GeometricField<Type, PatchField, GeoMesh >>& modes,
718 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
725 nmodes = snapshots.size() - 2;
728 M_Assert(nmodes <= snapshots.size() - 2,
729 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
731 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
735 label NBC = snapshots[0].boundaryField().size();
737 Eigen::VectorXd eigenValueseig;
738 Eigen::MatrixXd eigenVectoreig;
739 modes.resize(nmodes);
740 Info <<
"####### Performing the POD using EigenDecomposition " <<
741 snapshots[0].name() <<
" #######" << endl;
742 label ncv = snapshots.size();
743 Spectra::DenseSymMatProd<double> op(_corMatrix);
744 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEg;
745 Spectra::SymEigsSolver<Spectra::DenseSymMatProd<double >>
748 if (para->eigensolver ==
"spectra")
750 Info <<
"Using Spectra EigenSolver " << endl;
752 es.compute(Spectra::SortRule::LargestAlge);
753 M_Assert(es.info() == Spectra::CompInfo::Successful,
754 "The Eigenvalue Decomposition did not succeed");
755 eigenVectoreig = es.eigenvectors().real();
756 eigenValueseig = es.eigenvalues().real();
758 else if (para->eigensolver ==
"eigen")
760 Info <<
"Using Eigen EigenSolver " << endl;
761 esEg.compute(_corMatrix);
762 M_Assert(esEg.info() == Eigen::Success,
763 "The Eigenvalue Decomposition did not succeed");
764 eigenVectoreig = esEg.eigenvectors().real().rowwise().reverse().leftCols(
766 eigenValueseig = esEg.eigenvalues().real().reverse();
769 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
771 Eigen::VectorXd eigenValueseigLam =
772 eigenValueseig.real().array().cwiseInverse().sqrt() ;
773 Eigen::VectorXd eigenValueseigWeigted = eigenValueseig.head(
774 nmodes).real().array() ;
775 Eigen::MatrixXd modesEig = (SnapMatrix * eigenVectoreig) *
776 eigenValueseigLam.head(nmodes).asDiagonal() *
777 eigenValueseigWeigted.asDiagonal();
778 List<Eigen::MatrixXd> modesEigBC;
779 modesEigBC.resize(NBC);
781 for (label i = 0; i < NBC; i++)
783 modesEigBC[i] = (SnapMatrixBC[i] * eigenVectoreig) *
784 eigenValueseigLam.asDiagonal() * eigenValueseigWeigted.asDiagonal();
787 for (label i = 0; i < modes.size(); i++)
789 GeometricField<Type, PatchField, GeoMesh> tmp2(snapshots[0].name(),
791 Eigen::VectorXd vec = modesEig.col(i);
794 for (label k = 0; k < tmp2.boundaryField().size(); k++)
799 modes.set(i, tmp2.clone());
802 eigenValueseig = eigenValueseig / eigenValueseig.sum();
803 Eigen::VectorXd cumEigenValues(eigenValueseig);
805 for (label j = 1; j < cumEigenValues.size(); ++j)
807 cumEigenValues(j) += cumEigenValues(j - 1);
810 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
817 snapshots[0].name());
824 Eigen::saveMarketVector(eigenValueseig,
825 "./ITHACAoutput/POD/Eigenvalues_" + snapshots[0].name(), para->precision,
827 Eigen::saveMarketVector(cumEigenValues,
828 "./ITHACAoutput/POD/CumEigenvalues_" + snapshots[0].name(), para->precision,
833 Info <<
"Reading the existing modes" << endl;
838 "./ITHACAoutput/supremizer/");
848 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& modes,
849 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
853 PtrList<volVectorField>& snapshots, PtrList<volVectorField>& modes,
854 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
857template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
859 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots,
860 PtrList<GeometricField<Type, PatchField, GeoMesh >>& modes,
861 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
866 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
868 PtrList<volVectorField> Bases;
869 modes.resize(nmodes);
870 Info <<
"####### Performing POD using Singular Value Decomposition for " <<
871 snapshots[0].name() <<
" #######" << endl;
874 Eigen::VectorXd V3dSqrt = V.array().sqrt();
875 Eigen::VectorXd V3dInv = V3dSqrt.array().cwiseInverse();
876 auto VMsqr = V3dSqrt.asDiagonal();
877 auto VMsqrInv = V3dInv.asDiagonal();
878 Eigen::MatrixXd SnapMatrix2 = VMsqr * SnapMatrix;
879 Eigen::JacobiSVD<Eigen::MatrixXd> svd(SnapMatrix2,
880 Eigen::ComputeThinU | Eigen::ComputeThinV);
881 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
883 Eigen::VectorXd eigenValueseig;
884 Eigen::MatrixXd eigenVectoreig;
885 eigenValueseig = svd.singularValues().real();
886 eigenVectoreig = svd.matrixU().real();
887 Eigen::MatrixXd modesEig = VMsqrInv * eigenVectoreig;
888 GeometricField<Type, PatchField, GeoMesh> tmb_bu(snapshots[0].name(),
891 for (label i = 0; i < nmodes; i++)
893 Eigen::VectorXd vec = modesEig.col(i);
895 modes.set(i, tmb_bu.clone());
898 eigenValueseig = eigenValueseig / eigenValueseig.sum();
899 Eigen::VectorXd cumEigenValues(eigenValueseig);
901 for (label j = 1; j < cumEigenValues.size(); ++j)
903 cumEigenValues(j) += cumEigenValues(j - 1);
906 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
912 snapshots[0].name());
920 Eigen::saveMarketVector(eigenValueseig,
921 "./ITHACAoutput/POD/Eigenvalues_" + snapshots[0].name(), para->precision,
923 Eigen::saveMarketVector(cumEigenValues,
924 "./ITHACAoutput/POD/CumEigenvalues_" + snapshots[0].name(), para->precision,
929 Info <<
"Reading the existing modes" << endl;
934 "./ITHACAoutput/supremizer/");
944 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& modes,
945 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
949 PtrList<volVectorField>& snapshots, PtrList<volVectorField>& modes,
950 word fieldName,
bool podex,
bool supex,
bool sup, label nmodes,
955Eigen::MatrixXd
corMatrix(PtrList<volScalarField>& snapshots)
957 Info <<
"########## Filling the correlation matrix for " << snapshots[0].name()
958 <<
"##########" << endl;
959 Eigen::MatrixXd matrix( snapshots.size(), snapshots.size());
961 for (label i = 0; i < snapshots.size(); i++)
963 for (label j = 0; j <= i; j++)
965 matrix(i, j) = fvc::domainIntegrate(snapshots[i] * snapshots[j]).value();
969 for (label i = 1; i < snapshots.size(); i++)
971 for (label j = 0; j < i; j++)
973 matrix(j, i) = matrix(i, j);
983Eigen::MatrixXd
corMatrix(PtrList<volVectorField>& snapshots)
985 Info <<
"########## Filling the correlation matrix for " << snapshots[0].name()
986 <<
"##########" << endl;
987 Eigen::MatrixXd matrix( snapshots.size(), snapshots.size());
989 for (label i = 0; i < snapshots.size(); i++)
991 for (label j = 0; j <= i; j++)
993 matrix(i, j) = fvc::domainIntegrate(snapshots[i] & snapshots[j]).value();
997 for (label i = 1; i < snapshots.size(); i++)
999 for (label j = 0; j < i; j++)
1001 matrix(j, i) = matrix(i, j);
1010Eigen::MatrixXd
corMatrix(List<Eigen::SparseMatrix<double >>&
1013 Info <<
"########## Filling the correlation matrix for the matrix list ##########"
1015 Eigen::MatrixXd matrix(snapshots.size(), snapshots.size());
1017 for (label i = 0; i < snapshots.size(); i++)
1019 for (label j = 0; j <= i; j++)
1023 for (label k = 0; k < snapshots[i].cols(); k++)
1025 res += snapshots[i].col(k).dot(snapshots[j].col(k));
1032 for (label i = 1; i < snapshots.size(); i++)
1034 for (label j = 0; j < i; j++)
1036 matrix(j, i) = matrix(i, j);
1047 Info <<
"########## Filling the correlation matrix for the matrix list ##########"
1049 Eigen::MatrixXd matrix( snapshots.size(), snapshots.size());
1051 for (label i = 0; i < snapshots.size(); i++)
1053 for (label j = 0; j <= i; j++)
1055 matrix(i, j) = (snapshots[i].transpose() * snapshots[j]).trace();
1059 for (label i = 1; i < snapshots.size(); i++)
1061 for (label j = 0; j < i; j++)
1063 matrix(j, i) = matrix(i, j);
1073template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
1074void exportBases(PtrList<GeometricField<Type, PatchField, GeoMesh >>& s,
1075 PtrList<GeometricField<Type, PatchField, GeoMesh >>& bases,
1076 word fieldName,
bool sup)
1082 for (label i = 0; i < s.size(); i++)
1084 mkDir(
"./ITHACAoutput/supremizer/" + name(i + 1));
1085 fieldname =
"./ITHACAoutput/supremizer/" + name(i + 1) +
"/" +
1087 OFstream os(fieldname);
1088 bases[i].writeHeader(os);
1096 for (label i = 0; i < s.size(); i++)
1098 mkDir(
"./ITHACAoutput/POD/" + name(i + 1));
1099 fieldname =
"./ITHACAoutput/POD/" + name(i + 1) +
"/" + bases[i].name();
1100 OFstream os(fieldname);
1101 bases[i].writeHeader(os);
1107template void exportBases(PtrList<volVectorField>& s,
1108 PtrList<volVectorField>& bases, word fieldName,
bool sup);
1109template void exportBases(PtrList<volScalarField>& s,
1110 PtrList<volScalarField>& bases, word fieldName,
bool sup);
1118 mkDir(
"./ITHACAoutput/supremizer/");
1119 fieldname =
"./ITHACAoutput/supremizer/Eigenvalues_" + name;
1120 OFstream os(fieldname);
1122 for (label i = 0; i < Eigenvalues.size(); i++)
1124 os << Eigenvalues[i] << endl;
1130 mkDir(
"./ITHACAoutput/POD/");
1131 fieldname =
"./ITHACAoutput/POD/Eigenvalues_" + name;
1132 OFstream os(fieldname);
1134 for (label i = 0; i < Eigenvalues.size(); i++)
1136 os << Eigenvalues[i] << endl;
1147 mkDir(
"./ITHACAoutput/supremizer/");
1148 fieldname =
"./ITHACAoutput/supremizer/cumEigenvalues_" + name;
1149 OFstream os(fieldname);
1151 for (label i = 0; i < cumEigenvalues.size(); i++)
1153 os << cumEigenvalues[i] << endl;
1159 mkDir(
"./ITHACAoutput/POD/");
1160 fieldname =
"./ITHACAoutput/POD/cumEigenvalues_" + name;
1161 OFstream os(fieldname);
1163 for (label i = 0; i < cumEigenvalues.size(); i++)
1165 os << cumEigenvalues[i] << endl;
1171std::tuple<List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >>
1173 List<Eigen::VectorXd>& b, label nmodesA, label nmodesB, word MatrixName)
1176 List<Eigen::SparseMatrix<double >> ModesA(nmodesA);
1177 List<Eigen::VectorXd> ModesB(nmodesB);
1181 if (nmodesA > A.size() - 2 || nmodesB > A.size() - 2 )
1184 "The number of requested modes cannot be bigger than the number of Snapshots - 2"
1189 scalarField eigenValuesA(nmodesA);
1190 scalarField cumEigenValuesA(nmodesA);
1191 scalarField eigenValuesB(nmodesB);
1192 scalarField cumEigenValuesB(nmodesB);
1193 List<scalarField> eigenVectorA(nmodesA);
1194 List<scalarField> eigenVectorB(nmodesB);
1196 for (label i = 0; i < nmodesA; i++)
1198 eigenVectorA[i].setSize(A.size());
1201 for (label i = 0; i < nmodesB; i++)
1203 eigenVectorB[i].setSize(A.size());
1206 Eigen::MatrixXd corMatrixA =
corMatrix(A);
1207 Eigen::MatrixXd corMatrixB =
corMatrix(b);
1208 Info <<
"####### Performing the POD for the Matrix List #######" << endl;
1209 Spectra::DenseSymMatProd<double> opA(corMatrixA);
1210 Spectra::DenseSymMatProd<double> opB(corMatrixB);
1211 Spectra::SymEigsSolver<Spectra:: DenseSymMatProd<double >>
1212 esA(opA, nmodesA, nmodesA + 10);
1213 Spectra::SymEigsSolver<Spectra:: DenseSymMatProd<
double
1215 esB(opB, nmodesB, nmodesB + 10);
1218 esA.compute(Spectra::SortRule::LargestAlge);
1225 Info <<
"####### End of the POD for the Matrix List #######" << endl;
1226 Eigen::VectorXd eigenValueseigA;
1227 Eigen::MatrixXd eigenVectorseigA;
1228 Eigen::VectorXd eigenValueseigB;
1229 Eigen::MatrixXd eigenVectorseigB;
1231 if (esA.info() == Spectra::CompInfo::Successful)
1233 eigenValueseigA = esA.eigenvalues().real();
1234 eigenVectorseigA = esA.eigenvectors().real();
1236 if (esB.info() == Spectra::CompInfo::Successful && nmodesB != 1)
1238 eigenValueseigB = esB.eigenvalues().real();
1239 eigenVectorseigB = esB.eigenvectors().real();
1241 else if (nmodesB == 1)
1243 eigenValueseigB.resize(1);
1244 eigenVectorseigB.resize(A.size(), nmodesB);
1245 eigenValueseigB(0) = 1;
1246 eigenVectorseigB = eigenVectorseigB * 0;
1247 eigenVectorseigB(0, 0) = 1;
1251 Info <<
"The Eigenvalue solver in ITHACAPOD.H did not converge, exiting the code"
1258 Info <<
"The Eigenvalue solver in ITHACAPOD.H did not converge, exiting the code"
1263 for (label i = 0; i < nmodesA; i++)
1265 eigenValuesA[i] = eigenValueseigA(i) / eigenValueseigA.sum();
1268 for (label i = 0; i < nmodesB; i++)
1270 eigenValuesB[i] = eigenValueseigB(i) / eigenValueseigB.sum();
1273 for (label i = 0; i < nmodesA; i++)
1275 for (label k = 0; k < A.size(); k++)
1277 eigenVectorA[i][k] = eigenVectorseigA(k, i);
1281 for (label i = 0; i < nmodesB; i++)
1283 for (label k = 0; k < A.size(); k++)
1285 eigenVectorB[i][k] = eigenVectorseigB(k, i);
1289 cumEigenValuesA[0] = eigenValuesA[0];
1290 cumEigenValuesB[0] = eigenValuesB[0];
1292 for (label i = 1; i < nmodesA; i++)
1294 cumEigenValuesA[i] = cumEigenValuesA[i - 1] + eigenValuesA[i];
1297 for (label i = 1; i < nmodesB; i++)
1299 cumEigenValuesB[i] = cumEigenValuesB[i - 1] + eigenValuesB[i];
1302 Eigen::SparseMatrix<double> tmp_A;
1303 Eigen::VectorXd tmp_B;
1305 for (label i = 0; i < nmodesA; i++)
1307 tmp_A = eigenVectorA[i][0] * A[0];
1309 for (label k = 1; k < A.size(); k++)
1311 tmp_A += eigenVectorA[i][k] * A[k];
1317 for (label i = 0; i < nmodesB; i++)
1319 tmp_B = eigenVectorB[i][0] * b[0];
1321 for (label k = 1; k < A.size(); k++)
1323 tmp_B += eigenVectorB[i][k] * b[k];
1330 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"eigenValuesA_" + MatrixName);
1332 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"cumEigenValuesA_" + MatrixName);
1334 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"eigenValuesB_" + MatrixName);
1336 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"cumEigenValuesB_" + MatrixName);
1338 for (label i = 0; i < ModesA.size(); i++)
1341 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"A_" + MatrixName + name(i));
1344 for (label i = 0; i < ModesB.size(); i++)
1347 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"B_" + MatrixName + name(i));
1352 for (label i = 0; i < nmodesA; i++)
1355 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"A_" + MatrixName + name(i));
1358 for (label i = 0; i < nmodesB; i++)
1361 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"B_" + MatrixName + name(i));
1365 std::tuple <List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >> tupla;
1366 tupla = std::make_tuple(ModesA, ModesB);
1372 Eigen::MatrixXd Ortho = Matrix;
1375 for (label i = 0; i < Matrix.cols(); i++)
1377 for (label k = 0; k < i; k++)
1379 double num = Ortho.col(k).transpose() * Matrix.col(i);
1380 double den = (Ortho.col(k).transpose() * Ortho.col(k));
1381 double fact = num / den;
1382 Ortho.col(i) -= fact * Ortho.col(k) ;
1385 Ortho.col(i).normalize();
1391template<
class Type,
template<
class>
class PatchField,
class GeoMesh>
1393 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots,
1394 PtrList<GeometricField<Type, PatchField, GeoMesh >>& modes,
1395 PtrList<volScalarField>& Volumes, word fieldName,
bool podex,
bool supex,
1396 bool sup, label nmodes,
bool correctBC)
1400 if (nmodes == 0 && para->eigensolver ==
"spectra")
1402 nmodes = snapshots.size() - 2;
1407 nmodes = snapshots.size();
1412 M_Assert(nmodes <= snapshots.size() - 2,
1413 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
1416 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
1420 label NBC = snapshots[0].boundaryField().size();
1422 Eigen::MatrixXd _corMatrix(snapshots.size(), snapshots.size());
1423 Info <<
"Filling the correlation matrix for field " << snapshots[0].name() <<
1426 for (label i = 0; i < snapshots.size(); i++)
1428 for (label j = 0; j <= i; j++)
1430 Eigen::VectorXd Mij = (V.col(i).array() * V.col(j).array());
1431 Mij = Mij.array().abs().sqrt();
1432 _corMatrix(i, j) = SnapMatrix.col(i).transpose() * Mij.asDiagonal() *
1439 for (label i = 1; i < snapshots.size(); i++)
1441 for (label j = 0; j < i; j++)
1443 _corMatrix(j, i) = _corMatrix(i, j);
1447 Eigen::VectorXd eigenValueseig;
1448 Eigen::MatrixXd eigenVectoreig;
1449 modes.resize(nmodes);
1450 Info <<
"####### Performing the POD using EigenDecomposition for " <<
1451 snapshots[0].name() <<
" #######" << endl;
1452 label ncv = snapshots.size();
1453 Spectra::DenseSymMatProd<double> op(_corMatrix);
1454 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEg;
1458 Spectra::SymEigsSolver< Spectra::DenseSymMatProd<double >>
1459 es(op, nmodes, ncv);
1460 Info <<
"Using Spectra EigenSolver " << endl;
1462 es.compute(Spectra::SortRule::LargestAlge);
1463 M_Assert(es.info() == Spectra::CompInfo::Successful,
1464 "The Eigenvalue Decomposition did not succeed");
1465 eigenVectoreig = es.eigenvectors().real();
1466 eigenValueseig = es.eigenvalues().real();
1470 Info <<
"Using Eigen EigenSolver " << endl;
1471 esEg.compute(_corMatrix);
1472 M_Assert(esEg.info() == Eigen::Success,
1473 "The Eigenvalue Decomposition did not succeed");
1474 eigenVectoreig = esEg.eigenvectors().real().rowwise().reverse().leftCols(
1476 eigenValueseig = esEg.eigenvalues().real().reverse().head(nmodes);
1479 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
1481 Eigen::VectorXd eigenValueseigLam =
1482 eigenValueseig.real().array().cwiseInverse().sqrt() ;
1483 Eigen::MatrixXd modesEig = (SnapMatrix * eigenVectoreig) *
1484 eigenValueseigLam.asDiagonal();
1485 List<Eigen::MatrixXd> modesEigBC;
1486 modesEigBC.resize(NBC);
1488 for (label i = 0; i < NBC; i++)
1490 modesEigBC[i] = (SnapMatrixBC[i] * eigenVectoreig) *
1491 eigenValueseigLam.asDiagonal();
1494 for (label i = 0; i < modes.size(); i++)
1496 GeometricField<Type, PatchField, GeoMesh> tmp2(snapshots[0].name(),
1498 Eigen::VectorXd vec = modesEig.col(i);
1502 for (label k = 0; k < tmp2.boundaryField().size(); k++)
1507 modes.set(i, tmp2.clone());
1510 eigenValueseig = eigenValueseig / eigenValueseig.sum();
1511 Eigen::VectorXd cumEigenValues(eigenValueseig);
1513 for (label j = 1; j < cumEigenValues.size(); ++j)
1515 cumEigenValues(j) += cumEigenValues(j - 1);
1518 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
" #######"
1521 Eigen::saveMarketVector(eigenValueseig,
1522 "./ITHACAoutput/POD/Eigenvalues_" + snapshots[0].name(), para->
precision,
1524 Eigen::saveMarketVector(cumEigenValues,
1525 "./ITHACAoutput/POD/CumEigenvalues_" + snapshots[0].name(), para->
precision,
1530 Info <<
"Reading the existing modes" << endl;
1544 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& modes,
1545 PtrList<volScalarField>& Volumes, word fieldName,
bool podex,
bool supex,
1546 bool sup, label nmodes,
bool correctBC);
1549 PtrList<volVectorField>& snapshots, PtrList<volVectorField>& modes,
1550 PtrList<volScalarField>& Volumes, word fieldName,
bool podex,
bool supex,
1551 bool sup, label nmodes,
bool correctBC);
1553template<
typename type_matrix>
1554std::tuple<List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >>
1555DEIMmodes(PtrList<type_matrix>& MatrixList, label nmodesA, label nmodesB,
1559 List<Eigen::SparseMatrix<double >> ModesA(nmodesA);
1560 List<Eigen::VectorXd> ModesB(nmodesB);
1564 M_Assert(nmodesA <= MatrixList.size() - 2
1565 && nmodesB <= MatrixList.size() - 2,
1566 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
1567 std::tuple<List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >> snapshots
1570 Eigen::MatrixXd corMatrixA =
corMatrix(std::get<0>(snapshots));
1571 Eigen::MatrixXd corMatrixB =
corMatrix(std::get<1>(snapshots));
1572 Eigen::VectorXd eigenValueseigA;
1573 Eigen::MatrixXd eigenVectorseigA;
1574 Eigen::VectorXd eigenValueseigB;
1575 Eigen::MatrixXd eigenVectorseigB;
1577 if (para->eigensolver ==
"spectra")
1579 Info <<
"####### Performing the POD decomposition for the Matrix List using Spectra #######"
1581 Spectra::DenseSymMatProd<double> opA(corMatrixA);
1582 Spectra::DenseSymMatProd<double> opB(corMatrixB);
1583 label ncvA = MatrixList.size();
1584 label ncvB = MatrixList.size();
1585 Spectra::SymEigsSolver<Spectra:: DenseSymMatProd<double >>
1586 esA(opA, nmodesA, ncvA);
1587 Spectra::SymEigsSolver<Spectra:: DenseSymMatProd<
double
1589 esB(opB, nmodesB, ncvB);
1592 esA.compute(Spectra::SortRule::LargestAlge);
1593 M_Assert(esA.info() == Spectra::CompInfo::Successful,
1594 "The Eigenvalue Decomposition did not succeed");
1598 esB.compute(Spectra::SortRule::LargestAlge);
1599 M_Assert(esB.info() == Spectra::CompInfo::Successful,
1600 "The Eigenvalue Decomposition did not succeed");
1603 Info <<
"####### End of the POD decomposition for the Matrix List #######" <<
1605 eigenValueseigA = esA.eigenvalues().real();
1606 eigenVectorseigA = esA.eigenvectors().real();
1610 eigenValueseigB = esB.eigenvalues().real();
1611 eigenVectorseigB = esB.eigenvectors().real();
1615 eigenValueseigB.resize(1);
1616 eigenVectorseigB.resize(MatrixList.size(), nmodesB);
1617 eigenValueseigB(0) = 1;
1618 eigenVectorseigB = eigenVectorseigB * 0;
1619 eigenVectorseigB(0, 0) = 1;
1622 else if (para->eigensolver ==
"eigen")
1624 Info <<
"####### Performing the POD decomposition for the Matrix List using Eigen #######"
1626 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEgA;
1627 esEgA.compute(corMatrixA);
1628 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEgB;
1629 esEgB.compute(corMatrixB);
1630 M_Assert(esEgA.info() == Eigen::Success,
1631 "The Eigenvalue Decomposition did not succeed");
1632 M_Assert(esEgB.info() == Eigen::Success,
1633 "The Eigenvalue Decomposition did not succeed");
1634 eigenVectorseigA = esEgA.eigenvectors().real().rowwise().reverse().leftCols(
1636 eigenValueseigA = esEgA.eigenvalues().real().reverse().head(nmodesA);
1637 eigenVectorseigB = esEgB.eigenvectors().real().rowwise().reverse().leftCols(
1639 eigenValueseigB = esEgB.eigenvalues().real().reverse().head(nmodesB);
1642 Eigen::SparseMatrix<double> tmp_A;
1643 Eigen::VectorXd tmp_B;
1645 for (label i = 0; i < nmodesA; i++)
1647 tmp_A = eigenVectorseigA(0, i) * std::get<0>(snapshots)[0];
1649 for (label k = 1; k < MatrixList.size(); k++)
1651 tmp_A += eigenVectorseigA(k, i) * std::get<0>(snapshots)[k];
1657 for (label i = 0; i < nmodesB; i++)
1659 tmp_B = eigenVectorseigB(0, i) * std::get<1>(snapshots)[0];
1661 for (label k = 1; k < MatrixList.size(); k++)
1663 tmp_B += eigenVectorseigB(k, i) * std::get<1>(snapshots)[k];
1669 eigenValueseigA = eigenValueseigA / eigenValueseigA.sum();
1670 eigenValueseigB = eigenValueseigB / eigenValueseigB.sum();
1671 Eigen::VectorXd cumEigenValuesA(eigenValueseigA);
1672 Eigen::VectorXd cumEigenValuesB(eigenValueseigB);
1674 for (label j = 1; j < cumEigenValuesA.size(); ++j)
1676 cumEigenValuesA(j) += cumEigenValuesA(j - 1);
1679 for (label j = 1; j < cumEigenValuesB.size(); ++j)
1681 cumEigenValuesB(j) += cumEigenValuesB(j - 1);
1684 for (label i = 0; i < ModesA.size(); i++)
1687 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"A_" + MatrixName + name(i));
1690 for (label i = 0; i < ModesB.size(); i++)
1693 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"B_" + MatrixName + name(i));
1696 Eigen::saveMarketVector(eigenValueseigA,
1697 "./ITHACAoutput/DEIM/" + MatrixName +
"/eigenValuesA", para->precision,
1699 Eigen::saveMarketVector(eigenValueseigB,
1700 "./ITHACAoutput/DEIM/" + MatrixName +
"/eigenValuesB", para->precision,
1702 Eigen::saveMarketVector(cumEigenValuesA,
1703 "./ITHACAoutput/DEIM/" + MatrixName +
"/cumEigenValuesA", para->precision,
1705 Eigen::saveMarketVector(cumEigenValuesB,
1706 "./ITHACAoutput/DEIM/" + MatrixName +
"/cumEigenValuesB", para->precision,
1711 for (label i = 0; i < nmodesA; i++)
1714 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"A_" + MatrixName + name(i));
1717 for (label i = 0; i < nmodesB; i++)
1720 "./ITHACAoutput/DEIM/" + MatrixName +
"/",
"B_" + MatrixName + name(i));
1724 std::tuple <List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >> tupla;
1725 tupla = std::make_tuple(ModesA, ModesB);
1729template std::tuple<List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >>
1730DEIMmodes(PtrList<fvScalarMatrix>& MatrixList, label nmodesA,
1734template std::tuple<List<Eigen::SparseMatrix<double >>, List<Eigen::VectorXd >>
1735DEIMmodes(PtrList<fvVectorMatrix>& MatrixList, label nmodesA,
1739template<
class Type,
template<
class>
class PatchField,
class GeoMesh >
1740PtrList<GeometricField<Type, PatchField, GeoMesh >>
DEIMmodes(
1741 PtrList<GeometricField<Type, PatchField, GeoMesh >>& snapshots, label nmodes,
1742 word FunctionName, word fieldName)
1745 word PODkey =
"POD_" + fieldName;
1746 word PODnorm = para->ITHACAdict->lookupOrDefault<word>(PODkey,
"L2");
1747 M_Assert(PODnorm ==
"L2" ||
1748 PODnorm ==
"Frobenius",
"The PODnorm can be only L2 or Frobenius");
1749 Info <<
"Performing POD for " << fieldName <<
" using the " << PODnorm <<
1751 PtrList<GeometricField<Type, fvPatchField, volMesh >> modes;
1752 bool correctBC =
true;
1754 if (nmodes == 0 && para->eigensolver ==
"spectra")
1756 nmodes = snapshots.size() - 2;
1759 if (nmodes == 0 && para->eigensolver ==
"eigen")
1761 nmodes = snapshots.size();
1764 if (para->eigensolver ==
"spectra")
1766 M_Assert(nmodes <= snapshots.size() - 2,
1767 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
1774 label NBC = snapshots[0].boundaryField().size();
1775 Eigen::MatrixXd _corMatrix;
1777 if (PODnorm ==
"L2")
1781 else if (PODnorm ==
"Frobenius")
1786 if (Pstream::parRun())
1788 reduce(_corMatrix, sumOp<Eigen::MatrixXd>());
1791 Eigen::VectorXd eigenValueseig;
1792 Eigen::MatrixXd eigenVectoreig;
1793 modes.resize(nmodes);
1794 Info <<
"####### Performing the POD using EigenDecomposition " <<
1795 fieldName <<
" #######" << endl;
1796 label ncv = snapshots.size();
1797 Spectra::DenseSymMatProd<double> op(_corMatrix);
1798 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEg;
1800 if (para->eigensolver ==
"spectra")
1802 Spectra::SymEigsSolver< Spectra::DenseSymMatProd<double >>
1803 es(op, nmodes, ncv);
1804 Info <<
"Using Spectra EigenSolver " << endl;
1806 es.compute(Spectra::SortRule::LargestAlge);
1807 M_Assert(es.info() == Spectra::CompInfo::Successful,
1808 "The Eigenvalue Decomposition did not succeed");
1809 eigenVectoreig = es.eigenvectors().real();
1810 eigenValueseig = es.eigenvalues().real();
1812 else if (para->eigensolver ==
"eigen")
1814 Info <<
"Using Eigen EigenSolver " << endl;
1815 esEg.compute(_corMatrix);
1816 M_Assert(esEg.info() == Eigen::Success,
1817 "The Eigenvalue Decomposition did not succeed");
1818 eigenVectoreig = esEg.eigenvectors().real().rowwise().reverse().leftCols(
1820 eigenValueseig = esEg.eigenvalues().real().array().reverse();
1823 if (eigenValueseig.array().minCoeff() < 0)
1825 eigenValueseig = eigenValueseig.array() + 2 * abs(
1826 eigenValueseig.array().minCoeff());
1829 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
1831 Eigen::MatrixXd modesEig = (SnapMatrix * eigenVectoreig);
1833 Eigen::MatrixXd normFact(nmodes, 1);
1835 for (label i = 0; i < nmodes; i++)
1837 if (PODnorm ==
"L2")
1839 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * V.asDiagonal() *
1840 modesEig.col(i))(0, 0));
1842 if (Pstream::parRun())
1844 normFact(i, 0) = (modesEig.col(i).transpose() * V.asDiagonal() *
1845 modesEig.col(i))(0, 0);
1848 else if (PODnorm ==
"Frobenius")
1850 normFact(i, 0) = std::sqrt((modesEig.col(i).transpose() * modesEig.col(i))(0,
1853 if (Pstream::parRun())
1855 normFact(i, 0) = (modesEig.col(i).transpose() * modesEig.col(i))(0, 0);
1860 if (Pstream::parRun())
1862 reduce(normFact, sumOp<Eigen::MatrixXd>());
1865 if (Pstream::parRun())
1867 normFact = normFact.cwiseSqrt();
1870 List<Eigen::MatrixXd> modesEigBC;
1871 modesEigBC.resize(NBC);
1873 for (label i = 0; i < NBC; i++)
1875 modesEigBC[i] = (SnapMatrixBC[i] * eigenVectoreig);
1878 Info << normFact << endl;
1880 for (label i = 0; i < nmodes; i++)
1882 modesEig.col(i) = modesEig.col(i).array() / normFact(i, 0);
1884 for (label j = 0; j < NBC; j++)
1886 modesEigBC[j].col(i) = modesEigBC[j].col(i).array() / normFact(i, 0);
1890 for (label i = 0; i < modes.size(); i++)
1892 GeometricField<Type, PatchField, GeoMesh> tmp2(snapshots[0].name(),
1894 Eigen::VectorXd vec = modesEig.col(i);
1897 for (label k = 0; k < NBC; k++)
1902 modes.set(i, tmp2.clone());
1905 eigenValueseig = eigenValueseig / eigenValueseig.sum();
1906 Eigen::VectorXd cumEigenValues(eigenValueseig);
1908 for (label j = 1; j < cumEigenValues.size(); ++j)
1910 cumEigenValues(j) += cumEigenValues(j - 1);
1913 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
1917 for (label i = 0; i < modes.size(); i++)
1923 Eigen::saveMarketVector(eigenValueseig,
1924 "./ITHACAoutput/DEIM/eigenValues_" + fieldName, para->precision,
1926 Eigen::saveMarketVector(cumEigenValues,
1927 "./ITHACAoutput/DEIM/cumEigenValues_" + fieldName, para->precision,
1932 Info <<
"Reading the existing modes" << endl;
1939template<
class Field_type,
class Field_type_2>
1941 PtrList<Field_type>& snapshots, PtrList<Field_type>& modes,
1942 PtrList<Field_type_2>& fields2, word fieldName,
bool podex,
bool supex,
1943 bool sup, label nmodes,
bool correctBC)
1947 if ((podex == 0 && sup == 0) || (supex == 0 && sup == 1))
1949 if (para->eigensolver ==
"spectra" )
1953 nmodes = snapshots.size() - 2;
1956 M_Assert(nmodes <= snapshots.size() - 2,
1957 "The number of requested modes cannot be bigger than the number of Snapshots - 2");
1963 nmodes = snapshots.size();
1966 M_Assert(nmodes <= snapshots.size(),
1967 "The number of requested modes cannot be bigger than the number of Snapshots");
1972 label NBC = fields2[0].boundaryField().size();
1974 Eigen::MatrixXd _corMatrix = SnapMatrix.transpose() * VM.asDiagonal() *
1977 if (Pstream::parRun())
1979 reduce(_corMatrix, sumOp<Eigen::MatrixXd>());
1982 Eigen::VectorXd eigenValueseig;
1983 Eigen::MatrixXd eigenVectoreig;
1984 modes.resize(nmodes);
1985 Info <<
"####### Performing the POD using EigenDecomposition " <<
1986 fields2[0].name() <<
" #######" << endl;
1987 label ncv = snapshots.size();
1988 Spectra::DenseSymMatProd<double> op(_corMatrix);
1989 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esEg;
1993 Spectra::SymEigsSolver<Spectra::DenseSymMatProd<double >>
1994 es(op, nmodes, ncv);
1995 Info <<
"Using Spectra EigenSolver " << endl;
1997 es.compute(Spectra::SortRule::LargestAlge);
1998 M_Assert(es.info() == Spectra::CompInfo::Successful,
1999 "The Eigenvalue Decomposition did not succeed");
2000 eigenVectoreig = es.eigenvectors().real();
2001 eigenValueseig = es.eigenvalues().real();
2005 Info <<
"Using Eigen EigenSolver " << endl;
2006 esEg.compute(_corMatrix);
2007 M_Assert(esEg.info() == Eigen::Success,
2008 "The Eigenvalue Decomposition did not succeed");
2009 eigenVectoreig = esEg.eigenvectors().real().rowwise().reverse().leftCols(
2011 eigenValueseig = esEg.eigenvalues().real().reverse().head(nmodes);
2014 Info <<
"####### End of the POD for " << snapshots[0].name() <<
" #######" <<
2016 Eigen::VectorXd eigenValueseigLam =
2017 eigenValueseig.real().array().cwiseInverse().abs().sqrt() ;
2018 Eigen::MatrixXd modesEig = (SnapMatrix * eigenVectoreig) *
2019 eigenValueseigLam.asDiagonal();
2020 List<Eigen::MatrixXd> modesEigBC;
2021 modesEigBC.resize(NBC);
2023 for (label i = 0; i < modes.size(); i++)
2025 Field_type tmp2(snapshots[0].name(), snapshots[0] * 0);
2027 for (label j = 0; j < snapshots.size(); j++)
2029 tmp2 += eigenValueseigLam(i) * eigenVectoreig.col(i)(j) * snapshots[j];
2032 modes.set(i, tmp2.clone());
2035 eigenValueseig = eigenValueseig / eigenValueseig.sum();
2036 Eigen::VectorXd cumEigenValues(eigenValueseig);
2038 for (label j = 1; j < cumEigenValues.size(); ++j)
2040 cumEigenValues(j) += cumEigenValues(j - 1);
2043 Info <<
"####### Saving the POD bases for " << snapshots[0].name() <<
2049 snapshots[0].name());
2056 Eigen::saveMarketVector(eigenValueseig,
2057 "./ITHACAoutput/POD/Eigenvalues_" + snapshots[0].name(), para->
precision,
2059 Eigen::saveMarketVector(cumEigenValues,
2060 "./ITHACAoutput/POD/CumEigenvalues_" + snapshots[0].name(), para->
precision,
2065 Info <<
"Reading the existing modes" << endl;
2079 PtrList<surfaceScalarField>& snapshots, PtrList<surfaceScalarField>& modes,
2080 PtrList<volVectorField>& fields2, word fieldName,
bool podex,
bool supex,
2081 bool sup, label nmodes,
bool correctBC);
2084 PtrList<volScalarField>& snapshots, PtrList<volScalarField>& modes,
2085 PtrList<volVectorField>& fields2, word fieldName,
bool podex,
bool supex,
2086 bool sup, label nmodes,
bool correctBC);
2088template PtrList<volScalarField>
2090 PtrList<volScalarField>& SnapShotsMatrix,
2092 word FunctionName, word FieldName);
2094template PtrList<volVectorField>
2096 PtrList<volVectorField>& SnapShotsMatrix,
2098 word FunctionName, word FieldName);
2101scalar computeInnerProduct(
2102 const GeometricField<scalar, fvPatchField, volMesh>& field1,
2103 const GeometricField<scalar, fvPatchField, volMesh>& field2)
2105 return fvc::domainIntegrate(field1 * field2).value();
2109scalar computeInnerProduct(
2110 const GeometricField<vector, fvPatchField, volMesh>& field1,
2111 const GeometricField<vector, fvPatchField, volMesh>& field2)
2113 return fvc::domainIntegrate(field1 & field2).value();
2117scalar computeFrobeniusInnerProduct(
2118 const GeometricField<scalar, fvPatchField, volMesh>& field1,
2119 const GeometricField<scalar, fvPatchField, volMesh>& field2)
2121 return sum(field1.primitiveField() * field2.primitiveField());
2125scalar computeFrobeniusInnerProduct(
2126 const GeometricField<vector, fvPatchField, volMesh>& field1,
2127 const GeometricField<vector, fvPatchField, volMesh>& field2)
2129 return sum(field1.primitiveField()&field2.primitiveField());
Header file of the EigenFunctions class.
Header file of the ITHACAPOD class.
static GeometricField< scalar, PatchField, GeoMesh > Eigen2field(GeometricField< scalar, PatchField, GeoMesh > &field, Eigen::VectorXd &eigen_vector, bool correctBC=true)
Convert a vector in Eigen format into an OpenFOAM scalar GeometricField.
static List< Eigen::VectorXd > field2EigenBC(GeometricField< scalar, PatchField, GeoMesh > &field)
Convert an OpenFOAM scalar field to a List of Eigen Vectors, one for each boundary.
static std::tuple< List< Eigen::SparseMatrix< double > >, List< Eigen::VectorXd > > LFvMatrix2LSM(PtrList< fvMatrix< Type > > &MatrixList)
Convert a PtrList of OpenFOAM fvMatrix into a tuple of lists of Eigen Sparse Matrices and source vect...
static List< Eigen::MatrixXd > PtrList2EigenBC(PtrList< GeometricField< scalar, PatchField, GeoMesh > > &fields, label Nfields=-1)
Convert an OpenFOAM scalar field to a List of Eigen Vectors, one for each boundary.
static Eigen::MatrixXd PtrList2Eigen(PtrList< GeometricField< Type, PatchField, GeoMesh > > &fields, label Nfields=-1)
Convert a PtrList of snapshots to Eigen matrix (only internal field).
Class for the definition of some general parameters, the parameters must be defined from the file ITH...
word eigensolver
type of eigensolver used in the eigenvalue decomposition can be either be eigen or spectra
label precision
precision of the output Market Matrix objects (i.e. reduced matrices, eigenvalues,...
std::_Ios_Fmtflags outytpe
type of output format can be fixed or scientific
static ITHACAparameters * getInstance(const fvMesh &mesh, Time &localTime)
Gets an instance of ITHACAparameters, to be used if the instance is not existing.
namespace for the computation of the POD, it exploits bot the method of snapshots and SVD.
void exportcumEigenvalues(scalarField cumEigenvalues, fileName name, bool sup)
Export the cumulative eigenvalues as a txt file.
void getModesMemoryEfficient(GeometricField< Type, PatchField, GeoMesh > &templateField, word snapshotsPath, PtrList< GeometricField< Type, PatchField, GeoMesh > > &modes, word fieldName, bool podex, bool supex, bool sup, label nmodes, bool correctBC, autoPtr< GeometricField< Type, PatchField, GeoMesh > > meanField)
Gets the modes in a memory-efficient manner.
void exportEigenvalues(scalarField Eigenvalues, fileName name, bool sup)
Exports the eigenvalues as a txt file.
void getMeanMemoryEfficient(GeometricField< Type, PatchField, GeoMesh > &templateField, word snapshotsPath, autoPtr< GeometricField< Type, PatchField, GeoMesh > > &meanField, bool meanex)
Gets the mean field in a memory-efficient manner.
void getModes(PtrList< GeometricField< Type, PatchField, GeoMesh > > &snapshots, PtrList< GeometricField< Type, PatchField, GeoMesh > > &modes, word fieldName, bool podex, bool supex, bool sup, label nmodes, bool correctBC)
Computes the bases or reads them for a field.
std::tuple< List< Eigen::SparseMatrix< double > >, List< Eigen::VectorXd > > DEIMmodes(List< Eigen::SparseMatrix< double > > &A, List< Eigen::VectorXd > &b, label nmodesA, label nmodesB, word MatrixName)
Get the DEIM modes for a generic non a parametrized matrix coming from a differential operator functi...
void getNestedSnapshotMatrix(PtrList< GeometricField< Type, PatchField, GeoMesh > > &snapshots, PtrList< GeometricField< Type, PatchField, GeoMesh > > &ModesGlobal, word fieldName, label Npar, label NnestedOut)
Nested-POD approach.
void exportBases(PtrList< GeometricField< Type, PatchField, GeoMesh > > &s, PtrList< GeometricField< Type, PatchField, GeoMesh > > &bases, word fieldName, bool sup)
Export the Bases.
void getModesSVD(PtrList< GeometricField< Type, PatchField, GeoMesh > > &snapshots, PtrList< GeometricField< Type, PatchField, GeoMesh > > &modes, word fieldName, bool podex, bool supex, bool sup, label nmodes, bool correctBC)
Gets the bases for a scalar field using SVD instead of the method of snapshots.
Eigen::MatrixXd corMatrix(PtrList< volScalarField > &snapshots)
Construct the Correlation Matrix for Scalar Field.
void GrammSchmidt(Eigen::MatrixXd &Matrix)
Performs GrammSchmidt orthonormalization on an Eigen Matrix.
void getWeightedModes(PtrList< GeometricField< Type, PatchField, GeoMesh > > &snapshots, PtrList< GeometricField< Type, PatchField, GeoMesh > > &modes, word fieldName, bool podex, bool supex, bool sup, label nmodes, bool correctBC)
Computes the weighted bases (using the nested-pod approach) or read them for a vector field.
void SaveSparseMatrix(Eigen::SparseMatrix< T, Nrows, IND > &m, word folder, word MatrixName)
Export an Eigen sparse matrix into bynary format file.
GeometricField< Type, PatchField, GeoMesh > readFieldByIndex(const GeometricField< Type, PatchField, GeoMesh > &field, fileName casename, label index)
Function to read a single field by index from a folder.
void ReadDenseMatrix(MatrixType &Matrix, word folder, word MatrixName)
Read a dense matrix from a binary format file.
void exportFields(PtrList< GeometricField< Type, PatchField, GeoMesh > > &field, word folder, word fieldname)
Function to export a scalar of vector field.
void exportSolution(GeometricField< Type, PatchField, GeoMesh > &s, fileName subfolder, fileName folder, word fieldName)
Export a field to file in a certain folder and subfolder.
void exportList(T &list, word folder, word filename)
Export a list to file.
void SaveDenseMatrix(MatrixType &Matrix, word folder, word MatrixName)
Save a dense matrix to a binary format file.
void read_fields(PtrList< GeometricField< Type, PatchField, GeoMesh > > &Lfield, word Name, fileName casename, int first_snap, int n_snap)
Function to read a list of fields from the name of the field and casename.
void ReadSparseMatrix(Eigen::SparseMatrix< T, Nrows, IND > &m, word folder, word MatrixName)
Read an Eigen sparse matrix from a bynary format file.
Eigen::VectorXd getMassMatrixFV(GeometricField< Type, PatchField, GeoMesh > &snapshot)
Gets a vector containing the volumes of each cell of the mesh.
Eigen::MatrixXd getMassMatrix(PtrList< GeometricField< Type, PatchField, GeoMesh > > &modes, label Nmodes, bool consider_volumes)
Gets the mass matrix using the eigen routine.
void assignBC(GeometricField< scalar, fvPatchField, volMesh > &s, label BC_ind, double value)
Assign uniform Boundary Condition to a volScalarField.
bool check_folder(word folder)
Checks if a folder exists.
void createSymLink(word folder)
Creates symbolic links to 0, system and constant.