Loading...
Searching...
No Matches
02thermalBlock.C

Introduction to tutorial 2

The problems consists of a thermal block with a source term. The problem equations are:

\[ \nabla \cdot (k \nabla T) = S \]

where \(k\) is the diffusivity, \(T\) is the temperature and \(S\) is the source term. The problem discretised and formalized in matrix equation reads:

\[ AT = S \]

where \(A\) is the matrix of interpolation coefficients, \(T\) is the vector of unknowns and \(S\) is the vector representing the source term. The domain is subdivided in 9 different parts and each part has parametrized diffusivity. See the image below for a clarification.

Both the full order and the reduced order problem are solved exploiting the parametric affine decomposition of the differential operators:

\[ A = \sum_{i=1}^N \theta_i(\mu) A_i \]

For the operations performed by each command check the comments in the source 02thermalBlock.C file.

The ITHACAPODdict file

In this section are explained the main steps necessary to construct the tutorial N°2

The necessary 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 standard C++ header for input/output stream objects:

#include <iostream>

The OpenFOAM header files:

#include "fvCFD.H"
#include "IOmanip.H"
#include "Time.H"

The header file of ITHACA-FV necessary for this tutorial

#include "ITHACAPOD.H"
Header file of the ITHACAPOD class.
Header file of the ITHACAutilities namespace.

The Eigen library for matrix manipulation and linear and non-linear algebra operations:

#include <Eigen/Dense>

And we define some mathematical constants and include the standard header for common math operations:

#define _USE_MATH_DEFINES
#include <cmath>

Implementation of the tutorial02 class

Then we can define the tutorial02 class as a child of the laplacianProblem class

Class to implement a full order laplacian parametrized problem.
Class where the tutorial number 2 is implemented.
{
public:
explicit tutorial02(int argc, char* argv[])
:
laplacianProblem(argc, argv),
T(_T()),
nu(_nu()),
S(_S())
{}
tutorial02(int argc, char *argv[])
_T
Definition createT.H:30
volScalarField & T
Definition createT.H:46
dimensionedScalar & nu
_nu
_S
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))

The members of the class are the fields that needs to be manipulated during the resolution of the problem

Inside the class it is defined the offline solve method according to the specific parametrized problem that needs to be solved.

void offlineSolve(word folder = "./ITHACAoutput/Offline/")
void offlineSolve(word folder="./ITHACAoutput/Offline/")
It perform an offline Solve.
{

If the offline solve has already been performed than read the existing snapshots

if (offline)
bool offline
Boolean variable, it is 1 if the Offline phase has already been computed, else 0.
{
ITHACAstream::readMatrix(folder + "/mu_samples_mat.txt");
}
PtrList< volScalarField > Tfield
List of snapshots for the solution.
Eigen::MatrixXd mu_samples
Matrix of parameters to be used for PODI, where each row corresponds to a sample point....
List< Eigen::MatrixXd > readMatrix(word folder, word mat_name)
Read a three dimensional matrix from a txt file in Eigen format.
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.

else perform the offline solve where a loop over all the parameters is performed:

for (label i = 0; i < mu.rows(); i++)
Eigen::MatrixXd mu
Row matrix of parameters.
label i
Definition pEqn.H:46
{
for (label j = 0; j < mu.cols() ; j++)
{
// mu_now[i] =
theta[j] = mu(i, j);
}
List< scalar > theta
Theta (coefficients of the affine expansion)

a 0 internal constant value is assigned before each solve command with the lines

assignIF(T, IF);
void assignIF(T &s, G &value)
Assign internal field condition.

and the solve operation is performed, see also the laplacianProblem class for the definition of the methods

truthSolve(mu_now, folder);
void truthSolve()
Perform a TruthSolve.

The we need also to implement a method to set/define the source term that may be problem dependent. In this case the source term is defined with a hat function:

void SetSource()
void SetSource()
Define the source term function.
{
volScalarField yPos = T.mesh().C().component(vector::Y).ref();
volScalarField xPos = T.mesh().C().component(vector::X).ref();
{
S[counter] = Foam::sin(xPos[counter] / 0.9 * M_PI) + Foam::sin(
yPos[counter] / 0.9 * M_PI);
}
forAll(example_CG.gList, solutionI)
Definition CGtest.H:21
label counter
Counter used for the output of the full order solutions.
volScalarField xPos
volScalarField yPos
}

Define by:

\[ S = \sin(\frac{\pi}{L}\cdot x) + \sin(\frac{\pi}{L}\cdot y) \]

where \(L\) is the dimension of the thermal block which is equal to 0.9.

With the following is defined a method to set compute the parameter of the affine expansion:

void compute_nu()
void compute_nu()
Compute the diffusivity in each subdomain.
{

The list of parameters is resized according to number of parametrized regions

nu_list.resize(9);
PtrList< volScalarField > nu_list
Nu (diffusivity)

The nine different volScalarFields to identify the viscosity in each domain are initialized:

volScalarField nu1(nu);
volScalarField nu2(nu);
volScalarField nu3(nu);
volScalarField nu4(nu);
volScalarField nu5(nu);
volScalarField nu6(nu);
volScalarField nu7(nu);
volScalarField nu8(nu);
volScalarField nu9(nu);

and the 9 different boxes are defined:

Eigen::MatrixXd Box1(2, 3);
Box1 << 0, 0, 0, 0.3, 0.3, 0.1;
Eigen::MatrixXd Box2(2, 3);
Box2 << 0.3, 0, 0, 0.6, 0.3, 0.1;
Eigen::MatrixXd Box3(2, 3);
Box3 << 0.6, 0, 0, 0.91, 0.3, 0.1;
Eigen::MatrixXd Box4(2, 3);
Box4 << 0, 0.3, 0, 0.3, 0.6, 0.1;
Eigen::MatrixXd Box5(2, 3);
Box5 << 0.3, 0.3, 0, 0.6, 0.6, 0.1;
Eigen::MatrixXd Box6(2, 3);
Box6 << 0.6, 0.3, 0, 0.91, 0.6, 0.1;
Eigen::MatrixXd Box7(2, 3);
Box7 << 0, 0.6, 0, 0.3, 0.91, 0.1;
Eigen::MatrixXd Box8(2, 3);
Box8 << 0.3, 0.61, 0, 0.6, 0.91, 0.1;
Eigen::MatrixXd Box9(2, 3);

and for each of the defined boxes the relative diffusivity field is set to 1 inside the box and remain 0 elsewhere:

void setBoxToValue(GeometricField< Type, fvPatchField, volMesh > &field, Eigen::MatrixXd Box, Type value)
Set value of a volScalarField to a constant inside a given box.

See also the ITHACAutilities::setBoxToValue for more details.

The list of diffusivity fields is set with:

nu_list.set(0, (nu1).clone());
nu_list.set(1, (nu2).clone());
nu_list.set(2, (nu3).clone());
nu_list.set(3, (nu4).clone());
nu_list.set(4, (nu5).clone());
nu_list.set(5, (nu6).clone());
nu_list.set(6, (nu7).clone());
nu_list.set(7, (nu8).clone());
nu_list.set(8, (nu9).clone());
}

Definition of the main function

Once the tutorial02 class is defined the main function is defined, an example of type tutorial02 is constructed:

tutorial02 example(argc, argv);

the number of parameter is set:

example.Pnumber = 9;
example.setParameters();

the range of the parameters is defined:

example.mu_range.col(0) = Eigen::MatrixXd::Ones(9, 1) * 0.001;
example.mu_range.col(1) = Eigen::MatrixXd::Ones(9, 1) * 0.1;

and 500 random combinations of the parameters are generated:

example.genRandPar(500);

the size of the list of values that are multiplying the affine forms is set:

example.theta.resize(9);

the source term is defined, the compute_nu and assemble_operator functions are called

example.SetSource();
example.compute_nu();
example.assemble_operator();

then the Offline full order Solve is performed:

example.offlineSolve();

Once the Offline solve is performed the modes ar obtained using the ITHACAPOD::getModes function:

ITHACAPOD::getModes(example.Tfield, example.Tmodes, example._T().name(),
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.
Definition ITHACAPOD.C:93

and the projection is performed onto the POD modes using 10 modes

example.project(NmodesTproj);

Once the projection is performed we can construct a reduced object:

reducedLaplacian reduced(example);
Class to solve the online reduced problem associated with a Laplace problem.

and solve the reduced problem for some values of the parameters:

for (int i = 0; i < FOM_test.mu.rows(); i++)
{
reduced.solveOnline(FOM_test.mu.row(i));
}

Finally, once the online solve has been performed we can reconstruct the solution:

reduced.reconstruct("./ITHACAoutput/Reconstruction");

The plain program

Here there's the plain code

/*---------------------------------------------------------------------------*\
██╗████████╗██╗ ██╗ █████╗ ██████╗ █████╗ ███████╗██╗ ██╗
██║╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔══██╗ ██╔════╝██║ ██║
██║ ██║ ███████║███████║██║ ███████║█████╗█████╗ ██║ ██║
██║ ██║ ██╔══██║██╔══██║██║ ██╔══██║╚════╝██╔══╝ ╚██╗ ██╔╝
██║ ██║ ██║ ██║██║ ██║╚██████╗██║ ██║ ██║ ╚████╔╝
╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═══╝
* In real Time Highly Advanced Computational Applications for Finite Volumes
* Copyright (C) 2017 by the ITHACA-FV authors
-------------------------------------------------------------------------------
License
This file is part of ITHACA-FV
ITHACA-FV is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ITHACA-FV is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ITHACA-FV. If not, see <http://www.gnu.org/licenses/>.
Description
Example of a heat transfer Reduction Problem
SourceFiles
02thermalBlock.C
\*---------------------------------------------------------------------------*/
#include <iostream>
#include "fvCFD.H"
#include "IOmanip.H"
#include "Time.H"
#include "ITHACAPOD.H"
#include <Eigen/Dense>
#define _USE_MATH_DEFINES
#include <cmath>
{
public:
explicit tutorial02(int argc, char* argv[])
:
laplacianProblem(argc, argv),
T(_T()),
nu(_nu()),
S(_S())
{}
volScalarField& T;
volScalarField& nu;
volScalarField& S;
void offlineSolve(word folder = "./ITHACAoutput/Offline/")
{
if (offline)
{
ITHACAstream::readMatrix(folder + "/mu_samples_mat.txt");
}
else
{
List<scalar> mu_now(9);
scalar IF = 0;
for (label i = 0; i < mu.rows(); i++)
{
for (label j = 0; j < mu.cols() ; j++)
{
// mu_now[i] =
theta[j] = mu(i, j);
}
assignIF(T, IF);
Info << i << endl;
truthSolve(mu_now, folder);
}
}
}
void SetSource()
{
volScalarField yPos = T.mesh().C().component(vector::Y).ref();
volScalarField xPos = T.mesh().C().component(vector::X).ref();
{
S[counter] = Foam::sin(xPos[counter] / 0.9 * M_PI) + Foam::sin(
yPos[counter] / 0.9 * M_PI);
}
}
void compute_nu()
{
nu_list.resize(9);
volScalarField nu1(nu);
volScalarField nu2(nu);
volScalarField nu3(nu);
volScalarField nu4(nu);
volScalarField nu5(nu);
volScalarField nu6(nu);
volScalarField nu7(nu);
volScalarField nu8(nu);
volScalarField nu9(nu);
Eigen::MatrixXd Box1(2, 3);
Box1 << 0, 0, 0, 0.3, 0.3, 0.1;
Eigen::MatrixXd Box2(2, 3);
Box2 << 0.3, 0, 0, 0.6, 0.3, 0.1;
Eigen::MatrixXd Box3(2, 3);
Box3 << 0.6, 0, 0, 0.91, 0.3, 0.1;
Eigen::MatrixXd Box4(2, 3);
Box4 << 0, 0.3, 0, 0.3, 0.6, 0.1;
Eigen::MatrixXd Box5(2, 3);
Box5 << 0.3, 0.3, 0, 0.6, 0.6, 0.1;
Eigen::MatrixXd Box6(2, 3);
Box6 << 0.6, 0.3, 0, 0.91, 0.6, 0.1;
Eigen::MatrixXd Box7(2, 3);
Box7 << 0, 0.6, 0, 0.3, 0.91, 0.1;
Eigen::MatrixXd Box8(2, 3);
Box8 << 0.3, 0.61, 0, 0.6, 0.91, 0.1;
Eigen::MatrixXd Box9(2, 3);
Box9 << 0.6, 0.6, 0, 0.9, 0.91, 0.1;
nu_list.set(0, (nu1).clone());
nu_list.set(1, (nu2).clone());
nu_list.set(2, (nu3).clone());
nu_list.set(3, (nu4).clone());
nu_list.set(4, (nu5).clone());
nu_list.set(5, (nu6).clone());
nu_list.set(6, (nu7).clone());
nu_list.set(7, (nu8).clone());
nu_list.set(8, (nu9).clone());
}
{
for (int i = 0; i < nu_list.size(); i++)
{
operator_list.append(fvm::laplacian(nu_list[i], T));
}
}
};
void offline_stage(tutorial02& example, tutorial02& FOM_test);
void online_stage(tutorial02& example, tutorial02& FOM_test);
int main(int argc, char* argv[])
{
#if OPENFOAM > 1812
// load stage to perform
argList::addOption("stage", "offline", "Perform offline stage");
argList::addOption("stage", "online", "Perform online stage");
// add options for parallel run
HashTable<string> validParOptions;
validParOptions.set
(
"stage",
"offline"
);
validParOptions.set
(
"stage",
"online"
);
Pstream::addValidParOptions(validParOptions);
// Construct the tutorial object
tutorial02 example(argc, argv);
tutorial02 FOM_test(argc, argv);
if (example._args().get("stage").match("offline"))
{
// perform the offline stage, extracting the modes from the snapshots'
// dataset corresponding to parOffline
offline_stage(example, FOM_test);
}
else if (example._args().get("stage").match("online"))
{
// load precomputed modes and reduced matrices
offline_stage(example, FOM_test);
// perform online solve with respect to the parameters in parOnline
online_stage(example, FOM_test);
}
else
{
Info << "Pass '-stage offline', '-stage online'" << endl;
}
#else
if (argc == 1)
{
Info << "Pass 'offline' or 'online' as first arguments."
<< endl;
exit(0);
}
// process arguments removing "offline" or "online" keywords
int argc_proc = argc - 1;
char* argv_proc[argc_proc];
argv_proc[0] = argv[0];
if (argc > 2)
{
std::copy(argv + 2, argv + argc, argv_proc + 1);
}
argc--;
// Construct the tutorial object
tutorial02 example(argc, argv);
tutorial02 FOM_test(argc, argv);
if (std::strcmp(argv[1], "offline") == 0)
{
// perform the offline stage, extracting the modes from the snapshots'
// dataset corresponding to parOffline
offline_stage(example, FOM_test);
}
else if (std::strcmp(argv[1], "online") == 0)
{
// load precomputed modes and reduced matrices
offline_stage(example, FOM_test);
// perform online solve with respect to the parameters in parOnline
online_stage(example, FOM_test);
}
else
{
Info << "Pass offline, online" << endl;
}
#endif
exit(0);
}
void offline_stage(tutorial02& example, tutorial02& FOM_test)
{
// Read some parameters from file
example._runTime());
int NmodesTout = para->ITHACAdict->lookupOrDefault<int>("NmodesTout", 15);
int NmodesTproj = para->ITHACAdict->lookupOrDefault<int>("NmodesTproj", 10);
// Set the number of parameters
example.Pnumber = 9;
example.Tnumber = 500;
// Set the parameters
example.setParameters();
// Set the parameter ranges, in all the subdomains the diffusivity varies between
// 0.001 and 0.1
example.mu_range.col(0) = Eigen::MatrixXd::Ones(9, 1) * 0.001;
example.mu_range.col(1) = Eigen::MatrixXd::Ones(9, 1) * 0.1;
// Generate the Parameters
example.genRandPar(500);
// Set the size of the list of values that are multiplying the affine forms
example.theta.resize(9);
// Set the source term
example.SetSource();
// Compute the diffusivity field for each subdomain
example.compute_nu();
// Assemble all the operators of the affine decomposition
example.assemble_operator();
// Perform an Offline Solve
example.offlineSolve();
// Perform a POD decomposition and get the modes
ITHACAPOD::getModes(example.Tfield, example.Tmodes, example._T().name(),
example.podex, 0, 0,
NmodesTout);
// Perform the Galerkin projection onto the space spanned by the POD modes
example.project(NmodesTproj);
FOM_test.offline = false;
FOM_test.Pnumber = 9;
FOM_test.Tnumber = 50;
// Set the parameters
FOM_test.setParameters();
// Set the parameter ranges, in all the subdomains the diffusivity varies between
// 0.001 and 0.1
FOM_test.mu_range.col(0) = Eigen::MatrixXd::Ones(9, 1) * 0.001;
FOM_test.mu_range.col(1) = Eigen::MatrixXd::Ones(9, 1) * 0.1;
// Generate the Parameters
FOM_test.genRandPar(50);
// Set the size of the list of values that are multiplying the affine forms
FOM_test.theta.resize(9);
// Set the source term
FOM_test.SetSource();
// Compute the diffusivity field for each subdomain
FOM_test.compute_nu();
// Assemble all the operators of the affine decomposition
FOM_test.assemble_operator();
// Perform an Offline Solve
FOM_test.offlineSolve("./ITHACAoutput/FOMtest");
}
void online_stage(tutorial02& example, tutorial02& FOM_test)
{
// Create a reduced object
reducedLaplacian reduced(example);
// Solve the online reduced problem some new values of the parameters
for (int i = 0; i < FOM_test.mu.rows(); i++)
{
reduced.solveOnline(FOM_test.mu.row(i));
}
// Reconstruct the solution and store it into Reconstruction folder
reduced.reconstruct("./ITHACAoutput/Reconstruction");
// Compute the error on the testing set
Eigen::MatrixXd error = ITHACAutilities::errorL2Rel(FOM_test.Tfield,
reduced.Trec);
}
//--------
void online_stage(tutorial02 &example, tutorial02 &FOM_test)
void offline_stage(tutorial02 &example, tutorial02 &FOM_test)
Header file of the reducedLaplacian class.
Class for the definition of some general parameters, the parameters must be defined from the file ITH...
IOdictionary * ITHACAdict
Dictionary for input objects from file.
static ITHACAparameters * getInstance()
Gets an instance of ITHACAparameters, to be used if the instance is already existing.
PtrList< fvScalarMatrix > operator_list
List of operators.
autoPtr< volScalarField > _T
Temperature field.
void project(label Nmodes)
Perform a projection onto the POD modes.
autoPtr< volScalarField > _nu
Diffusivity.
autoPtr< fvMesh > _mesh
Mesh.
volScalarModes Tmodes
List of POD modes.
autoPtr< volScalarField > _S
Source Term.
autoPtr< Time > _runTime
Time.
label Pnumber
Number of parameters.
label Tnumber
Dimension of the training set (used only when gerating parameters without input)
void genRandPar()
Generate Random Numbers.
Eigen::MatrixXd mu_range
Range of the parameter spaces.
void setParameters()
Set Parameters Problems.
bool podex
Boolean variable, it is 1 if the POD has already been computed, else 0.
volScalarField & S
Source term field.
volScalarField & nu
Diffusivity field.
void assemble_operator()
Construct the operator_list where each term of the affine decomposition is stored.
volScalarField & T
[tutorial02] Temperature field
int main(int argc, char *argv[])
Header file of the laplacianProblem class.
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.