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