Implementation of tutorial 8 for DEIM reconstruction of a non linear function.
Introduction
In this tutorial we propose an example concerning the usage of the Discrete Empirical Interpolation Method (implemented in the HyperReduction class) for the approximation of a non-linear function. The following image illustrates the computational domain used to discretize the problem:

The non-linear function is described by a parametric Gaussian function:
$$ S(\mathbf{x},\mathbf{\mu}) = e^{-2(x-{\mu}_x-1)^2 - 2(y-{\mu}_y-0.5)^2}, $$
that is depending on the parameter vector $\mathbf{\mu} = [{\mu}_x, {\mu}_y]$ and on the location inside the domain $\mathbf{x} = [x,y]$. A training set of 100 samples is used to construct the "DEIM" modes which are later used for the approximation.
A detailed look into the code
This section explains the main steps necessary to construct the tutorial N°8.
The header files
First of all let's have a look to the header files that needs to be included and what they are responsible for. The header files of ITHACA-FV necessary for this tutorial are: <Foam2Eigen.H> for Eigen to OpenFOAM conversion of objects, <ITHACAstream.H> for ITHACA-FV input-output operations. <ITHACAPOD.H> for the POD decomposition, <hyperReduction.templates.H> for the "DEIM" approximation. Then we include chrono to compute the speedup:
Then, we construct the function DEIM_function to be approximated with the "DEIM" method from the HyperReduction class:
{
public:
HyperReduction(label n_modes, label n_nodes, Eigen::VectorXi initialSeeds, word problemName, SnapshotsLists &&...snapshotsLists)
Construct HyperReduction class, interpolation-based.
The method with the explicit definition of the non-linear function is:
static volScalarField evaluate_expression(volScalarField& S, Eigen::MatrixXd mu)
{
volScalarField yPos = S.mesh().C().component(vector::Y).ref();
volScalarField xPos = S.mesh().C().component(vector::X).ref();
for (auto i = 0; i < S.size(); i++)
{
S[i] = std::exp(- 2 * std::pow(xPos[i] - mu(0) - 1,
2) - 2 * std::pow(yPos[i] - mu(1) - 0.5, 2));
}
return S;
}
The method with the expression for the evaluation of the online coefficients is:
Eigen::VectorXd onlineCoeffs(Eigen::MatrixXd mu)
{
theta.resize(nodePoints().size());
auto f = evaluate_expression(subField(), mu);
for (int i = 0; i < nodePoints().size(); i++)
{
theta(i) = f[localNodePoints[i]];
}
return theta;
}
Now let's have a look at important commands in the main function. At first, we read the parameters from the ITHACAdict file and store the snapshots in a volScalarField:
int NDEIM = para->ITHACAdict->lookupOrDefault<int>("NDEIM", 15);
simpleControl simple(mesh);
#include "createFields.H"
PtrList<volScalarField> Sp;
volScalarField S
(
IOobject
(
"S",
runTime.timeName(),
mesh,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
T.mesh(),
dimensionedScalar("zero", dimensionSet(0, 0, -1, 1, 0, 0, 0), 0)
);
Class for the definition of some general parameters, the parameters must be defined from the file ITH...
static ITHACAparameters * getInstance()
Gets an instance of ITHACAparameters, to be used if the instance is already existing.
The, we define the parameter samples to train the non-linear function and we perform the offline training of the function. Finally, we assemble the snapshots list and export the solutions.
for (int i = 0; i < 100; i++)
{
DEIM_function::evaluate_expression(S, pars.row(i));
Sp.append((S).clone());
}
void exportSolution(GeometricField< Type, PatchField, GeoMesh > &s, fileName subfolder, fileName folder, word fieldName)
Export a field to file in a certain folder and subfolder.
Eigen::MatrixXd rand(label rows, label cols, double min, double max)
Generates random matrix with random values in an interval.
Compute the "DEIM" modes using the ITHACAPOD class:
Eigen::MatrixXd snapshotsModes;
Sp[0]).array();
S.name());
static Eigen::MatrixXd PtrList2Eigen(PtrList< GeometricField< Type, PatchField, GeoMesh > > &fields, label Nfields=-1)
Convert a PtrList of snapshots to Eigen matrix (only internal 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...
Eigen::VectorXd getMassMatrixFV(GeometricField< Type, PatchField, GeoMesh > &snapshot)
Gets a vector containing the volumes of each cell of the mesh.
It follows the construction of the HyperReduction object passing the maximum number of "DEIM" modes NDEIM, the initSeeds (none in this tutorial), the name used to store the output "DEIM" and the list of snapshots Sp. The constructor is taking the number of modes and the number of points to compute (for the "DEIM" method these numbers are equal).
Eigen::VectorXi initSeeds(0);
Then, we use the "DEIM" method HyperReduction::offlineGappyDEIM passing the "DEIM" modes and the normalizingWeights object:
c.offlineGappyDEIM(snapshotsModes, normalizingWeights);
The command to generate the submeshes used for pointwise evaluation of the function is:
c.generateSubmesh(2, mesh);
c.subField = c.interpolateField<volScalarField>(Sp[0]);
Then, the definition of a new sample value to test the accuracy of the method $\mu^*=(0,0)$ follows:
Eigen::MatrixXd par_new(2, 1);
par_new(0, 0) = 0;
par_new(1, 0) = 0;
The evaluation of the function using "DEIM" and reconstruction of the field follows:
Eigen::VectorXd aprfield = c.MatrixOnline * c.onlineCoeffs(par_new);
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.
Then, export the approximation:
Finally, we can evaluate the reference function at $\mu^*$ using the FOM, export the FOM solution and compute the error between DEIM and FOM.
DEIM_function::evaluate_expression(S, par_new);
double errorL2Rel(GeometricField< T, fvPatchField, volMesh > &field1, GeometricField< T, fvPatchField, volMesh > &field2, List< label > *labels)
Computes the relative error between two geometric Fields in L2 norm.
The plain code
The plain code is available here.