MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
pncmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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 "mesh_headers.hpp"
17#include "pncmesh.hpp"
20
21#include <numeric> // std::accumulate
22#include <map>
23#include <climits> // INT_MIN, INT_MAX
24#include <array>
25
26namespace mfem
27{
28
29using namespace bin_io;
30
31static int GetHexEdgeSplit(const int* nodes, int v1, int v2);
32
33static bool SameSplitScale(real_t a, real_t b)
34{
35#ifdef MFEM_USE_DOUBLE
36 constexpr real_t rel_tol = 1.0e-8;
37#else
38 constexpr real_t rel_tol = 1.0e-5;
39#endif
40 return std::abs(a - b) <= rel_tol *
41 std::max(real_t(1.0), std::max(std::abs(a), std::abs(b)));
42}
43
44static real_t DirectedHexEdgeScale(const int* nodes, const Refinement &ref,
45 int v0, int v1)
46{
47 const int dir = GetHexEdgeSplit(nodes, v0, v1);
48 static const int split_edges[3][4][2] =
49 {
50 {{0, 1}, {3, 2}, {4, 5}, {7, 6}},
51 {{1, 2}, {0, 3}, {5, 6}, {4, 7}},
52 {{0, 4}, {1, 5}, {2, 6}, {3, 7}}
53 };
54
55 for (int i = 0; i < 4; i++)
56 {
57 const int a = nodes[split_edges[dir][i][0]];
58 const int b = nodes[split_edges[dir][i][1]];
59 if (a == v0 && b == v1)
60 {
61 return ref.s[dir];
62 }
63 if (a == v1 && b == v0)
64 {
65 return 1.0 - ref.s[dir];
66 }
67 }
68
69 MFEM_ABORT("Shared face edge does not match the refinement direction.");
70 return 0.0;
71}
72
73ParNCMesh::ParNCMesh(MPI_Comm comm, const NCMesh &ncmesh,
74 const int *partitioning)
75 : NCMesh(ncmesh)
76{
77 MyComm = comm;
78 MPI_Comm_size(MyComm, &NRanks);
79 MPI_Comm_rank(MyComm, &MyRank);
80
81 // assign leaf elements to the processors by simply splitting the
82 // sequence of leaf elements into 'NRanks' parts
83 for (int i = 0; i < leaf_elements.Size(); i++)
84 {
85 elements[leaf_elements[i]].rank =
86 partitioning ? partitioning[i] : InitialPartition(i);
87 }
88
89 Update();
90
91 // note that at this point all processors still have all the leaf elements;
92 // we however may now start pruning the refinement tree to get rid of
93 // branches that only contain someone else's leaves (see Prune())
94}
95
96ParNCMesh::ParNCMesh(MPI_Comm comm, std::istream &input, int version,
97 int &curved, int &is_nc)
98 : NCMesh(input, version, curved, is_nc)
99{
100 MFEM_VERIFY(version != 11, "Nonconforming mesh format \"MFEM NC mesh v1.1\""
101 " is supported only in serial.");
102
103 MyComm = comm;
104 MPI_Comm_size(MyComm, &NRanks);
105
106 int my_rank;
107 MPI_Comm_rank(MyComm, &my_rank);
108
109 int max_rank = 0;
110 for (int i = 0; i < leaf_elements.Size(); i++)
111 {
112 max_rank = std::max(elements[leaf_elements[i]].rank, max_rank);
113 }
114
115 MFEM_VERIFY((my_rank == MyRank) && (max_rank < NRanks),
116 "Parallel mesh file doesn't seem to match current MPI setup. "
117 "Loading a parallel NC mesh with a non-matching communicator "
118 "size is not supported.");
119
120 bool iso = Iso;
121 MPI_Allreduce(&iso, &Iso, 1, MFEM_MPI_CXX_BOOL, MPI_LAND, MyComm);
122
123 Update();
124}
125
127// copy primary data only
128 : NCMesh(other)
129 , MyComm(other.MyComm)
130 , NRanks(other.NRanks)
131{
132 Update(); // mark all secondary stuff for recalculation
133}
134
139
141{
143
144 groups.clear();
145 group_id.clear();
146
147 CommGroup self;
148 self.push_back(MyRank);
149 groups.push_back(self);
150 group_id[self] = 0;
151
152 for (int i = 0; i < 3; i++)
153 {
156 entity_index_rank[i].DeleteAll();
159 }
160
164
168}
169
170void ParNCMesh::ElementSharesFace(int elem, int local, int face)
171{
172 // Analogous to ElementSharesEdge.
173
174 Element &el = elements[elem];
175 int f_index = faces[face].index;
176
177 int &owner = tmp_owner[f_index];
178 owner = std::min(owner, el.rank);
179
180 char &flag = tmp_shared_flag[f_index];
181 flag |= (el.rank == MyRank) ? 0x1 : 0x2;
182
183 entity_index_rank[2].Append(Connection(f_index, el.rank));
184
185 // derive globally consistent face ID from the global element sequence
186 int &el_loc = entity_elem_local[2][f_index];
187 if (el_loc < 0 || leaf_sfc_index[el.index] < leaf_sfc_index[(el_loc >> 4)])
188 {
189 el_loc = (el.index << 4) | local;
190 }
191}
192
194{
195 if (HaveTets()) { GetEdgeList(); } // needed by TraverseTetEdge()
196
197 // This is an extension of NCMesh::BuildFaceList() which also determines
198 // face ownership and prepares face processor groups.
199
203
204 if (Dim < 3 || !leaf_elements.Size()) { return; }
205
206 int nfaces = NFaces + NGhostFaces;
207
208 tmp_owner.SetSize(nfaces);
209 tmp_owner = INT_MAX;
210
211 tmp_shared_flag.SetSize(nfaces);
212 tmp_shared_flag = 0;
213
215 entity_index_rank[2].SetSize(0);
216
217 entity_elem_local[2].SetSize(nfaces);
218 entity_elem_local[2] = -1;
219
221
222 InitOwners(nfaces, entity_owner[2]);
224
227
228 // create simple conforming (cut-mesh) groups now
230 // NOTE: entity_index_rank[2] is not deleted until CalculatePMatrixGroups
231
233}
234
235void ParNCMesh::ElementSharesEdge(int elem, int local, int enode)
236{
237 // Called by NCMesh::BuildEdgeList when an edge is visited in a leaf element.
238 // This allows us to determine edge ownership and whether it is shared
239 // without duplicating all the HashTable lookups in NCMesh::BuildEdgeList().
240
241 Element &el= elements[elem];
242 int e_index = nodes[enode].edge_index;
243
244 int &owner = tmp_owner[e_index];
245 owner = std::min(owner, el.rank);
246
247 char &flag = tmp_shared_flag[e_index];
248 flag |= (el.rank == MyRank) ? 0x1 : 0x2;
249
250 entity_index_rank[1].Append(Connection(e_index, el.rank));
251
252 // derive globally consistent edge ID from the global element sequence
253 int &el_loc = entity_elem_local[1][e_index];
254 if (el_loc < 0 || leaf_sfc_index[el.index] < leaf_sfc_index[(el_loc >> 4)])
255 {
256 el_loc = (el.index << 4) | local;
257 }
258}
259
261{
262 // This is an extension of NCMesh::BuildEdgeList() which also determines
263 // edge ownership and prepares edge processor groups.
264
267 if (Dim < 3) { boundary_faces.SetSize(0); }
268
269 if (Dim < 2 || !leaf_elements.Size()) { return; }
270
271 int nedges = NEdges + NGhostEdges;
272
273 tmp_owner.SetSize(nedges);
274 tmp_owner = INT_MAX;
275
276 tmp_shared_flag.SetSize(nedges);
277 tmp_shared_flag = 0;
278
280 entity_index_rank[1].SetSize(0);
281
282 entity_elem_local[1].SetSize(nedges);
283 entity_elem_local[1] = -1;
284
286
287 InitOwners(nedges, entity_owner[1]);
289
292
293 // create simple conforming (cut-mesh) groups now
295 // NOTE: entity_index_rank[1] is not deleted until CalculatePMatrixGroups
296}
297
299{
300 const NCList &faceList = GetFaceList();
301 NCList::MeshIdAndType midt = faceList.GetMeshIdAndType(face);
302 if (!midt.id)
303 {
304 edges.SetSize(0);
305 return;
306 }
307
308 int V[4], E[4], Eo[4];
309 const int nfv = GetFaceVerticesEdges(*midt.id, V, E, Eo);
310 MFEM_ASSERT(nfv == 4, "");
311
312 edges.SetSize(nfv);
313 for (int i=0; i<nfv; ++i)
314 {
315 edges[i] = E[i];
316 }
317}
318
320{
321 NCMesh::Element &el = elements[elem]; // ghost element
322 MFEM_ASSERT(el.rank != MyRank, "");
323
324 MFEM_ASSERT(!el.ref_type, "not a leaf element.");
325
326 GeomInfo& gi = GI[el.Geom()];
327 edges.SetSize(gi.ne);
328
329 for (int j = 0; j < gi.ne; j++)
330 {
331 // get node for this edge
332 const int* ev = gi.edges[j];
333 int node[2] = { el.node[ev[0]], el.node[ev[1]] };
334
335 int enode = nodes.FindId(node[0], node[1]);
336 MFEM_ASSERT(enode >= 0, "edge node not found!");
337
338 Node &nd = nodes[enode];
339 MFEM_ASSERT(nd.HasEdge(), "edge not found!");
340
341 edges[j] = nd.edge_index;
342 }
343}
344
346{
347 NCMesh::Element &el = elements[elem]; // ghost element
348 MFEM_ASSERT(el.rank != MyRank, "");
349 MFEM_ASSERT(!el.ref_type, "not a leaf element.");
350
351 faces.SetSize(GI[el.Geom()].nf);
352 for (int j = 0; j < faces.Size(); j++)
353 {
354 faces[j] = GetFace(el, j)->index;
355 }
356}
357
358void ParNCMesh::ElementSharesVertex(int elem, int local, int vnode)
359{
360 // Analogous to ElementSharesEdge.
361
362 Element &el = elements[elem];
363 int v_index = nodes[vnode].vert_index;
364
365 int &owner = tmp_owner[v_index];
366 owner = std::min(owner, el.rank);
367
368 char &flag = tmp_shared_flag[v_index];
369 flag |= (el.rank == MyRank) ? 0x1 : 0x2;
370
371 entity_index_rank[0].Append(Connection(v_index, el.rank));
372
373 // derive globally consistent vertex ID from the global element sequence
374 int &el_loc = entity_elem_local[0][v_index];
375 if (el_loc < 0 || leaf_sfc_index[el.index] < leaf_sfc_index[(el_loc >> 4)])
376 {
377 el_loc = (el.index << 4) | local;
378 }
379}
380
382{
383 // This is an extension of NCMesh::BuildVertexList() which also determines
384 // vertex ownership and creates vertex processor groups.
385
386 int nvertices = NVertices + NGhostVertices;
387
388 tmp_owner.SetSize(nvertices);
389 tmp_owner = INT_MAX;
390
391 tmp_shared_flag.SetSize(nvertices);
392 tmp_shared_flag = 0;
393
395 entity_index_rank[0].SetSize(0);
396
397 entity_elem_local[0].SetSize(nvertices);
398 entity_elem_local[0] = -1;
399
401
402 InitOwners(nvertices, entity_owner[0]);
404
407
408 // create simple conforming (cut-mesh) groups now
410 // NOTE: entity_index_rank[0] is not deleted until CalculatePMatrixGroups
411}
412
413void ParNCMesh::InitOwners(int num, Array<GroupId> &entity_owner_)
414{
415 entity_owner_.SetSize(num);
416 for (int i = 0; i < num; i++)
417 {
418 entity_owner_[i] =
419 (tmp_owner[i] != INT_MAX) ? GetSingletonGroup(tmp_owner[i]) : 0;
420 }
421}
422
423void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
424{
425 MFEM_VERIFY(tmp_shared_flag.Size(), "wrong code path");
426
427 // combine flags of masters and slaves
428 for (int i = 0; i < list.masters.Size(); i++)
429 {
430 const Master &master = list.masters[i];
431 char &master_flag = tmp_shared_flag[master.index];
432 char master_old_flag = master_flag;
433
434 for (int j = master.slaves_begin; j < master.slaves_end; j++)
435 {
436 int si = list.slaves[j].index;
437 if (si >= 0)
438 {
439 char &slave_flag = tmp_shared_flag[si];
440 master_flag |= slave_flag;
441 slave_flag |= master_old_flag;
442 }
443 else // special case: prism edge-face constraint
444 {
445 if (entity_owner[1][FlipIndexSign(si)] != MyRank)
446 {
447 master_flag |= 0x2;
448 }
449 }
450 }
451 }
452
453 shared.Clear();
454
455 for (int i = 0; i < list.conforming.Size(); i++)
456 {
457 if (tmp_shared_flag[list.conforming[i].index] == 0x3)
458 {
459 shared.conforming.Append(list.conforming[i]);
460 }
461 }
462 for (int i = 0; i < list.masters.Size(); i++)
463 {
464 if (tmp_shared_flag[list.masters[i].index] == 0x3)
465 {
466 shared.masters.Append(list.masters[i]);
467 }
468 }
469 for (int i = 0; i < list.slaves.Size(); i++)
470 {
471 int si = list.slaves[i].index;
472 if (si >= 0 && tmp_shared_flag[si] == 0x3)
473 {
474 shared.slaves.Append(list.slaves[i]);
475 }
476 }
477}
478
480{
481 if (lhs.size() == rhs.size())
482 {
483 for (unsigned i = 0; i < lhs.size(); i++)
484 {
485 if (lhs[i] < rhs[i]) { return true; }
486 }
487 return false;
488 }
489 return lhs.size() < rhs.size();
490}
491
492#ifdef MFEM_DEBUG
493static bool group_sorted(const ParNCMesh::CommGroup &group)
495 for (unsigned i = 1; i < group.size(); i++)
496 {
497 if (group[i] <= group[i-1]) { return false; }
498 }
499 return true;
500}
501#endif
502
504{
505 if (group.size() == 1 && group[0] == MyRank)
506 {
507 return 0;
508 }
509 MFEM_ASSERT(group_sorted(group), "invalid group");
510 GroupId &id = group_id[group];
511 if (!id)
512 {
513 id = groups.size();
514 groups.push_back(group);
515 }
516 return id;
517}
518
520{
521 MFEM_ASSERT(rank != INT_MAX, "invalid rank");
522 static std::vector<int> group;
523 group.resize(1);
524 group[0] = rank;
525 return GetGroupId(group);
526}
527
528bool ParNCMesh::GroupContains(GroupId id, int rank) const
529{
530 // TODO: would std::lower_bound() pay off here? Groups are usually small.
531 const CommGroup &group = groups[id];
532 for (unsigned i = 0; i < group.size(); i++)
533 {
534 if (group[i] == rank) { return true; }
535 }
536 return false;
537}
538
539void ParNCMesh::CreateGroups(int nentities, Array<Connection> &index_rank,
540 Array<GroupId> &entity_group)
541{
542 index_rank.Sort();
543 index_rank.Unique();
544
545 entity_group.SetSize(nentities);
546 entity_group = 0;
547
548 CommGroup group;
549
550 for (auto begin = index_rank.begin(); begin != index_rank.end(); /* nothing */)
551 {
552 const auto &index = begin->from;
553 if (index >= nentities) { break; }
554
555 // Locate the next connection that is not from this index
556 const auto end = std::find_if(begin, index_rank.end(),
557 [&index](const mfem::Connection &c) { return c.from != index;});
558
559 // For each connection from this index, collect the ranks connected.
560 group.resize(std::distance(begin, end));
561 std::transform(begin, end, group.begin(), [](const mfem::Connection &c) { return c.to; });
562
563 // assign this entity's group and advance the search start
564 entity_group[index] = GetGroupId(group);
565 begin = end;
566 }
567}
568
569void ParNCMesh::AddConnections(int entity, int index, const Array<int> &ranks)
570{
571 for (auto rank : ranks)
572 {
573 entity_index_rank[entity].Append(Connection(index, rank));
574 }
575}
576
578{
579 // make sure all entity_index_rank[i] arrays are filled
583
584 int v[4], e[4], eo[4];
585
586 Array<int> ranks;
587 ranks.Reserve(256);
588
589 // connect slave edges to master edges and their vertices
590 for (const auto &master_edge : shared_edges.masters)
591 {
592 ranks.SetSize(0);
593 for (int j = master_edge.slaves_begin; j < master_edge.slaves_end; j++)
594 {
595 int owner = entity_owner[1][edge_list.slaves[j].index];
596 ranks.Append(groups[owner][0]);
597 }
598 ranks.Sort();
599 ranks.Unique();
600
601 AddConnections(1, master_edge.index, ranks);
602
603 GetEdgeVertices(master_edge, v);
604 for (int j = 0; j < 2; j++)
605 {
606 AddConnections(0, v[j], ranks);
607 }
608 }
609
610 // connect slave faces to master faces and their edges and vertices
611 for (const auto &master_face : shared_faces.masters)
612 {
613 ranks.SetSize(0);
614 for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
615 {
616 const int si = face_list.slaves[j].index;
617 const int owner =
618 (si >= 0) ? entity_owner[2][si] : // standard face dependency
619 entity_owner[1][FlipIndexSign(si)]; // prism edge-face dep
620 ranks.Append(groups[owner][0]);
621 }
622 ranks.Sort();
623 ranks.Unique();
624
625 AddConnections(2, master_face.index, ranks);
626
627 int nfv = GetFaceVerticesEdges(master_face, v, e, eo);
628 for (int j = 0; j < nfv; j++)
629 {
630 AddConnections(0, v[j], ranks);
631 AddConnections(1, e[j], ranks);
632 }
633 }
634
635 int nentities[3] =
636 {
640 };
641
642 // compress the index-rank arrays into group representation
643 for (int i = 0; i < 3; i++)
644 {
646 entity_index_rank[i].DeleteAll();
647 }
648}
649
651 const Element &e2,
652 int local[2])
653{
654 // Return face orientation in e2, assuming the face has orientation 0 in e1.
655 int ids[2][4];
656 const Element * const e[2] = { &e1, &e2 };
657 for (int i = 0; i < 2; i++)
658 {
659 // get local face number (remember that p1, p2, p3 are not in order, and
660 // p4 is not stored)
661 int lf = find_local_face(e[i]->Geom(),
662 find_node(*e[i], face.p1),
663 find_node(*e[i], face.p2),
664 find_node(*e[i], face.p3));
665 // optional output
666 if (local) { local[i] = lf; }
667
668 // get node IDs for the face as seen from e[i]
669 const int* fv = GI[e[i]->Geom()].faces[lf];
670 for (int j = 0; j < 4; j++)
671 {
672 ids[i][j] = e[i]->node[fv[j]];
673 }
674 }
675
676 return (ids[0][3] >= 0) ? Mesh::GetQuadOrientation(ids[0], ids[1])
677 /* */ : Mesh::GetTriOrientation(ids[0], ids[1]);
678}
679
681{
682 if (Dim < 3) { return; }
683
684 // Calculate orientation of shared conforming faces.
685 // NOTE: face orientation is calculated relative to its lower rank element.
686 // Thanks to the ghost layer this can be done locally, without communication.
687
689 face_orient = 0;
690
691 for (const auto &face : faces)
692 {
693 if (face.elem[0] >= 0 && face.elem[1] >= 0 && face.index < NFaces)
694 {
695 Element *e1 = &elements[face.elem[0]];
696 Element *e2 = &elements[face.elem[1]];
697
698 if (e1->rank == e2->rank) { continue; }
699 if (e1->rank > e2->rank) { std::swap(e1, e2); }
700
701 face_orient[face.index] = get_face_orientation(face, *e1, *e2);
702 }
703 }
704}
705
706void ParNCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
707 Array<int> &bdr_vertices,
708 Array<int> &bdr_edges, Array<int> &bdr_faces)
709{
710 NCMesh::GetBoundaryClosure(bdr_attr_is_ess, bdr_vertices, bdr_edges, bdr_faces);
711
712 if (Dim == 3)
713 {
714 // Mark masters of shared slave boundary faces as essential boundary
715 // faces. Some master faces may only have slave children.
716 for (const auto &mf : shared_faces.masters)
717 {
718 if (elements[mf.element].rank != MyRank) { continue; }
719 for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
720 {
721 const auto &sf = GetFaceList().slaves[j];
722 if (sf.index < 0)
723 {
724 // Edge-face constraint. Skip this edge.
725 continue;
726 }
727 const Face &face = *GetFace(elements[sf.element], sf.local);
728 if (face.Boundary() && bdr_attr_is_ess[face.attribute - 1])
729 {
730 bdr_faces.Append(mf.index);
731 }
732 }
733 }
734 }
735 else if (Dim == 2)
736 {
737 // Mark masters of shared slave boundary edges as essential boundary
738 // edges. Some master edges may only have slave children.
739 for (const auto &me : shared_edges.masters)
740 {
741 if (elements[me.element].rank != MyRank) { continue; }
742 for (int j = me.slaves_begin; j < me.slaves_end; j++)
743 {
744 const auto &se = GetEdgeList().slaves[j];
745 Face *face = GetFace(elements[se.element], se.local);
746 if (face && face->Boundary() && bdr_attr_is_ess[face->attribute - 1])
747 {
748 bdr_edges.Append(me.index);
749 }
750 }
751 }
752 }
753
754 // Filter, sort and unique an array, so it contains only local unique values.
755 auto FilterSortUnique = [](Array<int> &v, int N)
756 {
757 // Perform the O(N) filter before the O(NlogN) sort.
758 auto local = std::remove_if(v.begin(), v.end(), [N](int i) { return i >= N; });
759 std::sort(v.begin(), local);
760 v.SetSize(std::distance(v.begin(), std::unique(v.begin(), local)));
761 };
762
763 FilterSortUnique(bdr_vertices, NVertices);
764 FilterSortUnique(bdr_edges, NEdges);
765 FilterSortUnique(bdr_faces, NFaces);
766}
767
768
769//// Neighbors /////////////////////////////////////////////////////////////////
770
772{
773 if (element_type.Size()) { return; }
774
775 int nleaves = leaf_elements.Size();
776
777 element_type.SetSize(nleaves);
778 for (int i = 0; i < nleaves; i++)
779 {
780 element_type[i] = (elements[leaf_elements[i]].rank == MyRank) ? 1 : 0;
781 }
782
783 // determine the ghost layer
784 Array<char> ghost_set;
785 FindSetNeighbors(element_type, NULL, &ghost_set);
786
787 // find the neighbors of the ghost layer
788 Array<char> boundary_set;
789 FindSetNeighbors(ghost_set, NULL, &boundary_set);
790
793 for (int i = 0; i < nleaves; i++)
794 {
795 char &etype = element_type[i];
796 if (ghost_set[i])
797 {
798 etype = 2;
800 }
801 else if (boundary_set[i] && etype)
802 {
803 etype = 3;
805 }
806 }
807}
808
809bool ParNCMesh::CheckElementType(int elem, int type)
810{
811 Element &el = elements[elem];
812 if (!el.ref_type)
813 {
814 return (element_type[el.index] == type);
815 }
816 else
817 {
818 for (int i = 0; i < 8 && el.child[i] >= 0; i++)
819 {
820 if (!CheckElementType(el.child[i], type)) { return false; }
821 }
822 return true;
823 }
824}
825
827{
828 ranks.SetSize(0); // preserve capacity
829
830 // big shortcut: there are no neighbors if element_type == 1
831 if (CheckElementType(elem, 1)) { return; }
832
833 // ok, we do need to look for neighbors;
834 // at least we can only search in the ghost layer
837
838 // return a list of processors
839 for (int i = 0; i < tmp_neighbors.Size(); i++)
840 {
841 ranks.Append(elements[tmp_neighbors[i]].rank);
842 }
843 ranks.Sort();
844 ranks.Unique();
845}
846
847template<class T>
848static void set_to_array(const std::set<T> &set, Array<T> &array)
849{
850 array.Reserve(static_cast<int>(set.size()));
851 array.SetSize(0);
852 for (auto x : set)
853 {
854 array.Append(x);
855 }
856}
857
859{
860 UpdateLayers();
861
862 // TODO: look at groups instead?
863
864 std::set<int> ranks;
865 for (int i = 0; i < ghost_layer.Size(); i++)
866 {
867 ranks.insert(elements[ghost_layer[i]].rank);
868 }
869 set_to_array(ranks, neighbors);
870}
871
872
873//// ParMesh compatibility /////////////////////////////////////////////////////
874
875void ParNCMesh::MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
876 Table &group_shared, Array<char> *entity_geom,
877 char geom)
878{
879 const Array<GroupId> &conf_group = entity_conf_group[ent];
880
881 group_shared.MakeI(ngroups-1);
882
883 // count shared entities
884 int num_shared = 0;
885 for (int i = 0; i < conf_group.Size(); i++)
886 {
887 if (conf_group[i])
888 {
889 if (entity_geom && (*entity_geom)[i] != geom) { continue; }
890
891 num_shared++;
892 group_shared.AddAColumnInRow(conf_group[i]-1);
893 }
894 }
895
896 shared_local.SetSize(num_shared);
897 group_shared.MakeJ();
898
899 // fill shared_local and group_shared
900 for (int i = 0, j = 0; i < conf_group.Size(); i++)
901 {
902 if (conf_group[i])
903 {
904 if (entity_geom && (*entity_geom)[i] != geom) { continue; }
905
906 shared_local[j] = i;
907 group_shared.AddConnection(conf_group[i]-1, j);
908 j++;
909 }
910 }
911 group_shared.ShiftUpI();
912
913 // sort the groups consistently across processors
914 for (int i = 0; i < group_shared.Size(); i++)
915 {
916 int size = group_shared.RowSize(i);
917 int *row = group_shared.GetRow(i);
918
919 Array<int> ref_row(row, size);
920 ref_row.Sort([&](const int a, const int b)
921 {
922 int el_loc_a = entity_elem_local[ent][shared_local[a]];
923 int el_loc_b = entity_elem_local[ent][shared_local[b]];
924
925 int lsi_a = leaf_sfc_index[el_loc_a >> 4];
926 int lsi_b = leaf_sfc_index[el_loc_b >> 4];
927
928 if (lsi_a != lsi_b) { return lsi_a < lsi_b; }
929
930 return (el_loc_a & 0xf) < (el_loc_b & 0xf);
931 });
932 }
933}
934
936{
937 if (leaf_elements.Size())
938 {
939 // make sure we have entity_conf_group[x] and the ordering arrays
940 for (int ent = 0; ent < Dim; ent++)
941 {
942 GetSharedList(ent);
943 MFEM_VERIFY(entity_conf_group[ent].Size() ||
944 pmesh.GetNE() == 0, "Non empty partitions must be connected");
945 MFEM_VERIFY(entity_elem_local[ent].Size() ||
946 pmesh.GetNE() == 0, "Non empty partitions must be connected");
947 }
948 }
949
950 // create ParMesh groups, and the map (ncmesh_group -> pmesh_group)
951 Array<int> group_map(static_cast<int>(groups.size()));
952 {
953 group_map = 0;
954 IntegerSet iset;
955 ListOfIntegerSets int_groups;
956 for (unsigned i = 0; i < groups.size(); i++)
957 {
958 if (groups[i].size() > 1 || !i) // skip singleton groups
959 {
960 iset.Recreate(static_cast<int>(groups[i].size()), groups[i].data());
961 group_map[i] = int_groups.Insert(iset);
962 }
963 }
964 pmesh.gtopo.Create(int_groups, 822);
965 }
966
967 // renumber groups in entity_conf_group[] (due to missing singletons)
968 for (int ent = 0; ent < 3; ent++)
969 {
970 for (int i = 0; i < entity_conf_group[ent].Size(); i++)
971 {
972 GroupId &ecg = entity_conf_group[ent][i];
973 ecg = group_map[ecg];
974 }
975 }
976
977 // create shared to local index mappings and group tables
978 int ng = pmesh.gtopo.NGroups();
979 MakeSharedTable(ng, 0, pmesh.svert_lvert, pmesh.group_svert);
980 MakeSharedTable(ng, 1, pmesh.sedge_ledge, pmesh.group_sedge);
981
982 Array<int> slt, slq;
985
986 pmesh.sface_lface = slt;
987 pmesh.sface_lface.Append(slq);
988
989 // create shared_edges
990 for (int i = 0; i < pmesh.shared_edges.Size(); i++)
991 {
992 delete pmesh.shared_edges[i];
993 }
994 pmesh.shared_edges.SetSize(pmesh.sedge_ledge.Size());
995 for (int i = 0; i < pmesh.shared_edges.Size(); i++)
996 {
997 int el_loc = entity_elem_local[1][pmesh.sedge_ledge[i]];
998 MeshId edge_id(-1, leaf_elements[(el_loc >> 4)], (el_loc & 0xf));
999
1000 int v[2];
1001 GetEdgeVertices(edge_id, v, false);
1002 pmesh.shared_edges[i] = new Segment(v, 1);
1003 }
1004
1005 // create shared_trias
1006 pmesh.shared_trias.SetSize(slt.Size());
1007 for (int i = 0; i < slt.Size(); i++)
1008 {
1009 int el_loc = entity_elem_local[2][slt[i]];
1010 MeshId face_id(-1, leaf_elements[(el_loc >> 4)], (el_loc & 0xf));
1011
1012 int v[4], e[4], eo[4];
1013 GetFaceVerticesEdges(face_id, v, e, eo);
1014 pmesh.shared_trias[i].Set(v);
1015 }
1016
1017 // create shared_quads
1018 pmesh.shared_quads.SetSize(slq.Size());
1019 for (int i = 0; i < slq.Size(); i++)
1020 {
1021 int el_loc = entity_elem_local[2][slq[i]];
1022 MeshId face_id(-1, leaf_elements[(el_loc >> 4)], (el_loc & 0xf));
1023
1024 int e[4], eo[4];
1025 GetFaceVerticesEdges(face_id, pmesh.shared_quads[i].v, e, eo);
1026 }
1027
1028 // free the arrays, they're not needed anymore (until next mesh update)
1029 for (int ent = 0; ent < Dim; ent++)
1030 {
1033 }
1034}
1035
1037{
1038 ClearAuxPM();
1039
1040 const NCList &shared = (Dim == 3) ? GetSharedFaces() : GetSharedEdges();
1041 const NCList &full_list = (Dim == 3) ? GetFaceList() : GetEdgeList();
1042
1043 Array<Element*> fnbr;
1044 Array<Connection> send_elems;
1045 std::map<int, std::vector<int>> recv_elems;
1046
1047 // Counts the number of slave faces of a master. This may be larger than the
1048 // number of shared slaves if there exist degenerate slave-faces from
1049 // face-edge constraints.
1050 auto count_slaves = [&](int i, const Master& x)
1051 {
1052 return i + (x.slaves_end - x.slaves_begin);
1053 };
1054
1055 const int bound = shared.conforming.Size() + std::accumulate(
1056 shared.masters.begin(), shared.masters.end(),
1057 0, count_slaves);
1058
1059 fnbr.Reserve(bound);
1060 send_elems.Reserve(bound);
1061
1062 // If there are face neighbor elements with triangular faces, the
1063 // `face_nbr_el_ori` structure will need to be built. This requires
1064 // communication so we attempt to avoid it by checking first.
1065 bool face_nbr_w_tri_faces = false;
1066
1067 // go over all shared faces and collect face neighbor elements
1068 for (int i = 0; i < shared.conforming.Size(); i++)
1069 {
1070 const MeshId &cf = shared.conforming[i];
1071 Face* face = GetFace(elements[cf.element], cf.local);
1072 MFEM_ASSERT(face != NULL, "");
1073
1074 MFEM_ASSERT(face->elem[0] >= 0 && face->elem[1] >= 0, "");
1075 Element* e[2] = { &elements[face->elem[0]], &elements[face->elem[1]] };
1076
1077 if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
1078 MFEM_ASSERT(e[0]->rank != MyRank && e[1]->rank == MyRank, "");
1079
1080 face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
1081 face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
1082
1083 fnbr.Append(e[0]);
1084 send_elems.Append(Connection(e[0]->rank, e[1]->index));
1085 recv_elems[e[0]->rank].push_back(e[0]->index);
1086 }
1087
1088 for (int i = 0; i < shared.masters.Size(); i++)
1089 {
1090 const Master &mf = shared.masters[i];
1091 for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
1092 {
1093 const Slave &sf = full_list.slaves[j];
1094 if (sf.element < 0 || sf.index < 0) { continue; }
1095
1096 MFEM_ASSERT(mf.element >= 0, "");
1097 Element* e[2] = { &elements[mf.element], &elements[sf.element] };
1098
1099 bool loc0 = (e[0]->rank == MyRank);
1100 bool loc1 = (e[1]->rank == MyRank);
1101 if (loc0 == loc1)
1102 {
1103 // neither or both of these elements are on this rank.
1104 continue;
1105 }
1106 if (loc0) { std::swap(e[0], e[1]); }
1107
1108 face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
1109 face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
1110
1111 fnbr.Append(e[0]);
1112 send_elems.Append(Connection(e[0]->rank, e[1]->index));
1113 recv_elems[e[0]->rank].push_back(e[0]->index);
1114 }
1115 }
1116
1117 MFEM_ASSERT(fnbr.Size() <= bound,
1118 "oops, bad upper bound. fnbr.Size(): " << fnbr.Size() << ", bound: " << bound);
1119
1120 // remove duplicate face neighbor elements and sort them by rank & index
1121 // (note that the send table is sorted the same way and the order is also the
1122 // same on different processors, this is important for ExchangeFaceNbrData)
1123 fnbr.Sort();
1124 fnbr.Unique();
1125 fnbr.Sort([](const Element* a, const Element* b)
1126 {
1127 return (a->rank != b->rank) ? a->rank < b->rank
1128 /* */ : a->index < b->index;
1129 });
1130
1131 // put the ranks into 'face_nbr_group'
1132 for (int i = 0; i < fnbr.Size(); i++)
1133 {
1134 if (!i || fnbr[i]->rank != pmesh.face_nbr_group.Last())
1135 {
1136 pmesh.face_nbr_group.Append(fnbr[i]->rank);
1137 }
1138 }
1139 const int nranks = pmesh.face_nbr_group.Size();
1140
1141 // create a new mfem::Element for each face neighbor element
1142 pmesh.face_nbr_elements.SetSize(0);
1143 pmesh.face_nbr_elements.Reserve(fnbr.Size());
1144
1147
1148 Array<int> fnbr_index(NGhostElements);
1149 fnbr_index = -1;
1150
1151 std::map<int, int> vert_map;
1152 for (int i = 0; i < fnbr.Size(); i++)
1153 {
1154 NCMesh::Element* elem = fnbr[i];
1155 mfem::Element* fne = NewMeshElement(elem->geom);
1156 fne->SetAttribute(elem->attribute);
1157 pmesh.face_nbr_elements.Append(fne);
1158
1159 GeomInfo& gi = GI[(int) elem->geom];
1160 for (int k = 0; k < gi.nv; k++)
1161 {
1162 int &v = vert_map[elem->node[k]];
1163 if (!v) { v = static_cast<int>(vert_map.size()); }
1164 fne->GetVertices()[k] = v-1;
1165 }
1166
1167 if (!i || elem->rank != fnbr[i-1]->rank)
1168 {
1170 }
1171
1172 MFEM_ASSERT(elem->index >= NElements, "not a ghost element");
1173 fnbr_index[elem->index - NElements] = i;
1174 }
1175 pmesh.face_nbr_elements_offset.Append(fnbr.Size());
1176
1177 // create vertices in 'face_nbr_vertices'
1178 {
1179 pmesh.face_nbr_vertices.SetSize(static_cast<int>(vert_map.size()));
1180 if (coordinates.Size())
1181 {
1182 tmp_vertex = new TmpVertex[nodes.NumIds()]; // TODO: something cheaper?
1183 for (const auto &v : vert_map)
1184 {
1185 pmesh.face_nbr_vertices[v.second-1].SetCoords(
1186 spaceDim, CalcVertexPos(v.first));
1187 }
1188 delete [] tmp_vertex;
1189 }
1190 }
1191
1192 // make the 'send_face_nbr_elements' table
1193 send_elems.Sort();
1194 send_elems.Unique();
1195
1196 for (auto &kv : recv_elems)
1197 {
1198 std::sort(kv.second.begin(), kv.second.end());
1199 kv.second.erase(std::unique(kv.second.begin(), kv.second.end()),
1200 kv.second.end());
1201 }
1202
1203 for (int i = 0, last_rank = -1; i < send_elems.Size(); i++)
1204 {
1205 Connection &c = send_elems[i];
1206 if (c.from != last_rank)
1207 {
1208 // renumber rank to position in 'face_nbr_group'
1209 last_rank = c.from;
1210 c.from = pmesh.face_nbr_group.Find(c.from);
1211 }
1212 else
1213 {
1214 c.from = send_elems[i-1].from; // avoid search
1215 }
1216 }
1217 pmesh.send_face_nbr_elements.MakeFromList(nranks, send_elems);
1218
1219 // go over the shared faces again and modify their Mesh::FaceInfo
1220 for (const auto& cf : shared.conforming)
1221 {
1222 Face* face = GetFace(elements[cf.element], cf.local);
1223 Element* e[2] = { &elements[face->elem[0]], &elements[face->elem[1]] };
1224 if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
1225
1226 Mesh::FaceInfo &fi = pmesh.faces_info[cf.index];
1227 fi.Elem2No = FlipIndexSign(fnbr_index[e[0]->index - NElements]);
1228
1229 if (Dim == 3)
1230 {
1231 int local[2];
1232 int o = get_face_orientation(*face, *e[1], *e[0], local);
1233 fi.Elem2Inf = 64*local[1] + o;
1234 }
1235 else
1236 {
1237 fi.Elem2Inf = 64*find_element_edge(*e[0], face->p1, face->p3) + 1;
1238 }
1239 }
1240
1241 // If there are shared slaves, they will also need to be updated. First,
1242 // check whether the update has already been done.
1243 bool sharedUpdated = false;
1244 if (shared.slaves.Size())
1245 {
1246 int nfaces = NFaces, nghosts = NGhostFaces;
1247 if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
1248 sharedUpdated = (pmesh.faces_info.Size() == nfaces + nghosts);
1249 }
1250
1251 if (shared.slaves.Size() && !sharedUpdated)
1252 {
1253 int nfaces = NFaces, nghosts = NGhostFaces;
1254 if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
1255
1256 // enlarge Mesh::faces_info for ghost slaves
1257 MFEM_ASSERT(pmesh.GetNumFaces() == nfaces, "");
1258 pmesh.faces_info.SetSize(nfaces + nghosts);
1259 for (int i = nfaces; i < pmesh.faces_info.Size(); i++)
1260 {
1261 Mesh::FaceInfo &fi = pmesh.faces_info[i];
1262 fi.Elem1No = fi.Elem2No = -1;
1263 fi.Elem1Inf = fi.Elem2Inf = -1;
1264 fi.NCFace = -1;
1265 }
1266 // Note that some of the indices i >= nfaces in pmesh.faces_info will
1267 // remain untouched below and they will have Elem1No == -1, in particular.
1268
1269 // fill in FaceInfo for shared slave faces
1270 for (int i = 0; i < shared.masters.Size(); i++)
1271 {
1272 const Master &mf = shared.masters[i];
1273 for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
1274 {
1275 const Slave &sf = full_list.slaves[j];
1276 if (sf.element < 0) { continue; }
1277
1278 MFEM_ASSERT(mf.element >= 0, "");
1279 Element &sfe = elements[sf.element];
1280 Element &mfe = elements[mf.element];
1281
1282 bool sloc = (sfe.rank == MyRank);
1283 bool mloc = (mfe.rank == MyRank);
1284 if (sloc == mloc // both or neither face is owned by this processor
1285 || sf.index < 0) // the face is degenerate (i.e. a edge-face constraint)
1286 {
1287 continue;
1288 }
1289
1290 // This is a genuine slave face, the info associated with it must
1291 // be updated.
1292 Mesh::FaceInfo &fi = pmesh.faces_info[sf.index];
1293 fi.Elem1No = sfe.index;
1294 fi.Elem2No = mfe.index;
1295 fi.Elem1Inf = 64 * sf.local;
1296 fi.Elem2Inf = 64 * mf.local;
1297
1298 if (!sloc)
1299 {
1300 // 'fi' is the info for a ghost slave face with index:
1301 // sf.index >= nfaces
1302 std::swap(fi.Elem1No, fi.Elem2No);
1303 std::swap(fi.Elem1Inf, fi.Elem2Inf);
1304 // After the above swap, Elem1No refers to the local, master-side
1305 // element. In other words, side 1 IS NOT the side that generated
1306 // the face.
1307 }
1308 else
1309 {
1310 // 'fi' is the info for a local slave face with index:
1311 // sf.index < nfaces
1312 // Here, Elem1No refers to the local, slave-side element.
1313 // In other words, side 1 IS the side that generated the face.
1314 }
1315 MFEM_ASSERT(fi.Elem2No >= NElements, "");
1316 fi.Elem2No = FlipIndexSign(fnbr_index[fi.Elem2No - NElements]);
1317
1318 const DenseMatrix* pm = full_list.point_matrices[sf.geom][sf.matrix];
1319 if (!sloc && Dim == 3)
1320 {
1321 // ghost slave in 3D needs flipping orientation
1322 DenseMatrix* pm2 = new DenseMatrix(*pm);
1323 if (sf.geom == Geometry::Type::SQUARE)
1324 {
1325 std::swap((*pm2)(0, 1), (*pm2)(0, 3));
1326 std::swap((*pm2)(1, 1), (*pm2)(1, 3));
1327 }
1328 else if (sf.geom == Geometry::Type::TRIANGLE)
1329 {
1330 std::swap((*pm2)(0, 0), (*pm2)(0, 1));
1331 std::swap((*pm2)(1, 0), (*pm2)(1, 1));
1332 }
1333 aux_pm_store.Append(pm2);
1334
1335 fi.Elem2Inf ^= 1;
1336 pm = pm2;
1337
1338 // The problem is that sf.point_matrix is designed for P matrix
1339 // construction and always has orientation relative to the slave
1340 // face. In ParMesh::GetSharedFaceTransformations the result
1341 // would therefore be the same on both processors, which is not
1342 // how that function works for conforming faces. The orientation
1343 // of Loc1, Loc2 and Face needs to always be relative to Element
1344 // 1, which is the element containing the slave face on one
1345 // processor, but on the other it is the element containing the
1346 // master face. In the latter case we need to flip the pm.
1347 }
1348 else if (!sloc && Dim == 2)
1349 {
1350 fi.Elem2Inf ^= 1; // set orientation to 1
1351 // The point matrix (used to define "side 1" which is the same as
1352 // "parent side" in this case) does not require a flip since it
1353 // is aligned with the parent side, so NO flip is performed in
1354 // Mesh::ApplyLocalSlaveTransformation.
1355 }
1356
1357 fi.NCFace = pmesh.nc_faces_info.Size();
1358 pmesh.nc_faces_info.Append(Mesh::NCFaceInfo(true, sf.master, pm));
1359 }
1360 }
1361 }
1362
1363 // In 3D some extra orientation data structures can be needed.
1364 if (Dim == 3)
1365 {
1366 // Populates face_nbr_el_to_face, always needed.
1368
1369 if (face_nbr_w_tri_faces)
1370 {
1371 // There are face neighbor elements with triangular faces, need to
1372 // perform communication to ensure the orientation is valid.
1373 using RankToOrientation = std::map<int, std::vector<std::array<int, 6>>>;
1374 constexpr std::array<int, 6> unset_ori{{-1,-1,-1,-1,-1,-1}};
1375 const int rank = pmesh.GetMyRank();
1376
1377 // Loop over send elems, compute the orientation and place in the
1378 // buffer to send to each processor. Note elements are
1379 // lexicographically sorted with rank and element number, and this
1380 // ordering holds across processors.
1381 RankToOrientation send_rank_to_face_neighbor_orientations;
1382 Array<int> orientations, faces;
1383
1384 // send_elems goes from rank of the receiving processor, to the index
1385 // of the face neighbor element on this processor.
1386 for (const auto &se : send_elems)
1387 {
1388 const auto &true_rank = pmesh.face_nbr_group[se.from];
1389 pmesh.GetElementFaces(se.to, faces, orientations);
1390
1391 // Place a new entry of unset orientations
1392 send_rank_to_face_neighbor_orientations[true_rank].emplace_back(unset_ori);
1393
1394 // Copy the entries, any unset faces will remain -1.
1395 std::copy(orientations.begin(), orientations.end(),
1396 send_rank_to_face_neighbor_orientations[true_rank].back().begin());
1397 }
1398
1399 // Initialize the receive buffers and resize to match the expected
1400 // number of elements coming in. The copy ensures the appropriate rank
1401 // pairings are in place, and for a purely conformal interface, the
1402 // resize is a no-op.
1403 auto recv_rank_to_face_neighbor_orientations =
1404 send_rank_to_face_neighbor_orientations;
1405 for (auto &kv : recv_rank_to_face_neighbor_orientations)
1406 {
1407 kv.second.resize(recv_elems[kv.first].size());
1408 }
1409
1410 // For asynchronous send/recv, will use arrays of requests to monitor the
1411 // status of the connections.
1412 std::vector<MPI_Request> send_requests, recv_requests;
1413 std::vector<MPI_Status> status(nranks);
1414
1415 // NOTE: This is CRITICAL, to ensure the addresses of these requests
1416 // do not change between the send/recv and the wait.
1417 send_requests.reserve(nranks);
1418 recv_requests.reserve(nranks);
1419
1420 // Shared face communication is bidirectional -> any rank to whom
1421 // orientations must be sent, will need to send orientations back. The
1422 // orientation data is contiguous because std::array<int,6> is an
1423 // aggregate. Loop over each communication pairing, and dispatch the
1424 // buffer loaded with all the orientation data.
1425 for (const auto &kv : send_rank_to_face_neighbor_orientations)
1426 {
1427 send_requests.emplace_back(); // instantiate a request for tracking.
1428
1429 // low rank sends on low, high rank sends on high.
1430 const int send_tag = (rank < kv.first)
1431 ? std::min(rank, kv.first)
1432 : std::max(rank, kv.first);
1433 MPI_Isend(const_cast<int*>(&kv.second[0][0]), int(kv.second.size() * 6),
1434 MPI_INT, kv.first, send_tag, pmesh.MyComm, &send_requests.back());
1435 }
1436
1437 // Loop over the communication pairing again, and receive the
1438 // symmetric buffer from the other processor.
1439 for (auto &kv : recv_rank_to_face_neighbor_orientations)
1440 {
1441 recv_requests.emplace_back(); // instantiate a request for tracking
1442
1443 // low rank receives on high, high rank receives on low.
1444 const int recv_tag = (rank < kv.first)
1445 ? std::max(rank, kv.first)
1446 : std::min(rank, kv.first);
1447 MPI_Irecv(&kv.second[0][0], int(kv.second.size() * 6),
1448 MPI_INT, kv.first, recv_tag, pmesh.MyComm, &recv_requests.back());
1449 }
1450
1451 // Wait until all receive buffers are full before beginning to process.
1452 MPI_Waitall(int(recv_requests.size()), recv_requests.data(), status.data());
1453
1454 pmesh.face_nbr_el_ori.reset(new Table(pmesh.face_nbr_elements.Size(), 6));
1455 int elem = 0;
1456 for (const auto &kv : recv_rank_to_face_neighbor_orientations)
1457 {
1458 // All elements associated to this face-neighbor rank
1459 for (const auto &eo : kv.second)
1460 {
1461 std::copy(eo.begin(), eo.end(), pmesh.face_nbr_el_ori->GetRow(elem));
1462 ++elem;
1463 }
1464 }
1465 pmesh.face_nbr_el_ori->Finalize();
1466
1467 // Must wait for all send buffers to be released before the scope closes.
1468 MPI_Waitall(int(send_requests.size()), send_requests.data(), status.data());
1469 }
1470 }
1471 // NOTE: this function skips ParMesh::send_face_nbr_vertices and
1472 // ParMesh::face_nbr_vertices_offset, these are not used outside of ParMesh
1473}
1474
1476{
1477 for (int i = 0; i < aux_pm_store.Size(); i++)
1478 {
1479 delete aux_pm_store[i];
1480 }
1481 aux_pm_store.DeleteAll();
1482}
1483
1484//// Prune, Refine, Derefine ///////////////////////////////////////////////////
1485
1487{
1488 Element &el = elements[elem];
1489 if (el.ref_type)
1490 {
1491 bool remove[8];
1492 bool removeAll = true;
1493
1494 // determine which subtrees can be removed (and whether it's all of them)
1495 for (int i = 0; i < 8; i++)
1496 {
1497 remove[i] = false;
1498 if (el.child[i] >= 0)
1499 {
1500 remove[i] = PruneTree(el.child[i]);
1501 if (!remove[i]) { removeAll = false; }
1502 }
1503 }
1504
1505 // all children can be removed, let the (maybe indirect) parent do it
1506 if (removeAll) { return true; }
1507
1508 // not all children can be removed, but remove those that can be
1509 for (int i = 0; i < 8; i++)
1510 {
1511 if (remove[i]) { DerefineElement(el.child[i]); }
1512 }
1513
1514 return false; // need to keep this element and up
1515 }
1516 else
1517 {
1518 // return true if this leaf can be removed
1519 return el.rank < 0;
1520 }
1521}
1522
1524{
1525 if (!Iso && Dim == 3)
1526 {
1527 if (MyRank == 0)
1528 {
1529 MFEM_WARNING("Can't prune 3D aniso meshes yet.");
1530 }
1531 return;
1532 }
1533
1534 UpdateLayers();
1535
1536 for (int i = 0; i < leaf_elements.Size(); i++)
1537 {
1538 // rank of elements beyond the ghost layer is unknown / not updated
1539 if (element_type[i] == 0)
1540 {
1541 elements[leaf_elements[i]].rank = -1;
1542 // NOTE: rank == -1 will make the element disappear from leaf_elements
1543 // on next Update, see NCMesh::CollectLeafElements
1544 }
1545 }
1546
1547 // derefine subtrees whose leaves are all unneeded
1548 for (int i = 0; i < root_state.Size(); i++)
1549 {
1550 if (PruneTree(i)) { DerefineElement(i); }
1551 }
1552
1553 Update();
1554}
1555
1557 std::set<int> &conflicts)
1558{
1559 if (Dim < 3 || NRanks == 1) { return false; }
1560
1561 for (int i = 0; i < refinements.Size() && Iso; i++)
1562 {
1563 const Refinement &ref = refinements[i];
1564 if (ref.GetType() != Refinement::XYZ)
1565 {
1566 Iso = false;
1567 }
1568 }
1569
1570 // Reduce the Iso flag over all MPI ranks.
1571 bool globalIso = false;
1572 MPI_Allreduce(&Iso, &globalIso, 1, MFEM_MPI_CXX_BOOL, MPI_LAND, MyComm);
1573
1574 if (globalIso) { return false; }
1575
1576 // In the 3D parallel anisotropic case, check for conflicts on faces.
1578
1579 // Create refinement messages to all neighbors (NOTE: some may be empty).
1580 Array<int> neighbors;
1581 NeighborProcessors(neighbors);
1582 for (int i = 0; i < neighbors.Size(); i++)
1583 {
1584 send_ref[neighbors[i]].SetNCMesh(this);
1585 }
1586
1587 // Populate messages: all refinements that occur next to the processor
1588 // boundary need to be sent to the adjoining neighbors so they can keep
1589 // their ghost layer up to date.
1590 Array<int> ranks;
1591 ranks.Reserve(64);
1592 for (int i = 0; i < refinements.Size(); i++)
1593 {
1594 const Refinement &ref = refinements[i];
1595 MFEM_ASSERT(ref.index < NElements, "");
1596 const int elem = leaf_elements[ref.index];
1597 ElementNeighborProcessors(elem, ranks);
1598 for (int j = 0; j < ranks.Size(); j++)
1599 {
1600 send_ref[ranks[j]].AddRefinement(elem, ref);
1601 }
1602 }
1603
1604 // Send the messages (overlap with local refinements)
1606
1607 // Note that ghost refinements are not looked up using elemToRef. Local
1608 // refinements are recorded first in elemToRef, and ghosts only need to be
1609 // compared to local refinements. There is no need for ghost-to-ghost
1610 // comparisons.
1611 std::map<int, int> elemToRef; // Only for local refinements, not ghosts.
1612 for (int i = 0; i < refinements.Size(); i++)
1613 {
1614 elemToRef[leaf_elements[refinements[i].index]] = i;
1615 }
1616
1617 // Check local refinements
1618 for (int i = 0; i < refinements.Size(); i++)
1619 {
1620 const Refinement &ref = refinements[i];
1621 CheckRefinement(leaf_elements[ref.index], ref, refinements, elemToRef,
1622 conflicts);
1623 }
1624
1625 // Receive (ghost layer) refinements from all neighbors
1626 for (int j = 0; j < neighbors.Size(); j++)
1627 {
1628 int rank, size;
1630
1632 msg.SetNCMesh(this);
1633 msg.Recv(rank, size, MyComm);
1634
1635 // check the ghost refinements
1636 for (int i = 0; i < msg.Size(); i++)
1637 {
1638 Refinement ghost_ref(msg.elements[i], msg.values[i].ref_type);
1639 ghost_ref.SetScaleForType(msg.values[i].scale);
1640 CheckRefinement(msg.elements[i], ghost_ref, refinements, elemToRef,
1641 conflicts);
1642 }
1643 }
1644
1645 // Make sure we can delete the send buffers
1647
1648 CheckRefinementMaster(refinements, elemToRef, conflicts);
1649
1650 const bool conflict = conflicts.size() > 0;
1651 bool globalConflict = false;
1652 MPI_Allreduce(&conflict, &globalConflict, 1, MFEM_MPI_CXX_BOOL, MPI_LOR,
1653 MyComm);
1654 return globalConflict;
1655}
1656
1657int GetHexFaceDir(int face)
1658{
1659 // Hexahedron face vertices
1660 // From Geometry::Constants<Geometry::CUBE>::FaceVert[6][4] in fem/geom.cpp
1661 // {3, 2, 1, 0}, {0, 1, 5, 4}, {1, 2, 6, 5},
1662 // {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}
1663 constexpr std::array<int, 6> hexFaceDir = {2, 1, 0, 1, 0, 2};
1664 return hexFaceDir[face];
1665}
1666
1667char GetHexFaceRefType(const bool (&refDir)[3], int face)
1668{
1669 const int faceDir = GetHexFaceDir(face);
1670 std::array<int, 2> faceRefDir;
1671 int cnt = 0;
1672 for (int d=0; d<3; ++d)
1673 {
1674 if (d != faceDir)
1675 {
1676 faceRefDir[cnt] = refDir[d] ? 1 : 0;
1677 cnt++;
1678 }
1679 }
1680
1681 const char ref_type = (char)(faceRefDir[0] + (2 * faceRefDir[1]));
1682 return ref_type;
1683}
1684
1685// Assuming a vertical split of the master face with ordered vertices
1686// (vn1, vn2, vn3, vn4), check whether there is a horizontal split among the
1687// slave faces of this face. This recursive function is similar to
1688// NCMesh::CheckAnisoFace.
1689bool ParNCMesh::CheckRefAnisoFaceSplits(int vn1, int vn2, int vn3, int vn4,
1690 int level)
1691{
1692 const int mid23 = FindMidEdgeNode(vn2, vn3);
1693 const int mid41 = FindMidEdgeNode(vn4, vn1);
1694
1695 if (mid23 >= 0 && mid41 >= 0) // If horizontally split
1696 {
1697 const int midf = nodes.FindId(mid23, mid41);
1698 if (midf >= 0)
1699 {
1700 if (CheckRefAnisoFaceSplits(vn1, vn2, mid23, mid41, level + 1))
1701 {
1702 return true;
1703 }
1704 if (CheckRefAnisoFaceSplits(mid41, mid23, vn3, vn4, level + 1))
1705 {
1706 return true;
1707 }
1708 }
1709 }
1710
1711 if (level > 0) { return true; }
1712
1713 return false;
1714}
1715
1717 const std::map<int, int> &elemToRef,
1718 std::set<int> &conflicts)
1719{
1720 MFEM_VERIFY(Dim == 3, "");
1721 const NCList &faceList = GetFaceList();
1722
1723 for (const auto &mf : faceList.masters)
1724 {
1725 // Check for conflicts only if the master element is marked for refinement
1726 if (elemToRef.count(mf.element) == 0) { continue; }
1727
1728 const int refIndex = elemToRef.at(mf.element);
1729 const Refinement& ref = refinements[refIndex];
1730
1731 bool refDir[3];
1732 for (int i=0; i<3; ++i)
1733 refDir[i] = ref.s[i] > real_t{0};
1734
1735 const char faceRefType = GetHexFaceRefType(refDir, mf.local);
1736 if (faceRefType == 0) { continue; } // No refinement on this face
1737
1738 std::array<int, 4> fv;
1739 for (int i=0; i<4; ++i)
1740 {
1741 fv[i] = elements[mf.element].node[
1743 }
1744
1745 if (faceRefType != 2) // X or XY split w.r.t. the face.
1746 {
1747 // Check X face split
1748 if (CheckRefAnisoFaceSplits(fv[0], fv[1], fv[2], fv[3]))
1749 {
1750 conflicts.insert(refIndex);
1751 }
1752 }
1753
1754 if (faceRefType != 1) // Y or XY split w.r.t. the face.
1755 {
1756 // Check Y face split
1757 if (CheckRefAnisoFaceSplits(fv[1], fv[2], fv[3], fv[0]))
1758 {
1759 conflicts.insert(refIndex);
1760 }
1761 }
1762 }
1763}
1764
1765int FindHexFace(const int* no, int vn1, int vn2, int vn3, int vn4)
1766{
1767 std::set<int> v;
1768 v.insert({vn1, vn2, vn3, vn4});
1769
1770 int face = -1;
1771 for (int f=0; f<6; ++f)
1772 {
1773 bool allFound = true;
1774 for (int i=0; i<4; ++i)
1775 {
1776 const int vi = no[Geometry::Constants<Geometry::CUBE>::FaceVert[f][i]];
1777 if (v.count(vi) == 0)
1778 {
1779 allFound = false;
1780 }
1781 }
1782
1783 if (allFound)
1784 {
1785 MFEM_ASSERT(face == -1, "");
1786 face = f;
1787 }
1788 }
1789
1790 MFEM_ASSERT(face >= 0, "");
1791 return face;
1792}
1793
1794// Assumption: v1 and v2 are indices of hex vertices connected by an edge.
1795// The return value is {0,1,2} denoting split {X,Y,Z}.
1796static int GetHexEdgeSplit(const int* nodes, int v1, int v2)
1797{
1798 Array<int> v(2);
1799 v[0] = v1;
1800 v[1] = v2;
1801 v.Sort();
1802
1803 // Find the edge in the hexahedron
1804 int edge = -1;
1805 Array<int> ev(2);
1806 for (int i=0; i<12; ++i)
1807 {
1808 for (int j=0; j<2; ++j)
1809 {
1810 ev[j] = nodes[Geometry::Constants<Geometry::CUBE>::Edges[i][j]];
1811 }
1812 ev.Sort();
1813
1814 if (ev == v)
1815 {
1816 MFEM_ASSERT(edge == -1, "");
1817 edge = i;
1818 }
1819 }
1820
1821 MFEM_ASSERT(edge >= 0, "");
1822
1823 constexpr int edgeDir[12] = {0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2};
1824 return edgeDir[edge];
1825}
1826
1827void ParNCMesh::CheckRefAnisoFace(const Refinement &ref, int elem,
1828 int vn1, int vn2, int vn3, int vn4,
1829 const Array<Refinement> &refinements,
1830 const std::map<int, int> &elemToRef,
1831 std::set<int> &conflicts)
1832{
1833 Face* face = faces.Find(vn1, vn2, vn3, vn4);
1834 if (!face) { return; }
1835
1836 // Find the neighbor of this face.
1837 const int nghbIndex = face->elem[0] == elem ? face->elem[1] : face->elem[0];
1838 if (nghbIndex < 0) { return; }
1839
1840 Element &nghb = elements[nghbIndex];
1841 MFEM_ASSERT(nghb.ref_type == 0, "");
1842
1843 if (elemToRef.count(nghbIndex) > 0)
1844 {
1845 const int refIndex = elemToRef.at(nghbIndex);
1846 const Refinement& nghb_ref = refinements[refIndex];
1847
1848 bool refDir[3];
1849 for (int i=0; i<3; ++i)
1850 refDir[i] = nghb_ref.s[i] > real_t{0};
1851
1852 const int localFace = FindHexFace(nghb.node, vn1, vn2, vn3, vn4);
1853 const int faceDir = GetHexFaceDir(localFace);
1854 const char face_ref_type = GetHexFaceRefType(refDir, localFace);
1855 const bool faceAniso = face_ref_type == 1 ||
1856 face_ref_type == 2; // X or Y w.r.t. the face.
1857
1858 if (faceAniso)
1859 {
1860 // Determine whether the face is anisotropically split in the vertical
1861 // direction, with respect to the vertex ordering (vn1, vn2, vn3, vn4).
1862 int hexSplitOnFace = -1;
1863
1864 const int firstFaceDir = face_ref_type == 1 ? 0 : 1;
1865
1866 int cnt = 0;
1867 for (int i=0; i<3; ++i)
1868 {
1869 if (i == faceDir) { continue; }
1870
1871 if (firstFaceDir == cnt)
1872 {
1873 MFEM_ASSERT(hexSplitOnFace == -1, "");
1874 hexSplitOnFace = i;
1875 }
1876
1877 cnt++;
1878 }
1879 MFEM_ASSERT(cnt == 2 && hexSplitOnFace >= 0, "");
1880
1881 const int edgeSplit = GetHexEdgeSplit(nghb.node, vn1, vn2);
1882 if (edgeSplit != hexSplitOnFace)
1883 {
1884 conflicts.insert(refIndex);
1885 }
1886 else
1887 {
1888 const real_t elem_scale =
1889 DirectedHexEdgeScale(elements[elem].node, ref, vn1, vn2);
1890 const real_t nghb_scale =
1891 DirectedHexEdgeScale(nghb.node, nghb_ref, vn1, vn2);
1892 if (!SameSplitScale(elem_scale, nghb_scale))
1893 {
1894 conflicts.insert(refIndex);
1895 }
1896 }
1897 }
1898 }
1899 // The else case is that the neighbor is not refined, so there is no need to
1900 // check for conflicts.
1901}
1902
1903void ParNCMesh::CheckRefIsoFace(const Refinement &ref, int elem,
1904 int vn1, int vn2, int vn3, int vn4,
1905 int en1, int en2, int en3, int en4,
1906 const Array<Refinement> &refinements,
1907 const std::map<int, int> &elemToRef,
1908 std::set<int> &conflicts)
1909{
1910 CheckRefAnisoFace(ref, elem, vn1, vn2, en2, en4, refinements, elemToRef,
1911 conflicts);
1912 CheckRefAnisoFace(ref, elem, en4, en2, vn3, vn4, refinements, elemToRef,
1913 conflicts);
1914 CheckRefAnisoFace(ref, elem, vn4, vn1, en1, en3, refinements, elemToRef,
1915 conflicts);
1916 CheckRefAnisoFace(ref, elem, en3, en1, vn2, vn3, refinements, elemToRef,
1917 conflicts);
1918}
1919
1920void ParNCMesh::CheckRefinement(int elem, const Refinement &ref,
1921 const Array<Refinement> &refinements,
1922 const std::map<int, int> &elemToRef,
1923 std::set<int> &conflicts)
1924{
1925 const char ref_type = ref.GetType();
1926 const Element &el = elements[elem];
1927 MFEM_ASSERT(el.geom == Geometry::CUBE && el.ref_type == 0,
1928 "Element must be an unrefined hexahedron");
1929
1930 const int* no = el.node;
1931
1932 // Check the faces of this element being refined (depends on ref_type).
1933 // This follows the logic of NCMesh::RefineElement().
1934 if (ref_type == Refinement::X) // split along X axis
1935 {
1936 CheckRefAnisoFace(ref, elem, no[0], no[1], no[5], no[4], refinements,
1937 elemToRef, conflicts);
1938 CheckRefAnisoFace(ref, elem, no[2], no[3], no[7], no[6], refinements,
1939 elemToRef, conflicts);
1940 CheckRefAnisoFace(ref, elem, no[4], no[5], no[6], no[7], refinements,
1941 elemToRef, conflicts);
1942 CheckRefAnisoFace(ref, elem, no[3], no[2], no[1], no[0], refinements,
1943 elemToRef, conflicts);
1944 }
1945 else if (ref_type == Refinement::Y) // split along Y axis
1946 {
1947 CheckRefAnisoFace(ref, elem, no[1], no[2], no[6], no[5], refinements,
1948 elemToRef, conflicts);
1949 CheckRefAnisoFace(ref, elem, no[3], no[0], no[4], no[7], refinements,
1950 elemToRef, conflicts);
1951 CheckRefAnisoFace(ref, elem, no[5], no[6], no[7], no[4], refinements,
1952 elemToRef, conflicts);
1953 CheckRefAnisoFace(ref, elem, no[0], no[3], no[2], no[1], refinements,
1954 elemToRef, conflicts);
1955 }
1956 else if (ref_type == Refinement::Z) // split along Z axis
1957 {
1958 CheckRefAnisoFace(ref, elem, no[4], no[0], no[1], no[5], refinements,
1959 elemToRef, conflicts);
1960 CheckRefAnisoFace(ref, elem, no[5], no[1], no[2], no[6], refinements,
1961 elemToRef, conflicts);
1962 CheckRefAnisoFace(ref, elem, no[6], no[2], no[3], no[7], refinements,
1963 elemToRef, conflicts);
1964 CheckRefAnisoFace(ref, elem, no[7], no[3], no[0], no[4], refinements,
1965 elemToRef, conflicts);
1966 }
1967 else if (ref_type == Refinement::XY) // XY split
1968 {
1969 CheckRefAnisoFace(ref, elem, no[0], no[1], no[5], no[4], refinements,
1970 elemToRef, conflicts);
1971 CheckRefAnisoFace(ref, elem, no[1], no[2], no[6], no[5], refinements,
1972 elemToRef, conflicts);
1973 CheckRefAnisoFace(ref, elem, no[2], no[3], no[7], no[6], refinements,
1974 elemToRef, conflicts);
1975 CheckRefAnisoFace(ref, elem, no[3], no[0], no[4], no[7], refinements,
1976 elemToRef, conflicts);
1977
1978 const int mid01 = GetMidEdgeNode(no[0], no[1]);
1979 const int mid12 = GetMidEdgeNode(no[1], no[2]);
1980 const int mid23 = GetMidEdgeNode(no[2], no[3]);
1981 const int mid30 = GetMidEdgeNode(no[3], no[0]);
1982
1983 const int mid45 = GetMidEdgeNode(no[4], no[5]);
1984 const int mid56 = GetMidEdgeNode(no[5], no[6]);
1985 const int mid67 = GetMidEdgeNode(no[6], no[7]);
1986 const int mid74 = GetMidEdgeNode(no[7], no[4]);
1987
1988 CheckRefIsoFace(ref, elem, no[3], no[2], no[1], no[0], mid23, mid12, mid01,
1989 mid30, refinements, elemToRef, conflicts);
1990 CheckRefIsoFace(ref, elem, no[4], no[5], no[6], no[7], mid45, mid56, mid67,
1991 mid74, refinements, elemToRef, conflicts);
1992 }
1993 else if (ref_type == Refinement::XZ) // XZ split
1994 {
1995 CheckRefAnisoFace(ref, elem, no[3], no[2], no[1], no[0], refinements,
1996 elemToRef, conflicts);
1997 CheckRefAnisoFace(ref, elem, no[2], no[6], no[5], no[1], refinements,
1998 elemToRef, conflicts);
1999 CheckRefAnisoFace(ref, elem, no[6], no[7], no[4], no[5], refinements,
2000 elemToRef, conflicts);
2001 CheckRefAnisoFace(ref, elem, no[7], no[3], no[0], no[4], refinements,
2002 elemToRef, conflicts);
2003
2004 const int mid01 = GetMidEdgeNode(no[0], no[1]);
2005 const int mid23 = GetMidEdgeNode(no[2], no[3]);
2006 const int mid45 = GetMidEdgeNode(no[4], no[5]);
2007 const int mid67 = GetMidEdgeNode(no[6], no[7]);
2008
2009 const int mid04 = GetMidEdgeNode(no[0], no[4]);
2010 const int mid15 = GetMidEdgeNode(no[1], no[5]);
2011 const int mid26 = GetMidEdgeNode(no[2], no[6]);
2012 const int mid37 = GetMidEdgeNode(no[3], no[7]);
2013
2014 CheckRefIsoFace(ref, elem, no[0], no[1], no[5], no[4], mid01, mid15, mid45,
2015 mid04, refinements, elemToRef, conflicts);
2016 CheckRefIsoFace(ref, elem, no[2], no[3], no[7], no[6], mid23, mid37, mid67,
2017 mid26, refinements, elemToRef, conflicts);
2018 }
2019 else if (ref_type == Refinement::YZ) // YZ split
2020 {
2021 const int mid12 = GetMidEdgeNode(no[1], no[2]);
2022 const int mid30 = GetMidEdgeNode(no[3], no[0]);
2023 const int mid56 = GetMidEdgeNode(no[5], no[6]);
2024 const int mid74 = GetMidEdgeNode(no[7], no[4]);
2025
2026 const int mid04 = GetMidEdgeNode(no[0], no[4]);
2027 const int mid15 = GetMidEdgeNode(no[1], no[5]);
2028 const int mid26 = GetMidEdgeNode(no[2], no[6]);
2029 const int mid37 = GetMidEdgeNode(no[3], no[7]);
2030
2031 CheckRefAnisoFace(ref, elem, no[4], no[0], no[1], no[5], refinements,
2032 elemToRef, conflicts);
2033 CheckRefAnisoFace(ref, elem, no[0], no[3], no[2], no[1], refinements,
2034 elemToRef, conflicts);
2035 CheckRefAnisoFace(ref, elem, no[3], no[7], no[6], no[2], refinements,
2036 elemToRef, conflicts);
2037 CheckRefAnisoFace(ref, elem, no[7], no[4], no[5], no[6], refinements,
2038 elemToRef, conflicts);
2039
2040 CheckRefIsoFace(ref, elem, no[1], no[2], no[6], no[5], mid12, mid26, mid56,
2041 mid15, refinements, elemToRef, conflicts);
2042 CheckRefIsoFace(ref, elem, no[3], no[0], no[4], no[7], mid30, mid04, mid74,
2043 mid37, refinements, elemToRef, conflicts);
2044 }
2045 else if (ref_type == Refinement::XYZ) // XYZ split
2046 {
2047 const int mid01 = GetMidEdgeNode(no[0], no[1]);
2048 const int mid12 = GetMidEdgeNode(no[1], no[2]);
2049 const int mid23 = GetMidEdgeNode(no[2], no[3]);
2050 const int mid30 = GetMidEdgeNode(no[3], no[0]);
2051
2052 const int mid45 = GetMidEdgeNode(no[4], no[5]);
2053 const int mid56 = GetMidEdgeNode(no[5], no[6]);
2054 const int mid67 = GetMidEdgeNode(no[6], no[7]);
2055 const int mid74 = GetMidEdgeNode(no[7], no[4]);
2056
2057 const int mid04 = GetMidEdgeNode(no[0], no[4]);
2058 const int mid15 = GetMidEdgeNode(no[1], no[5]);
2059 const int mid26 = GetMidEdgeNode(no[2], no[6]);
2060 const int mid37 = GetMidEdgeNode(no[3], no[7]);
2061
2062 CheckRefIsoFace(ref, elem, no[3], no[2], no[1], no[0], mid23, mid12, mid01,
2063 mid30, refinements, elemToRef, conflicts);
2064 CheckRefIsoFace(ref, elem, no[0], no[1], no[5], no[4], mid01, mid15, mid45,
2065 mid04, refinements, elemToRef, conflicts);
2066 CheckRefIsoFace(ref, elem, no[1], no[2], no[6], no[5], mid12, mid26, mid56,
2067 mid15, refinements, elemToRef, conflicts);
2068 CheckRefIsoFace(ref, elem, no[2], no[3], no[7], no[6], mid23, mid37, mid67,
2069 mid26, refinements, elemToRef, conflicts);
2070 CheckRefIsoFace(ref, elem, no[3], no[0], no[4], no[7], mid30, mid04, mid74,
2071 mid37, refinements, elemToRef, conflicts);
2072 CheckRefIsoFace(ref, elem, no[4], no[5], no[6], no[7], mid45, mid56, mid67,
2073 mid74, refinements, elemToRef, conflicts);
2074 }
2075 else
2076 {
2077 MFEM_ABORT("Invalid refinement type.");
2078 }
2079}
2080
2081void ParNCMesh::Refine(const Array<Refinement> &refinements)
2082{
2083 if (NRanks == 1)
2084 {
2085 NCMesh::Refine(refinements);
2086 return;
2087 }
2088
2089 for (int i = 0; i < refinements.Size() && Iso; i++)
2090 {
2091 const Refinement &ref = refinements[i];
2092 if (ref.GetType() != Refinement::XYZ)
2093 {
2094 Iso = false;
2095 }
2096 }
2097
2099
2100 // create refinement messages to all neighbors (NOTE: some may be empty)
2101 Array<int> neighbors;
2102 NeighborProcessors(neighbors);
2103 for (int i = 0; i < neighbors.Size(); i++)
2104 {
2105 send_ref[neighbors[i]].SetNCMesh(this);
2106 }
2107
2108 // populate messages: all refinements that occur next to the processor
2109 // boundary need to be sent to the adjoining neighbors so they can keep
2110 // their ghost layer up to date
2111 Array<int> ranks;
2112 ranks.Reserve(64);
2113 for (int i = 0; i < refinements.Size(); i++)
2114 {
2115 const Refinement &ref = refinements[i];
2116 MFEM_ASSERT(ref.index < NElements, "");
2117 const int elem = leaf_elements[ref.index];
2118 ElementNeighborProcessors(elem, ranks);
2119 for (int j = 0; j < ranks.Size(); j++)
2120 {
2121 send_ref[ranks[j]].AddRefinement(elem, ref);
2122 }
2123 }
2124
2125 // send the messages (overlap with local refinements)
2126 NeighborRefinementMessage::IsendAll(send_ref, MyComm);
2127
2128 // do local refinements
2129 for (int i = 0; i < refinements.Size(); i++)
2130 {
2131 Refinement ref_i = refinements[i];
2132 ref_i.index = leaf_elements[refinements[i].index];
2133 NCMesh::RefineElement(ref_i);
2134 }
2135
2136 // receive (ghost layer) refinements from all neighbors
2137 for (int j = 0; j < neighbors.Size(); j++)
2138 {
2139 int rank, size;
2140 NeighborRefinementMessage::Probe(rank, size, MyComm);
2141
2143 msg.SetNCMesh(this);
2144 msg.Recv(rank, size, MyComm);
2145
2146 // do the ghost refinements
2147 for (int i = 0; i < msg.Size(); i++)
2148 {
2149 Refinement ghost_ref(msg.elements[i], msg.values[i].ref_type);
2150 ghost_ref.SetScaleForType(msg.values[i].scale);
2151 NCMesh::RefineElement(ghost_ref);
2152 }
2153 }
2154
2155 Update();
2156
2157 // make sure we can delete the send buffers
2158 NeighborRefinementMessage::WaitAllSent(send_ref);
2159}
2160
2161
2162void ParNCMesh::LimitNCLevel(int max_nc_level)
2163{
2164 MFEM_VERIFY(max_nc_level >= 1, "'max_nc_level' must be 1 or greater.");
2165
2166 while (1)
2167 {
2168 Array<Refinement> refinements;
2169 GetLimitRefinements(refinements, max_nc_level);
2170
2171 long long size = refinements.Size(), glob_size;
2172 MPI_Allreduce(&size, &glob_size, 1, MPI_LONG_LONG, MPI_SUM, MyComm);
2173
2174 if (!glob_size) { break; }
2175
2176 Refine(refinements);
2177 }
2178}
2179
2180void ParNCMesh::GetFineToCoarsePartitioning(const Array<int> &derefs,
2181 Array<int> &new_ranks) const
2182{
2183 new_ranks.SetSize(leaf_elements.Size()-GetNGhostElements());
2184 for (int i = 0; i < leaf_elements.Size()-GetNGhostElements(); i++)
2185 {
2186 new_ranks[i] = elements[leaf_elements[i]].rank;
2187 }
2188
2189 for (int i = 0; i < derefs.Size(); i++)
2190 {
2191 int row = derefs[i];
2192 MFEM_VERIFY(row >= 0 && row < derefinements.Size(),
2193 "invalid derefinement number.");
2194
2195 const int* fine = derefinements.GetRow(row);
2196 int size = derefinements.RowSize(row);
2197
2198 int coarse_rank = INT_MAX;
2199 for (int j = 0; j < size; j++)
2200 {
2201 int fine_rank = elements[leaf_elements[fine[j]]].rank;
2202 coarse_rank = std::min(coarse_rank, fine_rank);
2203 }
2204 for (int j = 0; j < size; j++)
2205 {
2206 new_ranks[fine[j]] = coarse_rank;
2207 }
2208 }
2209}
2210
2211void ParNCMesh::Derefine(const Array<int> &derefs)
2212{
2213 MFEM_VERIFY(Dim < 3 || Iso,
2214 "derefinement of 3D anisotropic meshes not implemented yet.");
2215
2216 InitDerefTransforms();
2217
2218 // store fine element ranks
2219 old_index_or_rank.SetSize(leaf_elements.Size());
2220 for (int i = 0; i < leaf_elements.Size(); i++)
2221 {
2222 old_index_or_rank[i] = elements[leaf_elements[i]].rank;
2223 }
2224
2225 // back up the leaf_elements array
2226 Array<int> old_elements;
2227 leaf_elements.Copy(old_elements);
2228
2229 // *** STEP 1: redistribute elements to avoid complex derefinements ***
2230
2231 Array<int> new_ranks(leaf_elements.Size());
2232 for (int i = 0; i < leaf_elements.Size(); i++)
2233 {
2234 new_ranks[i] = elements[leaf_elements[i]].rank;
2235 }
2236
2237 // make the lowest rank get all the fine elements for each derefinement
2238 for (int i = 0; i < derefs.Size(); i++)
2239 {
2240 int row = derefs[i];
2241 MFEM_VERIFY(row >= 0 && row < derefinements.Size(),
2242 "invalid derefinement number.");
2243
2244 const int* fine = derefinements.GetRow(row);
2245 int size = derefinements.RowSize(row);
2246
2247 int coarse_rank = INT_MAX;
2248 for (int j = 0; j < size; j++)
2249 {
2250 int fine_rank = elements[leaf_elements[fine[j]]].rank;
2251 coarse_rank = std::min(coarse_rank, fine_rank);
2252 }
2253 for (int j = 0; j < size; j++)
2254 {
2255 new_ranks[fine[j]] = coarse_rank;
2256 }
2257 }
2258
2259 int target_elements = 0;
2260 for (int i = 0; i < new_ranks.Size(); i++)
2261 {
2262 if (new_ranks[i] == MyRank) { target_elements++; }
2263 }
2264
2265 // redistribute elements slightly to get rid of complex derefinements
2266 // straddling processor boundaries *and* update the ghost layer
2267 RedistributeElements(new_ranks, target_elements, false);
2268
2269 // *** STEP 2: derefine now, communication similar to Refine() ***
2270
2272
2273 // create derefinement messages to all neighbors (NOTE: some may be empty)
2274 Array<int> neighbors;
2275 NeighborProcessors(neighbors);
2276 for (int i = 0; i < neighbors.Size(); i++)
2277 {
2278 send_deref[neighbors[i]].SetNCMesh(this);
2279 }
2280
2281 // derefinements that occur next to the processor boundary need to be sent
2282 // to the adjoining neighbors to keep their ghost layers in sync
2283 Array<int> ranks;
2284 ranks.Reserve(64);
2285 for (int i = 0; i < derefs.Size(); i++)
2286 {
2287 const int* fine = derefinements.GetRow(derefs[i]);
2288 int parent = elements[old_elements[fine[0]]].parent;
2289
2290 // send derefinement to neighbors
2291 ElementNeighborProcessors(parent, ranks);
2292 for (int j = 0; j < ranks.Size(); j++)
2293 {
2294 send_deref[ranks[j]].AddDerefinement(parent, new_ranks[fine[0]]);
2295 }
2296 }
2297 NeighborDerefinementMessage::IsendAll(send_deref, MyComm);
2298
2299 // restore old (pre-redistribution) element indices, for SetDerefMatrixCodes
2300 for (int i = 0; i < leaf_elements.Size(); i++)
2301 {
2302 elements[leaf_elements[i]].index = -1;
2303 }
2304 for (int i = 0; i < old_elements.Size(); i++)
2305 {
2306 elements[old_elements[i]].index = i;
2307 }
2308
2309 // do local derefinements
2310 Array<int> coarse;
2311 old_elements.Copy(coarse);
2312 for (int i = 0; i < derefs.Size(); i++)
2313 {
2314 const int* fine = derefinements.GetRow(derefs[i]);
2315 int parent = elements[old_elements[fine[0]]].parent;
2316
2317 // record the relation of the fine elements to their parent
2318 SetDerefMatrixCodes(parent, coarse);
2319
2320 NCMesh::DerefineElement(parent);
2321 }
2322
2323 // receive ghost layer derefinements from all neighbors
2324 for (int j = 0; j < neighbors.Size(); j++)
2325 {
2326 int rank, size;
2327 NeighborDerefinementMessage::Probe(rank, size, MyComm);
2328
2330 msg.SetNCMesh(this);
2331 msg.Recv(rank, size, MyComm);
2332
2333 // do the ghost derefinements
2334 for (int i = 0; i < msg.Size(); i++)
2335 {
2336 int elem = msg.elements[i];
2337 if (elements[elem].ref_type)
2338 {
2339 SetDerefMatrixCodes(elem, coarse);
2340 NCMesh::DerefineElement(elem);
2341 }
2342 elements[elem].rank = msg.values[i];
2343 }
2344 }
2345
2346 // update leaf_elements, Element::index etc.
2347 Update();
2348
2349 UpdateLayers();
2350
2351 // link old fine elements to the new coarse elements
2352 for (int i = 0; i < coarse.Size(); i++)
2353 {
2354 int index = elements[coarse[i]].index;
2355 if (element_type[index] == 0)
2356 {
2357 // this coarse element will get pruned, encode who owns it now
2358 index = FlipIndexSign(elements[coarse[i]].rank);
2359 }
2360 transforms.embeddings[i].parent = index;
2361 }
2362
2363 leaf_elements.Copy(old_elements);
2364
2365 Prune();
2366
2367 // renumber coarse element indices after pruning
2368 for (int i = 0; i < coarse.Size(); i++)
2369 {
2370 int &index = transforms.embeddings[i].parent;
2371 if (index >= 0)
2372 {
2373 index = elements[old_elements[index]].index;
2374 }
2375 }
2376
2377 // make sure we can delete all send buffers
2378 NeighborDerefinementMessage::WaitAllSent(send_deref);
2379}
2380
2381
2382template<typename Type>
2383void ParNCMesh::SynchronizeDerefinementData(Array<Type> &elem_data,
2384 const Table &deref_table)
2385{
2386 const MPI_Datatype datatype = MPITypeMap<Type>::mpi_type;
2387
2388 Array<MPI_Request*> requests;
2389 Array<int> neigh;
2390
2391 requests.Reserve(64);
2392 neigh.Reserve(8);
2393
2394 // make room for ghost values (indices beyond NumElements)
2395 elem_data.SetSize(leaf_elements.Size(), 0);
2396
2397 for (int i = 0; i < deref_table.Size(); i++)
2398 {
2399 const int* fine = deref_table.GetRow(i);
2400 int size = deref_table.RowSize(i);
2401 MFEM_ASSERT(size <= 8, "");
2402
2403 int ranks[8], min_rank = INT_MAX, max_rank = INT_MIN;
2404 for (int j = 0; j < size; j++)
2405 {
2406 ranks[j] = elements[leaf_elements[fine[j]]].rank;
2407 min_rank = std::min(min_rank, ranks[j]);
2408 max_rank = std::max(max_rank, ranks[j]);
2409 }
2410
2411 // exchange values for derefinements that straddle processor boundaries
2412 if (min_rank != max_rank)
2413 {
2414 neigh.SetSize(0);
2415 for (int j = 0; j < size; j++)
2416 {
2417 if (ranks[j] != MyRank) { neigh.Append(ranks[j]); }
2418 }
2419 neigh.Sort();
2420 neigh.Unique();
2421
2422 for (int j = 0; j < size; j++/*pass*/)
2423 {
2424 Type *data = &elem_data[fine[j]];
2425
2426 int rnk = ranks[j], len = 1; /*j;
2427 do { j++; } while (j < size && ranks[j] == rnk);
2428 len = j - len;*/
2429
2430 if (rnk == MyRank)
2431 {
2432 for (int k = 0; k < neigh.Size(); k++)
2433 {
2434 MPI_Request* req = new MPI_Request;
2435 MPI_Isend(data, len, datatype, neigh[k], 292, MyComm, req);
2436 requests.Append(req);
2437 }
2438 }
2439 else
2440 {
2441 MPI_Request* req = new MPI_Request;
2442 MPI_Irecv(data, len, datatype, rnk, 292, MyComm, req);
2443 requests.Append(req);
2444 }
2445 }
2446 }
2447 }
2448
2449 for (int i = 0; i < requests.Size(); i++)
2450 {
2451 MPI_Wait(requests[i], MPI_STATUS_IGNORE);
2452 delete requests[i];
2453 }
2454}
2455
2456// instantiate SynchronizeDerefinementData for int, double, and float
2457template void
2458ParNCMesh::SynchronizeDerefinementData<int>(Array<int> &, const Table &);
2459template void
2460ParNCMesh::SynchronizeDerefinementData<double>(Array<double> &, const Table &);
2461template void
2462ParNCMesh::SynchronizeDerefinementData<float>(Array<float> &, const Table &);
2463
2464
2465void ParNCMesh::CheckDerefinementNCLevel(const Table &deref_table,
2466 Array<int> &level_ok, int max_nc_level)
2467{
2468 Array<int> leaf_ok(leaf_elements.Size());
2469 leaf_ok = 1;
2470
2471 // check elements that we own
2472 for (int i = 0; i < deref_table.Size(); i++)
2473 {
2474 const int *fine = deref_table.GetRow(i),
2475 size = deref_table.RowSize(i);
2476
2477 int parent = elements[leaf_elements[fine[0]]].parent;
2478 Element &pa = elements[parent];
2479
2480 for (int j = 0; j < size; j++)
2481 {
2482 int child = leaf_elements[fine[j]];
2483 if (elements[child].rank == MyRank)
2484 {
2485 int splits[3];
2486 CountSplits(child, splits);
2487
2488 for (int k = 0; k < Dim; k++)
2489 {
2490 if ((pa.ref_type & (1 << k)) &&
2491 splits[k] >= max_nc_level)
2492 {
2493 leaf_ok[fine[j]] = 0; break;
2494 }
2495 }
2496 }
2497 }
2498 }
2499
2500 SynchronizeDerefinementData(leaf_ok, deref_table);
2501
2502 level_ok.SetSize(deref_table.Size());
2503 level_ok = 1;
2504
2505 for (int i = 0; i < deref_table.Size(); i++)
2506 {
2507 const int* fine = deref_table.GetRow(i),
2508 size = deref_table.RowSize(i);
2509
2510 for (int j = 0; j < size; j++)
2511 {
2512 if (!leaf_ok[fine[j]])
2513 {
2514 level_ok[i] = 0; break;
2515 }
2516 }
2517 }
2518}
2519
2520
2521//// Rebalance /////////////////////////////////////////////////////////////////
2522
2523void ParNCMesh::Rebalance(const Array<int> *custom_partition)
2524{
2525 send_rebalance_dofs.clear();
2526 recv_rebalance_dofs.clear();
2527
2528 Array<int> old_elements;
2529 leaf_elements.GetSubArray(0, NElements, old_elements);
2530
2531 if (!custom_partition) // SFC based partitioning
2532 {
2533 Array<int> new_ranks(leaf_elements.Size());
2534 new_ranks = -1;
2535
2536 // figure out new assignments for Element::rank
2537 long local_elems = NElements, total_elems = 0;
2538 MPI_Allreduce(&local_elems, &total_elems, 1, MPI_LONG, MPI_SUM, MyComm);
2539
2540 long first_elem_global = 0;
2541 MPI_Scan(&local_elems, &first_elem_global, 1, MPI_LONG, MPI_SUM, MyComm);
2542 first_elem_global -= local_elems;
2543
2544 for (int i = 0, j = 0; i < leaf_elements.Size(); i++)
2545 {
2546 if (elements[leaf_elements[i]].rank == MyRank)
2547 {
2548 new_ranks[i] = Partition(first_elem_global + (j++), total_elems);
2549 }
2550 }
2551
2552 int target_elements = PartitionFirstIndex(MyRank+1, total_elems)
2553 - PartitionFirstIndex(MyRank, total_elems);
2554
2555 // assign the new ranks and send elements (plus ghosts) to new owners
2556 RedistributeElements(new_ranks, target_elements, true);
2557 }
2558 else // whatever partitioning the user has passed
2559 {
2560 MFEM_VERIFY(custom_partition->Size() == NElements,
2561 "Size of the partition array must match the number "
2562 "of local mesh elements (ParMesh::GetNE()).");
2563
2564 Array<int> new_ranks;
2565 custom_partition->Copy(new_ranks);
2566 new_ranks.SetSize(leaf_elements.Size(), -1); // make room for ghosts
2567
2568 RedistributeElements(new_ranks, -1, true);
2569 }
2570
2571 // set up the old index array
2572 old_index_or_rank.SetSize(NElements);
2573 old_index_or_rank = -1;
2574 for (int i = 0; i < old_elements.Size(); i++)
2575 {
2576 Element &el = elements[old_elements[i]];
2577 if (el.rank == MyRank) { old_index_or_rank[el.index] = i; }
2578 }
2579
2580 // get rid of elements beyond the new ghost layer
2581 Prune();
2582}
2583
2584void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
2585 bool record_comm)
2586{
2587 bool sfc = (target_elements >= 0);
2588
2589 UpdateLayers();
2590
2591 // *** STEP 1: communicate new rank assignments for the ghost layer ***
2592
2593 NeighborElementRankMessage::Map send_ghost_ranks, recv_ghost_ranks;
2594
2595 ghost_layer.Sort([&](const int a, const int b)
2596 {
2597 return elements[a].rank < elements[b].rank;
2598 });
2599
2600 {
2601 Array<int> rank_neighbors;
2602
2603 // loop over neighbor ranks and their elements
2604 int begin = 0, end = 0;
2605 while (end < ghost_layer.Size())
2606 {
2607 // find range of elements belonging to one rank
2608 int rank = elements[ghost_layer[begin]].rank;
2609 while (end < ghost_layer.Size() &&
2610 elements[ghost_layer[end]].rank == rank) { end++; }
2611
2612 Array<int> rank_elems;
2613 rank_elems.MakeRef(&ghost_layer[begin], end - begin);
2614
2615 // find elements within boundary_layer that are neighbors to 'rank'
2616 rank_neighbors.SetSize(0);
2617 NeighborExpand(rank_elems, rank_neighbors, &boundary_layer);
2618
2619 // send a message with new rank assignments within 'rank_neighbors'
2620 NeighborElementRankMessage& msg = send_ghost_ranks[rank];
2621 msg.SetNCMesh(this);
2622
2623 msg.Reserve(rank_neighbors.Size());
2624 for (int i = 0; i < rank_neighbors.Size(); i++)
2625 {
2626 int elem = rank_neighbors[i];
2627 const Element &el = elements[elem];
2628 msg.AddElement(elem, new_ranks[el.index], el.attribute);
2629 }
2630
2631 msg.Isend(rank, MyComm);
2632
2633 // prepare to receive a message from the neighbor too, these will
2634 // be new the new rank assignments for our ghost layer
2635 recv_ghost_ranks[rank].SetNCMesh(this);
2636
2637 begin = end;
2638 }
2639 }
2640
2641 NeighborElementRankMessage::RecvAll(recv_ghost_ranks, MyComm);
2642
2643 // read new ranks for the ghost layer from messages received
2644 for (auto &kv : recv_ghost_ranks)
2645 {
2646 NeighborElementRankMessage &msg = kv.second;
2647 for (int i = 0; i < msg.Size(); i++)
2648 {
2649 int ghost_index = elements[msg.elements[i]].index;
2650 MFEM_ASSERT(element_type[ghost_index] == 2, "");
2651 const ElementRankAndAttribute &value = msg.values[i];
2652 new_ranks[ghost_index] = value.rank;
2653 elements[msg.elements[i]].attribute = value.attribute;
2654 }
2655 }
2656
2657 recv_ghost_ranks.clear();
2658
2659 // *** STEP 2: send elements that no longer belong to us to new assignees ***
2660
2661 /* The result thus far is just the array 'new_ranks' containing new owners
2662 for elements that we currently own plus new owners for the ghost layer.
2663 Next we keep elements that still belong to us and send ElementSets with
2664 the remaining elements to their new owners. Each batch of elements needs
2665 to be sent together with their neighbors so the receiver also gets a
2666 ghost layer that is up to date (this is why we needed Step 1). */
2667
2668 int received_elements = 0;
2669 for (int i = 0; i < leaf_elements.Size(); i++)
2670 {
2671 Element &el = elements[leaf_elements[i]];
2672 if (el.rank == MyRank && new_ranks[i] == MyRank)
2673 {
2674 received_elements++; // initialize to number of elements we're keeping
2675 }
2676 el.rank = new_ranks[i];
2677 }
2678
2679 int nsent = 0, nrecv = 0; // for debug check
2680
2681 RebalanceMessage::Map send_elems;
2682 {
2683 // sort elements we own by the new rank
2684 Array<int> owned_elements;
2685 owned_elements.MakeRef(leaf_elements.GetData(), NElements);
2686 owned_elements.Sort([&](const int a, const int b)
2687 {
2688 return elements[a].rank < elements[b].rank;
2689 });
2690
2691 Array<int> batch;
2692 batch.Reserve(1024);
2693
2694 // send elements to new owners
2695 int begin = 0, end = 0;
2696 while (end < NElements)
2697 {
2698 // find range of elements belonging to one rank
2699 int rank = elements[owned_elements[begin]].rank;
2700 while (end < owned_elements.Size() &&
2701 elements[owned_elements[end]].rank == rank) { end++; }
2702
2703 if (rank != MyRank)
2704 {
2705 Array<int> rank_elems;
2706 rank_elems.MakeRef(&owned_elements[begin], end - begin);
2707
2708 // expand the 'rank_elems' set by its neighbor elements (ghosts)
2709 batch.SetSize(0);
2710 NeighborExpand(rank_elems, batch);
2711
2712 // send the batch
2713 RebalanceMessage &msg = send_elems[rank];
2714 msg.SetNCMesh(this);
2715
2716 msg.Reserve(batch.Size());
2717 for (int i = 0; i < batch.Size(); i++)
2718 {
2719 int elem = batch[i];
2720 Element &el = elements[elem];
2721
2722 if ((element_type[el.index] & 1) || el.rank != rank)
2723 {
2724 msg.AddElement(elem, el.rank, el.attribute);
2725 }
2726 // NOTE: we skip 'ghosts' that are of the receiver's rank because
2727 // they are not really ghosts and would get sent multiple times,
2728 // disrupting the termination mechanism in Step 4.
2729 }
2730
2731 if (sfc)
2732 {
2733 msg.Isend(rank, MyComm);
2734 }
2735 else
2736 {
2737 // custom partitioning needs synchronous sends
2738 msg.Issend(rank, MyComm);
2739 }
2740 nsent++;
2741
2742 // also: record what elements we sent (excluding the ghosts)
2743 // so that SendRebalanceDofs can later send data for them
2744 if (record_comm)
2745 {
2746 send_rebalance_dofs[rank].SetElements(rank_elems, this);
2747 }
2748 }
2749
2750 begin = end;
2751 }
2752 }
2753
2754 // *** STEP 3: receive elements from others ***
2755
2756 RebalanceMessage msg;
2757 msg.SetNCMesh(this);
2758
2759 if (sfc)
2760 {
2761 /* We don't know from whom we're going to receive, so we need to probe.
2762 However, for the default SFC partitioning, we do know how many elements
2763 we're going to own eventually, so the termination condition is easy. */
2764
2765 while (received_elements < target_elements)
2766 {
2767 int rank, size;
2768 RebalanceMessage::Probe(rank, size, MyComm);
2769
2770 // receive message; note: elements are created as the message is decoded
2771 msg.Recv(rank, size, MyComm);
2772 nrecv++;
2773
2774 for (int i = 0; i < msg.Size(); i++)
2775 {
2776 const ElementRankAndAttribute &value = msg.values[i];
2777 Element &el = elements[msg.elements[i]];
2778 el.rank = value.rank;
2779 el.attribute = value.attribute;
2780
2781 if (value.rank == MyRank) { received_elements++; }
2782 }
2783
2784 // save the ranks we received from, for later use in RecvRebalanceDofs
2785 if (record_comm)
2786 {
2787 recv_rebalance_dofs[rank].SetNCMesh(this);
2788 }
2789 }
2790
2791 Update();
2792
2793 RebalanceMessage::WaitAllSent(send_elems);
2794 }
2795 else
2796 {
2797 /* The case (target_elements < 0) is used for custom partitioning.
2798 Here we need to employ the "non-blocking consensus" algorithm
2799 (https://scorec.rpi.edu/REPORTS/2015-9.pdf) to determine when the
2800 element exchange is finished. The algorithm uses a non-blocking
2801 barrier. */
2802
2803 MPI_Request barrier = MPI_REQUEST_NULL;
2804 int done = 0;
2805
2806 while (!done)
2807 {
2808 int rank, size;
2809 while (RebalanceMessage::IProbe(rank, size, MyComm))
2810 {
2811 // receive message; note: elements are created as the msg is decoded
2812 msg.Recv(rank, size, MyComm);
2813 nrecv++;
2814
2815 for (int i = 0; i < msg.Size(); i++)
2816 {
2817 const ElementRankAndAttribute &value = msg.values[i];
2818 Element &el = elements[msg.elements[i]];
2819 el.rank = value.rank;
2820 el.attribute = value.attribute;
2821 }
2822
2823 // save the ranks we received from, for later use in RecvRebalanceDofs
2824 if (record_comm)
2825 {
2826 recv_rebalance_dofs[rank].SetNCMesh(this);
2827 }
2828 }
2829
2830 if (barrier != MPI_REQUEST_NULL)
2831 {
2832 MPI_Test(&barrier, &done, MPI_STATUS_IGNORE);
2833 }
2834 else
2835 {
2836 if (RebalanceMessage::TestAllSent(send_elems))
2837 {
2838 int mpi_err = MPI_Ibarrier(MyComm, &barrier);
2839
2840 MFEM_VERIFY(mpi_err == MPI_SUCCESS, "");
2841 MFEM_VERIFY(barrier != MPI_REQUEST_NULL, "");
2842 }
2843 }
2844 }
2845
2846 Update();
2847 }
2848
2849 NeighborElementRankMessage::WaitAllSent(send_ghost_ranks);
2850
2851#ifdef MFEM_DEBUG
2852 int glob_sent, glob_recv;
2853 MPI_Reduce(&nsent, &glob_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2854 MPI_Reduce(&nrecv, &glob_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2855
2856 if (MyRank == 0)
2857 {
2858 MFEM_ASSERT(glob_sent == glob_recv,
2859 "(glob_sent, glob_recv) = ("
2860 << glob_sent << ", " << glob_recv << ")");
2861 }
2862#else
2863 MFEM_CONTRACT_VAR(nsent);
2864 MFEM_CONTRACT_VAR(nrecv);
2865#endif
2866}
2867
2868
2869void ParNCMesh::SendRebalanceDofs(int old_ndofs,
2870 const Table &old_element_dofs,
2871 long old_global_offset,
2873{
2874 Array<int> dofs;
2875 int vdim = space->GetVDim();
2876
2877 // fill messages (prepared by Rebalance) with element DOFs
2878 RebalanceDofMessage::Map::iterator it;
2879 for (it = send_rebalance_dofs.begin(); it != send_rebalance_dofs.end(); ++it)
2880 {
2881 RebalanceDofMessage &msg = it->second;
2882 msg.dofs.clear();
2883 int ne = static_cast<int>(msg.elem_ids.size());
2884 if (ne)
2885 {
2886 msg.dofs.reserve(old_element_dofs.RowSize(msg.elem_ids[0]) * ne * vdim);
2887 }
2888 for (int i = 0; i < ne; i++)
2889 {
2890 old_element_dofs.GetRow(msg.elem_ids[i], dofs);
2891 space->DofsToVDofs(dofs, old_ndofs);
2892 msg.dofs.insert(msg.dofs.end(), dofs.begin(), dofs.end());
2893 }
2894 msg.dof_offset = old_global_offset;
2895 }
2896
2897 // send the DOFs to element recipients from last Rebalance()
2898 RebalanceDofMessage::IsendAll(send_rebalance_dofs, MyComm);
2899}
2900
2901
2902void ParNCMesh::RecvRebalanceDofs(Array<int> &elements, Array<long> &dofs)
2903{
2904 // receive from the same ranks as in last Rebalance()
2905 RebalanceDofMessage::RecvAll(recv_rebalance_dofs, MyComm);
2906
2907 // count the size of the result
2908 int ne = 0, nd = 0;
2909 RebalanceDofMessage::Map::iterator it;
2910 for (it = recv_rebalance_dofs.begin(); it != recv_rebalance_dofs.end(); ++it)
2911 {
2912 RebalanceDofMessage &msg = it->second;
2913 ne += static_cast<int>(msg.elem_ids.size());
2914 nd += static_cast<int>(msg.dofs.size());
2915 }
2916
2917 elements.SetSize(ne);
2918 dofs.SetSize(nd);
2919
2920 // copy element indices and their DOFs
2921 ne = nd = 0;
2922 for (it = recv_rebalance_dofs.begin(); it != recv_rebalance_dofs.end(); ++it)
2923 {
2924 RebalanceDofMessage &msg = it->second;
2925 for (unsigned i = 0; i < msg.elem_ids.size(); i++)
2926 {
2927 elements[ne++] = msg.elem_ids[i];
2928 }
2929 for (unsigned i = 0; i < msg.dofs.size(); i++)
2930 {
2931 dofs[nd++] = msg.dof_offset + msg.dofs[i];
2932 }
2933 }
2934
2935 RebalanceDofMessage::WaitAllSent(send_rebalance_dofs);
2936}
2937
2938
2939//// ElementSet ////////////////////////////////////////////////////////////////
2940
2941ParNCMesh::ElementSet::ElementSet(const ElementSet &other)
2942 : ncmesh(other.ncmesh), include_ref_types(other.include_ref_types)
2943{
2944 other.data.Copy(data);
2945}
2946
2948{
2949 // helper to put an int to the data array
2950 data.Append(value & 0xff);
2951 data.Append((value >> 8) & 0xff);
2952 data.Append((value >> 16) & 0xff);
2953 data.Append((value >> 24) & 0xff);
2954}
2955
2957{
2958 // helper to get an int from the data array
2959 return (int) data[pos] +
2960 ((int) data[pos+1] << 8) +
2961 ((int) data[pos+2] << 16) +
2962 ((int) data[pos+3] << 24);
2963}
2964
2966{
2967 for (int i = 0; i < elements.Size(); i++)
2968 {
2969 int elem = elements[i];
2970 while (elem >= 0)
2971 {
2972 Element &el = ncmesh->elements[elem];
2973 if (el.flag == flag) { break; }
2974 el.flag = flag;
2975 elem = el.parent;
2976 }
2977 }
2978}
2979
2981{
2982 Element &el = ncmesh->elements[elem];
2983 if (!el.ref_type)
2984 {
2985 // we reached a leaf, mark this as zero child mask
2986 data.Append(0);
2987 }
2988 else
2989 {
2990 // check which subtrees contain marked elements
2991 int mask = 0;
2992 for (int i = 0; i < 8; i++)
2993 {
2994 if (el.child[i] >= 0 && ncmesh->elements[el.child[i]].flag)
2995 {
2996 mask |= 1 << i;
2997 }
2998 }
2999
3000 // write the bit mask and visit the subtrees
3001 data.Append(mask);
3002 if (include_ref_types)
3003 {
3004 data.Append(el.ref_type);
3005 }
3006
3007 for (int i = 0; i < 8; i++)
3008 {
3009 if (mask & (1 << i))
3010 {
3011 EncodeTree(el.child[i]);
3012 }
3013 }
3014 }
3015}
3016
3018{
3019 FlagElements(elements, 1);
3020
3021 // Each refinement tree that contains at least one element from the set
3022 // is encoded as HEADER + TREE, where HEADER is the root element number and
3023 // TREE is the output of EncodeTree().
3024 for (int i = 0; i < ncmesh->root_state.Size(); i++)
3025 {
3026 if (ncmesh->elements[i].flag)
3027 {
3028 WriteInt(i);
3029 EncodeTree(i);
3030 }
3031 }
3032 WriteInt(-1); // mark end of data
3033
3034 FlagElements(elements, 0);
3035}
3036
3037#ifdef MFEM_DEBUG
3039{
3040 std::ostringstream oss;
3041 for (int i = 0; i < ref_path.Size(); i++)
3042 {
3043 oss << " elem " << ref_path[i] << " (";
3044 const Element &el = ncmesh->elements[ref_path[i]];
3045 for (int j = 0; j < GI[el.Geom()].nv; j++)
3046 {
3047 if (j) { oss << ", "; }
3048 oss << ncmesh->RetrieveNode(el, j);
3049 }
3050 oss << ")\n";
3051 }
3052 return oss.str();
3053}
3054#endif
3055
3057 Array<int> &elements) const
3058{
3059#ifdef MFEM_DEBUG
3060 ref_path.Append(elem);
3061#endif
3062 int mask = data[pos++];
3063 if (!mask)
3064 {
3065 elements.Append(elem);
3066 }
3067 else
3068 {
3069 Element &el = ncmesh->elements[elem];
3070 if (include_ref_types)
3071 {
3072 int ref_type = data[pos++];
3073 if (!el.ref_type)
3074 {
3075 ncmesh->RefineElement(elem, ref_type);
3076 }
3077 else { MFEM_ASSERT(ref_type == el.ref_type, "") }
3078 }
3079 else
3080 {
3081 MFEM_ASSERT(el.ref_type != 0, "Path not found:\n"
3082 << RefPath() << " mask = " << mask);
3083 }
3084
3085 for (int i = 0; i < 8; i++)
3086 {
3087 if (mask & (1 << i))
3088 {
3089 DecodeTree(el.child[i], pos, elements);
3090 }
3091 }
3092 }
3093#ifdef MFEM_DEBUG
3094 ref_path.DeleteLast();
3095#endif
3096}
3097
3099{
3100 int root, pos = 0;
3101 while ((root = GetInt(pos)) >= 0)
3102 {
3103 pos += 4;
3104 DecodeTree(root, pos, elements);
3105 }
3106}
3107
3108void ParNCMesh::ElementSet::Dump(std::ostream &os) const
3109{
3110 write<int>(os, data.Size());
3111 os.write((const char*) data.GetData(), data.Size());
3112}
3113
3114void ParNCMesh::ElementSet::Load(std::istream &is)
3115{
3116 data.SetSize(read<int>(is));
3117 is.read((char*) data.GetData(), data.Size());
3118}
3119
3120
3121//// EncodeMeshIds/DecodeMeshIds ///////////////////////////////////////////////
3122
3124{
3128
3129 if (!shared_edges.masters.Size() &&
3130 !shared_faces.masters.Size()) { return; }
3131
3132 Array<bool> contains_rank(static_cast<int>(groups.size()));
3133 for (unsigned i = 0; i < groups.size(); i++)
3134 {
3135 contains_rank[i] = GroupContains(i, rank);
3136 }
3137
3138 Array<Pair<int, int> > find_v(ids[0].Size());
3139 for (int i = 0; i < ids[0].Size(); i++)
3140 {
3141 find_v[i].one = ids[0][i].index;
3142 find_v[i].two = i;
3143 }
3144 find_v.Sort();
3145
3146 // find vertices of master edges shared with 'rank', and modify their
3147 // MeshIds so their element/local matches the element of the master edge
3148 for (int i = 0; i < shared_edges.masters.Size(); i++)
3149 {
3150 const MeshId &edge_id = shared_edges.masters[i];
3151 if (contains_rank[entity_pmat_group[1][edge_id.index]])
3152 {
3153 int v[2], pos, k;
3154 GetEdgeVertices(edge_id, v);
3155 for (int j = 0; j < 2; j++)
3156 {
3157 if ((pos = find_v.FindSorted(Pair<int, int>(v[j], 0))) != -1)
3158 {
3159 // switch to an element/local that is safe for 'rank'
3160 k = find_v[pos].two;
3161 ChangeVertexMeshIdElement(ids[0][k], edge_id.element);
3162 ChangeRemainingMeshIds(ids[0], pos, find_v);
3163 }
3164 }
3165 }
3166 }
3167
3168 if (!shared_faces.masters.Size()) { return; }
3169
3170 Array<Pair<int, int> > find_e(ids[1].Size());
3171 for (int i = 0; i < ids[1].Size(); i++)
3172 {
3173 find_e[i].one = ids[1][i].index;
3174 find_e[i].two = i;
3175 }
3176 find_e.Sort();
3177
3178 // find vertices/edges of master faces shared with 'rank', and modify their
3179 // MeshIds so their element/local matches the element of the master face
3180 for (const MeshId &face_id : shared_faces.masters)
3181 {
3182 if (contains_rank[entity_pmat_group[2][face_id.index]])
3183 {
3184 int v[4], e[4], eo[4], pos, k;
3185 int nfv = GetFaceVerticesEdges(face_id, v, e, eo);
3186 for (int j = 0; j < nfv; j++)
3187 {
3188 if ((pos = find_v.FindSorted(Pair<int, int>(v[j], 0))) != -1)
3189 {
3190 k = find_v[pos].two;
3191 ChangeVertexMeshIdElement(ids[0][k], face_id.element);
3192 ChangeRemainingMeshIds(ids[0], pos, find_v);
3193 }
3194 if ((pos = find_e.FindSorted(Pair<int, int>(e[j], 0))) != -1)
3195 {
3196 k = find_e[pos].two;
3197 ChangeEdgeMeshIdElement(ids[1][k], face_id.element);
3198 ChangeRemainingMeshIds(ids[1], pos, find_e);
3199 }
3200 }
3201 }
3202 }
3203}
3204
3206{
3207 Element &el = elements[elem];
3208 MFEM_ASSERT(el.ref_type == 0, "");
3209
3210 GeomInfo& gi = GI[el.Geom()];
3211 for (int i = 0; i < gi.nv; i++)
3212 {
3213 if (nodes[el.node[i]].vert_index == id.index)
3214 {
3215 id.local = i;
3216 id.element = elem;
3217 return;
3218 }
3219 }
3220 MFEM_ABORT("Vertex not found.");
3221}
3222
3224{
3225 Element &old = elements[id.element];
3226 const int *old_ev = GI[old.Geom()].edges[(int) id.local];
3227 Node* node = nodes.Find(old.node[old_ev[0]], old.node[old_ev[1]]);
3228 MFEM_ASSERT(node != NULL, "Edge not found.");
3229
3230 Element &el = elements[elem];
3231 MFEM_ASSERT(el.ref_type == 0, "");
3232
3233 GeomInfo& gi = GI[el.Geom()];
3234 for (int i = 0; i < gi.ne; i++)
3235 {
3236 const int* ev = gi.edges[i];
3237 if ((el.node[ev[0]] == node->p1 && el.node[ev[1]] == node->p2) ||
3238 (el.node[ev[1]] == node->p1 && el.node[ev[0]] == node->p2))
3239 {
3240 id.local = i;
3241 id.element = elem;
3242 return;
3243 }
3244
3245 }
3246 MFEM_ABORT("Edge not found.");
3247}
3248
3250 const Array<Pair<int, int> > &find)
3251{
3252 const MeshId &first = ids[find[pos].two];
3253 while (++pos < find.Size() && ids[find[pos].two].index == first.index)
3254 {
3255 MeshId &other = ids[find[pos].two];
3256 other.element = first.element;
3257 other.local = first.local;
3258 }
3259}
3260
3261void ParNCMesh::EncodeMeshIds(std::ostream &os, Array<MeshId> ids[])
3262{
3263 std::map<int, int> stream_id;
3264
3265 // get a list of elements involved, dump them to 'os' and create the mapping
3266 // element_id: (Element index -> stream ID)
3267 {
3269 for (int type = 0; type < 3; type++)
3270 {
3271 for (int i = 0; i < ids[type].Size(); i++)
3272 {
3273 elements.Append(ids[type][i].element);
3274 }
3275 }
3276
3277 ElementSet eset(this);
3278 eset.Encode(elements);
3279 eset.Dump(os);
3280
3281 Array<int> decoded;
3282 decoded.Reserve(elements.Size());
3283 eset.Decode(decoded);
3284
3285 for (int i = 0; i < decoded.Size(); i++)
3286 {
3287 stream_id[decoded[i]] = i;
3288 }
3289 }
3290
3291 // write the IDs as element/local pairs
3292 for (int type = 0; type < 3; type++)
3293 {
3294 write<int>(os, ids[type].Size());
3295 for (int i = 0; i < ids[type].Size(); i++)
3296 {
3297 const MeshId& id = ids[type][i];
3298 write<int>(os, stream_id[id.element]); // TODO: variable 1-4 bytes
3299 write<char>(os, id.local);
3300 }
3301 }
3302}
3303
3304void ParNCMesh::DecodeMeshIds(std::istream &is, Array<MeshId> ids[])
3305{
3306 // read the list of elements
3307 ElementSet eset(this);
3308 eset.Load(is);
3309
3310 Array<int> elems;
3311 eset.Decode(elems);
3312
3313 // read vertex/edge/face IDs
3314 for (int type = 0; type < 3; type++)
3315 {
3316 int ne = read<int>(is);
3317 ids[type].SetSize(ne);
3318
3319 for (int i = 0; i < ne; i++)
3320 {
3321 int el_num = read<int>(is);
3322 int elem = elems[el_num];
3323 Element &el = elements[elem];
3324
3325 MFEM_VERIFY(!el.ref_type, "not a leaf element: " << el_num);
3326
3327 MeshId &id = ids[type][i];
3328 id.element = elem;
3329 id.local = read<char>(is);
3330
3331 // find vertex/edge/face index
3332 GeomInfo &gi = GI[el.Geom()];
3333 switch (type)
3334 {
3335 case 0:
3336 {
3337 id.index = nodes[el.node[(int) id.local]].vert_index;
3338 break;
3339 }
3340 case 1:
3341 {
3342 const int* ev = gi.edges[(int) id.local];
3343 Node* node = nodes.Find(el.node[ev[0]], el.node[ev[1]]);
3344 MFEM_ASSERT(node && node->HasEdge(), "edge not found.");
3345 id.index = node->edge_index;
3346 break;
3347 }
3348 default:
3349 {
3350 const int* fv = gi.faces[(int) id.local];
3351 Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
3352 el.node[fv[2]], el.node[fv[3]]);
3353 MFEM_ASSERT(face, "face not found.");
3354 id.index = face->index;
3355 }
3356 }
3357 }
3358 }
3359}
3360
3361void ParNCMesh::EncodeGroups(std::ostream &os, const Array<GroupId> &ids)
3362{
3363 // get a list of unique GroupIds
3364 std::map<GroupId, GroupId> stream_id;
3365 for (int i = 0; i < ids.Size(); i++)
3366 {
3367 if (i && ids[i] == ids[i-1]) { continue; }
3368 unsigned size = stream_id.size();
3369 GroupId &sid = stream_id[ids[i]];
3370 if (size != stream_id.size()) { sid = size; }
3371 }
3372
3373 // write the unique groups
3374 write<short>(os, stream_id.size());
3375 for (std::map<GroupId, GroupId>::iterator
3376 it = stream_id.begin(); it != stream_id.end(); ++it)
3377 {
3378 write<GroupId>(os, it->second);
3379 if (it->first >= 0)
3380 {
3381 const CommGroup &group = groups[it->first];
3382 write<short>(os, group.size());
3383 for (unsigned i = 0; i < group.size(); i++)
3384 {
3385 write<int>(os, group[i]);
3386 }
3387 }
3388 else
3389 {
3390 // special "invalid" group, marks forwarded rows
3391 write<short>(os, -1);
3392 }
3393 }
3394
3395 // write the list of all GroupIds
3396 write<int>(os, ids.Size());
3397 for (int i = 0; i < ids.Size(); i++)
3398 {
3399 write<GroupId>(os, stream_id[ids[i]]);
3400 }
3401}
3402
3403void ParNCMesh::DecodeGroups(std::istream &is, Array<GroupId> &ids)
3404{
3405 int ngroups = read<short>(is);
3406 Array<GroupId> sgroups(ngroups);
3407
3408 // read stream groups, convert to our groups
3409 CommGroup ranks;
3410 ranks.reserve(128);
3411 for (int i = 0; i < ngroups; i++)
3412 {
3413 int id = read<GroupId>(is);
3414 int size = read<short>(is);
3415 if (size >= 0)
3416 {
3417 ranks.resize(size);
3418 for (int ii = 0; ii < size; ii++)
3419 {
3420 ranks[ii] = read<int>(is);
3421 }
3422 sgroups[id] = GetGroupId(ranks);
3423 }
3424 else
3425 {
3426 sgroups[id] = -1; // forwarded
3427 }
3428 }
3429
3430 // read the list of IDs
3431 ids.SetSize(read<int>(is));
3432 for (int i = 0; i < ids.Size(); i++)
3433 {
3434 ids[i] = sgroups[read<GroupId>(is)];
3435 }
3436}
3437
3438
3439//// Messages //////////////////////////////////////////////////////////////////
3440
3441template<class ValueType, bool RefTypes, int Tag>
3443{
3444 std::ostringstream ostream;
3445
3446 Array<int> tmp_elements;
3447 tmp_elements.MakeRef(elements.data(), static_cast<int>(elements.size()));
3448
3449 ElementSet eset(pncmesh, RefTypes);
3450 eset.Encode(tmp_elements);
3451 eset.Dump(ostream);
3452
3453 // decode the element set to obtain a local numbering of elements
3454 Array<int> decoded;
3455 decoded.Reserve(tmp_elements.Size());
3456 eset.Decode(decoded);
3457
3458 std::map<int, int> element_index;
3459 for (int i = 0; i < decoded.Size(); i++)
3460 {
3461 element_index[decoded[i]] = i;
3462 }
3463
3464 write<int>(ostream, static_cast<int>(values.size()));
3465 MFEM_ASSERT(elements.size() == values.size(), "");
3466
3467 for (unsigned i = 0; i < values.size(); i++)
3468 {
3469 write<int>(ostream, element_index[elements[i]]); // element number
3470 write<ValueType>(ostream, values[i]);
3471 }
3472
3473 ostream.str().swap(data);
3474}
3475
3476template<class ValueType, bool RefTypes, int Tag>
3478{
3479 std::istringstream istream(data);
3480
3481 ElementSet eset(pncmesh, RefTypes);
3482 eset.Load(istream);
3483
3484 Array<int> tmp_elements;
3485 eset.Decode(tmp_elements);
3486
3487 int* el = tmp_elements.GetData();
3488 elements.assign(el, el + tmp_elements.Size());
3489 values.resize(elements.size());
3490
3491 int count = read<int>(istream);
3492 for (int i = 0; i < count; i++)
3493 {
3494 int index = read<int>(istream);
3495 MFEM_ASSERT(index >= 0 && (size_t) index < values.size(), "");
3496 values[index] = read<ValueType>(istream);
3497 }
3498
3499 // no longer need the raw data
3500 data.clear();
3501}
3502
3504 NCMesh *ncmesh)
3505{
3506 eset.SetNCMesh(ncmesh);
3507 eset.Encode(elems);
3508
3509 Array<int> decoded;
3510 decoded.Reserve(elems.Size());
3511 eset.Decode(decoded);
3512
3513 elem_ids.resize(decoded.Size());
3514 for (int i = 0; i < decoded.Size(); i++)
3515 {
3516 elem_ids[i] = eset.GetNCMesh()->elements[decoded[i]].index;
3517 }
3518}
3519
3520static void write_dofs(std::ostream &os, const std::vector<int> &dofs)
3521{
3522 write<int>(os, static_cast<int>(dofs.size()));
3523 // TODO: we should compress the ints, mostly they are contiguous ranges
3524 os.write((const char*) dofs.data(), dofs.size() * sizeof(int));
3525}
3526
3527static void read_dofs(std::istream &is, std::vector<int> &dofs)
3528{
3529 dofs.resize(read<int>(is));
3530 is.read((char*) dofs.data(), dofs.size() * sizeof(int));
3531}
3532
3534{
3535 std::ostringstream stream;
3536
3537 eset.Dump(stream);
3538 write<long>(stream, dof_offset);
3539 write_dofs(stream, dofs);
3540
3541 stream.str().swap(data);
3542}
3543
3545{
3546 std::istringstream stream(data);
3547
3548 eset.Load(stream);
3549 dof_offset = read<long>(stream);
3550 read_dofs(stream, dofs);
3551
3552 data.clear();
3553
3554 Array<int> elems;
3555 eset.Decode(elems);
3556
3557 elem_ids.resize(elems.Size());
3558 for (int i = 0; i < elems.Size(); i++)
3559 {
3560 elem_ids[i] = eset.GetNCMesh()->elements[elems[i]].index;
3561 }
3562}
3563
3564
3565//// Utility ///////////////////////////////////////////////////////////////////
3566
3567void ParNCMesh::GetDebugMesh(Mesh &debug_mesh) const
3568{
3569 // create a serial NCMesh containing all our elements (ghosts and all)
3570 NCMesh* copy = new NCMesh(*this);
3571
3572 Array<int> &cle = copy->leaf_elements;
3573 for (int i = 0; i < cle.Size(); i++)
3574 {
3575 Element &el = copy->elements[cle[i]];
3576 el.attribute = el.rank + 1;
3577 }
3578
3579 debug_mesh.InitFromNCMesh(*copy);
3580 debug_mesh.SetAttributes();
3581 debug_mesh.ncmesh = copy;
3582}
3583
3585{
3586 NCMesh::Trim();
3587
3591
3592 for (int i = 0; i < 3; i++)
3593 {
3596 entity_index_rank[i].DeleteAll();
3597 }
3598
3599 send_rebalance_dofs.clear();
3600 recv_rebalance_dofs.clear();
3601
3603
3604 ClearAuxPM();
3605}
3606
3608{
3609 return (elem_ids.capacity() + dofs.capacity()) * sizeof(int);
3610}
3611
3612template<typename K, typename V>
3613static std::size_t map_memory_usage(const std::map<K, V> &map)
3614{
3615 std::size_t result = 0;
3616 for (typename std::map<K, V>::const_iterator
3617 it = map.begin(); it != map.end(); ++it)
3618 {
3619 result += it->second.MemoryUsage();
3620 result += sizeof(std::pair<K, V>) + 3*sizeof(void*) + sizeof(bool);
3621 }
3622 return result;
3623}
3624
3626{
3627 std::size_t groups_size = groups.capacity() * sizeof(CommGroup);
3628 for (unsigned i = 0; i < groups.size(); i++)
3629 {
3630 groups_size += groups[i].capacity() * sizeof(int);
3631 }
3632 const int approx_node_size =
3633 sizeof(std::pair<CommGroup, GroupId>) + 3*sizeof(void*) + sizeof(bool);
3634 return groups_size + group_id.size() * approx_node_size;
3635}
3636
3637template<typename Type, int Size>
3638static std::size_t arrays_memory_usage(const Array<Type> (&arrays)[Size])
3639{
3640 std::size_t total = 0;
3641 for (int i = 0; i < Size; i++)
3642 {
3643 total += arrays[i].MemoryUsage();
3644 }
3645 return total;
3646}
3647
3648std::size_t ParNCMesh::MemoryUsage(bool with_base) const
3649{
3650 return (with_base ? NCMesh::MemoryUsage() : 0) +
3652 arrays_memory_usage(entity_owner) +
3653 arrays_memory_usage(entity_pmat_group) +
3654 arrays_memory_usage(entity_conf_group) +
3655 arrays_memory_usage(entity_elem_local) +
3665 arrays_memory_usage(entity_index_rank) +
3667 map_memory_usage(send_rebalance_dofs) +
3668 map_memory_usage(recv_rebalance_dofs) +
3670 aux_pm_store.MemoryUsage() +
3671 sizeof(ParNCMesh) - sizeof(NCMesh);
3672}
3673
3674int ParNCMesh::PrintMemoryDetail(bool with_base) const
3675{
3676 if (with_base) { NCMesh::PrintMemoryDetail(); }
3677
3678 mfem::out << GroupsMemoryUsage() << " groups\n"
3679 << arrays_memory_usage(entity_owner) << " entity_owner\n"
3680 << arrays_memory_usage(entity_pmat_group) << " entity_pmat_group\n"
3681 << arrays_memory_usage(entity_conf_group) << " entity_conf_group\n"
3682 << arrays_memory_usage(entity_elem_local) << " entity_elem_local\n"
3683 << shared_vertices.MemoryUsage() << " shared_vertices\n"
3684 << shared_edges.MemoryUsage() << " shared_edges\n"
3685 << shared_faces.MemoryUsage() << " shared_faces\n"
3686 << face_orient.MemoryUsage() << " face_orient\n"
3687 << element_type.MemoryUsage() << " element_type\n"
3688 << ghost_layer.MemoryUsage() << " ghost_layer\n"
3689 << boundary_layer.MemoryUsage() << " boundary_layer\n"
3690 << tmp_owner.MemoryUsage() << " tmp_owner\n"
3691 << tmp_shared_flag.MemoryUsage() << " tmp_shared_flag\n"
3692 << arrays_memory_usage(entity_index_rank) << " entity_index_rank\n"
3693 << tmp_neighbors.MemoryUsage() << " tmp_neighbors\n"
3694 << map_memory_usage(send_rebalance_dofs) << " send_rebalance_dofs\n"
3695 << map_memory_usage(recv_rebalance_dofs) << " recv_rebalance_dofs\n"
3696 << old_index_or_rank.MemoryUsage() << " old_index_or_rank\n"
3697 << aux_pm_store.MemoryUsage() << " aux_pm_store\n"
3698 << sizeof(ParNCMesh) - sizeof(NCMesh) << " ParNCMesh" << std::endl;
3699
3700 return leaf_elements.Size();
3701}
3702
3704{
3705 gelem.SetSize(NGhostElements);
3706
3707 for (int g=0; g<NGhostElements; ++g)
3708 {
3709 // This is an index in NCMesh::elements, an array of all elements, cf.
3710 // NCMesh::OnMeshUpdated.
3711 gelem[g] = leaf_elements[NElements + g];
3712 }
3713}
3714
3715// Note that this function is modeled after ParNCMesh::Refine().
3717 const Array<VarOrderElemInfo> & sendData, Array<VarOrderElemInfo> & recvData)
3718{
3719 recvData.SetSize(0);
3720
3721 if (NRanks == 1) { return; }
3722
3724
3725 // create refinement messages to all neighbors (NOTE: some may be empty)
3726 Array<int> neighbors;
3727 NeighborProcessors(neighbors);
3728 for (int i = 0; i < neighbors.Size(); i++)
3729 {
3730 send_ref[neighbors[i]].SetNCMesh(this);
3731 }
3732
3733 // populate messages: all refinements that occur next to the processor
3734 // boundary need to be sent to the adjoining neighbors so they can keep
3735 // their ghost layer up to date
3736 Array<int> ranks;
3737 ranks.Reserve(64);
3738 for (int i = 0; i < sendData.Size(); i++)
3739 {
3740 MFEM_ASSERT(sendData[i].element < (unsigned int) NElements, "");
3741 const int elem = leaf_elements[sendData[i].element];
3742 ElementNeighborProcessors(elem, ranks);
3743 for (int j = 0; j < ranks.Size(); j++)
3744 {
3745 send_ref[ranks[j]].AddRefinement(elem, sendData[i].order);
3746 }
3747 }
3748
3749 // send the messages (overlap with local refinements)
3751
3752 // receive (ghost layer) refinements from all neighbors
3753 for (int j = 0; j < neighbors.Size(); j++)
3754 {
3755 int rank, size;
3757
3759 msg.SetNCMesh(this);
3760 msg.Recv(rank, size, MyComm);
3761
3762 // Get the ghost refinement data
3763 const int os = recvData.Size();
3764 recvData.SetSize(os + msg.Size());
3765 for (int i = 0; i < msg.Size(); i++)
3766 {
3767 recvData[os + i].element = msg.elements[i];
3768 recvData[os + i].order = msg.values[i];
3769 }
3770 }
3771
3772 // make sure we can delete the send buffers
3774}
3775
3776} // namespace mfem
3777
3778#endif // MFEM_USE_MPI
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
Definition array.hpp:1010
void GetSubArray(int offset, int sa_size, Array< T > &sa) const
Copy sub array starting from offset out to the provided sa.
Definition array.hpp:1130
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:341
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:210
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
void DeleteAll()
Delete the whole array.
Definition array.hpp:1062
int Find(const T &el) const
Return the first index where 'el' is found; return -1 if not found.
Definition array.hpp:1000
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
T * GetData()
Returns the data.
Definition array.hpp:159
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1071
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:349
T * end()
STL-like end. Returns pointer after the last element of the array.
Definition array.hpp:398
T * begin()
STL-like begin. Returns pointer to the first element of the array.
Definition array.hpp:395
std::size_t MemoryUsage() const
Returns the number of bytes allocated for the array including any reserve.
Definition array.hpp:407
T & Last()
Return the last element in the array.
Definition array.hpp:974
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
Abstract data type element.
Definition element.hpp:29
virtual void GetVertices(Array< int > &v) const =0
Get the indices defining the vertices.
void SetAttribute(const int attr)
Set element's attribute.
Definition element.hpp:61
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
static bool IsTensorProduct(Type geom)
Definition geom.hpp:112
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
int NGroups() const
Return the number of groups.
A set of integers.
Definition sets.hpp:24
void Recreate(const int n, const int *p)
Create an integer set from C-array 'p' of 'n' integers. Overwrites any existing set data.
Definition sets.cpp:33
List of integer sets.
Definition sets.hpp:51
int Insert(const IntegerSet &s)
Check to see if set 's' is in the list. If not append it to the end of the list. Returns the index of...
Definition sets.cpp:56
Mesh data type.
Definition mesh.hpp:67
Array< FaceInfo > faces_info
Definition mesh.hpp:242
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7586
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
Array< NCFaceInfo > nc_faces_info
Definition mesh.hpp:243
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition mesh.cpp:11476
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7497
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
void GetElementFaces(int i, Array< int > &faces, Array< int > &ori) const
Return the indices and the orientations of all faces of element i.
Definition mesh.cpp:8318
virtual void SetAttributes(bool elem_attrs_changed=true, bool bdr_face_attrs_changed=true)
Determine the sets of unique attribute values in domain if elem_attrs_changed and boundary elements i...
Definition mesh.cpp:2016
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:318
A class for non-conforming AMR. The class is not used directly by the user, rather it is an extension...
Definition ncmesh.hpp:190
static GeomInfo GI[Geometry::NumGeom]
Definition ncmesh.hpp:1410
virtual void Update()
Definition ncmesh.cpp:268
void FindNeighbors(int elem, Array< int > &neighbors, const Array< int > *search_set=NULL)
Definition ncmesh.cpp:4322
virtual void Trim()
Save memory by releasing all non-essential and cached data.
Definition ncmesh.cpp:6964
const Face & GetFace(int i) const
Access a Face.
Definition ncmesh.hpp:716
mfem::Element * NewMeshElement(int geom) const
Definition ncmesh.cpp:2720
NCMesh()=default
int NGhostElements
Definition ncmesh.hpp:785
HashTable< Node > nodes
Definition ncmesh.hpp:683
static int find_node(const Element &el, int node)
Definition ncmesh.cpp:3278
static int find_element_edge(const Element &el, int vn0, int vn1, bool abort=true)
Definition ncmesh.cpp:3298
int PrintMemoryDetail() const
Definition ncmesh.cpp:7030
HashTable< Face > faces
Definition ncmesh.hpp:684
bool HaveTets() const
Return true if the mesh contains tetrahedral elements.
Definition ncmesh.hpp:848
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
Definition ncmesh.cpp:5639
Array< int > boundary_faces
subset of all faces, set by BuildFaceList
Definition ncmesh.hpp:795
BlockArray< Element > elements
Definition ncmesh.hpp:688
int FindMidEdgeNode(int node1, int node2) const
Definition ncmesh.cpp:336
Array< char > face_geom
face geometry by face index, set by OnMeshUpdated
Definition ncmesh.hpp:796
virtual void BuildFaceList()
Definition ncmesh.cpp:3677
TmpVertex * tmp_vertex
Definition ncmesh.hpp:1275
Array< int > leaf_elements
finest elements, in Mesh ordering (+ ghosts)
Definition ncmesh.hpp:787
const real_t * CalcVertexPos(int node) const
Definition ncmesh.cpp:2735
int NGhostVertices
Definition ncmesh.hpp:785
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Definition ncmesh.cpp:5670
Array< int > leaf_sfc_index
natural tree ordering of leaf elements
Definition ncmesh.hpp:788
NCList edge_list
lazy-initialized list of edges, see GetEdgeList
Definition ncmesh.hpp:792
Array< int > root_state
Definition ncmesh.hpp:765
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition ncmesh.hpp:369
virtual void BuildEdgeList()
Definition ncmesh.cpp:3802
bool Iso
true if the mesh only contains isotropic refinements
Definition ncmesh.hpp:590
virtual void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges, Array< int > &bdr_faces)
Get a list of vertices (2D/3D), edges (3D) and faces (3D) that coincide with boundary elements with t...
Definition ncmesh.cpp:5827
virtual void BuildVertexList()
Definition ncmesh.cpp:3914
Array< real_t > coordinates
Definition ncmesh.hpp:769
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:376
int MyRank
used in parallel, or when loading a parallel file in serial
Definition ncmesh.hpp:589
int spaceDim
dimensions of the elements and the vertex coordinates
Definition ncmesh.hpp:588
int NGhostEdges
Definition ncmesh.hpp:785
NCList vertex_list
lazy-initialized list of vertices, see GetVertexList
Definition ncmesh.hpp:793
void FindSetNeighbors(const Array< char > &elem_set, Array< int > *neighbors, Array< char > *neighbor_set=NULL)
Definition ncmesh.cpp:4211
long MemoryUsage() const
Return total number of bytes allocated.
Definition ncmesh.cpp:7007
void DerefineElement(int elem)
Derefine the element elem, does nothing on leaf elements.
Definition ncmesh.cpp:2038
NCList face_list
lazy-initialized list of faces, see GetFaceList
Definition ncmesh.hpp:791
int NGhostFaces
Definition ncmesh.hpp:785
static int find_local_face(int geom, int a, int b, int c)
Definition ncmesh.cpp:3316
A pair of objects.
Class for parallel meshes.
Definition pmesh.hpp:35
Table send_face_nbr_elements
Definition pmesh.hpp:467
Array< Element * > shared_edges
Definition pmesh.hpp:70
int GetMyRank() const
Definition pmesh.hpp:405
Array< int > sface_lface
Definition pmesh.hpp:87
Table group_sedge
Definition pmesh.hpp:79
Table group_svert
Shared objects in each group.
Definition pmesh.hpp:78
void BuildFaceNbrElementToFaceTable()
Definition pmesh.cpp:2739
Array< Vertex > face_nbr_vertices
Definition pmesh.hpp:465
MPI_Comm MyComm
Definition pmesh.hpp:46
Array< Vert4 > shared_quads
Definition pmesh.hpp:75
Table group_squad
Definition pmesh.hpp:81
Array< int > svert_lvert
Shared to local index mapping.
Definition pmesh.hpp:84
Array< int > sedge_ledge
Definition pmesh.hpp:85
Array< Element * > face_nbr_elements
Definition pmesh.hpp:464
GroupTopology gtopo
Definition pmesh.hpp:457
Array< int > face_nbr_group
Definition pmesh.hpp:461
Array< int > face_nbr_elements_offset
Definition pmesh.hpp:462
Array< Vert3 > shared_trias
Definition pmesh.hpp:74
std::unique_ptr< Table > face_nbr_el_ori
orientations for each face (from nbr processor)
Definition pmesh.hpp:93
Table group_stria
Definition pmesh.hpp:80
void DecodeTree(int elem, int &pos, Array< int > &elements) const
Definition pncmesh.cpp:3056
Array< unsigned char > data
encoded refinement (sub-)trees
Definition pncmesh.hpp:411
void Encode(const Array< int > &elements)
Definition pncmesh.cpp:3017
int GetInt(int pos) const
Definition pncmesh.cpp:2956
void Decode(Array< int > &elements) const
Definition pncmesh.cpp:3098
void FlagElements(const Array< int > &elements, char flag)
Definition pncmesh.cpp:2965
std::string RefPath() const
Definition pncmesh.cpp:3038
void Load(std::istream &is)
Definition pncmesh.cpp:3114
void Dump(std::ostream &os) const
Definition pncmesh.cpp:3108
std::vector< ValueType > values
Definition pncmesh.hpp:477
void SetNCMesh(ParNCMesh *pncmesh_)
Set pointer to ParNCMesh (needed to encode the message).
Definition pncmesh.hpp:486
std::map< int, NeighborDerefinementMessage > Map
Definition pncmesh.hpp:531
std::map< int, NeighborElementRankMessage > Map
Definition pncmesh.hpp:550
void AddElement(int elem, int rank, int attribute)
Definition pncmesh.hpp:548
std::map< int, NeighborPRefinementMessage > Map
Definition pncmesh.hpp:596
std::map< int, NeighborRefinementMessage > Map
Definition pncmesh.hpp:521
void SetElements(const Array< int > &elems, NCMesh *ncmesh)
Definition pncmesh.cpp:3503
std::map< int, RebalanceMessage > Map
Definition pncmesh.hpp:564
void AddElement(int elem, int rank, int attribute)
Definition pncmesh.hpp:562
A parallel extension of the NCMesh class.
Definition pncmesh.hpp:63
bool AnisotropicConflict(const Array< Refinement > &refinements, std::set< int > &conflicts)
Definition pncmesh.cpp:1556
Array< int > entity_elem_local[3]
Definition pncmesh.hpp:322
void MakeSharedTable(int ngroups, int ent, Array< int > &shared_local, Table &group_shared, Array< char > *entity_geom=NULL, char geom=0)
Definition pncmesh.cpp:875
Array< int > tmp_neighbors
Definition pncmesh.hpp:447
NCList shared_edges
Definition pncmesh.hpp:325
void CheckRefinement(int elem, const Refinement &ref, const Array< Refinement > &refinements, const std::map< int, int > &elemToRef, std::set< int > &conflicts)
Check whether the input refinement would cause a conflict.
Definition pncmesh.cpp:1920
Array< Connection > entity_index_rank[3]
Definition pncmesh.hpp:367
GroupMap group_id
Definition pncmesh.hpp:312
RebalanceDofMessage::Map send_rebalance_dofs
Definition pncmesh.hpp:611
void CalcFaceOrientations()
Definition pncmesh.cpp:680
void DecodeGroups(std::istream &is, Array< GroupId > &ids)
Definition pncmesh.cpp:3403
bool PruneTree(int elem)
Internal. Recursive part of Prune().
Definition pncmesh.cpp:1486
bool CheckElementType(int elem, int type)
Definition pncmesh.cpp:809
void GetGhostElements(Array< int > &gelem)
Definition pncmesh.cpp:3703
void Trim() override
Save memory by releasing all non-essential and cached data.
Definition pncmesh.cpp:3584
void BuildVertexList() override
Definition pncmesh.cpp:381
void FindEdgesOfGhostElement(int elem, Array< int > &edges)
Definition pncmesh.cpp:319
void FindFacesOfGhostElement(int elem, Array< int > &faces)
Definition pncmesh.cpp:345
Array< int > ghost_layer
list of elements whose 'element_type' == 2.
Definition pncmesh.hpp:337
void BuildFaceList() override
Definition pncmesh.cpp:193
void GetFaceNeighbors(class ParMesh &pmesh)
Definition pncmesh.cpp:1036
RebalanceDofMessage::Map recv_rebalance_dofs
Definition pncmesh.hpp:612
void ChangeVertexMeshIdElement(NCMesh::MeshId &id, int elem)
Definition pncmesh.cpp:3205
void UpdateLayers()
Definition pncmesh.cpp:771
void EncodeGroups(std::ostream &os, const Array< GroupId > &ids)
Definition pncmesh.cpp:3361
virtual ~ParNCMesh()
Definition pncmesh.cpp:135
void FindEdgesOfGhostFace(int face, Array< int > &edges)
Definition pncmesh.cpp:298
Array< GroupId > entity_conf_group[3]
Definition pncmesh.hpp:320
Array< int > boundary_layer
list of type 3 elements
Definition pncmesh.hpp:338
void AdjustMeshIds(Array< MeshId > ids[], int rank)
Definition pncmesh.cpp:3123
NCList shared_vertices
Definition pncmesh.hpp:325
std::size_t GroupsMemoryUsage() const
Definition pncmesh.cpp:3625
const NCList & GetSharedVertices()
Definition pncmesh.hpp:129
void ChangeEdgeMeshIdElement(NCMesh::MeshId &id, int elem)
Definition pncmesh.cpp:3223
void GetConformingSharedStructures(class ParMesh &pmesh)
Definition pncmesh.cpp:935
bool CheckRefAnisoFaceSplits(int vn1, int vn2, int vn3, int vn4, int level=0)
Definition pncmesh.cpp:1689
void ElementNeighborProcessors(int elem, Array< int > &ranks)
Definition pncmesh.cpp:826
static int get_face_orientation(const Face &face, const Element &e1, const Element &e2, int local[2]=NULL)
Definition pncmesh.cpp:650
void ElementSharesFace(int elem, int local, int face) override
Definition pncmesh.cpp:170
void BuildEdgeList() override
Definition pncmesh.cpp:260
Array< char > tmp_shared_flag
Definition pncmesh.hpp:366
NCList shared_faces
Definition pncmesh.hpp:325
Array< int > old_index_or_rank
Definition pncmesh.hpp:617
MPI_Comm MyComm
Definition pncmesh.hpp:305
std::vector< int > CommGroup
Definition pncmesh.hpp:155
void DecodeMeshIds(std::istream &is, Array< MeshId > ids[])
Definition pncmesh.cpp:3304
void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges, Array< int > &bdr_faces) override
Definition pncmesh.cpp:706
void EncodeMeshIds(std::ostream &os, Array< MeshId > ids[])
Definition pncmesh.cpp:3261
void CheckRefinementMaster(const Array< Refinement > &refinements, const std::map< int, int > &elemToRef, std::set< int > &conflicts)
Check whether any master face is marked for a conflicting refinement.
Definition pncmesh.cpp:1716
Array< DenseMatrix * > aux_pm_store
Stores modified point matrices created by GetFaceNeighbors.
Definition pncmesh.hpp:620
const NCList & GetSharedList(int entity)
Helper to get shared vertices/edges/faces ('entity' == 0/1/2 resp.).
Definition pncmesh.hpp:138
Array< int > tmp_owner
Definition pncmesh.hpp:365
GroupId GetSingletonGroup(int rank)
Definition pncmesh.cpp:519
void ChangeRemainingMeshIds(Array< MeshId > &ids, int pos, const Array< Pair< int, int > > &find)
Definition pncmesh.cpp:3249
Array< char > face_orient
Definition pncmesh.hpp:327
Array< char > element_type
Definition pncmesh.hpp:335
void InitOwners(int num, Array< GroupId > &entity_owner)
Definition pncmesh.cpp:413
GroupList groups
Definition pncmesh.hpp:311
Array< GroupId > entity_owner[3]
Definition pncmesh.hpp:315
void CalculatePMatrixGroups()
Definition pncmesh.cpp:577
void ElementSharesEdge(int elem, int local, int enode) override
Definition pncmesh.cpp:235
ParNCMesh()=default
const NCList & GetSharedEdges()
Definition pncmesh.hpp:130
void GetDebugMesh(Mesh &debug_mesh) const
Definition pncmesh.cpp:3567
void Update() override
Definition pncmesh.cpp:140
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
Definition pncmesh.cpp:528
const NCList & GetSharedFaces()
Definition pncmesh.hpp:131
Array< GroupId > entity_pmat_group[3]
Definition pncmesh.hpp:317
void MakeSharedList(const NCList &list, NCList &shared)
Definition pncmesh.cpp:423
void AddConnections(int entity, int index, const Array< int > &ranks)
Definition pncmesh.cpp:569
int InitialPartition(int index) const
Helper to get the partitioning when the serial mesh gets split initially.
Definition pncmesh.hpp:347
void NeighborProcessors(Array< int > &neighbors)
Definition pncmesh.cpp:858
void CreateGroups(int nentities, Array< Connection > &index_rank, Array< GroupId > &entity_group)
Definition pncmesh.cpp:539
GroupId GetGroupId(const CommGroup &group)
Definition pncmesh.cpp:503
void CommunicateGhostData(const Array< VarOrderElemInfo > &sendData, Array< VarOrderElemInfo > &recvData)
Definition pncmesh.cpp:3716
void ElementSharesVertex(int elem, int local, int vnode) override
Definition pncmesh.cpp:358
Data type line segment element.
Definition segment.hpp:23
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
int RowSize(int i) const
Definition table.hpp:122
void ShiftUpI()
Definition table.cpp:163
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
void AddConnection(int r, int c)
Definition table.hpp:89
void MakeI(int nrows)
Definition table.cpp:130
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:103
void MakeFromList(int nrows, const Array< Connection > &list)
Create the table from a list of connections {(from, to)}, where 'from' is a TYPE I index and 'to' is ...
Definition table.cpp:322
void MakeJ()
Definition table.cpp:140
void AddAColumnInRow(int r)
Definition table.hpp:86
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
string space
real_t f(const Vector &p)
mfem::real_t real_t
void write(std::ostream &os, T value)
Write 'value' to stream.
Definition binaryio.hpp:37
T read(std::istream &is)
Read a value from the stream and return it.
Definition binaryio.hpp:44
int FindHexFace(const int *no, int vn1, int vn2, int vn3, int vn4)
Definition pncmesh.cpp:1765
char GetHexFaceRefType(const bool(&refDir)[3], int face)
Definition pncmesh.cpp:1667
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
int GetHexFaceDir(int face)
Definition pncmesh.cpp:1657
MFEM_HOST_DEVICE int FlipIndexSign(int i)
Signed indices i -> -1 - i are used as a convention to encode orientation.
Definition globals.hpp:117
float real_t
Definition config.hpp:46
bool operator<(const Pair< A, B > &p, const Pair< A, B > &q)
Comparison operator for class Pair, based on the first element only.
void Update(Vector &x, int k, DenseMatrix &h, Vector &s, Array< Vector * > &v)
Definition solvers.cpp:1113
Helper struct for defining a connectivity table, see Table::MakeFromList.
Definition table.hpp:28
Helper struct to convert a C++ type to an MPI type.
This structure stores the low level information necessary to interpret the configuration of elements ...
Definition mesh.hpp:179
int rank
processor number (ParNCMesh), -1 if undefined/unknown
Definition ncmesh.hpp:666
int child[MaxElemChildren]
2-10 children (if ref_type != 0)
Definition ncmesh.hpp:671
char flag
generic flag/marker, can be used by algorithms
Definition ncmesh.hpp:664
int node[MaxElemNodes]
element corners (if ref_type == 0)
Definition ncmesh.hpp:670
char ref_type
bit mask of X,Y,Z refinements (bits 0,1,2 respectively)
Definition ncmesh.hpp:662
char geom
Geometry::Type of the element (char for storage only)
Definition ncmesh.hpp:661
int index
element number in the Mesh, -1 if refined
Definition ncmesh.hpp:665
int parent
parent element, -1 if this is a root element, -2 if free'd
Definition ncmesh.hpp:673
Geometry::Type Geom() const
Definition ncmesh.hpp:676
int elem[2]
up to 2 elements sharing the face
Definition ncmesh.hpp:640
bool Boundary() const
Definition ncmesh.hpp:644
int index
face number in the Mesh
Definition ncmesh.hpp:639
int attribute
boundary element attribute, -1 if internal face
Definition ncmesh.hpp:638
This holds in one place the constants about the geometries we support.
Definition ncmesh.hpp:1398
int faces[MaxElemFaces][4]
Definition ncmesh.hpp:1401
int edges[MaxElemEdges][2]
Definition ncmesh.hpp:1400
int slaves_end
slave faces
Definition ncmesh.hpp:277
Identifies a vertex/edge/face in both Mesh and NCMesh.
Definition ncmesh.hpp:260
int element
NCMesh::Element containing this vertex/edge/face.
Definition ncmesh.hpp:262
int index
Mesh number.
Definition ncmesh.hpp:261
signed char geom
Geometry::Type (faces only) (char to save RAM)
Definition ncmesh.hpp:264
signed char local
local number within 'element'
Definition ncmesh.hpp:263
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:301
Array< MeshId > conforming
All MeshIds corresponding to conformal faces.
Definition ncmesh.hpp:302
long MemoryUsage() const
Definition ncmesh.cpp:6979
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:304
void Clear()
Erase the contents of the conforming, master and slave arrays.
Definition ncmesh.cpp:3971
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:303
Array< DenseMatrix * > point_matrices[Geometry::NumGeom]
List of unique point matrices for each slave geometry.
Definition ncmesh.hpp:307
MeshIdAndType GetMeshIdAndType(int index) const
Return a mesh id and type for a given nc index.
Definition ncmesh.cpp:3990
bool HasEdge() const
Definition ncmesh.hpp:613
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:287
unsigned matrix
index into NCList::point_matrices[geom]
Definition ncmesh.hpp:289
int master
master number (in Mesh numbering)
Definition ncmesh.hpp:288
real_t s[3]
Definition ncmesh.hpp:45
void SetScaleForType(const real_t *scale)
Set the scale in the directions for the currently set type.
Definition ncmesh.cpp:540
int index
Mesh element number.
Definition ncmesh.hpp:42
char GetType() const
Return the type as char.
Definition ncmesh.cpp:580
void Issend(int rank, MPI_Comm comm)
Non-blocking synchronous send to processor 'rank'. Returns immediately. Completion (MPI_Wait/Test) me...
static void WaitAllSent(MapT &rank_msg)
Helper to wait for all messages in a map container to be sent.
void Isend(int rank, MPI_Comm comm)
Non-blocking send to processor 'rank'. Returns immediately. Completion (as tested by MPI_Wait/Test) d...
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
Helper to send all messages in a rank-to-message map container.
void Recv(int rank, int size, MPI_Comm comm)
Post-probe receive from processor 'rank' of message size 'size'.
static void Probe(int &rank, int &size, MPI_Comm comm)
Blocking probe for incoming message of this type from any rank. Returns the rank and message size.
std::array< int, NCMesh::MaxFaceNodes > nodes