Module reducedbasis
1. Import
The work has not yet been merged in develop . To include the module at current state, you need to checkout the branch feature/pyrb .
|
Line to import the module :
import feelpp.mor.reducedbasis.reducedbasis_timeOffline as mor_rb
This import load the module for offline generation, associated to the time-dependent case. For the time-independent case, you can import feelpp.mor.reducedbasis.reducedbasisOffline instead.
|
To set the environment, those module also need to be imported :
import sys, os
from feelpp.toolboxes.heat import * (1)
import feelpp.toolboxes.core as core (2)
from feelpp.operators import mass (3)
import feelpp.mor as mor (4)
import feelpp.core as fppc
1 | The toolbox heat is used to simulate the heat equation on the case studied. |
2 | The module core is used to set the environment. |
3 | The mass function is needed to create the mass matrix if we work on a time-dependent case. |
4 | The module mor is used to get the functions neededd by the toolbox mor. |
2. Set the environment
config = feelpp.localRepository("reducedbasis")
sys.argv = ["reducedbasis"]
o = core.toolboxes_options("heat")
o.add(mor.makeToolboxMorOptions())
e = feelpp.Environment(sys.argv, opts=o, config=config)
3. Set the environment
config = feelpp.globalRepository("reducedbasis")
sys.argv = ["reducedbasis"]
o = core.toolboxes_options("heat")
o.add(mor.makeToolboxMorOptions())
e = feelpp.Environment(sys.argv, opts=o, config=config)
4. Set the model
4.1. Description of the model
In this document, the cases used is thermal-fin
in 2D, with a \(\mathbb{P}_1\) discretization and stationnary.
This model is described in this page.
The usage would be the same for another case.
4.2. Code
Set the parameters nedded to the model :
-
config_file
: path to the cfg file -
dim
: the dimension of the case -
order
: the order of discretization (1
for \(\mathbb{P}_1\),2
for \(\mathbb{P}_2\)) -
time_depedent
: is the case stationnary or transient ? (for now always toFalse
) -
name
: name of the CRB case
config_file = "thermal-fin.cfg"
dim = 2
order = 1
time_dependent = False
name = "thermalfin-2d"
The configuration files for this case are available on github : geometry file, cfg file, and json file.
Then, we can create the model :
cfg = feelpp.readCfg(config_file)
feelpp.Environment.setConfigFile(config_file)
model_path = feelpp.Environment.expand(cfg['heat']['filename'])
j = feelpp.readJson(model_path)
try:
j.pop('PostProcess')
except KeyError as e:
print(f"There was no section {e} in the model")
Now we load the crb parameters, the path has to be set as the option toolboxmor.filename
in the cfg file.
crb_model_file = feelpp.Environment.expand(cfg['toolboxmor']['filename'])
crb_model_file
crb_model_properties = mor.CRBModelProperties(worldComm=feelpp.Environment.worldCommPtr())
crb_model_properties.setup(crb_model_file)
crb_model_outputs = crb_model_properties.outputs()
output_names = []
for n, _ in crb_model_outputs:
output_names.append(n)
print(f"Outputs of the models are {output_names}")
5. Set the toolboxes
heatBox = heat(dim=dim, order=order)
heatBox.init()
The parameter default_parameter
is defined in the section Parameters
of the JSON file.
modelParameters = heatBox.modelProperties().parameters()
default_parameter = modelParameters.toParameterValues()
model = mor.toolboxmor(name=name, dim=dim, time_dependent=time_dependent)
model.setFunctionSpaces( Vh=heatBox.spaceTemperature() )
def assembleDEIM(mu):
for i in range(0,mu.size()):
heatBox.addParameterInModelProperties(mu.parameterName(i), mu(i))
heatBox.updateParameterValues()
return heatBox.assembleRhs()
def assembleMDEIM(mu):
for i in range(0,mu.size()):
heatBox.addParameterInModelProperties(mu.parameterName(i), mu(i))
heatBox.updateParameterValues()
return heatBox.assembleMatrix()
model.setAssembleDEIM(fct=assembleDEIM)
model.setAssembleMDEIM(fct=assembleMDEIM)
model.initModel()
Set the toolboxes associated to the reduced meshes :
heatBoxDEIM = heat(dim=dim, order=order)
heatBoxDEIM.setModelProperties(j)
meshDEIM = model.getDEIMReducedMesh()
heatBoxDEIM.setMesh(meshDEIM)
heatBoxDEIM.init()
heatBoxMDEIM = heat(dim=dim, order=order)
heatBoxMDEIM.setModelProperties(j)
meshMDEIM = model.getMDEIMReducedMesh()
heatBoxMDEIM.setMesh(meshMDEIM)
heatBoxMDEIM.init()
def assembleOnlineDEIM(mu):
for i in range(0,mu.size()):
heatBoxDEIM.addParameterInModelProperties(mu.parameterName(i),mu(i))
heatBoxDEIM.updateParameterValues()
return heatBoxDEIM.assembleRhs()
def assembleOnlineMDEIM(mu):
for i in range(0,mu.size()):
heatBoxMDEIM.addParameterInModelProperties(mu.parameterName(i),mu(i))
heatBoxMDEIM.updateParameterValues()
return heatBoxMDEIM.assembleMatrix()
model.setOnlineAssembleDEIM(assembleOnlineDEIM)
model.setOnlineAssembleMDEIM(assembleOnlineMDEIM)
model.postInitModel()
model.setInitialized(True)
6. Offline generation
Information about the parameter space \(D^\mu\)
Dmu = model.parameterSpace()
print(Dmu.parameterNames())
print(Dmu.mumin())
print(Dmu.mumax())
Results
['Bi' ,'k_1' ,'k_2' ,'k_3' ,'k_4'] [1.00e-02,1.00e-01,1.00e-01,1.00e-01,1.00e-01] [1.00e+00,1.00e+01,1.00e+01,1.00e+01,1.00e+01]
mubar = Dmu.element(True, False)
mubar.setParameters(default_parameter)
print("mubar =")
mubar.view()
Results
mubar = Bi 0.01 k_1 0.1 k_2 0.1 k_3 0.1 k_4 0.1
Get affine decomposition \(A(\mu) = \displaystyle\sum_q \beta^q_A(\mu) A^q\), \(F(\mu)=\displaystyle\sum_p \beta_F^p(\mu) F^(\mu)\).
To use the matrices and vector in the class, we need to convert themn to petsc4py
objects, using the functions convertToPetscMat
and convertToPetscVec
.
affineDecomposition = model.getAffineDecomposition()
Aq_ = affineDecomposition[0]
Fq_ = affineDecomposition[1]
Aq = mor_rb.convertToPetscMat(Aq_[0])
Fq = []
for f in Fq_:
Fq.append(mor_rb.convertToPetscVec(f[0]))
print("Aq", Aq)
print("Fq", Fq)
Aq [<petsc4py.PETSc.Mat object at 0x7f5f693abba0>, <petsc4py.PETSc.Mat object at 0x7f5f7000d210>, <petsc4py.PETSc.Mat object at 0x7f5f693fd3f0>, <petsc4py.PETSc.Mat object at 0x7f5f693fd3a0>, <petsc4py.PETSc.Mat object at 0x7f5f693fd210>, <petsc4py.PETSc.Mat object at 0x7f5f693fd260>] Fq [[<petsc4py.PETSc.Vec object at 0x7f5f693fd350>], [<petsc4py.PETSc.Vec object at 0x7f5f693fd2b0>], [<petsc4py.PETSc.Vec object at 0x7f5f693fd1c0>]]
Now we create the object reduced_basis
.
rb = mor_rb.reducedbasisOffline(Aq=Aq, Fq=Fq, model=model, mubar=mubar,
output_names=output_names, use_dual_norm=False)
[reducedbasis] Online rb initialized [slepc4py] number of (smaller) eigenvalues computed : 16 [reducedbasis] Constant of coercivity : 0.9999999999999977 [slepc4py] number of eigenvalues computed : 16 [reducedbasis] Constant of continuity : 1.0000000000000018 [reducedbasis] Offline rb initialized
Generation of the basis, using the greedy algorithm.
Ntrain = 1000
s = Dmu.sampling()
s.sampling(Ntrain, "random")
Xi_train = s.getVector()
mu_0 = Dmu.element()
rb.greedy(mu_0, Xi_train, eps_tol=1e-6)
[reducedBasis] Start greedy algorithm [reducedBasis] Computing betas: 100%|█████████████████████████████████████████████████| 999/999 [00:25<00:00, 39.91it/s] [reducedBasis] Offline generation of the basis: 100%|█████████████████████████████████████| 1/1 [00:00<00:00, 32.94it/s] [reducedBasis] Gram-Schmidt orthonormalization done after 1 step [reducedBasis] Greedy, step 1: 100%|████████████████████████████████████████████████| 999/999 [00:00<00:00, 3301.45it/s] [reducedBasis] Greedy algo, N=1, Δ=2.735059e-06 (tol=1.000000e-06) µ=[2.17e-01,8.67e+00,7.04e+00,4.51e+00,8.96e-01] [reducedBasis] Gram-Schmidt orthonormalization done after 1 step [reducedBasis] Greedy, step 2: 100%|████████████████████████████████████████████████| 998/998 [00:00<00:00, 3034.75it/s] [reducedBasis] Greedy algo, N=2, Δ=5.947858e-07 (tol=1.000000e-06) µ=[4.39e-01,4.62e+00,4.23e-01,9.22e+00,4.94e-01] [reducedBasis] End greedy algorithm
Compute offline errors :
rb.computeOfflineErrorRhs()
rb.computeOfflineError()
The script generate_basis.py
run this offline part. See the dedicated page for more details.
== Online phase
mu = Dmu.element()
mu.view()
Bi 0.851312 k_1 8.50443 k_2 0.687991 k_3 8.1523 k_4 3.47058
The function getSolutions
returns the solutions of the model and the reduced model, the parameter k
gives the index of the output wanted, -1
standing for the compliant output.
rb.getSolutions(mu, k=0)
(array([1.75329542e-03, 8.16719018e-10]), 3.2730508127626815e-06)
We can compare it to the finite element solution :
rb.getSolutionsFE(mu, k=0)
(<petsc4py.PETSc.Vec at 0x7f5f3d97eac0>, 3.273050812763351e-06)
The two functions getSolutions
(resp. getSolutionsFE
) return the solution \(u_N(\mu)\) (resp. \(u(\mu)\)) and the value of the output \(s_N^k(\mu)\) (res. \(s^k(\mu)\)).
See the dedicated page for the API of ParameterSpaceElement
.