MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
psubmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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 "submesh_utils.hpp"
21#include "../segment.hpp"
22
23namespace 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
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 // This violation of const-ness may be justified in this instance because
51 // the exchange of face neighbor information only establishes or updates
52 // derived information without altering the primary mesh information,
53 // i.e., the topology, geometry, or region attributes.
54 const_cast<ParMesh&>(parent).ExchangeFaceNbrData();
55
56 if (from == SubMesh::From::Domain)
57 {
58 InitMesh(parent.Dimension(), parent.SpaceDimension(), 0, 0, 0);
59
60 std::tie(parent_vertex_ids_,
61 parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
62 attributes_);
63 }
64 else if (from == SubMesh::From::Boundary)
65 {
66 InitMesh(parent.Dimension() - 1, parent.SpaceDimension(), 0, 0, 0);
67
68 std::tie(parent_vertex_ids_,
69 parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
70 attributes_, true);
71 }
72
73 // Don't let boundary elements get generated automatically. This would
74 // generate boundary elements on each rank locally, which is topologically
75 // wrong for the distributed SubMesh.
76 FinalizeTopology(false);
77
78 parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
79 parent_to_submesh_vertex_ids_ = -1;
80 for (int i = 0; i < parent_vertex_ids_.Size(); i++)
81 {
82 parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
83 }
84
85 DSTable v2v(parent_.GetNV());
86 parent_.GetVertexToVertexTable(v2v);
87 for (int i = 0; i < NumOfEdges; i++)
88 {
89 Array<int> lv;
90 GetEdgeVertices(i, lv);
91
92 // Find vertices/edge in parent mesh
93 int parent_edge_id = v2v(parent_vertex_ids_[lv[0]],
94 parent_vertex_ids_[lv[1]]);
95 parent_edge_ids_.Append(parent_edge_id);
96 }
97
98 parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
99 parent_to_submesh_edge_ids_ = -1;
100 for (int i = 0; i < parent_edge_ids_.Size(); i++)
101 {
102 parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
103 }
104
105 if (Dim == 3)
106 {
107 parent_face_ids_ = SubMeshUtils::BuildFaceMap(parent_, *this,
108 parent_element_ids_);
109
110 parent_to_submesh_face_ids_.SetSize(parent.GetNFaces());
111 parent_to_submesh_face_ids_ = -1;
112 for (int i = 0; i < parent_face_ids_.Size(); i++)
113 {
114 parent_to_submesh_face_ids_[parent_face_ids_[i]] = i;
115 }
116
117 parent_face_ori_.SetSize(NumOfFaces);
118
119 for (int i = 0; i < NumOfFaces; i++)
120 {
121 Array<int> sub_vert;
122 GetFaceVertices(i, sub_vert);
123
124 Array<int> sub_par_vert(sub_vert.Size());
125 for (int j = 0; j < sub_vert.Size(); j++)
126 {
127 sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
128 }
129
130 Array<int> par_vert;
131 parent.GetFaceVertices(parent_face_ids_[i], par_vert);
132
133 if (par_vert.Size() == 3)
134 {
135 parent_face_ori_[i] = GetTriOrientation(par_vert, sub_par_vert);
136 }
137 else
138 {
139 parent_face_ori_[i] = GetQuadOrientation(par_vert, sub_par_vert);
140 }
141 }
142 }
143 else if (Dim == 2)
144 {
145 parent_face_ori_.SetSize(NumOfElements);
146
147 for (int i = 0; i < NumOfElements; i++)
148 {
149 Array<int> sub_vert;
150 GetElementVertices(i, sub_vert);
151
152 Array<int> sub_par_vert(sub_vert.Size());
153 for (int j = 0; j < sub_vert.Size(); j++)
154 {
155 sub_par_vert[j] = parent_vertex_ids_[sub_vert[j]];
156 }
157
158 Array<int> par_vert;
159 int be_ori = 0;
160 if (from == SubMesh::From::Boundary)
161 {
162 parent.GetBdrElementVertices(parent_element_ids_[i], par_vert);
163
164 int f = -1;
165 parent.GetBdrElementFace(parent_element_ids_[i], &f, &be_ori);
166 }
167 else
168 {
169 parent.GetElementVertices(parent_element_ids_[i], par_vert);
170 }
171
172 if (par_vert.Size() == 3)
173 {
174 int se_ori = GetTriOrientation(par_vert, sub_par_vert);
175 parent_face_ori_[i] = ComposeTriOrientations(be_ori, se_ori);
176 }
177 else
178 {
179 int se_ori = GetQuadOrientation(par_vert, sub_par_vert);
180 parent_face_ori_[i] = ComposeQuadOrientations(be_ori, se_ori);
181 }
182 }
183 }
184
185 ListOfIntegerSets groups;
186 IntegerSet group;
187 // the first group is the local one
188 group.Recreate(1, &MyRank);
189 groups.Insert(group);
190
191 // Every rank containing elements of the ParSubMesh attributes now has a
192 // local ParSubMesh. We have to connect the local meshes and assign global
193 // boundaries correctly.
194
195 Array<int> rhvtx;
196 FindSharedVerticesRanks(rhvtx);
197 AppendSharedVerticesGroups(groups, rhvtx);
198
199 Array<int> rhe;
200 FindSharedEdgesRanks(rhe);
201 AppendSharedEdgesGroups(groups, rhe);
202
203 Array<int> rhq, rht;
204 if (Dim == 3)
205 {
206 FindSharedFacesRanks(rht, rhq);
207 AppendSharedFacesGroups(groups, rht, rhq);
208 }
209
210 // Build the group communication topology
212 gtopo.Create(groups, 822);
213 int ngroups = groups.Size()-1;
214
215 int nsverts, nsedges, nstrias, nsquads;
216 BuildVertexGroup(ngroups, rhvtx, nsverts);
217 BuildEdgeGroup(ngroups, rhe, nsedges);
218 if (Dim == 3)
219 {
220 BuildFaceGroup(ngroups, rht, nstrias, rhq, nsquads);
221 }
222 else
223 {
224 group_stria.MakeI(ngroups);
227
228 group_squad.MakeI(ngroups);
231 }
232
233 BuildSharedVerticesMapping(nsverts, rhvtx);
234 BuildSharedEdgesMapping(nsedges, rhe);
235 if (Dim == 3)
236 {
237 BuildSharedFacesMapping(nstrias, rht, nsquads, rhq);
238 }
239
241
242 // Add boundaries
243 {
244 const int num_codim_1 = [this]()
245 {
246 if (Dim == 1) { return NumOfVertices; }
247 else if (Dim == 2) { return NumOfEdges; }
248 else if (Dim == 3) { return NumOfFaces; }
249 else { MFEM_ABORT("Invalid dimension."); return -1; }
250 }();
251
252 if (Dim == 3)
253 {
254 // In 3D we check for `bel_to_edge`. It shouldn't have been set
255 // previously.
256 delete bel_to_edge;
257 bel_to_edge = nullptr;
258 }
259
261 for (int i = 0; i < num_codim_1; i++)
262 {
264 {
266 }
267 }
268
271 Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
272 int max_bdr_attr = parent.bdr_attributes.Max();
273
274 for (int i = 0, j = 0; i < num_codim_1; i++)
275 {
277 {
278 boundary[j] = faces[i]->Duplicate(this);
279 be_to_face[j] = i;
280
281 if (from == SubMesh::From::Domain && Dim >= 2)
282 {
283 int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
284 parent_face_to_be[parent_edge_ids_[i]];
285 if (pbeid != -1)
286 {
287 boundary[j]->SetAttribute(parent.GetBdrAttribute(pbeid));
288 }
289 else
290 {
291 boundary[j]->SetAttribute(max_bdr_attr + 1);
292 }
293 }
294 else
295 {
296 boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
297 }
298 ++j;
299 }
300 }
301
302 if (from == SubMesh::From::Domain && Dim >= 2)
303 {
304 // Search for and count interior boundary elements
305 int InteriorBdrElems = 0;
306 for (int i=0; i<parent.GetNBE(); i++)
307 {
308 const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
309 const int submeshFaceIdx =
310 Dim == 3 ?
311 parent_to_submesh_face_ids_[parentFaceIdx] :
312 parent_to_submesh_edge_ids_[parentFaceIdx];
313
314 if (submeshFaceIdx == -1) { continue; }
315 if (GetFaceInformation(submeshFaceIdx).IsBoundary()) { continue; }
316
317 InteriorBdrElems++;
318 }
319
320 if (InteriorBdrElems > 0)
321 {
322 const int OldNumOfBdrElements = NumOfBdrElements;
323 NumOfBdrElements += InteriorBdrElems;
326
327 // Search for and transfer interior boundary elements
328 for (int i=0, j = OldNumOfBdrElements; i<parent.GetNBE(); i++)
329 {
330 const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
331 const int submeshFaceIdx =
332 parent_to_submesh_face_ids_[parentFaceIdx];
333
334 if (submeshFaceIdx == -1) { continue; }
335 if (GetFaceInformation(submeshFaceIdx).IsBoundary())
336 { continue; }
337
338 boundary[j] = faces[submeshFaceIdx]->Duplicate(this);
339 be_to_face[j] = submeshFaceIdx;
340 boundary[j]->SetAttribute(parent.GetBdrAttribute(i));
341
342 ++j;
343 }
344 }
345 }
346 }
347
348 if (Dim == 3)
349 {
351 }
352
353 // If the parent ParMesh has nodes and therefore is defined on a higher order
354 // geometry, we define this ParSubMesh as a curved ParSubMesh and transfer
355 // the GridFunction from the parent ParMesh to the ParSubMesh.
356 const GridFunction *parent_nodes = parent_.GetNodes();
357 if (parent_nodes)
358 {
359 const FiniteElementSpace *parent_fes = parent_nodes->FESpace();
360
362 parent_fes->FEColl()->GetOrder(),
363 parent_fes->IsDGSpace(),
364 spaceDim,
365 parent_fes->GetOrdering());
366
367 const ParGridFunction* pn = dynamic_cast<const ParGridFunction*>
368 (parent_.GetNodes());
369 MFEM_ASSERT(pn,
370 "Internal error. Object is supposed to be ParGridFunction.");
371
372 ParGridFunction* n = dynamic_cast<ParGridFunction*>
373 (this->GetNodes());
374 MFEM_ASSERT(n,
375 "Internal error. Object is supposed to be ParGridFunction.");
376
377 Transfer(*pn, *n);
378 }
379
380 if (Dim > 1)
381 {
382 if (!el_to_edge) { el_to_edge = new Table; }
384 }
385
386 if (Dim > 1 && from == SubMesh::From::Domain)
387 {
388 // Order 0 Raviart-Thomas space will have precisely 1 DoF per face.
389 // We can use this DoF to communicate boundary attribute numbers.
390 RT_FECollection fec_rt(0, Dim);
391 ParFiniteElementSpace parent_fes_rt(const_cast<ParMesh*>(&parent),
392 &fec_rt);
393
394 ParGridFunction parent_bdr_attr_gf(&parent_fes_rt);
395 parent_bdr_attr_gf = 0.0;
396
397 Array<int> vdofs;
398 DofTransformation doftrans;
399 int dof, faceIdx;
400 real_t sign, w;
401
402 // Copy boundary attribute numbers into local portion of a parallel
403 // grid function
404 parent_bdr_attr_gf.HostReadWrite(); // not modifying all entries
405 for (int i=0; i<parent.GetNBE(); i++)
406 {
407 faceIdx = parent.GetBdrElementFaceIndex(i);
408 const FaceInformation &faceInfo = parent.GetFaceInformation(faceIdx);
409 parent_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
410 dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
411
412 // Shared interior boundary elements are not duplicated across
413 // processor boundaries but ParGridFunction::ParallelAverage will
414 // assume both processors contribute to the averaged DoF value. So,
415 // we multiply shared boundary values by 2 so that the average
416 // produces the desired value.
417 w = faceInfo.IsShared() ? 2.0 : 1.0;
418
419 // The DoF sign is needed to ensure that non-shared interior
420 // boundary values sum properly rather than canceling.
421 parent_bdr_attr_gf[dof] = sign * w * parent.GetBdrAttribute(i);
422 }
423
424 Vector parent_bdr_attr(parent_fes_rt.GetTrueVSize());
425
426 // Compute the average of the attribute numbers
427 parent_bdr_attr_gf.ParallelAverage(parent_bdr_attr);
428 // Distribute boundary attributes to neighboring processors
429 parent_bdr_attr_gf.Distribute(parent_bdr_attr);
430
431 ParFiniteElementSpace submesh_fes_rt(this,
432 &fec_rt);
433
434 ParGridFunction submesh_bdr_attr_gf(&submesh_fes_rt);
435
436 // Transfer the averaged boundary attribute values to the submesh
437 auto transfer_map = ParSubMesh::CreateTransferMap(parent_bdr_attr_gf,
438 submesh_bdr_attr_gf);
439 transfer_map.Transfer(parent_bdr_attr_gf, submesh_bdr_attr_gf);
440
441 // Extract the boundary attribute numbers from the local portion
442 // of the ParGridFunction and set the corresponding boundary element
443 // attributes.
444 int attr;
445 for (int i=0; i<NumOfBdrElements; i++)
446 {
447 submesh_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
448 dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
449 attr = (int)std::round(std::abs(submesh_bdr_attr_gf[dof]));
450 if (attr != 0)
451 {
452 SetBdrAttribute(i, attr);
453 }
454 }
455 }
456
458 Finalize();
459}
460
461void ParSubMesh::FindSharedVerticesRanks(Array<int> &rhvtx)
462{
463 // create a GroupCommunicator on the shared vertices
464 GroupCommunicator svert_comm(parent_.gtopo);
465 parent_.GetSharedVertexCommunicator(svert_comm);
466 // Number of shared vertices
467 int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
468
469 rhvtx.SetSize(nsvtx);
470 rhvtx = 0;
471
472 // On each rank of the group, locally determine if the shared vertex is in
473 // the SubMesh.
474 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
475 {
476 const int group_sz = parent_.gtopo.GetGroupSize(g);
477 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
478 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
479 const int* group_lproc = parent_.gtopo.GetGroup(g);
480
481 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
482 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
483
484 const int my_group_id = my_group_id_ptr-group_lproc;
485
486 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
487 {
488 int plvtx = parent_.GroupVertex(g, gv);
489 int submesh_vertex_id = parent_to_submesh_vertex_ids_[plvtx];
490 if (submesh_vertex_id != -1)
491 {
492 rhvtx[sv] |= 1 << my_group_id;
493 }
494 }
495 }
496
497 // Compute the sum on the root rank and broadcast the result to all ranks.
498 svert_comm.Reduce(rhvtx, GroupCommunicator::Sum);
499 svert_comm.Bcast<int>(rhvtx, 0);
500}
501
502void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
503{
504 // create a GroupCommunicator on the shared edges
505 GroupCommunicator sedge_comm(parent_.gtopo);
506 parent_.GetSharedEdgeCommunicator(sedge_comm);
507
508 int nsedges = sedge_comm.GroupLDofTable().Size_of_connections();
509
510 // see rhvtx description
511 rhe.SetSize(nsedges);
512 rhe = 0;
513
514 // On each rank of the group, locally determine if the shared edge is in
515 // the SubMesh.
516 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
517 {
518 const int group_sz = parent_.gtopo.GetGroupSize(g);
519 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
520 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
521 const int* group_lproc = parent_.gtopo.GetGroup(g);
522
523 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
524 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
525
526 // rank id inside this group
527 const int my_group_id = my_group_id_ptr-group_lproc;
528
529 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
530 {
531 int ple, o;
532 parent_.GroupEdge(g, ge, ple, o);
533 int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
534 if (submesh_edge_id != -1)
535 {
536 rhe[se] |= 1 << my_group_id;
537 }
538 }
539 }
540
541 // Compute the sum on the root rank and broadcast the result to all ranks.
542 sedge_comm.Reduce(rhe, GroupCommunicator::Sum);
543 sedge_comm.Bcast<int>(rhe, 0);
544}
545
546void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
547{
548 GroupCommunicator squad_comm(parent_.gtopo);
549 parent_.GetSharedQuadCommunicator(squad_comm);
550
551 int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
552
553 rhq.SetSize(nsquad);
554 rhq = 0;
555
556 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
557 {
558 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
559 {
560 // Group size of a shared face is always 2
561
562 int plq, o;
563 parent_.GroupQuadrilateral(g, gq, plq, o);
564 int submesh_face_id = parent_to_submesh_face_ids_[plq];
565 if (submesh_face_id != -1)
566 {
567 rhq[sq] = 1;
568 }
569 }
570 }
571
572 // Compute the sum on the root rank and broadcast the result to all ranks.
573 squad_comm.Reduce(rhq, GroupCommunicator::Sum);
574 squad_comm.Bcast<int>(rhq, 0);
575
576 GroupCommunicator stria_comm(parent_.gtopo);
577 parent_.GetSharedTriCommunicator(stria_comm);
578
579 int nstria = stria_comm.GroupLDofTable().Size_of_connections();
580
581 rht.SetSize(nstria);
582 rht = 0;
583
584 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
585 {
586 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
587 {
588 // Group size of a shared face is always 2
589
590 int plt, o;
591 parent_.GroupTriangle(g, gt, plt, o);
592 int submesh_face_id = parent_to_submesh_face_ids_[plt];
593 if (submesh_face_id != -1)
594 {
595 rht[st] = 1;
596 }
597 }
598 }
599
600 // Compute the sum on the root rank and broadcast the result to all ranks.
601 stria_comm.Reduce(rht, GroupCommunicator::Sum);
602 stria_comm.Bcast<int>(rht, 0);
603}
604
605
606void ParSubMesh::AppendSharedVerticesGroups(ListOfIntegerSets &groups,
607 Array<int> &rhvtx)
608{
609 IntegerSet group;
610
611 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
612 {
613 const int group_sz = parent_.gtopo.GetGroupSize(g);
614 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
615 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
616 const int* group_lproc = parent_.gtopo.GetGroup(g);
617
618 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
619 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
620
621 const int my_group_id = my_group_id_ptr-group_lproc;
622
623 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
624 {
625 // Returns the parents local vertex id
626 int plvtx = parent_.GroupVertex(g, gv);
627 int submesh_vtx = parent_to_submesh_vertex_ids_[plvtx];
628
629 // Reusing the `rhvtx` array as shared vertex to group array.
630 if (submesh_vtx == -1)
631 {
632 // parent shared vertex is not in SubMesh
633 rhvtx[sv] = -1;
634 }
635 else if (rhvtx[sv] & ~(1 << my_group_id))
636 {
637 // shared vertex is present on this rank and others
638 MFEM_ASSERT(rhvtx[sv] & (1 << my_group_id), "error again");
639
640 // determine which other ranks have the shared vertex
641 Array<int> &ranks = group;
642 ranks.SetSize(0);
643 for (int i = 0; i < group_sz; i++)
644 {
645 if ((rhvtx[sv] >> i) & 1)
646 {
647 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
648 }
649 }
650 MFEM_ASSERT(ranks.Size() >= 2, "internal error");
651
652 rhvtx[sv] = groups.Insert(group) - 1;
653 }
654 else
655 {
656 // previously shared vertex is only present on this rank
657 rhvtx[sv] = -1;
658 }
659 }
660 }
661}
662
663void ParSubMesh::AppendSharedEdgesGroups(ListOfIntegerSets &groups,
664 Array<int> &rhe)
665{
666 IntegerSet group;
667
668 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
669 {
670 const int group_sz = parent_.gtopo.GetGroupSize(g);
671 MFEM_VERIFY((unsigned int)group_sz <= 8*sizeof(int), // 32
672 "Group size too large. Groups with more than 32 ranks are not supported, yet.");
673 const int* group_lproc = parent_.gtopo.GetGroup(g);
674
675 const int* my_group_id_ptr = std::find(group_lproc, group_lproc+group_sz, 0);
676 MFEM_ASSERT(my_group_id_ptr != group_lproc+group_sz, "internal error");
677
678 const int my_group_id = my_group_id_ptr-group_lproc;
679
680 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
681 {
682 int ple, o;
683 parent_.GroupEdge(g, ge, ple, o);
684 int submesh_edge = parent_to_submesh_edge_ids_[ple];
685
686 // Reusing the `rhe` array as shared edge to group array.
687 if (submesh_edge == -1)
688 {
689 // parent shared edge is not in SubMesh
690 rhe[se] = -1;
691 }
692 else if (rhe[se] & ~(1 << my_group_id))
693 {
694 // shared edge is present on this rank and others
695
696 // determine which other ranks have the shared edge
697 Array<int> &ranks = group;
698 ranks.SetSize(0);
699 for (int i = 0; i < group_sz; i++)
700 {
701 if ((rhe[se] >> i) & 1)
702 {
703 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[i]));
704 }
705 }
706 MFEM_ASSERT(ranks.Size() >= 2, "internal error");
707
708 rhe[se] = groups.Insert(group) - 1;
709 }
710 else
711 {
712 // previously shared edge is only present on this rank
713 rhe[se] = -1;
714 }
715 }
716 }
717}
718
719void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
720 Array<int>& rht, Array<int> &rhq)
721{
722 IntegerSet quad_group;
723
724 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
725 {
726 const int* group_lproc = parent_.gtopo.GetGroup(g);
727 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
728 {
729 const int group_sz = parent_.gtopo.GetGroupSize(g);
730 MFEM_ASSERT(group_sz == 2, "internal error");
731
732 int plq, o;
733 parent_.GroupQuadrilateral(g, gq, plq, o);
734 int submesh_face_id = parent_to_submesh_face_ids_[plq];
735
736 // Reusing the `rhq` array as shared face to group array.
737 if (submesh_face_id == -1)
738 {
739 // parent shared face is not in SubMesh
740 rhq[sq] = -1;
741 }
742 else if (rhq[sq] == group_sz)
743 {
744 // shared face is present on this rank and others
745
746 // There can only be two ranks in this group sharing faces. Add
747 // all ranks to a new communication group.
748 Array<int> &ranks = quad_group;
749 ranks.SetSize(0);
750 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
751 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
752
753 rhq[sq] = groups.Insert(quad_group) - 1;
754 }
755 else
756 {
757 // previously shared edge is only present on this rank
758 rhq[sq] = -1;
759 }
760 }
761 }
762
763 IntegerSet tria_group;
764
765 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
766 {
767 const int* group_lproc = parent_.gtopo.GetGroup(g);
768 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
769 {
770 const int group_sz = parent_.gtopo.GetGroupSize(g);
771 MFEM_ASSERT(group_sz == 2, "internal error");
772
773 int plt, o;
774 parent_.GroupTriangle(g, gt, plt, o);
775 int submesh_face_id = parent_to_submesh_face_ids_[plt];
776
777 // Reusing the `rht` array as shared face to group array.
778 if (submesh_face_id == -1)
779 {
780 // parent shared face is not in SubMesh
781 rht[st] = -1;
782 }
783 else if (rht[st] == group_sz)
784 {
785 // shared face is present on this rank and others
786
787 // There can only be two ranks in this group sharing faces. Add
788 // all ranks to a new communication group.
789 Array<int> &ranks = tria_group;
790 ranks.SetSize(0);
791 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
792 ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[1]));
793
794 rht[st] = groups.Insert(tria_group) - 1;
795 }
796 else
797 {
798 // previously shared edge is only present on this rank
799 rht[st] = -1;
800 }
801 }
802 }
803}
804
805void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
806 int& nsverts)
807{
808 group_svert.MakeI(ngroups);
809 for (int i = 0; i < rhvtx.Size(); i++)
810 {
811 if (rhvtx[i] >= 0)
812 {
814 }
815 }
816
818 nsverts = 0;
819 for (int i = 0; i < rhvtx.Size(); i++)
820 {
821 if (rhvtx[i] >= 0)
822 {
823 group_svert.AddConnection(rhvtx[i], nsverts++);
824 }
825 }
827}
828
829void ParSubMesh::BuildEdgeGroup(int ngroups, const Array<int>& rhe,
830 int& nsedges)
831{
832 group_sedge.MakeI(ngroups);
833 for (int i = 0; i < rhe.Size(); i++)
834 {
835 if (rhe[i] >= 0)
836 {
838 }
839 }
840
842 nsedges = 0;
843 for (int i = 0; i < rhe.Size(); i++)
844 {
845 if (rhe[i] >= 0)
846 {
847 group_sedge.AddConnection(rhe[i], nsedges++);
848 }
849 }
851}
852
853void ParSubMesh::BuildFaceGroup(int ngroups, const Array<int>& rht,
854 int& nstrias, const Array<int>& rhq, int& nsquads)
855{
856 group_squad.MakeI(ngroups);
857 for (int i = 0; i < rhq.Size(); i++)
858 {
859 if (rhq[i] >= 0)
860 {
862 }
863 }
864
866 nsquads = 0;
867 for (int i = 0; i < rhq.Size(); i++)
868 {
869 if (rhq[i] >= 0)
870 {
871 group_squad.AddConnection(rhq[i], nsquads++);
872 }
873 }
875
876 group_stria.MakeI(ngroups);
877 for (int i = 0; i < rht.Size(); i++)
878 {
879 if (rht[i] >= 0)
880 {
882 }
883 }
884
886 nstrias = 0;
887 for (int i = 0; i < rht.Size(); i++)
888 {
889 if (rht[i] >= 0)
890 {
891 group_stria.AddConnection(rht[i], nstrias++);
892 }
893 }
895}
896
897void ParSubMesh::BuildSharedVerticesMapping(const int nsverts,
898 const Array<int>& rhvtx)
899{
900 svert_lvert.Reserve(nsverts);
901
902 for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
903 {
904 for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
905 {
906 // Returns the parents local vertex id
907 int plvtx = parent_.GroupVertex(g, gv);
908 int submesh_vtx_id = parent_to_submesh_vertex_ids_[plvtx];
909 if ((submesh_vtx_id == -1) || (rhvtx[sv] == -1))
910 {
911 // parent shared vertex is not in SubMesh or is not shared
912 }
913 else
914 {
915 svert_lvert.Append(submesh_vtx_id);
916 }
917 }
918 }
919}
920
921void ParSubMesh::BuildSharedEdgesMapping(const int sedges_ct,
922 const Array<int>& rhe)
923{
924 shared_edges.Reserve(sedges_ct);
925 sedge_ledge.Reserve(sedges_ct);
926
927 for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
928 {
929 for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
930 {
931 int ple, o;
932 parent_.GroupEdge(g, ge, ple, o);
933 int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
934 if ((submesh_edge_id == -1) || rhe[se] == -1)
935 {
936 // parent shared edge is not in SubMesh or is not shared
937 }
938 else
939 {
940 Array<int> vert;
941 parent_.GetEdgeVertices(ple, vert);
942 // Swap order of vertices if orientation in parent group is -1
943 int v0 = parent_to_submesh_vertex_ids_[vert[(1-o)/2]];
944 int v1 = parent_to_submesh_vertex_ids_[vert[(1+o)/2]];
945
946 // The orienation of the shared edge relative to the local edge
947 // will be determined by whether v0 < v1 or v1 < v0
948 shared_edges.Append(new Segment(v0, v1, 1));
949 sedge_ledge.Append(submesh_edge_id);
950 }
951 }
952 }
953}
954
955void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
956 const Array<int>& rht,
957 const int nsquads, const Array<int>& rhq)
958{
959 shared_trias.Reserve(nstrias);
960 shared_quads.Reserve(nsquads);
961 sface_lface.Reserve(nstrias + nsquads);
962
963 // sface_lface should list the triangular shared faces first
964 // followed by the quadrilateral shared faces.
965
966 for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
967 {
968 for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
969 {
970 int plt, o;
971 parent_.GroupTriangle(g, gt, plt, o);
972 int submesh_face_id = parent_to_submesh_face_ids_[plt];
973 if ((submesh_face_id == -1) || rht[st] == -1)
974 {
975 // parent shared face is not in SubMesh or is not shared
976 }
977 else
978 {
979 Array<int> vert;
980
981 GetFaceVertices(submesh_face_id, vert);
982
983 int v0 = vert[0];
984 int v1 = vert[1];
985 int v2 = vert[2];
986
987 // See Mesh::GetTriOrientation for info on interpretting "o"
988 switch (o)
989 {
990 case 1:
991 std::swap(v0,v1);
992 break;
993 case 3:
994 std::swap(v2,v0);
995 break;
996 case 5:
997 std::swap(v1,v2);
998 break;
999 default:
1000 // Do nothing
1001 break;
1002 }
1003
1004 shared_trias.Append(Vert3(v0, v1, v2));
1005 sface_lface.Append(submesh_face_id);
1006 }
1007 }
1008 }
1009
1010 for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
1011 {
1012 for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
1013 {
1014 int plq, o;
1015 parent_.GroupQuadrilateral(g, gq, plq, o);
1016 int submesh_face_id = parent_to_submesh_face_ids_[plq];
1017 if ((submesh_face_id == -1) || rhq[sq] == -1)
1018 {
1019 // parent shared face is not in SubMesh or is not shared
1020 }
1021 else
1022 {
1023 Array<int> vert;
1024 GetFaceVertices(submesh_face_id, vert);
1025
1026 int v0 = vert[0];
1027 int v1 = vert[1];
1028 int v2 = vert[2];
1029 int v3 = vert[3];
1030
1031 // See Mesh::GetQuadOrientation for info on interpretting "o"
1032 switch (o)
1033 {
1034 case 1:
1035 std::swap(v1,v3);
1036 break;
1037 case 3:
1038 std::swap(v0,v1);
1039 std::swap(v2,v3);
1040 break;
1041 case 5:
1042 std::swap(v0,v2);
1043 break;
1044 case 7:
1045 std::swap(v0,v3);
1046 std::swap(v1,v2);
1047 break;
1048 default:
1049 // Do nothing
1050 break;
1051 }
1052
1053 shared_quads.Append(Vert4(v0, v1, v2, v3));
1054 sface_lface.Append(submesh_face_id);
1055 }
1056 }
1057 }
1058}
1059
1061{
1062 ParTransferMap map(src, dst);
1063 map.Transfer(src, dst);
1064}
1065
1067 const ParGridFunction &dst)
1068{
1069 return ParTransferMap(src, dst);
1070}
1071
1072} // namespace mfem
1073
1074#endif // MFEM_USE_MPI
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:162
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:697
int Size() const
Return the logical size of the array.
Definition array.hpp:144
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:769
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
Definition fespace.hpp:1017
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
void SetComm(MPI_Comm comm)
Set the MPI communicator to 'comm'.
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 GetGroupSize(int g) const
Get the number of processors in a group.
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
int GetElementToEdgeTable(Table &)
Definition mesh.cpp:7422
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1232
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:7252
void InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
Begin construction of a mesh.
Definition mesh.cpp:1635
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:282
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:6533
int NumOfBdrElements
Definition mesh.hpp:71
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition mesh.hpp:1442
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1438
Array< int > GetFaceToBdrElMap() const
Definition mesh.cpp:1477
Array< Element * > faces
Definition mesh.hpp:99
int Dim
Definition mesh.hpp:68
bool Nonconforming() const
Definition mesh.hpp:2229
int GetBdrAttribute(int i) const
Return the attribute of boundary element i.
Definition mesh.hpp:1339
static int ComposeQuadOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:6581
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1518
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:6444
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1235
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3135
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1160
Table * bel_to_edge
Definition mesh.hpp:232
Table * el_to_edge
Definition mesh.hpp:227
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1169
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition mesh.cpp:7862
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1163
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:8973
int NumOfVertices
Definition mesh.hpp:71
static int ComposeTriOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:6504
Array< int > be_to_face
Definition mesh.hpp:230
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1223
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7069
int NumOfElements
Definition mesh.hpp:71
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1456
int NumOfFaces
Definition mesh.hpp:72
int spaceDim
Definition mesh.hpp:69
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1229
Array< Element * > boundary
Definition mesh.hpp:98
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition mesh.hpp:2093
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:7397
int NumOfEdges
Definition mesh.hpp:72
void SetBdrAttribute(int i, int attr)
Set the attribute of boundary element i.
Definition mesh.hpp:1342
Class for parallel grid function.
Definition pgridfunc.hpp:33
Class for parallel meshes.
Definition pmesh.hpp:34
int GroupNQuadrilaterals(int group) const
Definition pmesh.hpp:449
MPI_Comm GetComm() const
Definition pmesh.hpp:402
Array< Element * > shared_edges
Definition pmesh.hpp:69
int GetMyRank() const
Definition pmesh.hpp:404
void GetSharedTriCommunicator(int ordering, GroupCommunicator &stria_comm) const
Get the shared face triangles GroupCommunicator.
Definition pmesh.cpp:1718
Array< int > sface_lface
Definition pmesh.hpp:86
void ExchangeFaceNbrData()
Definition pmesh.cpp:2069
Table group_sedge
Definition pmesh.hpp:78
int GetNRanks() const
Definition pmesh.hpp:403
Table group_svert
Shared objects in each group.
Definition pmesh.hpp:77
int GroupVertex(int group, int i) const
Definition pmesh.hpp:451
void GetSharedEdgeCommunicator(int ordering, GroupCommunicator &sedge_comm) const
Get the shared edges GroupCommunicator.
Definition pmesh.cpp:1646
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:2007
void SetAttributes() override
Determine the sets of unique attribute values in domain and boundary elements.
Definition pmesh.cpp:1589
void GetSharedQuadCommunicator(int ordering, GroupCommunicator &squad_comm) const
Get the shared face quadrilaterals GroupCommunicator.
Definition pmesh.cpp:1694
ParMesh()
Default constructor. Create an empty ParMesh.
Definition pmesh.hpp:335
MPI_Comm MyComm
Definition pmesh.hpp:45
Array< Vert4 > shared_quads
Definition pmesh.hpp:74
void Finalize(bool refine=false, bool fix_orientation=false) override
Finalize the construction of a general Mesh.
Definition pmesh.cpp:1524
Table group_squad
Definition pmesh.hpp:80
Array< int > svert_lvert
Shared to local index mapping.
Definition pmesh.hpp:83
Array< int > sedge_ledge
Definition pmesh.hpp:84
GroupTopology gtopo
Definition pmesh.hpp:426
int GroupNTriangles(int group) const
Definition pmesh.hpp:448
void GetSharedVertexCommunicator(int ordering, GroupCommunicator &svert_comm) const
Get the shared vertices GroupCommunicator.
Definition pmesh.cpp:1670
int GroupNEdges(int group) const
Definition pmesh.hpp:447
Array< Vert3 > shared_trias
Definition pmesh.hpp:73
void GroupQuadrilateral(int group, int i, int &face, int &o) const
Definition pmesh.cpp:1635
int GetNGroups() const
Definition pmesh.hpp:443
void GroupTriangle(int group, int i, int &face, int &o) const
Definition pmesh.cpp:1624
Table group_stria
Definition pmesh.hpp:79
void GroupEdge(int group, int i, int &edge, int &o) const
Definition pmesh.cpp:1616
int GroupNVertices(int group) const
Definition pmesh.hpp:446
Subdomain representation of a topological parent in another ParMesh.
Definition psubmesh.hpp:52
static ParTransferMap CreateTransferMap(const ParGridFunction &src, const ParGridFunction &dst)
Create a Transfer Map object.
static ParSubMesh CreateFromBoundary(const ParMesh &parent, Array< int > &boundary_attributes)
Create a surface ParSubMesh from it's parent.
Definition psubmesh.cpp:32
ParSubMesh()=delete
static ParSubMesh CreateFromDomain(const ParMesh &parent, Array< int > &domain_attributes)
Create a domain ParSubMesh from it's parent.
Definition psubmesh.cpp:26
static void Transfer(const ParGridFunction &src, ParGridFunction &dst)
Transfer the dofs of a ParGridFunction.
ParTransferMap represents a mapping of degrees of freedom from a source ParGridFunction to a destinat...
static const int GENERATED_ATTRIBUTE
Definition submesh.hpp:52
From
Indicator from which part of the parent Mesh the SubMesh is created.
Definition submesh.hpp:47
void ShiftUpI()
Definition table.cpp:115
void SetSize(int dim, int connections_per_row)
Set the size and the number of connections for the table.
Definition table.cpp:124
void AddConnection(int r, int c)
Definition table.hpp:80
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition table.cpp:81
void MakeJ()
Definition table.cpp:91
void AddAColumnInRow(int r)
Definition table.hpp:77
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...
float real_t
Definition config.hpp:43
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
T sq(T x)
bool IsBoundary() const
Return true if the face is a boundary face.
Definition mesh.hpp:1935