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);
63 massVector = ITHACAutilities::getMassMatrixFV(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;
74 fillPtrList();
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);
110 }
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);
117
118 if (PODnorm == "L2")
119 {
120 projectionError = ITHACAutilities::L2Norm(orthoSnap);
121 relProjectionError = projectionError / ITHACAutilities::L2Norm(snapshot);
122 }
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));
176 }
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}
222template<class Type, template<class> class PatchField, class GeoMesh>
224 GeometricField<Type, PatchField, GeoMesh>& inputField,
225 label numberOfModes)
226{
227 Eigen::VectorXd fieldEig = Foam2Eigen::field2Eigen(inputField);
228 Eigen::VectorXd projField;
229
230 if (numberOfModes == 0)
231 {
232 if (PODnorm == "L2")
233 {
234 projField = this->EigenModes[0].transpose() * fieldEig;
235 }
236 else if (PODnorm == "Frobenius")
237 {
238 projField = this->EigenModes[0].transpose() * fieldEig;
239 }
240 }
241 else
242 {
243 M_Assert(numberOfModes <= this->EigenModes[0].cols(),
244 "Number of required modes for projection is higher then the number of available ones");
245
246 if (PODnorm == "L2")
247 {
248 projField = (this->EigenModes[0].leftCols(numberOfModes)).transpose() *
249 massVector.asDiagonal() * fieldEig;
250 }
251 else if (PODnorm == "Frobenius")
252 {
253 projField = (this->EigenModes[0].leftCols(numberOfModes)).transpose() *
254 fieldEig;
255 }
256 }
257
258 return projField;
259}
260
261template<class Type, template<class> class PatchField, class GeoMesh>
262GeometricField<Type, PatchField, GeoMesh>
264 GeometricField<Type, PatchField, GeoMesh>& inputField,
265 Eigen::MatrixXd Coeff,
266 word Name)
267{
268 if (this->EigenModes.size() == 0)
269 {
270 this->toEigen();
271 }
272
273 label Nmodes = Coeff.rows();
274 Eigen::VectorXd InField = this->EigenModes[0].leftCols(Nmodes) * Coeff;
275 inputField = Foam2Eigen::Eigen2field(inputField, InField);
276 inputField.rename(Name);
277 Info << "WARNING: Boundary conditions are not reconstructed using incremental POD"
278 << endl;
279 return inputField;
280}
281
282template<class Type, template<class> class PatchField, class GeoMesh>
283GeometricField<Type, PatchField, GeoMesh>
285 GeometricField<Type, PatchField, GeoMesh>& snapshot,
286 label numberOfModes)
287{
288 Eigen::MatrixXd Modes;
289 Eigen::MatrixXd M_vol;
290 Eigen::MatrixXd M;
291 Eigen::MatrixXd projSnapI;
292 Eigen::MatrixXd projSnapCoeff;
293
294 if (numberOfModes == 0)
295 {
296 Modes = this->EigenModes[0];
297 }
298 else
299 {
300 Modes = this->EigenModes[0].leftCols(numberOfModes);
301 }
302
303 GeometricField<Type, PatchField, GeoMesh> Fr = snapshot;
304 Eigen::MatrixXd F_eigen = Foam2Eigen::field2Eigen(snapshot);
305
306 if (PODnorm == "L2")
307 {
308 M = Modes.transpose() * massVector.asDiagonal() * Modes;
309 projSnapI = Modes.transpose() * massVector.asDiagonal() * F_eigen;
310 }
311 else if (PODnorm == "Frobenius")
312 {
313 M = Modes.transpose() * Modes;
314 projSnapI = Modes.transpose() * F_eigen;
315 }
316 else
317 {
318 std::cout << "Inner product not defined" << endl;
319 exit(0);
320 }
321
322 projSnapCoeff = M.fullPivLu().solve(projSnapI);
323 reconstruct(Fr, projSnapCoeff, "projSnap");
324 return Fr;
325}
326
327template<class Type, template<class> class PatchField, class GeoMesh>
329 PtrList<GeometricField<Type, PatchField, GeoMesh>> snapshots,
330 PtrList<GeometricField<Type, PatchField, GeoMesh>>& projSnapshots,
331 label numberOfModes)
332{
333 M_Assert(numberOfModes <= this->size(),
334 "The number of Modes used for the projection cannot be bigger than the number of available modes");
335 projSnapshots.resize(snapshots.size());
336 Eigen::MatrixXd Modes;
337
338 if (numberOfModes == 0)
339 {
340 Modes = this->EigenModes[0];
341 }
342 else
343 {
344 Modes = this->EigenModes[0].leftCols(numberOfModes);
345 }
346
347 Eigen::MatrixXd M_vol;
348 Eigen::MatrixXd M;
349 Eigen::MatrixXd projSnapI;
350 Eigen::MatrixXd projSnapCoeff;
351
352 for (label i = 0; i < snapshots.size(); i++)
353 {
354 GeometricField<Type, PatchField, GeoMesh> Fr = snapshots[0];
355 Eigen::MatrixXd F_eigen = Foam2Eigen::field2Eigen(snapshots[i]);
356
357 if (PODnorm == "L2")
358 {
359 M = Modes.transpose() * massVector.asDiagonal() * Modes;
360 projSnapI = Modes.transpose() * massVector.asDiagonal() * F_eigen;
361 }
362 else if (PODnorm == "Frobenius")
363 {
364 M = Modes.transpose() * Modes;
365 projSnapI = Modes.transpose() * F_eigen;
366 }
367 else
368 {
369 std::cout << "Inner product not defined" << endl;
370 exit(0);
371 }
372
373 projSnapCoeff = M.fullPivLu().solve(projSnapI);
374 reconstruct(Fr, projSnapCoeff, "projSnap");
375 projSnapshots.set(i, Fr.clone());
376 }
377}
378
379
380
381template<class Type, template<class> class PatchField, class GeoMesh>
383{
384 ITHACAstream::exportFields(this->toPtrList(),
385 outputFolder,
386 "base");
387 ITHACAstream::exportMatrix(singularValues, "singularValues", "eigen",
388 outputFolder);
389}
390
391
Header file of the EigenFunctions class.
#define M_Assert(Expr, Msg)
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:517
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
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.
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.
incrementalPOD()
Constructors.
GeometricField< Type, PatchField, GeoMesh > projectSnapshot(GeometricField< Type, PatchField, GeoMesh > &snapshot, label numberOfModes=0)
Project a snapshot onto the POD space.
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.
singVal col(i)
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)
Eigen::MatrixXd singularValues
label i
Definition pEqn.H:46