510 lines
14 KiB
C++
510 lines
14 KiB
C++
|
|
#include "mfem.hpp"
|
|
#include "schwarz.hpp"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
|
|
void print(std::vector<int> const &input)
|
|
{
|
|
for (int i = 0; i < (int)input.size(); i++) {
|
|
std::cout << input.at(i) << ' ';
|
|
}
|
|
}
|
|
|
|
// constructor
|
|
patch_nod_info::patch_nod_info(Mesh *mesh_, int ref_levels_)
|
|
: mesh(mesh_), ref_levels(ref_levels_)
|
|
{
|
|
/* The patches are defined by all the "active" vertices of the coarse mesh
|
|
We define a low order H1 fespace and perform refinements so that we can get
|
|
the H1 prolongation operator recursively. This way we can easily find
|
|
all the patches that the fine mesh vertices contribute to. After the vertices
|
|
are done the edges, faces and elements can be found easily because they
|
|
contribute to the same patches as their vertices. */
|
|
|
|
// Number of patches
|
|
nrpatch = mesh->GetNV();
|
|
int dim = mesh->Dimension();
|
|
FiniteElementCollection *fec = new H1_FECollection(1, dim);
|
|
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
|
|
|
// First we need to construct a list of non-essential coarse grid vertices
|
|
|
|
// SparseMatrix *Pr = nullptr;
|
|
//initialize Pr with the Identity
|
|
Vector ones(fespace->GetTrueVSize());
|
|
ones = 1.0;
|
|
SparseMatrix * Pr = new SparseMatrix(ones);
|
|
// 4. Refine the mesh
|
|
for (int i = 0; i < ref_levels; i++)
|
|
{
|
|
const FiniteElementSpace cfespace(*fespace);
|
|
mesh->UniformRefinement();
|
|
// Update fespace
|
|
fespace->Update();
|
|
OperatorHandle Tr(Operator::MFEM_SPARSEMAT);
|
|
fespace->GetTransferOperator(cfespace, Tr);
|
|
Tr.SetOperatorOwner(false);
|
|
SparseMatrix *P;
|
|
Tr.Get(P);
|
|
if (!Pr)
|
|
{
|
|
Pr = P;
|
|
}
|
|
else
|
|
{
|
|
Pr = Mult(*P, *Pr);
|
|
}
|
|
}
|
|
// if there is no refinement the prolongation is the identity
|
|
Pr->Threshold(0.0);
|
|
int nvert = mesh->GetNV();
|
|
vertex_contr.resize(nvert);
|
|
for (int iv = 0; iv < nvert; iv++)
|
|
{
|
|
int nz = Pr->RowSize(iv);
|
|
vertex_contr[iv].SetSize(nz);
|
|
int *col = Pr->GetRowColumns(iv);
|
|
for (int i = 0; i < nz; i++)
|
|
{
|
|
vertex_contr[iv][i] = col[i];
|
|
}
|
|
}
|
|
|
|
delete Pr;
|
|
|
|
Array<int> edge_vertices;
|
|
int nedge = mesh->GetNEdges();
|
|
edge_contr.resize(nedge);
|
|
for (int ie = 0; ie < nedge; ie++)
|
|
{
|
|
mesh->GetEdgeVertices(ie, edge_vertices);
|
|
int nv = edge_vertices.Size(); // always 2 but ok
|
|
// The edge will contribute to the same patches as its vertices
|
|
for (int iv = 0; iv < nv; iv++)
|
|
{
|
|
int ivert = edge_vertices[iv];
|
|
edge_contr[ie].Append(vertex_contr[ivert]);
|
|
}
|
|
edge_contr[ie].Sort();
|
|
edge_contr[ie].Unique();
|
|
}
|
|
|
|
Array<int> face_vertices;
|
|
int nface = mesh->GetNFaces();
|
|
face_contr.resize(nface);
|
|
for (int ifc = 0; ifc < nface; ifc++)
|
|
{
|
|
mesh->GetFaceVertices(ifc, face_vertices);
|
|
int nv = face_vertices.Size();
|
|
// The face will contribute to the same patches as its vertices
|
|
for (int iv = 0; iv < nv; iv++)
|
|
{
|
|
int ivert = face_vertices[iv];
|
|
face_contr[ifc].Append(vertex_contr[ivert]);
|
|
}
|
|
face_contr[ifc].Sort();
|
|
face_contr[ifc].Unique();
|
|
}
|
|
|
|
Array<int> elem_vertices;
|
|
int nelem = mesh->GetNE();
|
|
elem_contr.resize(nelem);
|
|
for (int iel = 0; iel < nelem; iel++)
|
|
{
|
|
mesh->GetElementVertices(iel, elem_vertices);
|
|
int nv = elem_vertices.Size();
|
|
// The element will contribute to the same patches as its vertices
|
|
for (int iv = 0; iv < nv; iv++)
|
|
{
|
|
int ivert = elem_vertices[iv];
|
|
elem_contr[iel].Append(vertex_contr[ivert]);
|
|
}
|
|
elem_contr[iel].Sort();
|
|
elem_contr[iel].Unique();
|
|
}
|
|
delete fespace;
|
|
delete fec;
|
|
}
|
|
// Constructor of patch local problems
|
|
patch_assembly::patch_assembly(Mesh *cmesh_, int ref_levels_, FiniteElementSpace *fespace)
|
|
: cmesh(*cmesh_), ref_levels(ref_levels_)
|
|
{
|
|
patch_nod_info *patches = new patch_nod_info(&cmesh, ref_levels);
|
|
|
|
nrpatch = patches->nrpatch;
|
|
Pid.SetSize(nrpatch);
|
|
patch_dof_map.SetSize(nrpatch);
|
|
// Build a sparse matrix out of this map to extract the patch submatrix
|
|
Array<int> dofoffset(nrpatch);
|
|
dofoffset = 0;
|
|
int height = fespace->GetVSize();
|
|
// allocation of sparse matrices.
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
Pid[i] = new SparseMatrix(height);
|
|
}
|
|
// Now the filling of the matrices with vertex,edge,face,interior dofs
|
|
Mesh *mesh = fespace->GetMesh();
|
|
int nrvert = mesh->GetNV();
|
|
int nredge = mesh->GetNEdges();
|
|
int nrface = mesh->GetNFaces();
|
|
int nrelem = mesh->GetNE();
|
|
// First the vertices
|
|
for (int i = 0; i < nrvert; i++)
|
|
{
|
|
int np = patches->vertex_contr[i].Size();
|
|
Array<int> vertex_dofs;
|
|
fespace->GetVertexDofs(i, vertex_dofs);
|
|
int nv = vertex_dofs.Size();
|
|
|
|
for (int j = 0; j < np; j++)
|
|
{
|
|
int k = patches->vertex_contr[i][j];
|
|
for (int l = 0; l < nv; l++)
|
|
{
|
|
int m = vertex_dofs[l];
|
|
Pid[k]->Set(m, dofoffset[k], 1.0);
|
|
dofoffset[k]++;
|
|
}
|
|
}
|
|
}
|
|
// Edges
|
|
for (int i = 0; i < nredge; i++)
|
|
{
|
|
int np = patches->edge_contr[i].Size();
|
|
Array<int> edge_dofs;
|
|
fespace->GetEdgeInteriorDofs(i, edge_dofs);
|
|
int ne = edge_dofs.Size();
|
|
for (int j = 0; j < np; j++)
|
|
{
|
|
int k = patches->edge_contr[i][j];
|
|
for (int l = 0; l < ne; l++)
|
|
{
|
|
int m = edge_dofs[l];
|
|
Pid[k]->Set(m, dofoffset[k], 1.0);
|
|
dofoffset[k]++;
|
|
}
|
|
}
|
|
}
|
|
// Faces
|
|
for (int i = 0; i < nrface; i++)
|
|
{
|
|
int np = patches->face_contr[i].Size();
|
|
Array<int> face_dofs;
|
|
fespace->GetFaceInteriorDofs(i, face_dofs);
|
|
int nfc = face_dofs.Size();
|
|
for (int j = 0; j < np; j++)
|
|
{
|
|
int k = patches->face_contr[i][j];
|
|
for (int l = 0; l < nfc; l++)
|
|
{
|
|
int m = face_dofs[l];
|
|
Pid[k]->Set(m, dofoffset[k], 1.0);
|
|
dofoffset[k]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The following can be skipped in case of static condensation
|
|
// Elements
|
|
for (int i = 0; i < nrelem; i++)
|
|
{
|
|
int np = patches->elem_contr[i].Size();
|
|
Array<int> elem_dofs;
|
|
fespace->GetElementInteriorDofs(i, elem_dofs);
|
|
int nel = elem_dofs.Size();
|
|
for (int j = 0; j < np; j++)
|
|
{
|
|
int k = patches->elem_contr[i][j];
|
|
for (int l = 0; l < nel; l++)
|
|
{
|
|
int m = elem_dofs[l];
|
|
Pid[k]->Set(m, dofoffset[k], 1.0);
|
|
dofoffset[k]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
Pid[i]->SetWidth(dofoffset[i]);
|
|
Pid[i]->Finalize();
|
|
|
|
patch_dof_map[i].SetSize(Pid[i]->Width());
|
|
// copy from sparse matrix to a simple injection map
|
|
// use the traspose
|
|
SparseMatrix * temp = Transpose(*Pid[i]);
|
|
// Extract row by row of the transpose
|
|
for (int k =0; k<temp->Height(); ++k)
|
|
{
|
|
int * col = temp->GetRowColumns(k);
|
|
patch_dof_map[i][k] = col[0];
|
|
}
|
|
delete temp;
|
|
}
|
|
delete patches;
|
|
}
|
|
|
|
patch_assembly:: ~patch_assembly()
|
|
{
|
|
for (int i=0; i<nrpatch; i++)
|
|
{
|
|
delete Pid[i];
|
|
}
|
|
Pid.DeleteAll();
|
|
}
|
|
|
|
// constructor
|
|
SchwarzSmoother::SchwarzSmoother(Mesh *cmesh_, int ref_levels_, FiniteElementSpace *fespace_, SparseMatrix *A_, Array<int> ess_bdr)
|
|
: Solver(A_->Height(), A_->Width()), A(A_)
|
|
{
|
|
P = new patch_assembly(cmesh_, ref_levels_, fespace_);
|
|
|
|
ess_bdr = 0;
|
|
GetNonEssentialPatches(cmesh_, ess_bdr, patch_ids);
|
|
|
|
// nrpatch = P->nrpatch;
|
|
nrpatch = patch_ids.size();
|
|
A_local.SetSize(nrpatch);
|
|
invA_local.SetSize(nrpatch);
|
|
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
int k = patch_ids[i];
|
|
SparseMatrix *Pr = P->Pid[k];
|
|
// construct the local problems. Factor the patch matrices
|
|
A_local[i] = RAP(*Pr, *A, *Pr);
|
|
// if (i == 0) A_local[i]->PrintMatlab(cout);
|
|
invA_local[i] = new KLUSolver;
|
|
// invA_local[i] = new UMFPackSolver;
|
|
// invA_local[i]->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
|
invA_local[i]->SetOperator(*A_local[i]);
|
|
}
|
|
}
|
|
|
|
void SchwarzSmoother::GetNonEssentialPatches(Mesh *cmesh, const Array<int> &ess_bdr, vector<int> &patch_ids)
|
|
{
|
|
Array<int> ess_vertices;
|
|
Array<int> bdr_vertices;
|
|
|
|
for (int i = 0; i < cmesh->GetNBE(); i++)
|
|
{
|
|
int bdr = cmesh->GetBdrAttribute(i);
|
|
//check if it's essential;
|
|
if (ess_bdr[bdr - 1] == 1)
|
|
{
|
|
cmesh->GetBdrElementVertices(i, bdr_vertices);
|
|
ess_vertices.Append(bdr_vertices);
|
|
}
|
|
}
|
|
ess_vertices.Sort();
|
|
ess_vertices.Unique();
|
|
|
|
int nrpatch = cmesh->GetNV() - ess_vertices.Size();
|
|
patch_ids.resize(nrpatch);
|
|
|
|
if (ess_vertices.Size() > 0)
|
|
{
|
|
int m = 0;
|
|
int l = 0;
|
|
for (int i = 0; i < cmesh->GetNV(); i++)
|
|
{
|
|
if (m<ess_vertices.Size() && i == ess_vertices[m])
|
|
{
|
|
m++;
|
|
}
|
|
else
|
|
{
|
|
patch_ids[l] = i;
|
|
l++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < cmesh->GetNV(); i++)
|
|
{
|
|
patch_ids[i] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SchwarzSmoother::Mult(const Vector &r, Vector &z) const
|
|
{
|
|
// Apply the smoother patch on the restriction of the residual
|
|
z = 0.0;
|
|
Vector rnew(r);
|
|
Vector znew(z);
|
|
Vector raux(znew.Size());
|
|
Vector res_local, sol_local;
|
|
switch (sType)
|
|
{
|
|
case Schwarz::SmootherType::ADDITIVE:
|
|
{
|
|
for (int iter = 0; iter < maxit; iter++)
|
|
{
|
|
znew = 0.0;
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
int k = patch_ids[i];
|
|
Array<int> * dof_map = &P->patch_dof_map[k];
|
|
// SparseMatrix *Pr = P->Pid[k];
|
|
// res_local.SetSize(Pr->NumCols());
|
|
// sol_local.SetSize(Pr->NumCols());
|
|
// Pr->MultTranspose(rnew, res_local[i]);
|
|
int ndofs = dof_map->Size();
|
|
res_local.SetSize(ndofs);
|
|
sol_local.SetSize(ndofs);
|
|
rnew.GetSubVector(*dof_map, res_local);
|
|
|
|
invA_local[i]->Mult(res_local, sol_local);
|
|
znew.AddElementVector(*dof_map,sol_local);
|
|
// Pr->Mult(sol_local[i], zaux[i]);
|
|
// znew += zaux[i];
|
|
}
|
|
// Relaxation parameter
|
|
znew *= theta;
|
|
z += znew;
|
|
|
|
//Update residual
|
|
if (iter + 1 < maxit)
|
|
{
|
|
A->Mult(znew, raux);
|
|
rnew -= raux;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Schwarz::SmootherType::MULTIPLICATIVE:
|
|
{
|
|
// TODO
|
|
}
|
|
break;
|
|
case Schwarz::SmootherType::SYM_MULTIPLICATIVE:
|
|
{
|
|
// TODO
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
SchwarzSmoother:: ~SchwarzSmoother()
|
|
{
|
|
delete P;
|
|
for (int ip=0; ip<nrpatch; ++ip)
|
|
{
|
|
delete A_local[ip];
|
|
delete invA_local[ip];
|
|
}
|
|
A_local.DeleteAll();
|
|
invA_local.DeleteAll();
|
|
}
|
|
|
|
|
|
|
|
BlkSchwarzSmoother::BlkSchwarzSmoother(Mesh *cmesh_, int ref_levels_, FiniteElementSpace* fespace_, SparseMatrix *A_)
|
|
: Solver(A_->Height(), A_->Width()), A(A_)
|
|
{
|
|
P = new patch_assembly(cmesh_, ref_levels_, fespace_);
|
|
|
|
nrpatch = cmesh_->GetNV();
|
|
cout << "nrpatch = " << nrpatch << endl;
|
|
patch_ids.resize(nrpatch);
|
|
for (int i=0; i<nrpatch; i++) {patch_ids[i]=i;}
|
|
|
|
nrpatch = patch_ids.size();
|
|
A_local.SetSize(nrpatch);
|
|
invA_local.SetSize(nrpatch);
|
|
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
int k = patch_ids[i];
|
|
SparseMatrix *Pr = P->Pid[k];
|
|
Array<int> offsets_i(3);
|
|
Array<int> offsets_j(3);
|
|
offsets_i[0] = 0;
|
|
offsets_i[1] = Pr->Height();
|
|
offsets_i[2] = Pr->Height();
|
|
offsets_i.PartialSum();
|
|
offsets_j[0] = 0;
|
|
offsets_j[1] = Pr->Width();
|
|
offsets_j[2] = Pr->Width();
|
|
offsets_j.PartialSum();
|
|
|
|
BlockMatrix * BlockPr = new BlockMatrix(offsets_i,offsets_j);
|
|
BlockPr->SetBlock(0,0,Pr);
|
|
BlockPr->SetBlock(1,1,Pr);
|
|
// Fake blocks
|
|
SparseMatrix * fakemat = new SparseMatrix(Pr->Height(),Pr->Width()); fakemat->Finalize();
|
|
BlockPr->SetBlock(0,1,fakemat);
|
|
BlockPr->SetBlock(1,0,fakemat);
|
|
|
|
SparseMatrix *Bpr = BlockPr->CreateMonolithic();
|
|
A_local[i] = RAP(*Bpr, *A, *Bpr);
|
|
invA_local[i] = new UMFPackSolver;
|
|
invA_local[i]->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
|
invA_local[i]->SetOperator(*A_local[i]);
|
|
}
|
|
}
|
|
|
|
|
|
void BlkSchwarzSmoother::Mult(const Vector &r, Vector &z) const
|
|
{
|
|
// Apply the smoother patch on the restriction of the residual
|
|
Array<Vector> res_local(nrpatch);
|
|
Array<Vector> sol_local(nrpatch);
|
|
Array<Vector> zaux(nrpatch);
|
|
z = 0.0;
|
|
Vector rnew(r);
|
|
Vector znew(z);
|
|
|
|
for (int iter = 0; iter < maxit; iter++)
|
|
{
|
|
znew = 0.0;
|
|
for (int i = 0; i < nrpatch; i++)
|
|
{
|
|
int k = patch_ids[i];
|
|
SparseMatrix *Pr = P->Pid[k];
|
|
Array<int> offsets_i(3);
|
|
Array<int> offsets_j(3);
|
|
offsets_i[0] = 0;
|
|
offsets_i[1] = Pr->Height();
|
|
offsets_i[2] = Pr->Height();
|
|
offsets_i.PartialSum();
|
|
offsets_j[0] = 0;
|
|
offsets_j[1] = Pr->Width();
|
|
offsets_j[2] = Pr->Width();
|
|
offsets_j.PartialSum();
|
|
BlockMatrix * BlockPr = new BlockMatrix(offsets_i,offsets_j);
|
|
BlockPr->SetBlock(0,0,Pr);
|
|
BlockPr->SetBlock(1,1,Pr);
|
|
SparseMatrix * fakemat = new SparseMatrix(Pr->Height(),Pr->Width()); fakemat->Finalize();
|
|
BlockPr->SetBlock(0,1,fakemat);
|
|
BlockPr->SetBlock(1,0,fakemat);
|
|
SparseMatrix *Bpr = BlockPr->CreateMonolithic();
|
|
res_local[i].SetSize(Bpr->NumCols());
|
|
sol_local[i].SetSize(Bpr->NumCols());
|
|
Bpr->MultTranspose(rnew, res_local[i]);
|
|
|
|
invA_local[i]->Mult(res_local[i], sol_local[i]);
|
|
zaux[i].SetSize(r.Size());
|
|
zaux[i] = 0.0;
|
|
Bpr->Mult(sol_local[i], zaux[i]);
|
|
znew += zaux[i];
|
|
}
|
|
|
|
// Relaxation parameter
|
|
znew *= theta;
|
|
z += znew;
|
|
//Update residual
|
|
Vector raux(znew.Size());
|
|
A->Mult(znew, raux);
|
|
rnew -= raux;
|
|
}
|
|
} |