358 lines
10 KiB
C++
358 lines
10 KiB
C++
#include "mfem.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
void f_exact(const Vector &x, Vector &f)
|
|
{
|
|
double kappa = 2*M_PI;
|
|
if (x.Size() == 3)
|
|
{
|
|
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
|
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
|
|
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
|
|
}
|
|
else if (x.Size() == 2)
|
|
{
|
|
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
|
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
|
}
|
|
else
|
|
{
|
|
f(0) = (1. + kappa * kappa) * sin(kappa * x(0));
|
|
}
|
|
}
|
|
|
|
struct HybridizationSolver : Solver
|
|
{
|
|
Solver &solv;
|
|
Hybridization &h;
|
|
mutable Vector b_r, x_r;
|
|
HybridizationSolver(Solver &solv_, Hybridization &h_)
|
|
: Solver(solv_.Height()), solv(solv_), h(h_) { }
|
|
void SetOperator(const Operator&) { }
|
|
void Mult(const Vector &b, Vector &x) const
|
|
{
|
|
h.ReduceRHS(b, b_r);
|
|
x_r.SetSize(b_r.Size());
|
|
x_r = 0.0;
|
|
solv.Mult(b_r, x_r);
|
|
h.ComputeSolution(b, x_r, x);
|
|
}
|
|
};
|
|
|
|
struct PermutedSolver : Solver
|
|
{
|
|
Solver &solv;
|
|
Array<int> p;
|
|
mutable Vector bp, xp;
|
|
PermutedSolver(Solver &solv_, const Array<int> &p_)
|
|
: Solver(solv_.Height()), solv(solv_), p(p_), bp(p.Size()), xp(p.Size()) { }
|
|
void SetOperator(const Operator&) { }
|
|
void Mult(const Vector &b, Vector &x) const
|
|
{
|
|
for (int i=0; i<b.Size(); ++i) { bp[i] = p[i] < 0 ? -b[-1-p[i]] : b[p[i]]; }
|
|
solv.Mult(bp, xp);
|
|
for (int i=0; i<x.Size(); ++i)
|
|
{
|
|
int pi = p[i];
|
|
int s = pi < 0 ? -1 : 1;
|
|
x[pi < 0 ? -1-pi : pi] = s*xp[i];
|
|
}
|
|
}
|
|
};
|
|
|
|
const Array<int> &GetDofMap(FiniteElementSpace &fes, int i)
|
|
{
|
|
const FiniteElement *fe = fes.GetFE(i);
|
|
auto tfe = dynamic_cast<const TensorBasisElement*>(fe);
|
|
MFEM_ASSERT(tfe != NULL, "");
|
|
return tfe->GetDofMap();
|
|
}
|
|
|
|
Array<int> ComputeVectorFE_LORPermutation(
|
|
FiniteElementSpace &fes_ho,
|
|
FiniteElementSpace &fes_lor,
|
|
FiniteElement::MapType type)
|
|
{
|
|
// Given an index `i` of a LOR dof, `perm[i]` is the index of the
|
|
// corresponding HO dof.
|
|
Array<int> perm(fes_lor.GetVSize());
|
|
Array<int> vdof_ho, vdof_lor;
|
|
|
|
Mesh &mesh_lor = *fes_lor.GetMesh();
|
|
int dim = mesh_lor.Dimension();
|
|
const CoarseFineTransformations &cf_tr = mesh_lor.GetRefinementTransforms();
|
|
for (int ilor=0; ilor<mesh_lor.GetNE(); ++ilor)
|
|
{
|
|
int iho = cf_tr.embeddings[ilor].parent;
|
|
int lor_index = cf_tr.embeddings[ilor].matrix;
|
|
|
|
int p = fes_ho.GetOrder(iho);
|
|
int p1 = p+1;
|
|
int ndof_per_dim = (dim == 2) ? p*p1 :
|
|
type == FiniteElement::H_CURL ? p*p1*p1 : p*p*p1;
|
|
|
|
fes_ho.GetElementVDofs(iho, vdof_ho);
|
|
fes_lor.GetElementVDofs(ilor, vdof_lor);
|
|
|
|
const Array<int> &dofmap_ho = GetDofMap(fes_ho, iho);
|
|
const Array<int> &dofmap_lor = GetDofMap(fes_lor, ilor);
|
|
|
|
int off_x = lor_index % p;
|
|
int off_y = (lor_index / p) % p;
|
|
int off_z = (lor_index / p) / p;
|
|
|
|
auto absdof = [](int i) { return i < 0 ? -1-i : i; };
|
|
|
|
auto set_perm = [&](int off_lor, int off_ho, int n1, int n2)
|
|
{
|
|
for (int i1=0; i1<2; ++i1)
|
|
{
|
|
int m = (dim == 2 || type == FiniteElement::H_DIV) ? 1 : 2;
|
|
for (int i2=0; i2<m; ++i2)
|
|
{
|
|
int i;
|
|
i = dofmap_lor[off_lor + i1 + i2*2];
|
|
int s1 = i < 0 ? -1 : 1;
|
|
int idof_lor = vdof_lor[absdof(i)];
|
|
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
|
|
int s2 = i < 0 ? -1 : 1;
|
|
int idof_ho = vdof_ho[absdof(i)];
|
|
int s3 = idof_lor < 0 ? -1 : 1;
|
|
int s4 = idof_ho < 0 ? -1 : 1;
|
|
int s = s1*s2*s3*s4;
|
|
i = absdof(idof_ho);
|
|
perm[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
|
|
}
|
|
}
|
|
};
|
|
|
|
int offset;
|
|
|
|
if (type == FiniteElement::H_CURL)
|
|
{
|
|
// x
|
|
offset = off_x + off_y*p + off_z*p*p1;
|
|
set_perm(0, offset, p, p*p1);
|
|
// y
|
|
offset = ndof_per_dim + off_x + off_y*(p1) + off_z*p1*p;
|
|
set_perm(dim == 2 ? 2 : 4, offset, 1, p*p1);
|
|
// z
|
|
if (dim == 3)
|
|
{
|
|
offset = 2*ndof_per_dim + off_x + off_y*p1 + off_z*p1*p1;
|
|
set_perm(8, offset, 1, p+1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// x
|
|
offset = off_x + off_y*p1 + off_z*p*p1;
|
|
set_perm(0, offset, 1, 0);
|
|
// y
|
|
offset = ndof_per_dim + off_x + off_y*p + off_z*p1*p;
|
|
set_perm(2, offset, p, 0);
|
|
// z
|
|
if (dim == 3)
|
|
{
|
|
offset = 2*ndof_per_dim + off_x + off_y*p + off_z*p*p;
|
|
set_perm(4, offset, p*p, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return perm;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
MPI_Session mpi;
|
|
|
|
// 1. Parse command-line options.
|
|
const char *mesh_file = "../data/star.mesh";
|
|
int ref_levels = 0;
|
|
int order = 3;
|
|
const char *fe = "n";
|
|
bool hybridization = false;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
|
args.AddOption(&ref_levels, "-r", "--refine", "Uniform refinements.");
|
|
args.AddOption(&order, "-o", "--order", "Polynomial degree.");
|
|
args.AddOption(&fe, "-fe", "--fe-type", "FE type. n for Hcurl, r for Hdiv");
|
|
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
|
|
"--no-hybridization", "Enable hybridization.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
bool ND = false;
|
|
if (string(fe) == "n") { ND = true; }
|
|
else if (string(fe) == "r") { ND = false; }
|
|
else { MFEM_ABORT("Bad FE type. Must be 'n' or 'r'."); }
|
|
bool RT = !ND;
|
|
|
|
Mesh serial_mesh(mesh_file, 1, 1);
|
|
int dim = serial_mesh.Dimension();
|
|
for (int l = 0; l < ref_levels; l++) { serial_mesh.UniformRefinement(); }
|
|
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
|
serial_mesh.Clear();
|
|
|
|
int btype = BasisType::GaussLobatto;
|
|
ParMesh mesh_lor(&mesh, order, btype);
|
|
|
|
unique_ptr<FiniteElementCollection> fec_ho, fec_lor, fec_h;
|
|
unique_ptr<ParFiniteElementSpace> fes_h;
|
|
if (ND)
|
|
{
|
|
fec_ho.reset(new ND_FECollection(order, dim, BasisType::GaussLobatto,
|
|
BasisType::Integrated));
|
|
fec_lor.reset(new ND_FECollection(1, dim, BasisType::GaussLobatto,
|
|
BasisType::Integrated));
|
|
}
|
|
else
|
|
{
|
|
fec_ho.reset(new RT_FECollection(order-1, dim, BasisType::GaussLobatto,
|
|
BasisType::Integrated));
|
|
fec_lor.reset(new RT_FECollection(0, dim, BasisType::GaussLobatto,
|
|
BasisType::Integrated));
|
|
if (hybridization)
|
|
{
|
|
fec_h.reset(new DG_Interface_FECollection(0, dim));
|
|
fes_h.reset(new ParFiniteElementSpace(&mesh_lor, fec_h.get()));
|
|
}
|
|
}
|
|
|
|
ParFiniteElementSpace fes_ho(&mesh, fec_ho.get());
|
|
ParFiniteElementSpace fes_lor(&mesh_lor, fec_lor.get());
|
|
|
|
Array<int> ess_tdof_list;
|
|
ConstantCoefficient one(1.0);
|
|
Vector ones_vec(dim);
|
|
ones_vec = 1.0;
|
|
VectorFunctionCoefficient coeff(dim, f_exact);
|
|
|
|
ParBilinearForm a_ho(&fes_ho), a_lor(&fes_lor);
|
|
// ParBilinearForm a_ho(&fes_lor), a_lor(&fes_lor);
|
|
a_ho.AddDomainIntegrator(new VectorFEMassIntegrator);
|
|
a_lor.AddDomainIntegrator(new VectorFEMassIntegrator);
|
|
if (ND)
|
|
{
|
|
a_ho.AddDomainIntegrator(new CurlCurlIntegrator);
|
|
a_lor.AddDomainIntegrator(new CurlCurlIntegrator);
|
|
}
|
|
else
|
|
{
|
|
a_ho.AddDomainIntegrator(new DivDivIntegrator);
|
|
a_lor.AddDomainIntegrator(new DivDivIntegrator);
|
|
if (hybridization)
|
|
{
|
|
a_lor.EnableHybridization(fes_h.get(), new NormalTraceJumpIntegrator,
|
|
ess_tdof_list);
|
|
}
|
|
}
|
|
a_ho.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
a_ho.Assemble();
|
|
a_lor.Assemble();
|
|
a_lor.Finalize();
|
|
|
|
LinearForm b_lor(&fes_lor);
|
|
b_lor.AddDomainIntegrator(new VectorFEDomainLFIntegrator(coeff));
|
|
b_lor.Assemble();
|
|
|
|
LinearForm b_ho(&fes_ho);
|
|
b_ho.AddDomainIntegrator(new VectorFEDomainLFIntegrator(coeff));
|
|
b_ho.Assemble();
|
|
|
|
GridFunction x_ho(&fes_ho), x_lor(&fes_lor);
|
|
x_ho = 0.0;
|
|
x_lor = 0.0;
|
|
|
|
Vector X_ho, B_ho, X_lor, B_lor;
|
|
OperatorHandle A_ho, A_lor;
|
|
a_ho.FormLinearSystem(ess_tdof_list, x_ho, b_ho, A_ho, X_ho, B_ho);
|
|
a_lor.FormLinearSystem(ess_tdof_list, x_lor, b_lor, A_lor, X_lor, B_lor);
|
|
|
|
unique_ptr<Solver> solv_lor, amg;
|
|
SparseMatrix diag;
|
|
A_lor.As<HypreParMatrix>()->GetDiag(diag);
|
|
if (RT && hybridization)
|
|
{
|
|
amg.reset(new HypreBoomerAMG(*A_lor.As<HypreParMatrix>()));
|
|
// amg.reset(new UMFPackSolver(diag));
|
|
solv_lor.reset(new HybridizationSolver(*amg, *a_lor.GetHybridization()));
|
|
}
|
|
else if (RT && dim == 3)
|
|
{
|
|
solv_lor.reset(new HypreADS(&fes_lor));
|
|
}
|
|
else
|
|
{
|
|
solv_lor.reset(new HypreAMS(&fes_lor));
|
|
}
|
|
solv_lor->SetOperator(*A_lor);
|
|
|
|
FiniteElement::MapType t = ND ? FiniteElement::H_CURL : FiniteElement::H_DIV;
|
|
Array<int> perm = ComputeVectorFE_LORPermutation(fes_ho, fes_lor, t);
|
|
|
|
// TEMP
|
|
// for (int i=0; i<perm.Size(); ++i) { perm[i] = i; }
|
|
|
|
PermutedSolver solv_lor_perm(*solv_lor, perm);
|
|
|
|
CGSolver cg(MPI_COMM_WORLD);
|
|
cg.SetAbsTol(0.0);
|
|
cg.SetRelTol(1e-12);
|
|
cg.SetMaxIter(100);
|
|
cg.SetPrintLevel(1);
|
|
|
|
cg.SetOperator(*A_ho);
|
|
cg.SetPreconditioner(solv_lor_perm);
|
|
// cg.SetPreconditioner(*solv_lor);
|
|
cg.Mult(B_ho, X_ho);
|
|
|
|
// cg.SetOperator(*A_lor);
|
|
// cg.SetPreconditioner(*amg);
|
|
// cg.Mult(B_lor, X_lor);
|
|
|
|
a_ho.RecoverFEMSolution(X_ho, b_ho, x_ho);
|
|
|
|
ParaViewDataCollection dc("LOR", &mesh);
|
|
dc.SetPrefixPath("ParaView");
|
|
dc.SetHighOrderOutput(true);
|
|
dc.SetLevelsOfDetail(order);
|
|
dc.RegisterField("u", &x_ho);
|
|
dc.SetCycle(0);
|
|
dc.SetTime(0.0);
|
|
dc.Save();
|
|
|
|
// solv_lor.Mult(B_lor, X_lor);
|
|
// a_lor.RecoverFEMSolution(X_lor, b_lor, x_lor);
|
|
x_lor = x_ho;
|
|
|
|
for (int i=0; i<perm.Size(); ++i)
|
|
{
|
|
int pi = perm[i];
|
|
int s = pi < 0 ? -1 : 1;
|
|
x_lor[i] = s*x_ho[pi < 0 ? -1-pi : pi];
|
|
}
|
|
|
|
dc.SetMesh(&mesh_lor);
|
|
dc.DeregisterField("u");
|
|
dc.RegisterField("u", &x_lor);
|
|
dc.SetLevelsOfDetail(1);
|
|
dc.SetCycle(1);
|
|
dc.SetTime(1.0);
|
|
dc.Save();
|
|
|
|
return 0;
|
|
}
|