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