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