MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
psubmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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
13
14#ifdef MFEM_USE_MPI
15
16#include <iostream>
17#include <unordered_set>
18#include <algorithm>
19#include "psubmesh.hpp"
20#include "pncsubmesh.hpp"
21#include "submesh_utils.hpp"
22#include "../segment.hpp"
23
24namespace mfem
25{
26
28 const Array<int> &domain_attributes)
29{
30 return ParSubMesh(parent, SubMesh::From::Domain, domain_attributes);
31}
32
34 const Array<int> &boundary_attributes)
35{
36 return ParSubMesh(parent, SubMesh::From::Boundary, boundary_attributes);
37}
38
40 const Array<int> &attributes) : parent_(parent), from_(from),
41 attributes_(attributes)
42{
43 MyComm = parent.GetComm();
44 NRanks = parent.GetNRanks();
45 MyRank = parent.GetMyRank();
46
47 // This violation of const-ness may be justified in this instance because the
48 // exchange of face neighbor information only establishes or updates derived
49 // information without altering the primary mesh information, i.e., the
50 // topology, geometry, or region attributes.
51 const_cast<ParMesh&>(parent).ExchangeFaceNbrData();
52
53 if (from == SubMesh::From::Domain)
54 {
55 InitMesh(parent.Dimension(), parent.SpaceDimension(), 0, 0, 0);
56
57 std::tie(parent_vertex_ids_,
58 parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
59 attributes_);
60 }
61 else if (from == SubMesh::From::Boundary)
62 {
63 InitMesh(parent.Dimension() - 1, parent.SpaceDimension(), 0, 0, 0);
64
65 std::tie(parent_vertex_ids_,
66 parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
67 attributes_, true);
68 }
69
70 parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
71 parent_to_submesh_vertex_ids_ = -1;
72 for (int i = 0; i < parent_vertex_ids_.Size(); i++)
73 {
74 parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
75 }
76
77 parent_to_submesh_element_ids_.SetSize(from == From::Boundary ? parent.GetNBE()
78 : parent.GetNE());
79 parent_to_submesh_element_ids_ = -1;
80 for (int i = 0; i < parent_element_ids_.Size(); i++)
81 {
82 parent_to_submesh_element_ids_[parent_element_ids_[i]] = i;
83 }
84
85 // Don't let boundary elements get generated automatically. This would
86 // generate boundary elements on each rank locally, which is topologically
87 // wrong for the distributed SubMesh.
88 FinalizeTopology(false);
89
90 if (parent.Nonconforming())
91 {
92 pncmesh = new ParNCSubMesh(*this, *parent.pncmesh, from, attributes);
93 pncsubmesh_ = dynamic_cast<ParNCSubMesh*>(pncmesh);
97
98 // Update the submesh to parent vertex mapping, NCSubMesh reordered the
99 // vertices so the map to parent is no longer valid.
100 parent_to_submesh_vertex_ids_ = -1;
101 for (int i = 0; i < parent_vertex_ids_.Size(); i++)
102 {
103 // vertex -> node -> parent node -> parent vertex
104 auto node = pncsubmesh_->vertex_nodeId[i];
105 auto parent_node = pncsubmesh_->parent_node_ids_[node];
106 auto parent_vertex = parent.pncmesh->GetNodeVertex(parent_node);
107 parent_vertex_ids_[i] = parent_vertex;
108 parent_to_submesh_vertex_ids_[parent_vertex] = i;
109 }
112 }
113
115 DSTable v2v(parent_.GetNV());
116 parent_.GetVertexToVertexTable(v2v);
117 for (int i = 0; i < NumOfEdges; i++)
118 {
119 Array<int> lv;
120 GetEdgeVertices(i, lv);
121
122 // Find vertices/edge in parent mesh
123 int parent_edge_id = v2v(parent_vertex_ids_[lv[0]],
124 parent_vertex_ids_[lv[1]]);
125 parent_edge_ids_.Append(parent_edge_id);
126 }
127
128 parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
129 parent_to_submesh_edge_ids_ = -1;
130 for (int i = 0; i < parent_edge_ids_.Size(); i++)
131 {
132 parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
133 }
134
135 if (Dim == 3)
136 {
137 parent_face_ids_ = SubMeshUtils::BuildFaceMap(parent_, *this,
138 parent_element_ids_);
139
140 parent_to_submesh_face_ids_.SetSize(parent.GetNFaces());
141 parent_to_submesh_face_ids_ = -1;
142 for (int i = 0; i < parent_face_ids_.Size(); i++)
143 {
144 parent_to_submesh_face_ids_[parent_face_ids_[i]] = i;
145 }
146
147 parent_face_ori_.SetSize(NumOfFaces);
148 for (int i = 0; i < NumOfFaces; i++)
149 {
150 Array<int> sub_vert;
151 GetFaceVertices(i, sub_vert);
152
153 Array<int> sub_par_vert(sub_vert.Size());
154 for (int j = 0; j < sub_vert.Size(); j++)
155 {
156 sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
157 }
158
159 Array<int> par_vert;
160 parent.GetFaceVertices(parent_face_ids_[i], par_vert);
161
162 if (par_vert.Size() == 3)
163 {
164 parent_face_ori_[i] = GetTriOrientation(par_vert, sub_par_vert);
165 }
166 else
167 {
168 parent_face_ori_[i] = GetQuadOrientation(par_vert, sub_par_vert);
169 }
170 }
171 }
172 else if (Dim == 2)
173 {
174 parent_face_ori_.SetSize(NumOfElements);
175
176 for (int i = 0; i < NumOfElements; i++)
177 {
178 Array<int> sub_vert;
179 GetElementVertices(i, sub_vert);
180
181 Array<int> sub_par_vert(sub_vert.Size());
182 for (int j = 0; j < sub_vert.Size(); j++)
183 {
184 sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
185 }
186
187 Array<int> par_vert;
188 int be_ori = 0;
189 if (from == SubMesh::From::Boundary)
190 {
191 parent.GetBdrElementVertices(parent_element_ids_[i], par_vert);
192
193 int f = -1;
194 parent.GetBdrElementFace(parent_element_ids_[i], &f, &be_ori);
195 }
196 else
197 {
198 parent.GetElementVertices(parent_element_ids_[i], par_vert);
199 }
200
201 if (par_vert.Size() == 3)
202 {
203 int se_ori = GetTriOrientation(par_vert, sub_par_vert);
204 parent_face_ori_[i] = ComposeTriOrientations(be_ori, se_ori);
205 }
206 else
207 {
208 int se_ori = GetQuadOrientation(par_vert, sub_par_vert);
209 parent_face_ori_[i] = ComposeQuadOrientations(be_ori, se_ori);
210 }
211 }
212 }
213
214 ListOfIntegerSets groups;
215 IntegerSet group;
216 // the first group is the local one
217 group.Recreate(1, &MyRank);
218 groups.Insert(group);
219
220 // Every rank containing elements of the ParSubMesh attributes now has a
221 // local ParSubMesh. We have to connect the local meshes and assign global
222 // boundaries correctly.
223 Array<int> rhvtx;
224 FindSharedVerticesRanks(rhvtx);
225 AppendSharedVerticesGroups(groups, rhvtx);
226
227 Array<int> rhe;
228 FindSharedEdgesRanks(rhe);
229 AppendSharedEdgesGroups(groups, rhe);
230
231 Array<int> rhq, rht;
232 if (Dim == 3)
233 {
234 FindSharedFacesRanks(rht, rhq);
235 AppendSharedFacesGroups(groups, rht, rhq);
236 }
237
238
239 // Build the group communication topology
241 gtopo.Create(groups, 822);
242 int ngroups = groups.Size()-1;
243
244 int nsverts, nsedges, nstrias, nsquads;
245 BuildVertexGroup(ngroups, rhvtx, nsverts);
246 BuildEdgeGroup(ngroups, rhe, nsedges);
247 if (Dim == 3)
248 {
249 BuildFaceGroup(ngroups, rht, nstrias, rhq, nsquads);
250 }
251 else
252 {
253 group_stria.MakeI(ngroups);
256
257 group_squad.MakeI(ngroups);
260 }
261
262 BuildSharedVerticesMapping(nsverts, rhvtx);
263 BuildSharedEdgesMapping(nsedges, rhe);
264 if (Dim == 3)
265 {
266 BuildSharedFacesMapping(nstrias, rht, nsquads, rhq);
267 }
268
270
272 (from == SubMesh::From::Domain)
273 ? FindGhostBoundaryElementAttributes()
274 : std::unordered_map<int,int> {});
275
276 if (Dim > 1)
277 {
278 if (!el_to_edge) { el_to_edge = new Table; }
279 NumOfEdges = GetElementToEdgeTable(*el_to_edge);
280 }
281 if (Dim > 2)
282 {
283 GetElementToFaceTable();
284 }
285
286 // If the parent ParMesh has nodes and therefore is defined on a higher order
287 // geometry, we define this ParSubMesh as a curved ParSubMesh and transfer
288 // the GridFunction from the parent ParMesh to the ParSubMesh.
289 const GridFunction *parent_nodes = parent_.GetNodes();
290 if (parent_nodes)
291 {
292 const FiniteElementSpace *parent_fes = parent_nodes->FESpace();
293
294 SetCurvature(
295 parent_fes->FEColl()->GetOrder(),
296 parent_fes->IsDGSpace(),
297 spaceDim,
298 parent_fes->GetOrdering());
299
300 const ParGridFunction* pn = dynamic_cast<const ParGridFunction*>
301 (parent_.GetNodes());
302 MFEM_ASSERT(pn,
303 "Internal error. Object is supposed to be ParGridFunction.");
304
305 ParGridFunction* n = dynamic_cast<ParGridFunction*>
306 (this->GetNodes());
307 MFEM_ASSERT(n,
308 "Internal error. Object is supposed to be ParGridFunction.");
309
310 Transfer(*pn, *n);
311 }
312 SetAttributes();
313 Finalize();
314}
315
316void ParSubMesh::FindSharedVerticesRanks(Array<int> &rhvtx)
317{
318 // create a GroupCommunicator on the shared vertices
319 GroupCommunicator svert_comm(parent_.gtopo);
320 parent_.GetSharedVertexCommunicator(svert_comm);
321 // Number of shared vertices
322 int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
323
324 rhvtx.SetSize(nsvtx);
325 rhvtx = 0;
326
327 // On each rank of the group, locally determine if the shared vertex is in
328 // the SubMesh.
329 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
330 {
331 const int group_sz = parent_.gtopo.GetGroupSize(g);
332 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
333 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
334 const int* group_lproc = parent_.gtopo.GetGroup(g);
335
336 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
337 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
338
339 const int my_group_id = my_group_id_ptr-group_lproc;
340
341 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
342 {
343 int plvtx = parent_.GroupVertex(g, gv);
344 int submesh_vertex_id = parent_to_submesh_vertex_ids_[plvtx];
345 if (submesh_vertex_id != -1)
346 {
347 rhvtx[sv] |= 1 << my_group_id;
348 }
349 }
350 }
351
352
353 // Compute the sum on the root rank and broadcast the result to all ranks.
354 svert_comm.Reduce(rhvtx, GroupCommunicator::Sum);
355 svert_comm.Bcast<int>(rhvtx, 0);
356}
357
358void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
359{
360 // create a GroupCommunicator on the shared edges
361 GroupCommunicator sedge_comm(parent_.gtopo);
362 parent_.GetSharedEdgeCommunicator(sedge_comm);
363
364 int nsedges = sedge_comm.GroupLDofTable().Size_of_connections();
365
366 // see rhvtx description
367 rhe.SetSize(nsedges);
368 rhe = 0;
369
370 // On each rank of the group, locally determine if the shared edge is in the
371 // SubMesh.
372 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
373 {
374 const int group_sz = parent_.gtopo.GetGroupSize(g);
375 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
376 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
377 const int* group_lproc = parent_.gtopo.GetGroup(g);
378
379 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
380 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
381
382 // rank id inside this group
383 const int my_group_id = my_group_id_ptr-group_lproc;
384
385 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
386 {
387 int ple = parent_.GroupEdge(g, ge);
388 int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
389 if (submesh_edge_id != -1)
390 {
391 rhe[se] |= 1 << my_group_id;
392 }
393 }
394 }
395
396
397 // Compute the sum on the root rank and broadcast the result to all ranks.
398 sedge_comm.Reduce(rhe, GroupCommunicator::Sum);
399 sedge_comm.Bcast<int>(rhe, 0);
400}
401
402void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
403{
404 GroupCommunicator stria_comm(parent_.gtopo);
405 parent_.GetSharedTriCommunicator(stria_comm);
406 int nstria = stria_comm.GroupLDofTable().Size_of_connections();
407 rht.SetSize(nstria);
408 rht = 0;
409
410 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
411 {
412 MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
413 || parent_.GroupNTriangles(g) == 0,
414 parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNTriangles(g));
415 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
416 {
417 // Group size of a shared face is always 2
418 int plt = parent_.GroupTriangle(g, gt);
419 int submesh_face_id = parent_to_submesh_face_ids_[plt];
420 if (submesh_face_id != -1)
421 {
422 rht[st] = 1;
423 }
424 }
425 }
426
427 // Compute the sum on the root rank and broadcast the result to all ranks.
428 stria_comm.Reduce(rht, GroupCommunicator::Sum);
429 stria_comm.Bcast<int>(rht, 0);
430
431 GroupCommunicator squad_comm(parent_.gtopo);
432 parent_.GetSharedQuadCommunicator(squad_comm);
433 int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
434 rhq.SetSize(nsquad);
435 rhq = 0;
436
437 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
438 {
439 MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
440 || parent_.GroupNQuadrilaterals(g) == 0,
441 parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNQuadrilaterals(g));
442 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
443 {
444 // Group size of a shared face is always 2
445 int plq = parent_.GroupQuadrilateral(g, gq);
446 int submesh_face_id = parent_to_submesh_face_ids_[plq];
447 if (submesh_face_id != -1)
448 {
449 rhq[sq] = 1;
450 }
451 }
452 }
453
454 // Compute the sum on the root rank and broadcast the result to all ranks.
455 squad_comm.Reduce(rhq, GroupCommunicator::Sum);
456 squad_comm.Bcast<int>(rhq, 0);
457}
458
459
460void ParSubMesh::AppendSharedVerticesGroups(ListOfIntegerSets &groups,
461 Array<int> &rhvtx)
462{
463 IntegerSet group;
464
465 // g = 0 corresponds to the singleton group of each rank alone.
466 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
467 {
468 const int group_sz = parent_.gtopo.GetGroupSize(g);
469 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
470 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
471 const int* group_lproc = parent_.gtopo.GetGroup(g);
472
473 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
474 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
475
476 const int my_group_id = my_group_id_ptr-group_lproc;
477
478 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
479 {
480 // Returns the parents local vertex id
481 int plvtx = parent_.GroupVertex(g, gv);
482 int submesh_vtx = parent_to_submesh_vertex_ids_[plvtx];
483
484 // Reusing the `rhvtx` array as shared vertex to group array.
485 if (submesh_vtx == -1)
486 {
487 // parent shared vertex is not in SubMesh
488 rhvtx[sv] = -1;
489 }
490 else if (rhvtx[sv] & ~(1 << my_group_id))
491 {
492 // shared vertex is present on this rank and others
493 MFEM_ASSERT(rhvtx[sv] & (1 << my_group_id), "error again");
494
495 // determine which other ranks have the shared vertex
496 Array<int> &ranks = group;
497 ranks.SetSize(0);
498 for (int i = 0; i < group_sz; i++)
499 {
500 if ((rhvtx[sv] >> i) & 1)
501 {
502 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
503 }
504 }
505 MFEM_ASSERT(ranks.Size() >= 2, "internal error");
506
507 rhvtx[sv] = groups.Insert(group) - 1;
508 }
509 else
510 {
511 // previously shared vertex is only present on this rank
512 rhvtx[sv] = -1;
513 }
514 }
515 }
516}
517
518void ParSubMesh::AppendSharedEdgesGroups(ListOfIntegerSets &groups,
519 Array<int> &rhe)
520{
521 IntegerSet group;
522
523 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
524 {
525 const int group_sz = parent_.gtopo.GetGroupSize(g);
526 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
527 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
528 const int* group_lproc = parent_.gtopo.GetGroup(g);
529
530 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
531 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
532
533 const int my_group_id = my_group_id_ptr-group_lproc;
534
535 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
536 {
537 int ple = parent_.GroupEdge(g, ge);
538 int submesh_edge = parent_to_submesh_edge_ids_[ple];
539
540 // Reusing the `rhe` array as shared edge to group array.
541 if (submesh_edge == -1)
542 {
543 // parent shared edge is not in SubMesh
544 rhe[se] = -1;
545 }
546 else if (rhe[se] & ~(1 << my_group_id))
547 {
548 // shared edge is present on this rank and others
549
550 // determine which other ranks have the shared edge
551 Array<int> &ranks = group;
552 ranks.SetSize(0);
553 for (int i = 0; i < group_sz; i++)
554 {
555 if ((rhe[se] >> i) & 1)
556 {
557 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
558 }
559 }
560 MFEM_ASSERT(ranks.Size() >= 2, "internal error");
561
562 rhe[se] = groups.Insert(group) - 1;
563 }
564 else
565 {
566 // previously shared edge is only present on this rank
567 rhe[se] = -1;
568 }
569 }
570 }
571}
572
573void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
574 Array<int>& rht, Array<int> &rhq)
575{
576 IntegerSet quad_group;
577
578 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
579 {
580 const int* group_lproc = parent_.gtopo.GetGroup(g);
581 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
582 {
583 const int group_sz = parent_.gtopo.GetGroupSize(g);
584 MFEM_ASSERT(group_sz == 2, "internal error");
585
586 int plq = parent_.GroupQuadrilateral(g, gq);
587 int submesh_face_id = parent_to_submesh_face_ids_[plq];
588
589 // Reusing the `rhq` array as shared face to group array.
590 if (submesh_face_id == -1)
591 {
592 // parent shared face is not in SubMesh
593 rhq[sq] = -1;
594 }
595 else if (rhq[sq] == group_sz)
596 {
597 // shared face is present on this rank and others
598
599 // There can only be two ranks in this group sharing faces. Add all
600 // ranks to a new communication group.
601 Array<int> &ranks = quad_group;
602 ranks.SetSize(0);
603 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
604 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
605
606 rhq[sq] = groups.Insert(quad_group) - 1;
607 }
608 else
609 {
610 // previously shared edge is only present on this rank
611 rhq[sq] = -1;
612 }
613 }
614 }
615
616 IntegerSet tria_group;
617
618 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
619 {
620 const int* group_lproc = parent_.gtopo.GetGroup(g);
621 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
622 {
623 const int group_sz = parent_.gtopo.GetGroupSize(g);
624 MFEM_ASSERT(group_sz == 2, "internal error");
625
626 int plt = parent_.GroupTriangle(g, gt);
627 int submesh_face_id = parent_to_submesh_face_ids_[plt];
628
629 // Reusing the `rht` array as shared face to group array.
630 if (submesh_face_id == -1)
631 {
632 // parent shared face is not in SubMesh
633 rht[st] = -1;
634 }
635 else if (rht[st] == group_sz)
636 {
637 // shared face is present on this rank and others
638
639 // There can only be two ranks in this group sharing faces. Add all
640 // ranks to a new communication group.
641 Array<int> &ranks = tria_group;
642 ranks.SetSize(0);
643 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
644 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
645
646 rht[st] = groups.Insert(tria_group) - 1;
647 }
648 else
649 {
650 // previously shared edge is only present on this rank
651 rht[st] = -1;
652 }
653 }
654 }
655}
656
657void BuildGroup(Table &group, int ngroups, const Array<int>& rh, int &ns)
658{
659 group.MakeI(ngroups);
660 for (int i = 0; i < rh.Size(); i++)
661 {
662 if (rh[i] >= 0)
663 {
664 group.AddAColumnInRow(rh[i]);
665 }
666 }
667
668 group.MakeJ();
669 ns = 0;
670 for (int i = 0; i < rh.Size(); i++)
671 {
672 if (rh[i] >= 0)
673 {
674 group.AddConnection(rh[i], ns++);
675 }
676 }
677 group.ShiftUpI();
678}
679
680void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
681 int& nsverts)
682{
683 BuildGroup(group_svert, ngroups, rhvtx, nsverts);
684}
685
686void ParSubMesh::BuildEdgeGroup(int ngroups, const Array<int>& rhe,
687 int& nsedges)
688{
689 BuildGroup(group_sedge, ngroups, rhe, nsedges);
690}
691
692void ParSubMesh::BuildFaceGroup(int ngroups, const Array<int>& rht,
693 int& nstrias, const Array<int>& rhq, int& nsquads)
694{
695 BuildGroup(group_squad, ngroups, rhq, nsquads);
696 BuildGroup(group_stria, ngroups, rht, nstrias);
697}
698
699void ParSubMesh::BuildSharedVerticesMapping(const int nsverts,
700 const Array<int>& rhvtx)
701{
702 svert_lvert.Reserve(nsverts);
703
704 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
705 {
706 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
707 {
708 // Returns the parents local vertex id
709 int plvtx = parent_.GroupVertex(g, gv);
710 int submesh_vtx_id = parent_to_submesh_vertex_ids_[plvtx];
711 if ((submesh_vtx_id == -1) || (rhvtx[sv] == -1))
712 {
713 // parent shared vertex is not in SubMesh or is not shared
714 }
715 else
716 {
717 svert_lvert.Append(submesh_vtx_id);
718 }
719 }
720 }
721}
722
723void ParSubMesh::BuildSharedEdgesMapping(const int sedges_ct,
724 const Array<int>& rhe)
725{
726 shared_edges.Reserve(sedges_ct);
727 sedge_ledge.Reserve(sedges_ct);
728
729 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
730 {
731 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
732 {
733 int ple, o;
734 parent_.GroupEdge(g, ge, ple, o);
735 int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
736 if ((submesh_edge_id == -1) || rhe[se] == -1)
737 {
738 // parent shared edge is not in SubMesh or is not shared
739 }
740 else
741 {
742 Array<int> vert;
743 parent_.GetEdgeVertices(ple, vert);
744 // Swap order of vertices if orientation in parent group is -1
745 int v0 = parent_to_submesh_vertex_ids_[vert[(1-o)/2]];
746 int v1 = parent_to_submesh_vertex_ids_[vert[(1+o)/2]];
747
748 // The orienation of the shared edge relative to the local edge will
749 // be determined by whether v0 < v1 or v1 < v0
750 shared_edges.Append(new Segment(v0, v1, 1));
751 sedge_ledge.Append(submesh_edge_id);
752 }
753 }
754 }
755}
756
757void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
758 const Array<int>& rht,
759 const int nsquads, const Array<int>& rhq)
760{
761 shared_trias.Reserve(nstrias);
762 shared_quads.Reserve(nsquads);
763 sface_lface.Reserve(nstrias + nsquads);
764
765 // sface_lface should list the triangular shared faces first followed by the
766 // quadrilateral shared faces.
767 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
768 {
769 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
770 {
771 int plt, o;
772 parent_.GroupTriangle(g, gt, plt, o);
773 int submesh_face_id = parent_to_submesh_face_ids_[plt];
774 if ((submesh_face_id == -1) || rht[st] == -1)
775 {
776 // parent shared face is not in SubMesh or is not shared
777 }
778 else
779 {
780 Array<int> vert;
781
782 GetFaceVertices(submesh_face_id, vert);
783
784 int v0 = vert[0];
785 int v1 = vert[1];
786 int v2 = vert[2];
787
788 // See Mesh::GetTriOrientation for info on interpretting "o"
789 switch (o)
790 {
791 case 1:
792 std::swap(v0,v1);
793 break;
794 case 3:
795 std::swap(v2,v0);
796 break;
797 case 5:
798 std::swap(v1,v2);
799 break;
800 default:
801 // Do nothing
802 break;
803 }
804
805 shared_trias.Append(Vert3(v0, v1, v2));
806 sface_lface.Append(submesh_face_id);
807 }
808 }
809 }
810
811 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
812 {
813 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
814 {
815 int plq, o;
816 parent_.GroupQuadrilateral(g, gq, plq, o);
817 int submesh_face_id = parent_to_submesh_face_ids_[plq];
818 if ((submesh_face_id == -1) || rhq[sq] == -1)
819 {
820 // parent shared face is not in SubMesh or is not shared
821 }
822 else
823 {
824 Array<int> vert;
825 GetFaceVertices(submesh_face_id, vert);
826
827 int v0 = vert[0];
828 int v1 = vert[1];
829 int v2 = vert[2];
830 int v3 = vert[3];
831
832 // See Mesh::GetQuadOrientation for info on interpreting "o"
833 switch (o)
834 {
835 case 1:
836 std::swap(v1,v3);
837 break;
838 case 3:
839 std::swap(v0,v1);
840 std::swap(v2,v3);
841 break;
842 case 5:
843 std::swap(v0,v2);
844 break;
845 case 7:
846 std::swap(v0,v3);
847 std::swap(v1,v2);
848 break;
849 default:
850 // Do nothing
851 break;
852 }
853
854 shared_quads.Append(Vert4(v0, v1, v2, v3));
855 sface_lface.Append(submesh_face_id);
856 }
857 }
858 }
859}
860
861std::unordered_map<int, int>
862ParSubMesh::FindGhostBoundaryElementAttributes() const
863{
864 // Loop over shared faces in the parent mesh, find their attributes if they
865 // exist, and map to local faces in the submesh.
866 std::unordered_map<int,int> lface_boundary_attribute;
867 const auto &face_to_be = parent_.GetFaceToBdrElMap();
868 if (Dim == 3)
869 {
870 GroupCommunicator squad_comm(parent_.gtopo);
871 parent_.GetSharedQuadCommunicator(squad_comm);
872 int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
873
874 GroupCommunicator stria_comm(parent_.gtopo);
875 parent_.GetSharedTriCommunicator(stria_comm);
876 int nstria = stria_comm.GroupLDofTable().Size_of_connections();
877
878 Array<int> stba(nstria), sqba(nsquad);
879 Array<int> parent_ltface(nstria), parent_lqface(nsquad);
880 stba = 0; sqba = 0;
881 parent_ltface = -1; parent_lqface = -1;
882 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
883 {
884 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
885 {
886 // Group size of a shared face is always 2
887 int plt = parent_.GroupTriangle(g, gt);
888 auto pbe = face_to_be[plt];
889 if (pbe >= 0)
890 {
891 stba[st] = parent_.GetBdrAttribute(pbe);
892 }
893 parent_ltface[st] = plt;
894 }
895 }
896 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
897 {
898 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
899 {
900 // Group size of a shared face is always 2
901 int plq = parent_.GroupQuadrilateral(g, gq);
902 auto pbe = face_to_be[plq];
903 if (pbe >= 0)
904 {
905 sqba[sq] = parent_.GetBdrAttribute(pbe);
906 }
907 parent_lqface[sq] = plq;
908 }
909 }
910#ifdef MFEM_DEBUG
911 auto pre_stba = stba;
912 auto pre_sqba = sqba;
913#endif
914 stria_comm.Reduce(stba, GroupCommunicator::Sum);
915 stria_comm.Bcast<int>(stba, 0);
916 squad_comm.Reduce(sqba, GroupCommunicator::Sum);
917 squad_comm.Bcast<int>(sqba, 0);
918#ifdef MFEM_DEBUG
919 {
920 Array<int> fail_indices;
921 fail_indices.Reserve(stba.Size());
922 for (int i = 0; i < stba.Size(); i++)
923 if (pre_stba[i] != 0 && pre_stba[i] != stba[i])
924 {
925 fail_indices.Append(i);
926 }
927 MFEM_ASSERT(fail_indices.Size() == 0, [&]()
928 {
929 std::stringstream msg;
930 msg << "More than one rank found attribute on shared tri face: ";
931 for (auto x : fail_indices)
932 {
933 msg << x << ' ';
934 }
935 return msg.str();
936 }());
937 }
938
939 {
940 Array<int> fail_indices;
941 fail_indices.Reserve(sqba.Size());
942 for (int i = 0; i < sqba.Size(); i++)
943 if (pre_sqba[i] != 0 && pre_sqba[i] != sqba[i])
944 {
945 fail_indices.Append(i);
946 }
947 MFEM_ASSERT(fail_indices.Size() == 0, [&]()
948 {
949 std::stringstream msg;
950 msg << "More than one rank found attribute on shared quad face: ";
951 for (auto x : fail_indices)
952 {
953 msg << x << ' ';
954 }
955 return msg.str();
956 }());
957 }
958#endif
959 int nghost = 0;
960 for (auto x : stba)
961 if (x > 0) { ++nghost; }
962
963 for (auto x : sqba)
964 if (x > 0) { ++nghost; }
965
966 lface_boundary_attribute.reserve(nghost);
967 for (int i = 0; i < stba.Size(); i++)
968 if (stba[i] > 0)
969 {
970 MFEM_ASSERT(parent_ltface[i] > -1, i);
971 lface_boundary_attribute[parent_ltface[i]] = stba[i];
972 }
973 for (int i = 0; i < sqba.Size(); i++)
974 if (sqba[i] > 0)
975 {
976 MFEM_ASSERT(parent_lqface[i] > -1, i);
977 lface_boundary_attribute[parent_lqface[i]] = sqba[i];
978 }
979 }
980 else if (Dim == 2)
981 {
982 GroupCommunicator sedge_comm(parent_.gtopo);
983 parent_.GetSharedEdgeCommunicator(sedge_comm);
984 int nsedge = sedge_comm.GroupLDofTable().Size_of_connections();
985
986 Array<int> seba(nsedge), parent_ledge(nsedge);
987 seba = 0; parent_ledge = -1;
988 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
989 {
990 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
991 {
992 // Group size of a shared edge is always 2
993 int ple = parent_.GroupEdge(g, ge);
994 auto pbe = face_to_be[ple];
995 if (pbe >= 0)
996 {
997 seba[se] = parent_.GetBdrAttribute(pbe);
998 }
999 parent_ledge[se] = ple;
1000 }
1001 }
1002
1003#ifdef MFEM_DEBUG
1004 auto pre_seba = seba;
1005#endif
1006 sedge_comm.Reduce(seba, GroupCommunicator::Sum);
1007 sedge_comm.Bcast<int>(seba, 0);
1008#ifdef MFEM_DEBUG
1009 {
1010 Array<int> fail_indices;
1011 fail_indices.Reserve(seba.Size());
1012 for (int i = 0; i < seba.Size(); i++)
1013 if (pre_seba[i] != 0 && pre_seba[i] != seba[i])
1014 {
1015 fail_indices.Append(i);
1016 }
1017 MFEM_ASSERT(fail_indices.Size() == 0, [&]()
1018 {
1019 std::stringstream msg;
1020 msg << "More than one rank found attribute on shared edge: ";
1021 for (auto x : fail_indices)
1022 {
1023 msg << x << ' ';
1024 }
1025 return msg.str();
1026 }());
1027 }
1028#endif
1029 int nghost = 0;
1030 for (auto x : seba)
1031 if (x > 0) { ++nghost; }
1032
1033 lface_boundary_attribute.reserve(nghost);
1034 for (int i = 0; i < seba.Size(); i++)
1035 if (seba[i] > 0)
1036 {
1037 MFEM_ASSERT(parent_ledge[i] > -1, i);
1038 lface_boundary_attribute[parent_ledge[i]] = seba[i];
1039 }
1040 }
1041 else if (Dim == 1)
1042 {
1043 GroupCommunicator svert_comm(parent_.gtopo);
1044 parent_.GetSharedVertexCommunicator(svert_comm);
1045 int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
1046
1047 Array<int> svba(nsvtx), parent_lvtx(nsvtx);
1048 svba = 0; parent_lvtx = -1;
1049 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
1050 {
1051 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
1052 {
1053 // Group size of a shared vertex is always 2
1054 int plv = parent_.GroupVertex(g, gv);
1055 auto pbe = face_to_be[plv];
1056 if (pbe >= 0)
1057 {
1058 svba[sv] = parent_.GetBdrAttribute(pbe);
1059 }
1060 parent_lvtx[sv] = plv;
1061 }
1062 }
1063
1064#ifdef MFEM_DEBUG
1065 auto pre_svba = svba;
1066#endif
1067 svert_comm.Reduce(svba, GroupCommunicator::Sum);
1068 svert_comm.Bcast<int>(svba, 0);
1069#ifdef MFEM_DEBUG
1070 {
1071 Array<int> fail_indices;
1072 fail_indices.Reserve(svba.Size());
1073 for (int i = 0; i < svba.Size(); i++)
1074 if (pre_svba[i] != 0 && pre_svba[i] != svba[i])
1075 {
1076 fail_indices.Append(i);
1077 }
1078 MFEM_ASSERT(fail_indices.Size() == 0, [&]()
1079 {
1080 std::stringstream msg;
1081 msg << "More than one rank found attribute on shared vertex: ";
1082 for (auto x : fail_indices)
1083 {
1084 msg << x << ' ';
1085 }
1086 return msg.str();
1087 }());
1088 }
1089#endif
1090 int nghost = 0;
1091 for (auto x : svba)
1092 if (x > 0) { ++nghost; }
1093
1094 lface_boundary_attribute.reserve(nghost);
1095 for (int i = 0; i < svba.Size(); i++)
1096 if (svba[i] > 0)
1097 {
1098 MFEM_ASSERT(parent_lvtx[i] > -1, i);
1099 lface_boundary_attribute[parent_lvtx[i]] = svba[i];
1100 }
1101 }
1102 return lface_boundary_attribute;
1103}
1104
1105
1106void ParSubMesh::Transfer(const ParGridFunction &src, ParGridFunction &dst)
1107{
1108 CreateTransferMap(src, dst).Transfer(src, dst);
1109}
1110
1111ParTransferMap ParSubMesh::CreateTransferMap(const ParGridFunction &src,
1112 const ParGridFunction &dst)
1113{
1114 return ParTransferMap(src, dst);
1115}
1116
1117} // namespace mfem
1118
1119#endif // MFEM_USE_MPI
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
int Size() const
Return the logical size of the array.
Definition array.hpp:147
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:830
void SetComm(MPI_Comm comm)
Set the MPI communicator to 'comm'.
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1288
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:7565
void InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
Begin construction of a mesh.
Definition mesh.cpp:1850
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:6815
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition mesh.hpp:1512
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1508
void GenerateNCFaceInfo()
Definition mesh.cpp:8059
int Dim
Definition mesh.hpp:76
bool Nonconforming() const
Return a bool indicating whether this mesh is nonconforming.
Definition mesh.hpp:2367
static int ComposeQuadOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:6863
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition mesh.cpp:10658
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:6726
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1291
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3362
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1219
static int ComposeTriOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:6786
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1279
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7351
int NumOfElements
Definition mesh.hpp:79
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1526
int NumOfFaces
Definition mesh.hpp:80
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1285
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:299
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:7716
int NumOfEdges
Definition mesh.hpp:80
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition mesh.hpp:288
void OnMeshUpdated(Mesh *mesh)
Definition ncmesh.cpp:2885
int GetNodeVertex(int node)
Given a node index, return the vertex index associated.
Definition ncmesh.hpp:505
Array< int > vertex_nodeId
vertex-index to node-id map, see UpdateVertices
Definition ncmesh.hpp:725
Class for parallel grid function.
Definition pgridfunc.hpp:50
Class for parallel meshes.
Definition pmesh.hpp:34
MPI_Comm GetComm() const
Definition pmesh.hpp:402
int GetMyRank() const
Definition pmesh.hpp:404
void ExchangeFaceNbrData()
Definition pmesh.cpp:2068
int GetNRanks() const
Definition pmesh.hpp:403
void ReduceMeshGen()
Definition pmesh.cpp:891
void SetAttributes() override
Determine the sets of unique attribute values in domain and boundary elements.
Definition pmesh.cpp:1588
MPI_Comm MyComm
Definition pmesh.hpp:45
Table group_squad
Definition pmesh.hpp:80
GroupTopology gtopo
Definition pmesh.hpp:456
ParNCMesh * pncmesh
Definition pmesh.hpp:469
Table group_stria
Definition pmesh.hpp:79
Array< int > parent_node_ids_
Subdomain representation of a topological parent in another ParMesh.
Definition psubmesh.hpp:54
static ParSubMesh CreateFromDomain(const ParMesh &parent, const Array< int > &domain_attributes)
Create a domain ParSubMesh from its parent.
Definition psubmesh.cpp:27
friend class ParNCSubMesh
Definition psubmesh.hpp:55
ParSubMesh()=delete
static ParSubMesh CreateFromBoundary(const ParMesh &parent, const Array< int > &boundary_attributes)
Create a surface ParSubMesh from its parent.
Definition psubmesh.cpp:33
ParTransferMap represents a mapping of degrees of freedom from a source ParGridFunction to a destinat...
From
Indicator from which part of the parent Mesh the SubMesh is created.
Definition submesh.hpp:49
void ShiftUpI()
Definition table.cpp:187
void AddConnection(int r, int c)
Definition table.hpp:88
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition table.cpp:153
void MakeJ()
Definition table.cpp:163
void AddAColumnInRow(int r)
Definition table.hpp:85
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,...
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 AddBoundaryElements(SubMeshT &mesh, const std::unordered_map< int, int > &lface_to_boundary_attribute)
Add boundary elements to the SubMesh.
void BuildGroup(Table &group, int ngroups, const Array< int > &rh, int &ns)
Definition psubmesh.cpp:657
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
T sq(T x)