281 lines
9.7 KiB
C++
281 lines
9.7 KiB
C++
// MFEM Example 26
|
|
//
|
|
// Compile with: make ex26
|
|
//
|
|
// Sample runs: ex26 -m ../data/star.mesh
|
|
// ex26 -m ../data/fichera.mesh
|
|
// ex26 -m ../data/beam-hex.mesh
|
|
//
|
|
// Device sample runs:
|
|
// ex26 -d cuda
|
|
// ex26 -d raja-cuda
|
|
// ex26 -d occa-cuda
|
|
// ex26 -d raja-omp
|
|
// ex26 -d occa-omp
|
|
// ex26 -d ceed-cpu
|
|
// ex26 -d ceed-cuda
|
|
// ex26 -m ../data/beam-hex.mesh -d cuda
|
|
//
|
|
// Description: This example code demonstrates the use of MFEM to define a
|
|
// simple finite element discretization of the Laplace problem
|
|
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
|
|
// as in example 1. It highlights on the creation of a hierarchy
|
|
// of discretization spaces with partial assembly and the
|
|
// construction of an efficient p-multigrid preconditioner for the
|
|
// iterative solver.
|
|
|
|
#include "mfem.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
class MultigridDiffusionOperator : public MultigridOperator
|
|
{
|
|
private:
|
|
Array<BilinearForm*> bfs;
|
|
Array<Array<int>*> essentialTrueDofs;
|
|
ConstantCoefficient one;
|
|
|
|
public:
|
|
/// Constructor for a multigrid diffusion operator for a given FiniteElementSpaceHierarchy. Uses Chebyshev accelerated smoothing.
|
|
MultigridDiffusionOperator(FiniteElementSpaceHierarchy& spaceHierarchy,
|
|
Array<int>& ess_bdr, int chebyshevOrder = 2)
|
|
: one(1.0)
|
|
{
|
|
ConstructCoarseOperatorAndSolver(spaceHierarchy, ess_bdr);
|
|
|
|
for (int level = 1; level < spaceHierarchy.GetNumLevels(); ++level)
|
|
{
|
|
BilinearForm* form = new BilinearForm(&spaceHierarchy.GetFESpaceAtLevel(level));
|
|
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
AddIntegrators(form);
|
|
form->Assemble();
|
|
bfs.Append(form);
|
|
|
|
essentialTrueDofs.Append(new Array<int>());
|
|
spaceHierarchy.GetFESpaceAtLevel(level).GetEssentialTrueDofs(
|
|
ess_bdr, *essentialTrueDofs.Last());
|
|
|
|
OperatorPtr opr;
|
|
opr.SetType(Operator::ANY_TYPE);
|
|
form->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
|
|
opr.SetOperatorOwner(false);
|
|
|
|
Vector diag(spaceHierarchy.GetFESpaceAtLevel(level).GetTrueVSize());
|
|
form->AssembleDiagonal(diag);
|
|
|
|
Solver* smoother = new OperatorChebyshevSmoother(
|
|
opr.Ptr(), diag, *essentialTrueDofs.Last(), chebyshevOrder);
|
|
|
|
Operator* P =
|
|
new TransferOperator(spaceHierarchy.GetFESpaceAtLevel(level - 1),
|
|
spaceHierarchy.GetFESpaceAtLevel(level));
|
|
|
|
AddLevel(opr.Ptr(), smoother, P, true, true, true);
|
|
}
|
|
}
|
|
|
|
virtual ~MultigridDiffusionOperator()
|
|
{
|
|
for (int i = 0; i < bfs.Size(); ++i)
|
|
{
|
|
delete bfs[i];
|
|
}
|
|
|
|
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
|
|
{
|
|
delete essentialTrueDofs[i];
|
|
}
|
|
}
|
|
|
|
void EliminateBCs(Vector& x, Vector& b, Vector& X, Vector& B)
|
|
{
|
|
OperatorPtr oper;
|
|
bfs.Last()->FormLinearSystem(*essentialTrueDofs.Last(), x, b,
|
|
oper, X, B);
|
|
}
|
|
|
|
void RecoverFEMSolution(const Vector& X, const Vector& b, Vector& x)
|
|
{
|
|
bfs.Last()->RecoverFEMSolution(X, b, x);
|
|
}
|
|
|
|
private:
|
|
void AddIntegrators(BilinearForm* form)
|
|
{
|
|
form->AddDomainIntegrator(new DiffusionIntegrator(one));
|
|
}
|
|
|
|
void ConstructCoarseOperatorAndSolver(FiniteElementSpaceHierarchy&
|
|
spaceHierarchy,
|
|
Array<int>& ess_bdr)
|
|
{
|
|
BilinearForm* form = new BilinearForm(&spaceHierarchy.GetFESpaceAtLevel(0));
|
|
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
AddIntegrators(form);
|
|
form->Assemble();
|
|
bfs.Append(form);
|
|
|
|
essentialTrueDofs.Append(new Array<int>());
|
|
spaceHierarchy.GetFESpaceAtLevel(0).GetEssentialTrueDofs(
|
|
ess_bdr, *essentialTrueDofs.Last());
|
|
|
|
OperatorPtr opr;
|
|
opr.SetType(Operator::ANY_TYPE);
|
|
form->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
|
|
opr.SetOperatorOwner(false);
|
|
|
|
CGSolver* pcg = new CGSolver();
|
|
pcg->SetPrintLevel(-1);
|
|
pcg->SetMaxIter(200);
|
|
pcg->SetRelTol(sqrt(1e-4));
|
|
pcg->SetAbsTol(0.0);
|
|
pcg->SetOperator(*opr.Ptr());
|
|
|
|
AddCoarsestLevel(opr.Ptr(), pcg, true, true);
|
|
}
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// 1. Parse command-line options.
|
|
const char *mesh_file = "../data/star.mesh";
|
|
int geometricrefinements = 0;
|
|
int orderrefinements = 2;
|
|
const char *device_config = "cpu";
|
|
bool visualization = true;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&mesh_file, "-m", "--mesh",
|
|
"Mesh file to use.");
|
|
args.AddOption(&geometricrefinements, "-gr", "--geometricrefinements",
|
|
"Number of geometric refinements done prior to order refinements.");
|
|
args.AddOption(&orderrefinements, "-or", "--orderrefinements",
|
|
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
|
|
args.AddOption(&device_config, "-d", "--device",
|
|
"Device configuration string, see Device::Configure().");
|
|
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
|
"--no-visualization",
|
|
"Enable or disable GLVis visualization.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
// 2. Enable hardware devices such as GPUs, and programming models such as
|
|
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
|
Device device(device_config);
|
|
device.Print();
|
|
|
|
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
|
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
|
// the same code.
|
|
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
|
int dim = mesh->Dimension();
|
|
|
|
// 4. Refine the mesh to increase the resolution. In this example we do
|
|
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
|
// largest number that gives a final mesh with no more than 50,000
|
|
// elements.
|
|
{
|
|
int ref_levels =
|
|
(int)floor(log(5000./mesh->GetNE())/log(2.)/dim);
|
|
for (int l = 0; l < ref_levels; l++)
|
|
{
|
|
mesh->UniformRefinement();
|
|
}
|
|
}
|
|
|
|
// 5. Define a finite element space hierarchy on the mesh. Here we use
|
|
// continuous Lagrange finite elements. We start with order 1 on the
|
|
// coarse level and increase the order by of factor of 2 for each
|
|
// additional level.
|
|
FiniteElementCollection *fec = new H1_FECollection(1, dim);
|
|
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
|
|
|
Array<FiniteElementCollection*> collections;
|
|
collections.Append(fec);
|
|
FiniteElementSpaceHierarchy spaceHierarchy(mesh, fespace, true, true);
|
|
for (int level = 0; level < geometricrefinements; ++level)
|
|
{
|
|
spaceHierarchy.AddUniformlyRefinedLevel();
|
|
}
|
|
for (int level = 0; level < orderrefinements; ++level)
|
|
{
|
|
collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
|
|
spaceHierarchy.AddOrderRefinedLevel(collections.Last());
|
|
}
|
|
|
|
cout << "Number of finite element unknowns: "
|
|
<< spaceHierarchy.GetFinestFESpace().GetTrueVSize() << endl;
|
|
|
|
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
|
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
|
// the basis functions in the finite element fespace.
|
|
LinearForm *b = new LinearForm(&spaceHierarchy.GetFinestFESpace());
|
|
ConstantCoefficient one(1.0);
|
|
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
|
b->Assemble();
|
|
|
|
// 7. Define the solution vector x as a finite element grid function
|
|
// corresponding to fespace. Initialize x with initial guess of zero,
|
|
// which satisfies the boundary conditions.
|
|
GridFunction x(&spaceHierarchy.GetFinestFESpace());
|
|
x = 0.0;
|
|
|
|
// 8. Create the multigrid operator using the previously created
|
|
// FiniteElementSpaceHierarchy and additional boundary information. This operator
|
|
// is then used to create the MultigridSolver as a preconditioner in the
|
|
// iterative solver.
|
|
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
|
ess_bdr = 1;
|
|
MultigridDiffusionOperator mgOperator(spaceHierarchy, ess_bdr);
|
|
MultigridSolver M(&mgOperator, MultigridSolver::CycleType::VCYCLE, 1, 1);
|
|
|
|
cout << "Size of linear system: " <<
|
|
mgOperator.GetOperatorAtFinestLevel()->Height() << endl;
|
|
|
|
Vector B, X;
|
|
mgOperator.EliminateBCs(x, *b, X, B);
|
|
|
|
// 9. Solve the linear system A X = B.
|
|
PCG(mgOperator, M, B, X, 1, 2000, 1e-12, 0.0);
|
|
|
|
// 10. Recover the solution as a finite element grid function.
|
|
mgOperator.RecoverFEMSolution(X, *b, x);
|
|
|
|
// 11. Save the refined mesh and the solution. This output can be viewed later
|
|
// using GLVis: "glvis -m refined.mesh -g sol.gf".
|
|
ofstream mesh_ofs("refined.mesh");
|
|
mesh_ofs.precision(8);
|
|
spaceHierarchy.GetFinestFESpace().GetMesh()->Print(mesh_ofs);
|
|
ofstream sol_ofs("sol.gf");
|
|
sol_ofs.precision(8);
|
|
x.Save(sol_ofs);
|
|
|
|
// 12. Send the solution by socket to a GLVis server.
|
|
if (visualization)
|
|
{
|
|
char vishost[] = "localhost";
|
|
int visport = 19916;
|
|
socketstream sol_sock(vishost, visport);
|
|
sol_sock.precision(8);
|
|
sol_sock << "solution\n" << *spaceHierarchy.GetFinestFESpace().GetMesh()
|
|
<< x << flush;
|
|
}
|
|
|
|
// 13. Free the used memory.
|
|
delete b;
|
|
for (int level = 0; level < collections.Size(); ++level)
|
|
{
|
|
delete collections[level];
|
|
}
|
|
|
|
return 0;
|
|
}
|