#include "additive_schwarz.hpp" // constructor OverlappingCartesianMeshPartition::OverlappingCartesianMeshPartition(Mesh *mesh_) : mesh(mesh_) { // default overlap size is 2 elements int dim = mesh->Dimension(); int n = pow(mesh->GetNE(), 1.0/(double)dim); nx = 16; ny = 1; nz = 1; if (nx > n) { nx = n; MFEM_WARNING("Changed partition in the x direction to nx = " << n << endl); } if (ny > n) { ny = n; MFEM_WARNING("Changed partition in the y direction to ny = " << n << endl); } if (nz > n) { nz = n; MFEM_WARNING("Changed partition in the z direction to nz = " << n << endl); } if (dim == 2) nz = 1; int nxyz[3] = {nx,ny,nz}; nrpatch = nx*ny*nz; Vector pmin, pmax; mesh->GetBoundingBox(pmin, pmax); double h = GetUniformMeshElementSize(mesh); element_map.resize(nrpatch); double ppt[dim]; Vector pt(ppt, dim); int nrelem = mesh->GetNE(); for (int el = 0; el < nrelem; el++) { mesh->GetElementTransformation(el)->Transform( Geometries.GetCenter(mesh->GetElementBaseGeometry(el)), pt); // Given the center coordinates determine the patches that this element contributes to Array idx0(dim); Array idx1(dim); Array idx2(dim); vector> idx(3); if (dim == 2) idx[2].Append(0); for (int i = 0; i= nxyz[i]) idx0[i] = nxyz[i]-1; if (idx1[i] < 0) idx1[i] = 0; if (idx1[i] >= nxyz[i]) idx1[i] = nxyz[i]-1; if (idx2[i] < 0) idx2[i] = 0; if (idx2[i] >= nxyz[i]) idx2[i] = nxyz[i]-1; // convenient to put in one list idx[i].Append(idx0[i]); if (idx1[i] != idx0[i]) idx[i].Append(idx1[i]); if (idx2[i] != idx0[i] && idx2[i] != idx1[i]) idx[i].Append(idx2[i]); } // Now loop through all the combinations according to the idx above // in case of dim = 2 then kk = 0 for (int k=0; kDimension(); nx = 5; ny = 1; nz = 1; int nxyz[3] = {nx,ny,nz}; nrpatch = nx*ny*nz; Vector pmin, pmax; mesh->GetBoundingBox(pmin, pmax); int nrelem = mesh->GetNE(); int partitioning[nrelem]; // determine the partitioning using the centers of the elements double ppt[dim]; Vector pt(ppt, dim); for (int el = 0; el < nrelem; el++) { mesh->GetElementTransformation(el)->Transform( Geometries.GetCenter(mesh->GetElementBaseGeometry(el)), pt); int part = 0; for (int i = dim-1; i >= 0; i--) { int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i]))); if (idx < 0) { idx = 0; } if (idx >= nxyz[i]) { idx = nxyz[i]-1; } part = part * nxyz[i] + idx; } partitioning[el] = part; } element_map.resize(nrpatch); for (int iel = 0; iel < nrelem; iel++) { int ip = partitioning[iel]; element_map[ip].Append(iel); } } STPOverlappingCartesianMeshPartition::STPOverlappingCartesianMeshPartition(Mesh *mesh_) : mesh(mesh_) { int dim = mesh->Dimension(); nx = 9; ny = 1; nz = 1; int nxyz[3] = {nx,ny,nz}; // nrpatch = nx*ny*nz; Vector pmin, pmax; mesh->GetBoundingBox(pmin, pmax); int nrelem = mesh->GetNE(); int partitioning[nrelem]; // determine the partitioning using the centers of the elements double ppt[dim]; Vector pt(ppt, dim); for (int el = 0; el < nrelem; el++) { mesh->GetElementTransformation(el)->Transform( Geometries.GetCenter(mesh->GetElementBaseGeometry(el)), pt); int part = 0; for (int i = dim-1; i >= 0; i--) { int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i]))); if (idx < 0) { idx = 0; } if (idx >= nxyz[i]) { idx = nxyz[i]-1; } part = part * nxyz[i] + idx; } partitioning[el] = part; } // element_map.resize(nrpatch); // for (int iel = 0; iel < nrelem; iel++) // { // int ip = partitioning[iel]; // element_map[ip].Append(iel); // } // // Append the next subdomain to the previous // for (int ip = 0; ip> elem_map; int npatch = nx*ny*nz; elem_map.resize(npatch); for (int iel = 0; iel < nrelem; iel++) { int ip = partitioning[iel]; elem_map[ip].Append(iel); } // Append the next subdomain to the previous nrpatch = nx*ny*nz-1; element_map.resize(nrpatch); for (int ip = 0; ipGetNV(); element_map.resize(nrpatch); //every element will contribute to the the patches of its vertices // loop through the elements int nrelems = mesh->GetNE(); for (int iel=0; iel vertices; mesh->GetElementVertices(iel,vertices); int nrvert = vertices.Size(); // fill in the element contribution lists for (int iv = 0; iv< nrvert; ++iv) { int ip = vertices[iv]; element_map[ip].Append(iel); } } } MeshPartition::MeshPartition(Mesh* mesh_, int part): mesh(mesh_) { partition_kind = part; if (part == 1) { cout << "Non Overlapping Cartesian Partition " << endl; CartesianMeshPartition partition(mesh); element_map = partition.element_map; nx = partition.nx; ny = partition.ny; nz = partition.nz; } // else if (part == 3 || part == 4) else if (part == 2) { cout << "Overlapping Cartesian Partition " << endl; OverlappingCartesianMeshPartition partition(mesh); element_map = partition.element_map; nx = partition.nx; ny = partition.ny; nz = partition.nz; } else if (part == 3 || part == 4) // else if (part == 2) { cout << "STP Overlapping Cartesian Partition " << endl; STPOverlappingCartesianMeshPartition partition(mesh); element_map = partition.element_map; nx = partition.nx; ny = partition.ny; nz = partition.nz; } else { cout << "Overlapping Vertex based partition " << endl; VertexMeshPartition partition(mesh); element_map = partition.element_map; partition_kind = 0; } nrpatch = element_map.size(); int dim = mesh->Dimension(); patch_mesh.SetSize(nrpatch); for (int ip = 0; ip patch_vertices; for (int iel=0; iel elem_vertices; int iel_idx = element_map[ip][iel]; mesh->GetElementVertices(iel_idx,elem_vertices); patch_vertices.Append(elem_vertices); } patch_vertices.Sort(); patch_vertices.Unique(); int patch_nrvertices = patch_vertices.Size(); // create the mesh patch_mesh[ip] = new Mesh(dim,patch_nrvertices,patch_nrelems); // Add the vertices for (int iv = 0; ivAddVertex(mesh->GetVertex(vert_idx)); } // Add the elements (for now search through all the vertices in the patch is needed) for (int iel=0; iel elem_vertices; int iel_idx = element_map[ip][iel]; mesh->GetElementVertices(iel_idx,elem_vertices); int nrvert = elem_vertices.Size(); int ind[nrvert]; for (int iv = 0; ivGetElementType(element_map[ip][iel]); AddElementToMesh(patch_mesh[ip],elem_type,ind); } patch_mesh[ip]->FinalizeTopology(); } } void MeshPartition::AddElementToMesh(Mesh * mesh,mfem::Element::Type elem_type, int * ind) { switch (elem_type) { case Element::QUADRILATERAL: mesh->AddQuad(ind); break; case Element::TRIANGLE : mesh->AddTri(ind); break; case Element::HEXAHEDRON : mesh->AddHex(ind); break; case Element::TETRAHEDRON : mesh->AddTet(ind); break; default: MFEM_ABORT("Unknown element type"); break; } } void MeshPartition::PrintElementMap() { mfem::out << "Element map" << endl; for (int ip = 0; ip meshes, string mfilename, string sfilename) { int nrmeshes = meshes.Size(); for (int ip = 0; ipPrint(mesh_ofs); L2_FECollection L2fec(1,meshes[ip]->Dimension()); FiniteElementSpace L2fes(meshes[ip], &L2fec); GridFunction x(&L2fes); ConstantCoefficient alpha((double)ip); x.ProjectCoefficient(alpha); ostringstream sol_name; sol_name << sfilename << setfill('0') << setw(6) << ip; ofstream sol_ofs(sol_name.str().c_str()); x.Save(sol_ofs); } } MeshPartition::~MeshPartition() { for (int ip = 0; ip & ess_tdofs, int part) : bf(bf_) { fespace = bf->FESpace(); Mesh * mesh = fespace->GetMesh(); const FiniteElementCollection *fec = fespace->FEColl(); // list of dofs to distiguish between interior/boundary and essential Array global_tdofs(fespace->GetTrueVSize()); Array bdr_tdofs(fespace->GetTrueVSize()); global_tdofs = 0; // Mark boundary dofs and ess_dofs if (mesh->bdr_attributes.Size()) { Array ess_bdr(mesh->bdr_attributes.Max()); ess_bdr = 1; fespace->GetEssentialTrueDofs(ess_bdr, bdr_tdofs); } // mark boundary dofs for (int i = 0; ipatch_mesh); nrpatch = p->nrpatch; patch_fespaces.SetSize(nrpatch); patch_dof_map.resize(nrpatch); patch_mat.SetSize(nrpatch); patch_mat_inv.SetSize(nrpatch); ess_tdof_list.resize(nrpatch); ess_int_tdofs.resize(nrpatch); for (int ip=0; ippatch_mesh[ip],fec); // construct the patch tdof to global tdof map int nrdof = patch_fespaces[ip]->GetTrueVSize(); patch_dof_map[ip].SetSize(nrdof); // loop through the elements in the patch for (int iel = 0; ielelement_map[ip].Size(); ++iel) { // index in the global mesh int iel_idx = p->element_map[ip][iel]; // get the dofs of this element Array patch_elem_dofs; Array global_elem_dofs; patch_fespaces[ip]->GetElementDofs(iel,patch_elem_dofs); fespace->GetElementDofs(iel_idx,global_elem_dofs); // the sizes have to match MFEM_VERIFY(patch_elem_dofs.Size() == global_elem_dofs.Size(), "Size inconsistency"); // loop through the dofs and take into account the signs; int ndof = patch_elem_dofs.Size(); for (int i = 0; i= 0) ? pdof_ : abs(pdof_) - 1; int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1; patch_dof_map[ip][pdof] = gdof; } } // Define the patch bilinear form and apply boundary conditions (only the LHS) Array ess_temp_list; if (p->patch_mesh[ip]->bdr_attributes.Size()) { Array ess_bdr(p->patch_mesh[ip]->bdr_attributes.Max()); ess_bdr = 1; patch_fespaces[ip]->GetEssentialTrueDofs(ess_bdr, ess_temp_list); } // Adjust the essential tdof list for each patch for (int i=0; iThreshold(0.0); // Save the inverse patch_mat_inv[ip] = new KLUSolver; patch_mat_inv[ip]->SetOperator(*patch_mat[ip]); } delete p; } void PatchAssembly::print_patch_dof_map() { mfem::out << "Patch dof map" << endl; for (int ip = 0; ip & global_ess_tdof_list, int i) : Solver(bf_->FESpace()->GetTrueVSize(), bf_->FESpace()->GetTrueVSize()), part(i) { p = new PatchAssembly(bf_, global_ess_tdof_list, part); nrpatch = p->nrpatch; } void AddSchwarz::Mult(const Vector &r, Vector &z) const { z = 0.0; Vector rnew(r); Vector znew(z); Vector raux(znew.Size()); Vector res_local, sol_local; for (int iter = 0; iter < maxit; iter++) { znew = 0.0; for (int ip = 0; ip < nrpatch; ip++) { Array * dof_map = &p->patch_dof_map[ip]; int ndofs = dof_map->Size(); res_local.SetSize(ndofs); sol_local.SetSize(ndofs); rnew.GetSubVector(*dof_map, res_local); Array ess_bdr_indices = p->ess_tdof_list[ip]; // for the overlapping case // zero out the entries corresponding to the ess_bdr p->patch_mat_inv[ip]->Mult(res_local, sol_local); if (!part) { sol_local.SetSubVector(ess_bdr_indices,0.0); } znew.AddElementVector(*dof_map,sol_local); } // Relaxation parameter znew *= theta; z += znew; // Update residual if (iter + 1 < maxit) { A->Mult(znew, raux); rnew -= raux; } } } AddSchwarz::~AddSchwarz() { delete p; } double GetUniformMeshElementSize(Mesh * mesh) { int dim = mesh->Dimension(); int nrelem = mesh->GetNE(); DenseMatrix J(dim); double hmin, hmax; hmin = infinity(); hmax = -infinity(); Vector attr(nrelem); for (int iel=0; ielGetElementBaseGeometry(iel); ElementTransformation *T = mesh->GetElementTransformation(iel); T->SetIntPoint(&Geometries.GetCenter(geom)); Geometries.JacToPerfJac(geom, T->Jacobian(), J); attr(iel) = J.Det(); attr(iel) = pow(abs(attr(iel)), 1.0/double(dim)); hmin = min(hmin, attr(iel)); hmax = max(hmax, attr(iel)); } MFEM_VERIFY(abs(hmin-hmax) < 1e-12, "Case not supported yet") return hmax; } Mesh * ExtendMesh(Mesh * mesh, const Array & directions) { // extrute on one dimension // flag = 1 +x, -1 -x, 2 +y, -2 +y , 3 +z, -3, -z // copy the original mesh; Mesh * mesh_orig = new Mesh(*mesh); if (!directions.Size()) return mesh_orig; int dim = mesh_orig->Dimension(); Mesh * mesh_ext=nullptr; for (int j=0; jGetNV(); ++i) { double * coords = mesh_orig->GetVertex(i); switch (abs(d)) { case 1: if (coords[0] == val) k++; break; case 2: if (coords[1] == val) k++; break; case 3: if (coords[2] == val) k++; break; } } int nrvertices = mesh_orig->GetNV() + k; int nrelements = mesh_orig->GetNE() + pow(pow(k,1.0/(dim-1))-1.0,dim-1); mesh_ext = new Mesh(dim, nrvertices, nrelements); // Add existing vertices Array vmap(mesh_orig->GetNV()); vmap = 0; k = mesh_orig->GetNV(); for (int i=0; iGetNV(); ++i) { double * vert = mesh_orig->GetVertex(i); mesh_ext->AddVertex(vert); switch (abs(d)) { case 1: if (vert[0] == val) { vmap[i] = k; k++; } break; case 2: if (vert[1] == val) { vmap[i] = k; k++; } break; case 3: if (vert[2] == val) { vmap[i] = k; k++; } break; } } // Add existing elements for (int i=0; iGetNE(); ++i) { Arrayind; mesh_orig->GetElementVertices(i,ind); if (dim == 2) { mesh_ext->AddQuad(ind); } else if (dim == 3) { mesh_ext->AddHex(ind); } } // Add new vertices k = mesh_orig->GetNV(); for (int i=0; iGetNV(); ++i) { double * vert = mesh_orig->GetVertex(i); switch (abs(d)) { case 1: if (vert[0] == val) { double coords[dim]; coords[0] = vert[0] + h; coords[1] = vert[1]; if (dim == 3) coords[2] = vert[2]; mesh_ext->AddVertex(coords); } break; case 2: if (vert[1] == val) { double coords[dim]; coords[0] = vert[0]; coords[1] = vert[1] + h; if (dim == 3) coords[2] = vert[2]; mesh_ext->AddVertex(coords); } break; case 3: if (vert[2] == val) { double coords[dim]; coords[0] = vert[0]; coords[1] = vert[1]; coords[2] = vert[2] + h; mesh_ext->AddVertex(coords); } break; } } // loop through boundary elements and extend in the given direction for (int i=0; iGetNBE(); ++i) { Array vertices; mesh_orig->GetBdrElementVertices(i,vertices); if (dim == 2) { int ind[4]; if (vmap[vertices[0]] && vmap[vertices[1]]) { ind[0] = vmap[vertices[0]]; ind[1] = vmap[vertices[1]]; ind[2] = vertices[1]; ind[3] = vertices[0]; mesh_ext->AddQuad(ind); } } else if (dim == 3) { int ind[8]; if (vmap[vertices[0]] && vmap[vertices[1]] && vmap[vertices[2]] && vmap[vertices[3]]) { ind[0] = vmap[vertices[0]]; ind[1] = vmap[vertices[1]]; ind[2] = vmap[vertices[2]]; ind[3] = vmap[vertices[3]]; ind[4] = vertices[0]; ind[5] = vertices[1]; ind[6] = vertices[2]; ind[7] = vertices[3]; mesh_ext->AddHex(ind); } } } mesh_ext->FinalizeTopology(); if (j