#include "mfem.hpp" #include "schwarz.hpp" #include #include using namespace std; using namespace mfem; void print(std::vector 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 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 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 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 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 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 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 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 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; kHeight(); ++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 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 &ess_bdr, vector &patch_ids) { Array ess_vertices; Array 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 (mGetNV(); 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 * 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; ipHeight(), 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; iPid[k]; Array offsets_i(3); Array 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 res_local(nrpatch); Array sol_local(nrpatch); Array 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 offsets_i(3); Array 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; } }