MFEM  v4.6.0
Finite element discretization library
psubmesh.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "../../config/config.hpp"
13 
14 #ifdef MFEM_USE_MPI
15 
16 #include <iostream>
17 #include <unordered_set>
18 #include <algorithm>
19 #include "psubmesh.hpp"
20 #include "submesh_utils.hpp"
21 #include "../segment.hpp"
22 
23 namespace mfem
24 {
25 
27  Array<int> &domain_attributes)
28 {
29  return ParSubMesh(parent, SubMesh::From::Domain, domain_attributes);
30 }
31 
33  Array<int> &boundary_attributes)
34 {
35  return ParSubMesh(parent, SubMesh::From::Boundary, boundary_attributes);
36 }
37 
38 ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
39  Array<int> &attributes) : parent_(parent), from_(from), attributes_(attributes)
40 {
41  if (Nonconforming())
42  {
43  MFEM_ABORT("SubMesh does not support non-conforming meshes");
44  }
45 
46  MyComm = parent.GetComm();
47  NRanks = parent.GetNRanks();
48  MyRank = parent.GetMyRank();
49 
50  if (from == SubMesh::From::Domain)
51  {
52  InitMesh(parent.Dimension(), parent.SpaceDimension(), 0, 0, 0);
53 
54  std::tie(parent_vertex_ids_,
55  parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
56  attributes_);
57  }
58  else if (from == SubMesh::From::Boundary)
59  {
60  InitMesh(parent.Dimension() - 1, parent.SpaceDimension(), 0, 0, 0);
61 
62  std::tie(parent_vertex_ids_,
63  parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
64  attributes_, true);
65  }
66 
67  // Don't let boundary elements get generated automatically. This would
68  // generate boundary elements on each rank locally, which is topologically
69  // wrong for the distributed SubMesh.
70  FinalizeTopology(false);
71 
72  parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
73  parent_to_submesh_vertex_ids_ = -1;
74  for (int i = 0; i < parent_vertex_ids_.Size(); i++)
75  {
76  parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
77  }
78 
79  DSTable v2v(parent_.GetNV());
80  parent_.GetVertexToVertexTable(v2v);
81  for (int i = 0; i < NumOfEdges; i++)
82  {
83  Array<int> lv;
84  GetEdgeVertices(i, lv);
85 
86  // Find vertices/edge in parent mesh
87  int parent_edge_id = v2v(parent_vertex_ids_[lv[0]],
88  parent_vertex_ids_[lv[1]]);
89  parent_edge_ids_.Append(parent_edge_id);
90  }
91 
92  parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
93  parent_to_submesh_edge_ids_ = -1;
94  for (int i = 0; i < parent_edge_ids_.Size(); i++)
95  {
96  parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
97  }
98 
99  if (Dim == 3)
100  {
101  parent_face_ids_ = SubMeshUtils::BuildFaceMap(parent_, *this,
102  parent_element_ids_);
103 
104  parent_to_submesh_face_ids_.SetSize(parent.GetNFaces());
105  parent_to_submesh_face_ids_ = -1;
106  for (int i = 0; i < parent_face_ids_.Size(); i++)
107  {
108  parent_to_submesh_face_ids_[parent_face_ids_[i]] = i;
109  }
110 
111  parent_face_ori_.SetSize(NumOfFaces);
112 
113  for (int i = 0; i < NumOfFaces; i++)
114  {
115  Array<int> sub_vert;
116  GetFaceVertices(i, sub_vert);
117 
118  Array<int> sub_par_vert(sub_vert.Size());
119  for (int j = 0; j < sub_vert.Size(); j++)
120  {
121  sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
122  }
123 
124  Array<int> par_vert;
125  parent.GetFaceVertices(parent_face_ids_[i], par_vert);
126 
127  if (par_vert.Size() == 3)
128  {
129  parent_face_ori_[i] = GetTriOrientation(par_vert, sub_par_vert);
130  }
131  else
132  {
133  parent_face_ori_[i] = GetQuadOrientation(par_vert, sub_par_vert);
134  }
135  }
136  }
137  else if (Dim == 2)
138  {
139  parent_face_ori_.SetSize(NumOfElements);
140 
141  for (int i = 0; i < NumOfElements; i++)
142  {
143  Array<int> sub_vert;
144  GetElementVertices(i, sub_vert);
145 
146  Array<int> sub_par_vert(sub_vert.Size());
147  for (int j = 0; j < sub_vert.Size(); j++)
148  {
149  sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
150  }
151 
152  Array<int> par_vert;
153  int be_ori = 0;
154  if (from == SubMesh::From::Boundary)
155  {
156  parent.GetBdrElementVertices(parent_element_ids_[i], par_vert);
157 
158  int f = -1;
159  parent.GetBdrElementFace(parent_element_ids_[i], &f, &be_ori);
160  }
161  else
162  {
163  parent.GetElementVertices(parent_element_ids_[i], par_vert);
164  }
165 
166  if (par_vert.Size() == 3)
167  {
168  int se_ori = GetTriOrientation(par_vert, sub_par_vert);
169  parent_face_ori_[i] = ComposeTriOrientations(be_ori, se_ori);
170  }
171  else
172  {
173  int se_ori = GetQuadOrientation(par_vert, sub_par_vert);
174  parent_face_ori_[i] = ComposeQuadOrientations(be_ori, se_ori);
175  }
176  }
177  }
178 
179  ListOfIntegerSets groups;
180  IntegerSet group;
181  // the first group is the local one
182  group.Recreate(1, &MyRank);
183  groups.Insert(group);
184 
185  // Every rank containing elements of the ParSubMesh attributes now has a
186  // local ParSubMesh. We have to connect the local meshes and assign global
187  // boundaries correctly.
188 
189  Array<int> rhvtx;
190  FindSharedVerticesRanks(rhvtx);
191  AppendSharedVerticesGroups(groups, rhvtx);
192 
193  Array<int> rhe;
194  FindSharedEdgesRanks(rhe);
195  AppendSharedEdgesGroups(groups, rhe);
196 
197  Array<int> rhq, rht;
198  if (Dim == 3)
199  {
200  FindSharedFacesRanks(rht, rhq);
201  AppendSharedFacesGroups(groups, rht, rhq);
202  }
203 
204  // Build the group communication topology
206  gtopo.Create(groups, 822);
207  int ngroups = groups.Size()-1;
208 
209  int nsverts, nsedges, nstrias, nsquads;
210  BuildVertexGroup(ngroups, rhvtx, nsverts);
211  BuildEdgeGroup(ngroups, rhe, nsedges);
212  if (Dim == 3)
213  {
214  BuildFaceGroup(ngroups, rht, nstrias, rhq, nsquads);
215  }
216  else
217  {
218  group_stria.MakeI(ngroups);
219  group_stria.MakeJ();
221 
222  group_squad.MakeI(ngroups);
223  group_squad.MakeJ();
225  }
226 
227  BuildSharedVerticesMapping(nsverts, rhvtx);
228  BuildSharedEdgesMapping(nsedges, rhe);
229  if (Dim == 3)
230  {
231  BuildSharedFacesMapping(nstrias, rht, nsquads, rhq);
232  }
233 
235 
236  // Add boundaries
237  {
238  int num_of_faces_or_edges =
239  (Dim == 3) ? NumOfFaces :
240  ((Dim == 2) ? NumOfEdges : NumOfVertices);
241  Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
242 
243  if (Dim == 3)
244  {
245  // In 3D we check for `bel_to_edge`. It shouldn't have been set
246  // previously.
247  delete bel_to_edge;
248  bel_to_edge = nullptr;
249  }
250 
251  NumOfBdrElements = 0;
252  for (int i = 0; i < num_of_faces_or_edges; i++)
253  {
255  {
257  }
258  }
259 
260  boundary.SetSize(NumOfBdrElements);
261  be2face.SetSize(NumOfBdrElements);
262  Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
263  int max_bdr_attr = parent.bdr_attributes.Max();
264  for (int i = 0, j = 0; i < num_of_faces_or_edges; i++)
265  {
266  if (GetFaceInformation(i).IsBoundary())
267  {
268  boundary[j] = faces[i]->Duplicate(this);
269  if (from == SubMesh::From::Domain && Dim >= 2)
270  {
271  int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
272  parent_face_to_be[parent_edge_ids_[i]];
273  if (pbeid != -1)
274  {
275  boundary[j]->SetAttribute(parent.GetBdrAttribute(pbeid));
276  }
277  else
278  {
279  boundary[j]->SetAttribute(max_bdr_attr + 1);
280  }
281  }
282  else
283  {
284  boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
285  }
286  be2face[j++] = i;
287  }
288  }
289  }
290 
291  if (Dim == 3)
292  {
294  }
295 
296  // If the parent ParMesh has nodes and therefore is defined on a higher order
297  // geometry, we define this ParSubMesh as a curved ParSubMesh and transfer
298  // the GridFunction from the parent ParMesh to the ParSubMesh.
299  const GridFunction *parent_nodes = parent_.GetNodes();
300  if (parent_nodes)
301  {
302  const FiniteElementSpace *parent_fes = parent_nodes->FESpace();
303 
304  SetCurvature(
305  parent_fes->FEColl()->GetOrder(),
306  parent_fes->IsDGSpace(),
307  spaceDim,
308  parent_fes->GetOrdering());
309 
310  const ParGridFunction* pn = dynamic_cast<const ParGridFunction*>
311  (parent_.GetNodes());
312  MFEM_ASSERT(pn,
313  "Internal error. Object is supposed to be ParGridFunction.");
314 
315  ParGridFunction* n = dynamic_cast<ParGridFunction*>
316  (this->GetNodes());
317  MFEM_ASSERT(n,
318  "Internal error. Object is supposed to be ParGridFunction.");
319 
320  Transfer(*pn, *n);
321  }
322 
323  if (Dim > 1)
324  {
325  if (!el_to_edge) { el_to_edge = new Table; }
327  }
328 
329  SetAttributes();
330  Finalize();
331 }
332 
333 void ParSubMesh::FindSharedVerticesRanks(Array<int> &rhvtx)
334 {
335  // create a GroupCommunicator on the shared vertices
336  GroupCommunicator svert_comm(parent_.gtopo);
337  parent_.GetSharedVertexCommunicator(svert_comm);
338  // Number of shared vertices
339  int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
340 
341  rhvtx.SetSize(nsvtx);
342  rhvtx = 0;
343 
344  // On each rank of the group, locally determine if the shared vertex is in
345  // the SubMesh.
346  for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
347  {
348  const int group_sz = parent_.gtopo.GetGroupSize(g);
349  MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
350  "Group size too large. Groups with more than 32 ranks are not supported, yet.");
351  const int* group_lproc = parent_.gtopo.GetGroup(g);
352 
353  const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
354  MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
355 
356  const int my_group_id = my_group_id_ptr-group_lproc;
357 
358  for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
359  {
360  int plvtx = parent_.GroupVertex(g, gv);
361  int submesh_vertex_id = parent_to_submesh_vertex_ids_[plvtx];
362  if (submesh_vertex_id != -1)
363  {
364  rhvtx[sv] |= 1 << my_group_id;
365  }
366  }
367  }
368 
369  // Compute the sum on the root rank and broadcast the result to all ranks.
370  svert_comm.Reduce(rhvtx, GroupCommunicator::Sum);
371  svert_comm.Bcast<int>(rhvtx, 0);
372 }
373 
374 void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
375 {
376  // create a GroupCommunicator on the shared edges
377  GroupCommunicator sedge_comm(parent_.gtopo);
378  parent_.GetSharedEdgeCommunicator(sedge_comm);
379 
380  int nsedges = sedge_comm.GroupLDofTable().Size_of_connections();
381 
382  // see rhvtx description
383  rhe.SetSize(nsedges);
384  rhe = 0;
385 
386  // On each rank of the group, locally determine if the shared edge is in
387  // the SubMesh.
388  for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
389  {
390  const int group_sz = parent_.gtopo.GetGroupSize(g);
391  MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
392  "Group size too large. Groups with more than 32 ranks are not supported, yet.");
393  const int* group_lproc = parent_.gtopo.GetGroup(g);
394 
395  const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
396  MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
397 
398  // rank id inside this group
399  const int my_group_id = my_group_id_ptr-group_lproc;
400 
401  for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
402  {
403  int ple, o;
404  parent_.GroupEdge(g, ge, ple, o);
405  int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
406  if (submesh_edge_id != -1)
407  {
408  rhe[se] |= 1 << my_group_id;
409  }
410  }
411  }
412 
413  // Compute the sum on the root rank and broadcast the result to all ranks.
414  sedge_comm.Reduce(rhe, GroupCommunicator::Sum);
415  sedge_comm.Bcast<int>(rhe, 0);
416 }
417 
418 void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
419 {
420  GroupCommunicator squad_comm(parent_.gtopo);
421  parent_.GetSharedQuadCommunicator(squad_comm);
422 
423  int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
424 
425  rhq.SetSize(nsquad);
426  rhq = 0;
427 
428  for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
429  {
430  for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
431  {
432  // Group size of a shared face is always 2
433 
434  int plq, o;
435  parent_.GroupQuadrilateral(g, gq, plq, o);
436  int submesh_face_id = parent_to_submesh_face_ids_[plq];
437  if (submesh_face_id != -1)
438  {
439  rhq[sq] = 1;
440  }
441  }
442  }
443 
444  // Compute the sum on the root rank and broadcast the result to all ranks.
445  squad_comm.Reduce(rhq, GroupCommunicator::Sum);
446  squad_comm.Bcast<int>(rhq, 0);
447 
448  GroupCommunicator stria_comm(parent_.gtopo);
449  parent_.GetSharedTriCommunicator(stria_comm);
450 
451  int nstria = stria_comm.GroupLDofTable().Size_of_connections();
452 
453  rht.SetSize(nstria);
454  rht = 0;
455 
456  for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
457  {
458  for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
459  {
460  // Group size of a shared face is always 2
461 
462  int plt, o;
463  parent_.GroupTriangle(g, gt, plt, o);
464  int submesh_face_id = parent_to_submesh_face_ids_[plt];
465  if (submesh_face_id != -1)
466  {
467  rht[st] = 1;
468  }
469  }
470  }
471 
472  // Compute the sum on the root rank and broadcast the result to all ranks.
473  stria_comm.Reduce(rht, GroupCommunicator::Sum);
474  stria_comm.Bcast<int>(rht, 0);
475 }
476 
477 
478 void ParSubMesh::AppendSharedVerticesGroups(ListOfIntegerSets &groups,
479  Array<int> &rhvtx)
480 {
481  IntegerSet group;
482 
483  for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
484  {
485  const int group_sz = parent_.gtopo.GetGroupSize(g);
486  MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
487  "Group size too large. Groups with more than 32 ranks are not supported, yet.");
488  const int* group_lproc = parent_.gtopo.GetGroup(g);
489 
490  const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
491  MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
492 
493  const int my_group_id = my_group_id_ptr-group_lproc;
494 
495  for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
496  {
497  // Returns the parents local vertex id
498  int plvtx = parent_.GroupVertex(g, gv);
499  int submesh_vtx = parent_to_submesh_vertex_ids_[plvtx];
500 
501  // Reusing the `rhvtx` array as shared vertex to group array.
502  if (submesh_vtx == -1)
503  {
504  // parent shared vertex is not in SubMesh
505  rhvtx[sv] = -1;
506  }
507  else if (rhvtx[sv] & ~(1 << my_group_id))
508  {
509  // shared vertex is present on this rank and others
510  MFEM_ASSERT(rhvtx[sv] & (1 << my_group_id), "error again");
511 
512  // determine which other ranks have the shared vertex
513  Array<int> &ranks = group;
514  ranks.SetSize(0);
515  for (int i = 0; i < group_sz; i++)
516  {
517  if ((rhvtx[sv] >> i) & 1)
518  {
519  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
520  }
521  }
522  MFEM_ASSERT(ranks.Size() >= 2, "internal error");
523 
524  rhvtx[sv] = groups.Insert(group) - 1;
525  }
526  else
527  {
528  // previously shared vertex is only present on this rank
529  rhvtx[sv] = -1;
530  }
531  }
532  }
533 }
534 
535 void ParSubMesh::AppendSharedEdgesGroups(ListOfIntegerSets &groups,
536  Array<int> &rhe)
537 {
538  IntegerSet group;
539 
540  for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
541  {
542  const int group_sz = parent_.gtopo.GetGroupSize(g);
543  MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
544  "Group size too large. Groups with more than 32 ranks are not supported, yet.");
545  const int* group_lproc = parent_.gtopo.GetGroup(g);
546 
547  const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
548  MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
549 
550  const int my_group_id = my_group_id_ptr-group_lproc;
551 
552  for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
553  {
554  int ple, o;
555  parent_.GroupEdge(g, ge, ple, o);
556  int submesh_edge = parent_to_submesh_edge_ids_[ple];
557 
558  // Reusing the `rhe` array as shared edge to group array.
559  if (submesh_edge == -1)
560  {
561  // parent shared edge is not in SubMesh
562  rhe[se] = -1;
563  }
564  else if (rhe[se] & ~(1 << my_group_id))
565  {
566  // shared edge is present on this rank and others
567 
568  // determine which other ranks have the shared edge
569  Array<int> &ranks = group;
570  ranks.SetSize(0);
571  for (int i = 0; i < group_sz; i++)
572  {
573  if ((rhe[se] >> i) & 1)
574  {
575  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
576  }
577  }
578  MFEM_ASSERT(ranks.Size() >= 2, "internal error");
579 
580  rhe[se] = groups.Insert(group) - 1;
581  }
582  else
583  {
584  // previously shared edge is only present on this rank
585  rhe[se] = -1;
586  }
587  }
588  }
589 }
590 
591 void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
592  Array<int>& rht, Array<int> &rhq)
593 {
594  IntegerSet quad_group;
595 
596  for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
597  {
598  const int* group_lproc = parent_.gtopo.GetGroup(g);
599  for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
600  {
601  const int group_sz = parent_.gtopo.GetGroupSize(g);
602  MFEM_ASSERT(group_sz == 2, "internal error");
603 
604  int plq, o;
605  parent_.GroupQuadrilateral(g, gq, plq, o);
606  int submesh_face_id = parent_to_submesh_face_ids_[plq];
607 
608  // Reusing the `rhq` array as shared face to group array.
609  if (submesh_face_id == -1)
610  {
611  // parent shared face is not in SubMesh
612  rhq[sq] = -1;
613  }
614  else if (rhq[sq] == group_sz)
615  {
616  // shared face is present on this rank and others
617 
618  // There can only be two ranks in this group sharing faces. Add
619  // all ranks to a new communication group.
620  Array<int> &ranks = quad_group;
621  ranks.SetSize(0);
622  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
623  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
624 
625  rhq[sq] = groups.Insert(quad_group) - 1;
626  }
627  else
628  {
629  // previously shared edge is only present on this rank
630  rhq[sq] = -1;
631  }
632  }
633  }
634 
635  IntegerSet tria_group;
636 
637  for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
638  {
639  const int* group_lproc = parent_.gtopo.GetGroup(g);
640  for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
641  {
642  const int group_sz = parent_.gtopo.GetGroupSize(g);
643  MFEM_ASSERT(group_sz == 2, "internal error");
644 
645  int plt, o;
646  parent_.GroupTriangle(g, gt, plt, o);
647  int submesh_face_id = parent_to_submesh_face_ids_[plt];
648 
649  // Reusing the `rht` array as shared face to group array.
650  if (submesh_face_id == -1)
651  {
652  // parent shared face is not in SubMesh
653  rht[st] = -1;
654  }
655  else if (rht[st] == group_sz)
656  {
657  // shared face is present on this rank and others
658 
659  // There can only be two ranks in this group sharing faces. Add
660  // all ranks to a new communication group.
661  Array<int> &ranks = tria_group;
662  ranks.SetSize(0);
663  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
664  ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
665 
666  rht[st] = groups.Insert(tria_group) - 1;
667  }
668  else
669  {
670  // previously shared edge is only present on this rank
671  rht[st] = -1;
672  }
673  }
674  }
675 }
676 
677 void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
678  int& nsverts)
679 {
680  group_svert.MakeI(ngroups);
681  for (int i = 0; i < rhvtx.Size(); i++)
682  {
683  if (rhvtx[i] >= 0)
684  {
685  group_svert.AddAColumnInRow(rhvtx[i]);
686  }
687  }
688 
689  group_svert.MakeJ();
690  nsverts = 0;
691  for (int i = 0; i < rhvtx.Size(); i++)
692  {
693  if (rhvtx[i] >= 0)
694  {
695  group_svert.AddConnection(rhvtx[i], nsverts++);
696  }
697  }
699 }
700 
701 void ParSubMesh::BuildEdgeGroup(int ngroups, const Array<int>& rhe,
702  int& nsedges)
703 {
704  group_sedge.MakeI(ngroups);
705  for (int i = 0; i < rhe.Size(); i++)
706  {
707  if (rhe[i] >= 0)
708  {
710  }
711  }
712 
713  group_sedge.MakeJ();
714  nsedges = 0;
715  for (int i = 0; i < rhe.Size(); i++)
716  {
717  if (rhe[i] >= 0)
718  {
719  group_sedge.AddConnection(rhe[i], nsedges++);
720  }
721  }
723 }
724 
725 void ParSubMesh::BuildFaceGroup(int ngroups, const Array<int>& rht,
726  int& nstrias, const Array<int>& rhq, int& nsquads)
727 {
728  group_squad.MakeI(ngroups);
729  for (int i = 0; i < rhq.Size(); i++)
730  {
731  if (rhq[i] >= 0)
732  {
734  }
735  }
736 
737  group_squad.MakeJ();
738  nsquads = 0;
739  for (int i = 0; i < rhq.Size(); i++)
740  {
741  if (rhq[i] >= 0)
742  {
743  group_squad.AddConnection(rhq[i], nsquads++);
744  }
745  }
747 
748  group_stria.MakeI(ngroups);
749  for (int i = 0; i < rht.Size(); i++)
750  {
751  if (rht[i] >= 0)
752  {
754  }
755  }
756 
757  group_stria.MakeJ();
758  nstrias = 0;
759  for (int i = 0; i < rht.Size(); i++)
760  {
761  if (rht[i] >= 0)
762  {
763  group_stria.AddConnection(rht[i], nstrias++);
764  }
765  }
767 }
768 
769 void ParSubMesh::BuildSharedVerticesMapping(const int nsverts,
770  const Array<int>& rhvtx)
771 {
772  svert_lvert.Reserve(nsverts);
773 
774  for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
775  {
776  for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
777  {
778  // Returns the parents local vertex id
779  int plvtx = parent_.GroupVertex(g, gv);
780  int submesh_vtx_id = parent_to_submesh_vertex_ids_[plvtx];
781  if ((submesh_vtx_id == -1) || (rhvtx[sv] == -1))
782  {
783  // parent shared vertex is not in SubMesh or is not shared
784  }
785  else
786  {
787  svert_lvert.Append(submesh_vtx_id);
788  }
789  }
790  }
791 }
792 
793 void ParSubMesh::BuildSharedEdgesMapping(const int sedges_ct,
794  const Array<int>& rhe)
795 {
796  shared_edges.Reserve(sedges_ct);
797  sedge_ledge.Reserve(sedges_ct);
798 
799  for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
800  {
801  for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
802  {
803  int ple, o;
804  parent_.GroupEdge(g, ge, ple, o);
805  int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
806  if ((submesh_edge_id == -1) || rhe[se] == -1)
807  {
808  // parent shared edge is not in SubMesh or is not shared
809  }
810  else
811  {
812  Array<int> vert;
813  parent_.GetEdgeVertices(ple, vert);
814  // Swap order of vertices if orientation in parent group is -1
815  int v0 = parent_to_submesh_vertex_ids_[vert[(1-o)/2]];
816  int v1 = parent_to_submesh_vertex_ids_[vert[(1+o)/2]];
817 
818  // The orienation of the shared edge relative to the local edge
819  // will be determined by whether v0 < v1 or v1 < v0
820  shared_edges.Append(new Segment(v0, v1, 1));
821  sedge_ledge.Append(submesh_edge_id);
822  }
823  }
824  }
825 }
826 
827 void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
828  const Array<int>& rht,
829  const int nsquads, const Array<int>& rhq)
830 {
831  shared_trias.Reserve(nstrias);
832  shared_quads.Reserve(nsquads);
833  sface_lface.Reserve(nstrias + nsquads);
834 
835  // sface_lface should list the triangular shared faces first
836  // followed by the quadrilateral shared faces.
837 
838  for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
839  {
840  for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
841  {
842  int plt, o;
843  parent_.GroupTriangle(g, gt, plt, o);
844  int submesh_face_id = parent_to_submesh_face_ids_[plt];
845  if ((submesh_face_id == -1) || rht[st] == -1)
846  {
847  // parent shared face is not in SubMesh or is not shared
848  }
849  else
850  {
851  Array<int> vert;
852 
853  GetFaceVertices(submesh_face_id, vert);
854 
855  int v0 = vert[0];
856  int v1 = vert[1];
857  int v2 = vert[2];
858 
859  // See Mesh::GetTriOrientation for info on interpretting "o"
860  switch (o)
861  {
862  case 1:
863  std::swap(v0,v1);
864  break;
865  case 3:
866  std::swap(v2,v0);
867  break;
868  case 5:
869  std::swap(v1,v2);
870  break;
871  default:
872  // Do nothing
873  break;
874  }
875 
876  shared_trias.Append(Vert3(v0, v1, v2));
877  sface_lface.Append(submesh_face_id);
878  }
879  }
880  }
881 
882  for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
883  {
884  for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
885  {
886  int plq, o;
887  parent_.GroupQuadrilateral(g, gq, plq, o);
888  int submesh_face_id = parent_to_submesh_face_ids_[plq];
889  if ((submesh_face_id == -1) || rhq[sq] == -1)
890  {
891  // parent shared face is not in SubMesh or is not shared
892  }
893  else
894  {
895  Array<int> vert;
896  GetFaceVertices(submesh_face_id, vert);
897 
898  int v0 = vert[0];
899  int v1 = vert[1];
900  int v2 = vert[2];
901  int v3 = vert[3];
902 
903  // See Mesh::GetQuadOrientation for info on interpretting "o"
904  switch (o)
905  {
906  case 1:
907  std::swap(v1,v3);
908  break;
909  case 3:
910  std::swap(v0,v1);
911  std::swap(v2,v3);
912  break;
913  case 5:
914  std::swap(v0,v2);
915  break;
916  case 7:
917  std::swap(v0,v3);
918  std::swap(v1,v2);
919  break;
920  default:
921  // Do nothing
922  break;
923  }
924 
925  shared_quads.Append(Vert4(v0, v1, v2, v3));
926  sface_lface.Append(submesh_face_id);
927  }
928  }
929  }
930 }
931 
933 {
934  ParTransferMap map(src, dst);
935  map.Transfer(src, dst);
936 }
937 
939  const ParGridFunction &dst)
940 {
941  return ParTransferMap(src, dst);
942 }
943 
944 } // namespace mfem
945 
946 #endif // MFEM_USE_MPI
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
static ParSubMesh CreateFromBoundary(const ParMesh &parent, Array< int > &boundary_attributes)
Create a surface ParSubMesh from it&#39;s parent.
Definition: psubmesh.cpp:32
Array< int > GetFaceToBdrElMap() const
Definition: mesh.cpp:1445
void GetSharedVertexCommunicator(int ordering, GroupCommunicator &svert_comm) const
Get the shared vertices GroupCommunicator.
Definition: pmesh.cpp:1661
int NRanks
Definition: pmesh.hpp:38
void GetSharedQuadCommunicator(int ordering, GroupCommunicator &squad_comm) const
Get the shared face quadrilaterals GroupCommunicator.
Definition: pmesh.cpp:1685
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition: table.cpp:81
static int ComposeQuadOrientations(int ori_a_b, int ori_b_c)
Definition: mesh.cpp:5999
void GetSharedTriCommunicator(int ordering, GroupCommunicator &stria_comm) const
Get the shared face triangles GroupCommunicator.
Definition: pmesh.cpp:1709
Array< Element * > boundary
Definition: mesh.hpp:91
int Dimension() const
Dimension of the reference space used within the elements.
Definition: mesh.hpp:1020
int NumOfEdges
Definition: mesh.hpp:70
Array< int > sface_lface
Definition: pmesh.hpp:78
void GetVertexToVertexTable(DSTable &) const
Definition: mesh.cpp:6725
bool Nonconforming() const
Definition: mesh.hpp:1969
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition: array.cpp:68
int NumOfElements
Definition: mesh.hpp:69
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition: mesh.hpp:1297
int GetNeighborRank(int i) const
Return the MPI rank of neighbor &#39;i&#39;.
int GetNEdges() const
Return the number of edges.
Definition: mesh.hpp:1092
Array< Vert3 > shared_trias
Definition: pmesh.hpp:65
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition: mesh.cpp:5951
void GroupEdge(int group, int i, int &edge, int &o) const
Definition: pmesh.cpp:1607
Array< Element * > faces
Definition: mesh.hpp:92
Array< int > sedge_ledge
Definition: pmesh.hpp:76
Table group_stria
Definition: pmesh.hpp:71
Subdomain representation of a topological parent in another ParMesh.
Definition: psubmesh.hpp:51
std::function< double(const Vector &)> f(double mass_coeff)
Definition: lor_mms.hpp:30
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition: mesh.hpp:1083
void ExchangeFaceNbrData()
Definition: pmesh.cpp:2117
void GetBdrElementFace(int i, int *f, int *o) const
Return the index and the orientation of the face of bdr element i. (3D)
Definition: mesh.cpp:6565
MPI_Comm MyComm
Definition: pmesh.hpp:37
void Finalize(bool refine=false, bool fix_orientation=false) override
Finalize the construction of a general Mesh.
Definition: pmesh.cpp:1516
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
Array< Element * > shared_edges
Definition: pmesh.hpp:61
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:759
static ParSubMesh CreateFromDomain(const ParMesh &parent, Array< int > &domain_attributes)
Create a domain ParSubMesh from it&#39;s parent.
Definition: psubmesh.cpp:26
int GroupNVertices(int group) const
Definition: pmesh.hpp:395
int GroupVertex(int group, int i) const
Definition: pmesh.hpp:400
void AddConnection(int r, int c)
Definition: table.hpp:80
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition: mesh.cpp:7192
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition: mesh.cpp:6382
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition: mesh.hpp:1311
int GetNGroups() const
Definition: pmesh.hpp:392
ParTransferMap represents a mapping of degrees of freedom from a source ParGridFunction to a destinat...
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition: array.hpp:159
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition: mesh.hpp:1293
const int * GetGroup(int g) const
Return a pointer to a list of neighbors for a given group. Neighbor 0 is the local processor...
int GetElementToEdgeTable(Table &, Array< int > &)
Definition: mesh.cpp:6750
void GetSharedEdgeCommunicator(int ordering, GroupCommunicator &sedge_comm) const
Get the shared edges GroupCommunicator.
Definition: pmesh.cpp:1637
int NumOfBdrElements
Definition: mesh.hpp:69
Table * el_to_edge
Definition: mesh.hpp:220
MPI_Comm GetComm() const
Definition: pmesh.hpp:351
Array< Vert4 > shared_quads
Definition: pmesh.hpp:66
int GroupNTriangles(int group) const
Definition: pmesh.hpp:397
int GetMyRank() const
Definition: pmesh.hpp:353
int GetBdrAttribute(int i) const
Return the attribute of boundary element i.
Definition: mesh.hpp:1196
void Transfer(const ParGridFunction &src, ParGridFunction &dst) const
Transfer the source ParGridFunction to the destination ParGridFunction.
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition: mesh.hpp:275
static const int GENERATED_ATTRIBUTE
Definition: submesh.hpp:52
std::tuple< Array< int >, Array< int > > AddElementsToMesh(const Mesh &parent, Mesh &mesh, const Array< int > &attributes, bool from_boundary)
Given a Mesh parent and another Mesh mesh using the list of attributes in attributes, this function adds matching elements with those attributes from parent to mesh.
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition: mesh.cpp:2945
void AddAColumnInRow(int r)
Definition: table.hpp:77
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition: mesh.hpp:1853
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:687
Array< int > BuildFaceMap(const Mesh &pm, const Mesh &sm, const Array< int > &parent_element_ids)
Given two meshes that have a parent to SubMesh relationship create a face map, using a SubMesh to par...
void InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
Begin construction of a mesh.
Definition: mesh.cpp:1603
void SetAttributes() override
Determine the sets of unique attribute values in domain and boundary elements.
Definition: pmesh.cpp:1580
void ShiftUpI()
Definition: table.cpp:115
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition: mesh.hpp:1023
Table * bel_to_edge
Definition: mesh.hpp:224
From
Indicator from which part of the parent Mesh the SubMesh is created.
Definition: submesh.hpp:46
Array< int > be_to_edge
Definition: mesh.hpp:223
int GetNRanks() const
Definition: pmesh.hpp:352
int GetGroupSize(int g) const
Get the number of processors in a group.
Table group_sedge
Definition: pmesh.hpp:70
static ParTransferMap CreateTransferMap(const ParGridFunction &src, const ParGridFunction &dst)
Create a Transfer Map object.
Definition: psubmesh.cpp:938
void MakeJ()
Definition: table.cpp:91
Table group_svert
Shared objects in each group.
Definition: pmesh.hpp:69
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition: pmesh.cpp:2056
bool IsBoundary() const
Return true if the face is a boundary face.
Definition: mesh.hpp:1706
ParSubMesh()=delete
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
FaceInformation GetFaceInformation(int f) const
Definition: mesh.cpp:1138
int Dim
Definition: mesh.hpp:66
void SetComm(MPI_Comm comm)
Set the MPI communicator to &#39;comm&#39;.
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition: mesh.hpp:1095
int GroupNEdges(int group) const
Definition: pmesh.hpp:396
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition: mesh.cpp:5862
Array< int > svert_lvert
Shared to local index mapping.
Definition: pmesh.hpp:75
int GroupNQuadrilaterals(int group) const
Definition: pmesh.hpp:398
void GetNodes(Vector &node_coord) const
Definition: mesh.cpp:8302
int MyRank
Definition: pmesh.hpp:38
int spaceDim
Definition: mesh.hpp:67
Class for parallel grid function.
Definition: pgridfunc.hpp:32
static void Transfer(const ParGridFunction &src, ParGridFunction &dst)
Transfer the dofs of a ParGridFunction.
Definition: psubmesh.cpp:932
void GroupQuadrilateral(int group, int i, int &face, int &o) const
Definition: pmesh.cpp:1626
int NumOfVertices
Definition: mesh.hpp:69
Array< int > be_to_face
Definition: mesh.hpp:225
GroupTopology gtopo
Definition: pmesh.hpp:375
void GroupTriangle(int group, int i, int &face, int &o) const
Definition: pmesh.cpp:1615
Class for parallel meshes.
Definition: pmesh.hpp:32
static int ComposeTriOrientations(int ori_a_b, int ori_b_c)
Definition: mesh.cpp:5922
Table group_squad
Definition: pmesh.hpp:72
int NumOfFaces
Definition: mesh.hpp:70