Loading...
Searching...
No Matches
incrementalPOD.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ██╗████████╗██╗ ██╗ █████╗ ██████╗ █████╗ ███████╗██╗ ██╗
3 ██║╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔══██╗ ██╔════╝██║ ██║
4 ██║ ██║ ███████║███████║██║ ███████║█████╗█████╗ ██║ ██║
5 ██║ ██║ ██╔══██║██╔══██║██║ ██╔══██║╚════╝██╔══╝ ╚██╗ ██╔╝
6 ██║ ██║ ██║ ██║██║ ██║╚██████╗██║ ██║ ██║ ╚████╔╝
7 ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═══╝
8
9 * In real Time Highly Advanced Computational Applications for Finite Volumes
10 * Copyright (C) 2017 by the ITHACA-FV authors
11-------------------------------------------------------------------------------
12
13 License
14 This file is part of ITHACA-FV
15
16 ITHACA-FV is free software: you can redistribute it and/or modify
17 it under the terms of the GNU Lesser General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 ITHACA-FV is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU Lesser General Public License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with ITHACA-FV. If not, see <http://www.gnu.org/licenses/>.
28
29\*---------------------------------------------------------------------------*/
30
33
34#include "incrementalPOD.H"
35#include "EigenFunctions.H"
36
37template<class Type, template<class> class PatchField, class GeoMesh>
39 GeometricField<Type, PatchField, GeoMesh>& snapshot,
40 double _tol, word _PODnorm)
41{
42 Info << "WARNING: the projection of the BC has not been implemented yet!" <<
43 endl;
44 tolleranceSVD = _tol;
45 PODnorm = _PODnorm;
46 M_Assert(PODnorm == "L2" ||
47 PODnorm == "Frobenius", "The PODnorm can be only L2 or Frobenius");
48 initialize(snapshot);
49}
50
51template<class Type, template<class> class PatchField, class GeoMesh>
53 GeometricField<Type, PatchField, GeoMesh>& snapshot)
54{
55 Info << "Initializing the incremental POD" << endl;
56 M_Assert(tolleranceSVD > 0, "Set up the tollerance before initialization");
57 M_Assert(rank == 0, "POD already initialized");
58 double snapNorm = ITHACAutilities::L2Norm(snapshot);
59
60 if (PODnorm == "L2")
61 {
62 Eigen::VectorXd snapshotEig = Foam2Eigen::field2Eigen(snapshot);
64 singularValues.resize(1);
65 singularValues[0] = Foam::sqrt((snapshotEig.transpose() *
66 massVector.asDiagonal() *
67 snapshotEig)(0, 0));
68 snapshotEig = snapshotEig / singularValues[0];
69 GeometricField<Type, PatchField, GeoMesh> tmp(snapshot);
70 tmp = Foam2Eigen::Eigen2field(tmp, snapshotEig);
71 this->append(tmp.clone());
72 this->toEigen();
73 rank = 1;
75 }
76 else
77 {
78 if (snapNorm > tolleranceSVD)
79 {
80 singularValues.resize(1);
81 singularValues[0] = snapNorm;
82 this->append(snapshot / snapNorm);
83 this->toEigen();
84 rank = 1;
85 }
86 else
87 {
88 rank = 0;
89 }
90 }
91
92 Info << "Initialization ended" << endl;
93}
94
95template<class Type, template<class> class PatchField, class GeoMesh>
97 GeometricField<Type, PatchField, GeoMesh>& snapshot)
98{
99 Info << "********************************************************************"
100 << endl;
101 Info << "Adding a snapshot" << endl;
102 Info << "Initial rank = " << rank << endl;
103 Eigen::VectorXd snapshotEig = Foam2Eigen::field2Eigen(snapshot);
104 double projectionError;
105 double relProjectionError;
106
107 if (rank == 0)
108 {
109 initialize(snapshot);
111
112 Eigen::VectorXd redCoeff = project(snapshot);
113 GeometricField<Type, PatchField, GeoMesh> projectSnap = projectSnapshot(
114 snapshot);
115 GeometricField<Type, PatchField, GeoMesh> orthoSnap(snapshot - projectSnap);
116 Eigen::VectorXd orthogonalSnap = Foam2Eigen::field2Eigen(orthoSnap);
118 if (PODnorm == "L2")
119 {
120 projectionError = ITHACAutilities::L2Norm(orthoSnap);
121 relProjectionError = projectionError / ITHACAutilities::L2Norm(snapshot);
123 else if (PODnorm == "Frobenius")
124 {
125 projectionError = snapshotEig.transpose() * snapshotEig -
126 (redCoeff.transpose() * redCoeff)(0, 0);
127 projectionError = Foam::sqrt(std::abs(projectionError));
128 relProjectionError = projectionError / (snapshotEig.transpose() * snapshotEig);
129 }
130
131 Info << "Relative projection error = " << relProjectionError << endl;
132 Eigen::MatrixXd Q = Eigen::MatrixXd::Zero(rank + 1, rank + 1);
133 Q.topLeftCorner(rank, rank) = singularValues.asDiagonal();
134 Q.topRightCorner(rank, 1) = redCoeff;
135 Q(rank, rank) = projectionError;
136
137 if (relProjectionError < tolleranceSVD)
138 {
139 Info << "Projection error is smaller than the tollerance" << endl;
140 Q(rank, rank) = 0;
141 }
142
143 Eigen::JacobiSVD<Eigen::MatrixXd> svd(Q,
144 Eigen::ComputeThinU | Eigen::ComputeThinV);
145 Eigen::MatrixXd newModes = svd.matrixU();
146 Eigen::VectorXd newSingVal = svd.singularValues();
147
148 if (relProjectionError < tolleranceSVD || rank >= snapshotEig.size())
149 {
150 Info << "Projection error is small or matrix is full rank." <<
151 endl << "SVD rank held constant" << endl;
152 this->EigenModes[0] = this->EigenModes[0] * newModes.topLeftCorner(rank, rank);
153 singularValues = newSingVal.head(rank);
154 }
155 else
156 {
157 Info << "New snapshot is not in the span of the POD space." << endl
158 << "SVD rank increases" << endl;
159 orthogonalSnap = orthogonalSnap / projectionError;
160 Eigen::MatrixXd temp(this->EigenModes[0].rows(),
161 this->EigenModes[0].cols() + 1);
162 temp << this->EigenModes[0], orthogonalSnap;
163 this->EigenModes[0] = temp * newModes;
164 singularValues = newSingVal;
165 rank++;
166 }
167
168 Info << "New POD rank = " << rank << endl;
169 double orthogonalPar;
170
171 if (PODnorm == "L2")
172 {
173 orthogonalPar = std::abs(
174 this->EigenModes[0].col(this->EigenModes[0].cols() - 1).transpose() *
175 massVector.asDiagonal() * this->EigenModes[0].col(0));
177 else if (PODnorm == "Frobenius")
178 {
179 orthogonalPar = std::abs(this->EigenModes[0].col(0).transpose() *
180 this->EigenModes[0].col(this->EigenModes[0].cols() - 1));
181 }
182
183 double EPS = 2.2204e-16;
184 Info << "Orthogonality = " << orthogonalPar << endl;
185
186 if (orthogonalPar > std::min(tolleranceSVD, EPS * this->EigenModes[0].rows()))
187 {
188 Info << "Orthogonalization required" << endl;
189
190 if (PODnorm == "L2")
191 {
192 ITHACAPOD::weightedGramSchmidt(this->EigenModes[0], massVector);
193 }
194 else if (PODnorm == "Frobenius")
195 {
196 Eigen::HouseholderQR<Eigen::MatrixXd> qr(this->EigenModes[0]);
197 Eigen::MatrixXd thinQ(Eigen::MatrixXd::Identity(this->EigenModes[0].rows(),
198 rank));
199 this->EigenModes[0] = qr.householderQ() * thinQ;
200 }
201 }
202
203 fillPtrList();
204 Info << "********************************************************************"
205 << endl;
206}
207
208template<class Type, template<class> class PatchField, class GeoMesh>
210{
211 this->resize(rank);
212
213 for (label i = 0; i < rank; i++)
214 {
215 GeometricField<Type, PatchField, GeoMesh> tmp(this->first().name(),
216 this->first());
217 Eigen::VectorXd vec = this->EigenModes[0].col(i);
218 tmp = Foam2Eigen::Eigen2field(tmp, vec);
219 this->set(i, tmp.clone());
220 }
221}
222
223template<class Type, template<class> class PatchField, class GeoMesh>
225 GeometricField<Type, PatchField, GeoMesh>& inputField,
226 label numberOfModes)
227{
228 Eigen::VectorXd fieldEig = Foam2Eigen::field2Eigen(inputField);
229 Eigen::VectorXd projField;
230
231 if (numberOfModes == 0)
232 {
233 if (PODnorm == "L2")
234 {
235 projField = this->EigenModes[0].transpose() * fieldEig;
236 }
237 else if (PODnorm == "Frobenius")
238 {
239 projField = this->EigenModes[0].transpose() * fieldEig;
240 }
241 }
242 else
243 {
244 M_Assert(numberOfModes <= this->EigenModes[0].cols(),
245 "Number of required modes for projection is higher then the number of available ones");
246
247 if (PODnorm == "L2")
248 {
249 projField = (this->EigenModes[0].leftCols(numberOfModes)).transpose() *
250 massVector.asDiagonal() * fieldEig;
251 }
252 else if (PODnorm == "Frobenius")
253 {
254 projField = (this->EigenModes[0].leftCols(numberOfModes)).transpose() *
255 fieldEig;
256 }
257 }
258
259 return projField;
260}
261
262template<class Type, template<class> class PatchField, class GeoMesh>
263GeometricField<Type, PatchField, GeoMesh>
265 GeometricField<Type, PatchField, GeoMesh>& inputField,
266 Eigen::MatrixXd Coeff,
267 word Name)
268{
269 if (this->EigenModes.size() == 0)
270 {
271 this->toEigen();
272 }
273
274 label Nmodes = Coeff.rows();
275 Eigen::VectorXd InField = this->EigenModes[0].leftCols(Nmodes) * Coeff;
276 inputField = Foam2Eigen::Eigen2field(inputField, InField);
277 inputField.rename(Name);
278 Info << "WARNING: Boundary conditions are not reconstructed using incremental POD"
279 << endl;
280 return inputField;
281}
282
283template<class Type, template<class> class PatchField, class GeoMesh>
284GeometricField<Type, PatchField, GeoMesh>
286 GeometricField<Type, PatchField, GeoMesh>& snapshot,
287 label numberOfModes)
288{
289 Eigen::MatrixXd Modes;
290 Eigen::MatrixXd M_vol;
291 Eigen::MatrixXd M;
292 Eigen::MatrixXd projSnapI;
293 Eigen::MatrixXd projSnapCoeff;
294
295 if (numberOfModes == 0)
296 {
297 Modes = this->EigenModes[0];
298 }
299 else
300 {
301 Modes = this->EigenModes[0].leftCols(numberOfModes);
302 }
303
304 GeometricField<Type, PatchField, GeoMesh> Fr = snapshot;
305 Eigen::MatrixXd F_eigen = Foam2Eigen::field2Eigen(snapshot);
306
307 if (PODnorm == "L2")
308 {
309 M = Modes.transpose() * massVector.asDiagonal() * Modes;
310 projSnapI = Modes.transpose() * massVector.asDiagonal() * F_eigen;
311 }
312 else if (PODnorm == "Frobenius")
313 {
314 M = Modes.transpose() * Modes;
315 projSnapI = Modes.transpose() * F_eigen;
316 }
317 else
318 {
319 std::cout << "Inner product not defined" << endl;
320 exit(0);
321 }
322
323 projSnapCoeff = M.fullPivLu().solve(projSnapI);
324 reconstruct(Fr, projSnapCoeff, "projSnap");
325 return Fr;
326}
327
328template<class Type, template<class> class PatchField, class GeoMesh>
330 PtrList<GeometricField<Type, PatchField, GeoMesh >> snapshots,
331 PtrList<GeometricField<Type, PatchField, GeoMesh >>& projSnapshots,
332 label numberOfModes)
333{
334 M_Assert(numberOfModes <= this->size(),
335 "The number of Modes used for the projection cannot be bigger than the number of available modes");
336 projSnapshots.resize(snapshots.size());
337 Eigen::MatrixXd Modes;
338
339 if (numberOfModes == 0)
340 {
341 Modes = this->EigenModes[0];
342 }
343 else
344 {
345 Modes = this->EigenModes[0].leftCols(numberOfModes);
346 }
347
348 Eigen::MatrixXd M_vol;
349 Eigen::MatrixXd M;
350 Eigen::MatrixXd projSnapI;
351 Eigen::MatrixXd projSnapCoeff;
352
353 for (label i = 0; i < snapshots.size(); i++)
354 {
355 GeometricField<Type, PatchField, GeoMesh> Fr = snapshots[0];
356 Eigen::MatrixXd F_eigen = Foam2Eigen::field2Eigen(snapshots[i]);
357
358 if (PODnorm == "L2")
359 {
360 M = Modes.transpose() * massVector.asDiagonal() * Modes;
361 projSnapI = Modes.transpose() * massVector.asDiagonal() * F_eigen;
362 }
363 else if (PODnorm == "Frobenius")
364 {
365 M = Modes.transpose() * Modes;
366 projSnapI = Modes.transpose() * F_eigen;
367 }
368 else
369 {
370 std::cout << "Inner product not defined" << endl;
371 exit(0);
372 }
373
374 projSnapCoeff = M.fullPivLu().solve(projSnapI);
375 reconstruct(Fr, projSnapCoeff, "projSnap");
376 projSnapshots.set(i, Fr.clone());
377 }
378}
379
380
381
382template<class Type, template<class> class PatchField, class GeoMesh>
391
392
Header file of the EigenFunctions class.
#define M_Assert(Expr, Msg)
Ufield append(U.clone())
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.
Definition Foam2Eigen.C:514
static Eigen::VectorXd field2Eigen(GeometricField< tensor, PatchField, GeoMesh > &field)
Convert a vector OpenFOAM field into an Eigen Vector.
Definition Foam2Eigen.C:40
Implementation of a container class derived from PtrList.
Definition Modes.H:69
List< Eigen::MatrixXd > EigenModes
Definition Modes.H:73
List< Eigen::MatrixXd > toEigen()
Method that convert a PtrList of modes into Eigen matrices filling the EigenModes object.
Definition Modes.C:38
PtrList< GeometricField< Type, PatchField, GeoMesh > > & toPtrList()
Function that returns the Modes object as a standard PtrList.
Definition Modes.H:86
Implementation of a incremental POD algorithm according to Oxberry et al.
void addSnapshot(GeometricField< Type, PatchField, GeoMesh > &snapshot)
Add a snapshot to the POD space.
void initialize(GeometricField< Type, PatchField, GeoMesh > &snapshot)
Initialize the incremental POD algorithm.
void fillPtrList()
Fill the POD modes prtList from the Eigen matrix.
Eigen::VectorXd singularValues
Vector of the singular values.
void writeModes()
Write to modes to file.
void projectSnapshots(PtrList< GeometricField< Type, PatchField, GeoMesh > > snapshots, PtrList< GeometricField< Type, PatchField, GeoMesh > > &projSnapshots, label numberOfModes=0)
Project a list of snapshots onto the reduced space.
Eigen::VectorXd project(GeometricField< Type, PatchField, GeoMesh > &inputField, label numberOfModes=0)
Project a field into the reduced space.
GeometricField< Type, PatchField, GeoMesh > reconstruct(GeometricField< Type, PatchField, GeoMesh > &inputField, Eigen::MatrixXd Coeff, word Name)
Reconstruct a field from the reduced coefficients.
word outputFolder
Folder where the modes and singular values are saved.
incrementalPOD()
Constructors.
double tolleranceSVD
Tollerance for the incremental SVD.
Eigen::VectorXd massVector
Vector of the cells volumes.
int rank
Rank of the reduced space.
GeometricField< Type, PatchField, GeoMesh > projectSnapshot(GeometricField< Type, PatchField, GeoMesh > &snapshot, label numberOfModes=0)
Project a snapshot onto the POD space.
word PODnorm
POD norm used for projection. It can be L2 or Frobenius.
Tf resize(coldSideSize)
Header file of the incrementalPOD class.
void weightedGramSchmidt(Eigen::MatrixXd &matrix, Eigen::VectorXd &weights)
Weighted Gram-Schmidt orthogonalization.
void exportFields(PtrList< GeometricField< Type, PatchField, GeoMesh > > &field, word folder, word fieldname)
Function to export a scalar of vector field.
void exportMatrix(Eigen::Matrix< T, -1, dim > &matrix, word Name, word type, word folder)
Export the reduced matrices in numpy (type=python), matlab (type=matlab) and txt (type=eigen) format ...
double L2Norm(GeometricField< scalar, fvPatchField, volMesh > &field)
Definition ITHACAerror.C:41
Eigen::VectorXd getMassMatrixFV(GeometricField< Type, PatchField, GeoMesh > &snapshot)
Gets a vector containing the volumes of each cell of the mesh.
Info<< endl;Info<< "*********************************************************"<< endl;Info<< "Performing test for the parameterized BC inverse solver"<< endl;Info<< endl;word outputFolder="./ITHACAoutput/parameterizedBCtest_RBFparameter/";volScalarField gTrueField=example_paramBC.list2Field(example_paramBC.gTrue);ITHACAstream::exportSolution(gTrueField, "1", outputFolder, "gTrue");Eigen::VectorXd rbfWidth=EigenFunctions::ExpSpaced(0.001, 100, rbfWidthTest_size);ITHACAstream::exportMatrix(rbfWidth, "rbfShapeParameters", "eigen", outputFolder);Eigen::VectorXd residualNorms;residualNorms.resize(rbfWidthTest_size);Eigen::VectorXd heatFluxL2norm(rbfWidthTest_size);Eigen::VectorXd heatFluxLinfNorm=heatFluxL2norm;Eigen::VectorXd condNumber=heatFluxL2norm;Eigen::MatrixXd singVal;for(int i=0;i< rbfWidthTest_size;i++){ Info<< "*********************************************************"<< endl;Info<< "RBF parameter "<< rbfWidth(i)<< endl;Info<< "Test "<< i+1<< endl;Info<< endl;example_paramBC.set_gParametrized("rbf", rbfWidth(i));example_paramBC.parameterizedBCoffline(1);example_paramBC.parameterizedBC("fullPivLU");volScalarField gParametrizedField=example_paramBC.list2Field(example_paramBC.g);ITHACAstream::exportSolution(gParametrizedField, std::to_string(i+1), outputFolder, "gParametrized");volScalarField &T(example_paramBC._T());ITHACAstream::exportSolution(T, std::to_string(i+1), outputFolder, "T");residualNorms(i)=Foam::sqrt(example_paramBC.residual.squaredNorm());Eigen::MatrixXd A=example_paramBC.Theta.transpose() *example_paramBC.Theta;Eigen::JacobiSVD< Eigen::MatrixXd > svd(A, Eigen::ComputeThinU|Eigen::ComputeThinV)
singVal col(i)
label i
Definition pEqn.H:46