MFEM  v3.4
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
pncmesh.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
3 // reserved. See file COPYRIGHT for details.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability see http://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the GNU Lesser General Public License (as published by the Free
10 // Software Foundation) version 2.1 dated February 1999.
11 
12 #include "../config/config.hpp"
13 
14 #ifdef MFEM_USE_MPI
15 
16 #include "mesh_headers.hpp"
17 #include "pncmesh.hpp"
18 #include "../general/binaryio.hpp"
19 
20 #include <map>
21 #include <climits> // INT_MIN, INT_MAX
22 
23 namespace mfem
24 {
25 
26 using namespace bin_io;
27 
28 ParNCMesh::ParNCMesh(MPI_Comm comm, const NCMesh &ncmesh)
29  : NCMesh(ncmesh)
30 {
31  MyComm = comm;
32  MPI_Comm_size(MyComm, &NRanks);
33  MPI_Comm_rank(MyComm, &MyRank);
34 
35  // assign leaf elements to the processors by simply splitting the
36  // sequence of leaf elements into 'NRanks' parts
37  for (int i = 0; i < leaf_elements.Size(); i++)
38  {
40  }
41 
42  Update();
43 
44  // note that at this point all processors still have all the leaf elements;
45  // we however may now start pruning the refinement tree to get rid of
46  // branches that only contain someone else's leaves (see Prune())
47 }
48 
50 {
51  ClearAuxPM();
52 }
53 
55 {
57 
58  groups.clear();
59  group_id.clear();
60  groups_augmented = false;
61 
62  CommGroup self;
63  self.push_back(MyRank);
64  groups.push_back(self);
65  group_id[self] = 0;
66 
70 
74 }
75 
77 {
78  // This is an override of NCMesh::AssignLeafIndices(). The difference is
79  // that we shift all elements we own to the beginning of the array
80  // 'leaf_elements' and assign all ghost elements indices >= NElements. This
81  // will make the ghosts skipped in NCMesh::GetMeshComponents.
82 
83  // Also note that the ordering of ghosts and non-ghosts is preserved here,
84  // which is important for ParNCMesh::GetFaceNeighbors.
85 
86  Array<int> ghosts;
87  ghosts.Reserve(leaf_elements.Size());
88 
89  NElements = 0;
90  for (int i = 0; i < leaf_elements.Size(); i++)
91  {
92  int elem = leaf_elements[i];
93  if (elements[elem].rank == MyRank)
94  {
95  leaf_elements[NElements++] = elem;
96  }
97  else
98  {
99  ghosts.Append(elem);
100  }
101  }
102  NGhostElements = ghosts.Size();
103 
105  leaf_elements.Append(ghosts);
106 
108 }
109 
111 {
112  // This is an override of NCMesh::UpdateVertices. This version first
113  // assigns vert_index to vertices of elements of our rank. Only these
114  // vertices then make it to the Mesh in NCMesh::GetMeshComponents.
115  // The remaining (ghost) vertices are assigned indices greater or equal to
116  // Mesh::GetNV().
117 
118  for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
119  {
120  if (node->HasVertex()) { node->vert_index = -1; }
121  }
122 
123  NVertices = 0;
124  for (int i = 0; i < leaf_elements.Size(); i++)
125  {
126  Element &el = elements[leaf_elements[i]];
127  if (el.rank == MyRank)
128  {
129  for (int j = 0; j < GI[(int) el.geom].nv; j++)
130  {
131  int &vindex = nodes[el.node[j]].vert_index;
132  if (vindex < 0) { vindex = NVertices++; }
133  }
134  }
135  }
136 
138  for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
139  {
140  if (node->HasVertex() && node->vert_index >= 0)
141  {
142  vertex_nodeId[node->vert_index] = node.index();
143  }
144  }
145 
146  NGhostVertices = 0;
147  for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
148  {
149  if (node->HasVertex() && node->vert_index < 0)
150  {
151  node->vert_index = NVertices + (NGhostVertices++);
152  }
153  }
154 }
155 
157 {
158  // This is an override (or extension of) NCMesh::OnMeshUpdated().
159  // In addition to getting edge/face indices from 'mesh', we also
160  // assign indices to ghost edges/faces that don't exist in the 'mesh'.
161 
162  // clear edge_index and Face::index
163  for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
164  {
165  if (node->HasEdge()) { node->edge_index = -1; }
166  }
167  for (face_iterator face = faces.begin(); face != faces.end(); ++face)
168  {
169  face->index = -1;
170  }
171 
172  // go assign existing edge/face indices
173  NCMesh::OnMeshUpdated(mesh);
174 
175  // assign ghost edge indices
176  NEdges = mesh->GetNEdges();
177  NGhostEdges = 0;
178  for (node_iterator node = nodes.begin(); node != nodes.end(); ++node)
179  {
180  if (node->HasEdge() && node->edge_index < 0)
181  {
182  node->edge_index = NEdges + (NGhostEdges++);
183  }
184  }
185 
186  // assign ghost face indices
187  NFaces = mesh->GetNumFaces();
188  NGhostFaces = 0;
189  for (face_iterator face = faces.begin(); face != faces.end(); ++face)
190  {
191  if (face->index < 0) { face->index = NFaces + (NGhostFaces++); }
192  }
193 
194  if (Dim == 2)
195  {
196  // in 2D we have fake faces because of DG
197  MFEM_ASSERT(NFaces == NEdges, "");
198  MFEM_ASSERT(NGhostFaces == NGhostEdges, "");
199  }
200 }
201 
202 void ParNCMesh::ElementSharesFace(int elem, int face)
203 {
204  // Analogous to ElementSharesEdge.
205 
206  int el_rank = elements[elem].rank;
207  int f_index = faces[face].index;
208 
209  int &owner = tmp_owner[f_index];
210  owner = std::min(owner, el_rank);
211 
212  index_rank.Append(Connection(f_index, el_rank));
213 }
214 
216 {
217  // This is an extension of NCMesh::BuildFaceList() which also determines
218  // face ownership and creates face processor groups.
219 
220  int nfaces = NFaces + NGhostFaces;
221  tmp_owner.SetSize(nfaces);
222  tmp_owner = INT_MAX;
223 
224  index_rank.SetSize(6*leaf_elements.Size() * 3/2);
225  index_rank.SetSize(0);
226 
228 
230 
231  InitOwners(nfaces, face_owner);
232  InitGroups(nfaces, face_group);
233 
235 
237  index_rank.DeleteAll();
238 }
239 
240 void ParNCMesh::ElementSharesEdge(int elem, int enode)
241 {
242  // Called by NCMesh::BuildEdgeList when an edge is visited in a leaf element.
243  // This allows us to determine edge ownership and processors that share it
244  // without duplicating all the HashTable lookups in NCMesh::BuildEdgeList().
245 
246  int el_rank = elements[elem].rank;
247  int e_index = nodes[enode].edge_index;
248 
249  int &owner = tmp_owner[e_index];
250  owner = std::min(owner, el_rank);
251 
252  index_rank.Append(Connection(e_index, el_rank));
253 }
254 
256 {
257  // This is an extension of NCMesh::BuildEdgeList() which also determines
258  // edge ownership and creates edge processor groups.
259 
260  int nedges = NEdges + NGhostEdges;
261  tmp_owner.SetSize(nedges);
262  tmp_owner = INT_MAX;
263 
264  index_rank.SetSize(12*leaf_elements.Size() * 3/2);
265  index_rank.SetSize(0);
266 
268 
270 
271  InitOwners(nedges, edge_owner);
272  InitGroups(nedges, edge_group);
273 
275  index_rank.DeleteAll();
276 }
277 
278 void ParNCMesh::ElementSharesVertex(int elem, int vnode)
279 {
280  // Analogous to ElementSharesEdge.
281 
282  int el_rank = elements[elem].rank;
283  int v_index = nodes[vnode].vert_index;
284 
285  int &owner = tmp_owner[v_index];
286  owner = std::min(owner, el_rank);
287 
288  index_rank.Append(Connection(v_index, el_rank));
289 }
290 
292 {
293  // This is an extension of NCMesh::BuildVertexList() which also determines
294  // vertex ownership and creates vertex processor groups.
295 
296  int nvertices = NVertices + NGhostVertices;
297  tmp_owner.SetSize(nvertices);
298  tmp_owner = INT_MAX;
299 
300  index_rank.SetSize(8*leaf_elements.Size());
301  index_rank.SetSize(0);
302 
304 
305  InitOwners(nvertices, vertex_owner);
306  InitGroups(nvertices, vertex_group);
307 
309  index_rank.DeleteAll();
310 }
311 
313 {
314  if (lhs.size() == rhs.size())
315  {
316  for (unsigned i = 0; i < lhs.size(); i++)
317  {
318  if (lhs[i] < rhs[i]) { return true; }
319  }
320  return false;
321  }
322  return lhs.size() < rhs.size();
323 }
324 
325 #ifdef MFEM_DEBUG
326 static bool group_sorted(const ParNCMesh::CommGroup &group)
327 {
328  for (unsigned i = 1; i < group.size(); i++)
329  {
330  if (group[i] <= group[i-1]) { return false; }
331  }
332  return true;
333 }
334 #endif
335 
337 {
338  if (group.size() == 1 && group[0] == MyRank)
339  {
340  return 0;
341  }
342  MFEM_ASSERT(group_sorted(group), "invalid group");
343  GroupId &id = group_id[group];
344  if (!id)
345  {
346  id = groups.size();
347  groups.push_back(group);
348  }
349  return id;
350 }
351 
353 {
354  if (g1 == g2) { return g1; }
355 
356  CommGroup &cg1 = groups[g1], &cg2 = groups[g2];
357 
358  CommGroup join;
359  join.reserve(cg1.size() + cg2.size());
360  join.insert(join.end(), cg1.begin(), cg1.end());
361  join.insert(join.end(), cg2.begin(), cg2.end());
362 
363  std::sort(join.begin(), join.end());
364  join.erase(std::unique(join.begin(), join.end()), join.end());
365 
366  return GetGroupId(join);
367 }
368 
370 {
371  if (rank == INT_MAX) { return -1; } // invalid
372  static std::vector<int> group;
373  group.resize(1);
374  group[0] = rank;
375  return GetGroupId(group);
376 }
377 
378 bool ParNCMesh::GroupContains(GroupId id, int rank) const
379 {
380  // TODO: would std::lower_bound() pay off here? Groups are usually small.
381  const CommGroup &group = groups[id];
382  for (unsigned i = 0; i < group.size(); i++)
383  {
384  if (group[i] == rank) { return true; }
385  }
386  return false;
387 }
388 
389 void ParNCMesh::InitOwners(int num, Array<GroupId> &entity_owner)
390 {
391  entity_owner.SetSize(num);
392  for (int i = 0; i < num; i++)
393  {
394  entity_owner[i] = GetSingletonGroup(tmp_owner[i]);
395  }
396 }
397 
398 void ParNCMesh::InitGroups(int num, Array<GroupId> &entity_group)
399 {
400  entity_group.SetSize(num);
401  entity_group = 0;
402 
403  index_rank.Sort();
404  index_rank.Unique();
405 
406  CommGroup group;
407  group.reserve(128);
408 
409  int begin = 0, end = 0;
410  while (begin < index_rank.Size())
411  {
412  int index = index_rank[begin].from;
413  while (end < index_rank.Size() && index_rank[end].from == index)
414  {
415  end++;
416  }
417  group.resize(end - begin);
418  for (int i = begin; i < end; i++)
419  {
420  group[i - begin] = index_rank[i].to;
421  }
422  entity_group[index] = GetGroupId(group);
423  begin = end;
424  }
425 }
426 
427 struct MasterSlaveInfo
428 {
429  int master; // master index if this is a slave
430  int slaves_begin, slaves_end; // slave list if this is a master
431  MasterSlaveInfo() : master(-1), slaves_begin(0), slaves_end(0) {}
432 };
433 
434 void ParNCMesh::AddMasterSlaveConnections(int nitems, const NCList& list)
435 {
436 #if 0 // optimal version for P matrix construction but breaks ParMesh::Print and DG
437  Array<int> masters(nitems);
438  masters = -1;
439 
440  for (unsigned i = 0; i < list.slaves.size(); i++)
441  {
442  const Slave& sf = list.slaves[i];
443  masters[sf.index] = sf.master;
444  }
445 
446  // We need the processor groups of master edges/faces to contain the ranks of
447  // their slaves, so that master DOFs get sent to those who share the slaves.
448  // This is done by appending more items to the 'index_rank' array, before it
449  // is sorted and converted to groups.
450  // (Note that a master/slave edge can be shared by more than one processor.)
451 
452  int size = index_rank.Size();
453  for (int i = 0; i < size; i++)
454  {
455  int index = index_rank[i].from;
456  int rank = index_rank[i].to;
457 
458  int master = masters[index];
459  if (master >= 0)
460  {
461  // 'index' is a slave, add its rank to the master's group
462  index_rank.Append(Connection(master, rank));
463  }
464  }
465 #else // suboptimal version fully linking master and slave entities in groups
466 
467  // create an auxiliary structure for each edge/face
468  std::vector<MasterSlaveInfo> info(nitems);
469 
470  for (unsigned i = 0; i < list.masters.size(); i++)
471  {
472  const Master &mf = list.masters[i];
473  info[mf.index].slaves_begin = mf.slaves_begin;
474  info[mf.index].slaves_end = mf.slaves_end;
475  }
476  for (unsigned i = 0; i < list.slaves.size(); i++)
477  {
478  const Slave& sf = list.slaves[i];
479  info[sf.index].master = sf.master;
480  }
481 
482  // We need the processor groups of master edges/faces to contain the ranks of
483  // their slaves (so that master DOFs get sent to those who share the slaves).
484  // Conversely, we need the groups of slave edges/faces to contain the ranks
485  // of their masters. Both can be done by appending more items to the
486  // 'index_rank' array, before it is sorted and converted to the group table.
487  // (Note that a master/slave edge can be shared by more than one processor.)
488 
489  int size = index_rank.Size();
490  for (int i = 0; i < size; i++)
491  {
492  int index = index_rank[i].from;
493  int rank = index_rank[i].to;
494 
495  const MasterSlaveInfo &msi = info[index];
496  if (msi.master >= 0)
497  {
498  // 'index' is a slave, add its rank to the master's group
499  index_rank.Append(Connection(msi.master, rank));
500  }
501  else
502  {
503  for (int j = msi.slaves_begin; j < msi.slaves_end; j++)
504  {
505  // 'index' is a master, add its rank to the groups of the slaves
506  index_rank.Append(Connection(list.slaves[j].index, rank));
507  }
508  }
509  }
510 #endif
511 }
512 
514 {
515  if (groups_augmented) { return; }
516 
518  GetSharedEdges();
519  GetSharedFaces();
520 
521  if (!shared_edges.masters.size() && !shared_faces.masters.size()) { return; }
522 
523  // augment comm groups of vertices of shared master edges, so that their
524  // DOFs get sent to the slave ranks along with master edge DOFs
525  for (unsigned i = 0; i < shared_edges.masters.size(); i++)
526  {
527  int v[2];
528  const MeshId &edge_id = shared_edges.masters[i];
529  GetEdgeVertices(edge_id, v);
530 
531  for (int j = 0; j < 2; j++)
532  {
533  vertex_group[v[j]] = JoinGroups(vertex_group[v[j]],
534  edge_group[edge_id.index]);
535  }
536  }
537 
538  // similarly, augment comm groups of vertices and edges of shared master
539  // faces, to make sure slave ranks receive all the necessary master DOFs
540  for (unsigned i = 0; i < shared_faces.masters.size(); i++)
541  {
542  int v[4], e[4], eo[4];
543  const MeshId &face_id = shared_faces.masters[i];
544  GetFaceVerticesEdges(face_id, v, e, eo);
545 
546  int f_group = face_group[face_id.index];
547  for (int j = 0; j < 4; j++)
548  {
549  vertex_group[v[j]] = JoinGroups(vertex_group[v[j]], f_group);
550  edge_group[e[j]] = JoinGroups(edge_group[e[j]], f_group);
551  }
552  }
553 
554  groups_augmented = true;
555 
556  // force recreating shared entities according to new groups
560 }
561 
563 {
564  group_shared.SetSize(groups.size());
565  group_shared = false;
566 
567  // A vertex/edge/face is shared if its group contains more than one
568  // processor and at the same time one of them is ourselves.
569  for (unsigned i = 0; i < groups.size(); i++)
570  {
571  const CommGroup &group = groups[i];
572  if (group.size() > 1)
573  {
574  for (unsigned j = 0; j < group.size(); j++)
575  {
576  if (group[j] == MyRank)
577  {
578  group_shared[i] = true;
579  break;
580  }
581  }
582  }
583  }
584 }
585 
586 void ParNCMesh::MakeShared(const Array<GroupId> &entity_group,
587  const NCList &list, NCList &shared)
588 {
589  Array<bool> group_shared;
590  GetGroupShared(group_shared);
591 
592  shared.Clear();
593 
594  for (unsigned i = 0; i < list.conforming.size(); i++)
595  {
596  if (group_shared[entity_group[list.conforming[i].index]])
597  {
598  shared.conforming.push_back(list.conforming[i]);
599  }
600  }
601  for (unsigned i = 0; i < list.masters.size(); i++)
602  {
603  if (group_shared[entity_group[list.masters[i].index]])
604  {
605  shared.masters.push_back(list.masters[i]);
606  }
607  }
608  for (unsigned i = 0; i < list.slaves.size(); i++)
609  {
610  if (group_shared[entity_group[list.slaves[i].index]])
611  {
612  shared.slaves.push_back(list.slaves[i]);
613  }
614  }
615 }
616 
618  int local[2])
619 {
620  // Return face orientation in e2, assuming the face has orientation 0 in e1.
621  int ids[2][4];
622  Element* e[2] = { &e1, &e2 };
623  for (int i = 0; i < 2; i++)
624  {
625  // get local face number (remember that p1, p2, p3 are not in order, and
626  // p4 is not stored)
627  int lf = find_hex_face(find_node(*e[i], face.p1),
628  find_node(*e[i], face.p2),
629  find_node(*e[i], face.p3));
630  // optional output
631  if (local) { local[i] = lf; }
632 
633  // get node IDs for the face as seen from e[i]
634  const int* fv = GI[Geometry::CUBE].faces[lf];
635  for (int j = 0; j < 4; j++)
636  {
637  ids[i][j] = e[i]->node[fv[j]];
638  }
639  }
640  return Mesh::GetQuadOrientation(ids[0], ids[1]);
641 }
642 
644 {
645  if (Dim < 3) { return; }
646 
647  // Calculate orientation of shared conforming faces.
648  // NOTE: face orientation is calculated relative to its lower rank element.
649  // Thanks to the ghost layer this can be done locally, without communication.
650 
652  face_orient = 0;
653 
654  for (face_iterator face = faces.begin(); face != faces.end(); ++face)
655  {
656  if (face->elem[0] >= 0 && face->elem[1] >= 0 && face->index < NFaces)
657  {
658  Element *e1 = &elements[face->elem[0]];
659  Element *e2 = &elements[face->elem[1]];
660 
661  if (e1->rank == e2->rank) { continue; }
662  if (e1->rank > e2->rank) { std::swap(e1, e2); }
663 
664  face_orient[face->index] = get_face_orientation(*face, *e1, *e2);
665  }
666  }
667 }
668 
669 void ParNCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
670  Array<int> &bdr_vertices,
671  Array<int> &bdr_edges)
672 {
673  NCMesh::GetBoundaryClosure(bdr_attr_is_ess, bdr_vertices, bdr_edges);
674 
675  int i, j;
676  // filter out ghost vertices
677  for (i = j = 0; i < bdr_vertices.Size(); i++)
678  {
679  if (bdr_vertices[i] < NVertices) { bdr_vertices[j++] = bdr_vertices[i]; }
680  }
681  bdr_vertices.SetSize(j);
682 
683  // filter out ghost edges
684  for (i = j = 0; i < bdr_edges.Size(); i++)
685  {
686  if (bdr_edges[i] < NEdges) { bdr_edges[j++] = bdr_edges[i]; }
687  }
688  bdr_edges.SetSize(j);
689 }
690 
691 
692 //// Neighbors /////////////////////////////////////////////////////////////////
693 
695 {
696  if (element_type.Size()) { return; }
697 
698  int nleaves = leaf_elements.Size();
699 
700  element_type.SetSize(nleaves);
701  for (int i = 0; i < nleaves; i++)
702  {
703  element_type[i] = (elements[leaf_elements[i]].rank == MyRank) ? 1 : 0;
704  }
705 
706  // determine the ghost layer
707  Array<char> ghost_set;
708  FindSetNeighbors(element_type, NULL, &ghost_set);
709 
710  // find the neighbors of the ghost layer
711  Array<char> boundary_set;
712  FindSetNeighbors(ghost_set, NULL, &boundary_set);
713 
714  ghost_layer.SetSize(0);
716  for (int i = 0; i < nleaves; i++)
717  {
718  char &etype = element_type[i];
719  if (ghost_set[i])
720  {
721  etype = 2;
723  }
724  else if (boundary_set[i] && etype)
725  {
726  etype = 3;
728  }
729  }
730 }
731 
732 bool ParNCMesh::CheckElementType(int elem, int type)
733 {
734  Element &el = elements[elem];
735  if (!el.ref_type)
736  {
737  return (element_type[el.index] == type);
738  }
739  else
740  {
741  for (int i = 0; i < 8 && el.child[i] >= 0; i++)
742  {
743  if (!CheckElementType(el.child[i], type)) { return false; }
744  }
745  return true;
746  }
747 }
748 
750 {
751  ranks.SetSize(0); // preserve capacity
752 
753  // big shortcut: there are no neighbors if element_type == 1
754  if (CheckElementType(elem, 1)) { return; }
755 
756  // ok, we do need to look for neighbors;
757  // at least we can only search in the ghost layer
760 
761  // return a list of processors
762  for (int i = 0; i < tmp_neighbors.Size(); i++)
763  {
764  ranks.Append(elements[tmp_neighbors[i]].rank);
765  }
766  ranks.Sort();
767  ranks.Unique();
768 }
769 
770 template<class T>
771 static void set_to_array(const std::set<T> &set, Array<T> &array)
772 {
773  array.Reserve(set.size());
774  array.SetSize(0);
775  for (std::set<int>::iterator it = set.begin(); it != set.end(); ++it)
776  {
777  array.Append(*it);
778  }
779 }
780 
782 {
783  UpdateLayers();
784 
785  std::set<int> ranks;
786  for (int i = 0; i < ghost_layer.Size(); i++)
787  {
788  ranks.insert(elements[ghost_layer[i]].rank);
789  }
790  set_to_array(ranks, neighbors);
791 }
792 
794 {
795  return (a->rank != b->rank) ? a->rank < b->rank
796  /* */ : a->index < b->index;
797 }
798 
800 {
801  ClearAuxPM();
802 
803  const NCList &shared = (Dim == 3) ? GetSharedFaces() : GetSharedEdges();
804  const NCList &full_list = (Dim == 3) ? GetFaceList() : GetEdgeList();
805 
806  Array<Element*> fnbr;
807  Array<Connection> send_elems;
808 
809  int bound = shared.conforming.size() + shared.slaves.size();
810 
811  fnbr.Reserve(bound);
812  send_elems.Reserve(bound);
813 
814  // go over all shared faces and collect face neighbor elements
815  for (unsigned i = 0; i < shared.conforming.size(); i++)
816  {
817  const MeshId &cf = shared.conforming[i];
818  Face* face = GetFace(elements[cf.element], cf.local);
819  MFEM_ASSERT(face != NULL, "");
820 
821  MFEM_ASSERT(face->elem[0] >= 0 && face->elem[1] >= 0, "");
822  Element* e[2] = { &elements[face->elem[0]], &elements[face->elem[1]] };
823 
824  if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
825  MFEM_ASSERT(e[0]->rank != MyRank && e[1]->rank == MyRank, "");
826 
827  fnbr.Append(e[0]);
828  send_elems.Append(Connection(e[0]->rank, e[1]->index));
829  }
830 
831  for (unsigned i = 0; i < shared.masters.size(); i++)
832  {
833  const Master &mf = shared.masters[i];
834  for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
835  {
836  const Slave &sf = full_list.slaves[j];
837 
838  MFEM_ASSERT(mf.element >= 0 && sf.element >= 0, "");
839  Element* e[2] = { &elements[mf.element], &elements[sf.element] };
840 
841  bool loc0 = (e[0]->rank == MyRank);
842  bool loc1 = (e[1]->rank == MyRank);
843  if (loc0 == loc1) { continue; }
844  if (loc0) { std::swap(e[0], e[1]); }
845 
846  fnbr.Append(e[0]);
847  send_elems.Append(Connection(e[0]->rank, e[1]->index));
848  }
849  }
850 
851  MFEM_ASSERT(fnbr.Size() <= bound, "oops, bad upper bound");
852 
853  // remove duplicate face neighbor elements and sort them by rank & index
854  // (note that the send table is sorted the same way and the order is also the
855  // same on different processors, this is important for ExchangeFaceNbrData)
856  fnbr.Sort();
857  fnbr.Unique();
859 
860  // put the ranks into 'face_nbr_group'
861  for (int i = 0; i < fnbr.Size(); i++)
862  {
863  if (!i || fnbr[i]->rank != pmesh.face_nbr_group.Last())
864  {
865  pmesh.face_nbr_group.Append(fnbr[i]->rank);
866  }
867  }
868  int nranks = pmesh.face_nbr_group.Size();
869 
870  // create a new mfem::Element for each face neighbor element
871  pmesh.face_nbr_elements.SetSize(0);
872  pmesh.face_nbr_elements.Reserve(fnbr.Size());
873 
876 
877  Array<int> fnbr_index(NGhostElements);
878  fnbr_index = -1;
879 
880  std::map<int, int> vert_map;
881  for (int i = 0; i < fnbr.Size(); i++)
882  {
883  Element* elem = fnbr[i];
884  mfem::Element* fne = NewMeshElement(elem->geom);
885  fne->SetAttribute(elem->attribute);
886  pmesh.face_nbr_elements.Append(fne);
887 
888  GeomInfo& gi = GI[(int) elem->geom];
889  for (int k = 0; k < gi.nv; k++)
890  {
891  int &v = vert_map[elem->node[k]];
892  if (!v) { v = vert_map.size(); }
893  fne->GetVertices()[k] = v-1;
894  }
895 
896  if (!i || elem->rank != fnbr[i-1]->rank)
897  {
899  }
900 
901  MFEM_ASSERT(elem->index >= NElements, "not a ghost element");
902  fnbr_index[elem->index - NElements] = i;
903  }
904  pmesh.face_nbr_elements_offset.Append(fnbr.Size());
905 
906  // create vertices in 'face_nbr_vertices'
907  {
908  pmesh.face_nbr_vertices.SetSize(vert_map.size());
909  tmp_vertex = new TmpVertex[nodes.NumIds()]; // TODO: something cheaper?
910 
911  std::map<int, int>::iterator it;
912  for (it = vert_map.begin(); it != vert_map.end(); ++it)
913  {
914  pmesh.face_nbr_vertices[it->second-1].SetCoords(
915  spaceDim, CalcVertexPos(it->first));
916  }
917  delete [] tmp_vertex;
918  }
919 
920  // make the 'send_face_nbr_elements' table
921  send_elems.Sort();
922  send_elems.Unique();
923 
924  for (int i = 0, last_rank = -1; i < send_elems.Size(); i++)
925  {
926  Connection &c = send_elems[i];
927  if (c.from != last_rank)
928  {
929  // renumber rank to position in 'face_nbr_group'
930  last_rank = c.from;
931  c.from = pmesh.face_nbr_group.Find(c.from);
932  }
933  else
934  {
935  c.from = send_elems[i-1].from; // avoid search
936  }
937  }
938  pmesh.send_face_nbr_elements.MakeFromList(nranks, send_elems);
939 
940  // go over the shared faces again and modify their Mesh::FaceInfo
941  for (unsigned i = 0; i < shared.conforming.size(); i++)
942  {
943  const MeshId &cf = shared.conforming[i];
944  Face* face = GetFace(elements[cf.element], cf.local);
945 
946  Element* e[2] = { &elements[face->elem[0]], &elements[face->elem[1]] };
947  if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
948 
949  Mesh::FaceInfo &fi = pmesh.faces_info[cf.index];
950  fi.Elem2No = -1 - fnbr_index[e[0]->index - NElements];
951 
952  if (Dim == 3)
953  {
954  int local[2];
955  int o = get_face_orientation(*face, *e[1], *e[0], local);
956  fi.Elem2Inf = 64*local[1] + o;
957  }
958  else
959  {
960  fi.Elem2Inf = 64*find_element_edge(*e[0], face->p1, face->p3) + 1;
961  }
962  }
963 
964  if (shared.slaves.size())
965  {
966  int nfaces = NFaces, nghosts = NGhostFaces;
967  if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
968 
969  // enlarge Mesh::faces_info for ghost slaves
970  MFEM_ASSERT(pmesh.faces_info.Size() == nfaces, "");
971  MFEM_ASSERT(pmesh.GetNumFaces() == nfaces, "");
972  pmesh.faces_info.SetSize(nfaces + nghosts);
973  for (int i = nfaces; i < pmesh.faces_info.Size(); i++)
974  {
975  Mesh::FaceInfo &fi = pmesh.faces_info[i];
976  fi.Elem1No = fi.Elem2No = -1;
977  fi.Elem1Inf = fi.Elem2Inf = -1;
978  fi.NCFace = -1;
979  }
980  // Note that some of the indices i >= nfaces in pmesh.faces_info will
981  // remain untouched below and they will have Elem1No == -1, in particular.
982 
983  // fill in FaceInfo for shared slave faces
984  for (unsigned i = 0; i < shared.masters.size(); i++)
985  {
986  const Master &mf = shared.masters[i];
987  for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
988  {
989  const Slave &sf = full_list.slaves[j];
990 
991  MFEM_ASSERT(sf.element >= 0 && mf.element >= 0, "");
992  Element &sfe = elements[sf.element];
993  Element &mfe = elements[mf.element];
994 
995  bool sloc = (sfe.rank == MyRank);
996  bool mloc = (mfe.rank == MyRank);
997  if (sloc == mloc) { continue; }
998 
999  Mesh::FaceInfo &fi = pmesh.faces_info[sf.index];
1000  fi.Elem1No = sfe.index;
1001  fi.Elem2No = mfe.index;
1002  fi.Elem1Inf = 64 * sf.local;
1003  fi.Elem2Inf = 64 * mf.local;
1004 
1005  if (!sloc)
1006  {
1007  // 'fi' is the info for a ghost slave face with index:
1008  // sf.index >= nfaces
1009  std::swap(fi.Elem1No, fi.Elem2No);
1010  std::swap(fi.Elem1Inf, fi.Elem2Inf);
1011  // After the above swap, Elem1No refers to the local, master-side
1012  // element. In other words, side 1 IS NOT the side that generated
1013  // the face.
1014  }
1015  else
1016  {
1017  // 'fi' is the info for a local slave face with index:
1018  // sf.index < nfaces
1019  // Here, Elem1No refers to the local, slave-side element.
1020  // In other words, side 1 IS the side that generated the face.
1021  }
1022  MFEM_ASSERT(fi.Elem2No >= NElements, "");
1023  fi.Elem2No = -1 - fnbr_index[fi.Elem2No - NElements];
1024 
1025  const DenseMatrix* pm = &sf.point_matrix;
1026  if (!sloc && Dim == 3)
1027  {
1028  // ghost slave in 3D needs flipping orientation
1029  DenseMatrix* pm2 = new DenseMatrix(*pm);
1030  std::swap((*pm2)(0,1), (*pm2)(0,3));
1031  std::swap((*pm2)(1,1), (*pm2)(1,3));
1032  aux_pm_store.Append(pm2);
1033 
1034  fi.Elem2Inf ^= 1;
1035  pm = pm2;
1036 
1037  // The problem is that sf.point_matrix is designed for P matrix
1038  // construction and always has orientation relative to the slave
1039  // face. In ParMesh::GetSharedFaceTransformations the result
1040  // would therefore be the same on both processors, which is not
1041  // how that function works for conforming faces. The orientation
1042  // of Loc1, Loc2 and Face needs to always be relative to Element
1043  // 1, which is the element containing the slave face on one
1044  // processor, but on the other it is the element containing the
1045  // master face. In the latter case we need to flip the pm.
1046  }
1047 
1048  MFEM_ASSERT(fi.NCFace < 0, "");
1049  fi.NCFace = pmesh.nc_faces_info.Size();
1050  pmesh.nc_faces_info.Append(Mesh::NCFaceInfo(true, sf.master, pm));
1051  }
1052  }
1053  }
1054 
1055  // NOTE: this function skips ParMesh::send_face_nbr_vertices and
1056  // ParMesh::face_nbr_vertices_offset, these are not used outside of ParMesh
1057 }
1058 
1060 {
1061  for (int i = 0; i < aux_pm_store.Size(); i++)
1062  {
1063  delete aux_pm_store[i];
1064  }
1065  aux_pm_store.DeleteAll();
1066 }
1067 
1068 
1069 //// Prune, Refine, Derefine ///////////////////////////////////////////////////
1070 
1071 bool ParNCMesh::PruneTree(int elem)
1072 {
1073  Element &el = elements[elem];
1074  if (el.ref_type)
1075  {
1076  bool remove[8];
1077  bool removeAll = true;
1078 
1079  // determine which subtrees can be removed (and whether it's all of them)
1080  for (int i = 0; i < 8; i++)
1081  {
1082  remove[i] = false;
1083  if (el.child[i] >= 0)
1084  {
1085  remove[i] = PruneTree(el.child[i]);
1086  if (!remove[i]) { removeAll = false; }
1087  }
1088  }
1089 
1090  // all children can be removed, let the (maybe indirect) parent do it
1091  if (removeAll) { return true; }
1092 
1093  // not all children can be removed, but remove those that can be
1094  for (int i = 0; i < 8; i++)
1095  {
1096  if (remove[i]) { DerefineElement(el.child[i]); }
1097  }
1098 
1099  return false; // need to keep this element and up
1100  }
1101  else
1102  {
1103  // return true if this leaf can be removed
1104  return el.rank < 0;
1105  }
1106 }
1107 
1109 {
1110  if (!Iso && Dim == 3)
1111  {
1112  if (MyRank == 0)
1113  {
1114  MFEM_WARNING("Can't prune 3D aniso meshes yet.");
1115  }
1116  return;
1117  }
1118 
1119  UpdateLayers();
1120 
1121  for (int i = 0; i < leaf_elements.Size(); i++)
1122  {
1123  // rank of elements beyond the ghost layer is unknown / not updated
1124  if (element_type[i] == 0)
1125  {
1126  elements[leaf_elements[i]].rank = -1;
1127  // NOTE: rank == -1 will make the element disappear from leaf_elements
1128  // on next Update, see NCMesh::CollectLeafElements
1129  }
1130  }
1131 
1132  // derefine subtrees whose leaves are all unneeded
1133  for (int i = 0; i < root_count; i++)
1134  {
1135  if (PruneTree(i)) { DerefineElement(i); }
1136  }
1137 
1138  Update();
1139 }
1140 
1141 
1142 void ParNCMesh::Refine(const Array<Refinement> &refinements)
1143 {
1144  for (int i = 0; i < refinements.Size(); i++)
1145  {
1146  const Refinement &ref = refinements[i];
1147  MFEM_VERIFY(ref.ref_type == 7 || Dim < 3,
1148  "anisotropic parallel refinement not supported yet in 3D.");
1149  }
1150  MFEM_VERIFY(Iso || Dim < 3,
1151  "parallel refinement of 3D aniso meshes not supported yet.");
1152 
1154 
1155  // create refinement messages to all neighbors (NOTE: some may be empty)
1156  Array<int> neighbors;
1157  NeighborProcessors(neighbors);
1158  for (int i = 0; i < neighbors.Size(); i++)
1159  {
1160  send_ref[neighbors[i]].SetNCMesh(this);
1161  }
1162 
1163  // populate messages: all refinements that occur next to the processor
1164  // boundary need to be sent to the adjoining neighbors so they can keep
1165  // their ghost layer up to date
1166  Array<int> ranks;
1167  ranks.Reserve(64);
1168  for (int i = 0; i < refinements.Size(); i++)
1169  {
1170  const Refinement &ref = refinements[i];
1171  MFEM_ASSERT(ref.index < NElements, "");
1172  int elem = leaf_elements[ref.index];
1173  ElementNeighborProcessors(elem, ranks);
1174  for (int j = 0; j < ranks.Size(); j++)
1175  {
1176  send_ref[ranks[j]].AddRefinement(elem, ref.ref_type);
1177  }
1178  }
1179 
1180  // send the messages (overlap with local refinements)
1182 
1183  // do local refinements
1184  for (int i = 0; i < refinements.Size(); i++)
1185  {
1186  const Refinement &ref = refinements[i];
1188  }
1189 
1190  // receive (ghost layer) refinements from all neighbors
1191  for (int j = 0; j < neighbors.Size(); j++)
1192  {
1193  int rank, size;
1195 
1197  msg.SetNCMesh(this);
1198  msg.Recv(rank, size, MyComm);
1199 
1200  // do the ghost refinements
1201  for (int i = 0; i < msg.Size(); i++)
1202  {
1203  NCMesh::RefineElement(msg.elements[i], msg.values[i]);
1204  }
1205  }
1206 
1207  Update();
1208 
1209  // make sure we can delete the send buffers
1211 }
1212 
1213 
1214 void ParNCMesh::LimitNCLevel(int max_nc_level)
1215 {
1216  MFEM_VERIFY(max_nc_level >= 1, "'max_nc_level' must be 1 or greater.");
1217 
1218  while (1)
1219  {
1220  Array<Refinement> refinements;
1221  GetLimitRefinements(refinements, max_nc_level);
1222 
1223  long size = refinements.Size(), glob_size;
1224  MPI_Allreduce(&size, &glob_size, 1, MPI_LONG, MPI_SUM, MyComm);
1225 
1226  if (!glob_size) { break; }
1227 
1228  Refine(refinements);
1229  }
1230 }
1231 
1232 void ParNCMesh::Derefine(const Array<int> &derefs)
1233 {
1234  MFEM_VERIFY(Dim < 3 || Iso,
1235  "derefinement of 3D anisotropic meshes not implemented yet.");
1236 
1238 
1239  // store fine element ranks
1241  for (int i = 0; i < leaf_elements.Size(); i++)
1242  {
1244  }
1245 
1246  // back up the leaf_elements array
1247  Array<int> old_elements;
1248  leaf_elements.Copy(old_elements);
1249 
1250  // *** STEP 1: redistribute elements to avoid complex derefinements ***
1251 
1252  Array<int> new_ranks(leaf_elements.Size());
1253  for (int i = 0; i < leaf_elements.Size(); i++)
1254  {
1255  new_ranks[i] = elements[leaf_elements[i]].rank;
1256  }
1257 
1258  // make the lowest rank get all the fine elements for each derefinement
1259  for (int i = 0; i < derefs.Size(); i++)
1260  {
1261  int row = derefs[i];
1262  MFEM_VERIFY(row >= 0 && row < derefinements.Size(),
1263  "invalid derefinement number.");
1264 
1265  const int* fine = derefinements.GetRow(row);
1266  int size = derefinements.RowSize(row);
1267 
1268  int coarse_rank = INT_MAX;
1269  for (int j = 0; j < size; j++)
1270  {
1271  int fine_rank = elements[leaf_elements[fine[j]]].rank;
1272  coarse_rank = std::min(coarse_rank, fine_rank);
1273  }
1274  for (int j = 0; j < size; j++)
1275  {
1276  new_ranks[fine[j]] = coarse_rank;
1277  }
1278  }
1279 
1280  int target_elements = 0;
1281  for (int i = 0; i < new_ranks.Size(); i++)
1282  {
1283  if (new_ranks[i] == MyRank) { target_elements++; }
1284  }
1285 
1286  // redistribute elements slightly to get rid of complex derefinements
1287  // straddling processor boundaries *and* update the ghost layer
1288  RedistributeElements(new_ranks, target_elements, false);
1289 
1290  // *** STEP 2: derefine now, communication similar to Refine() ***
1291 
1293 
1294  // create derefinement messages to all neighbors (NOTE: some may be empty)
1295  Array<int> neighbors;
1296  NeighborProcessors(neighbors);
1297  for (int i = 0; i < neighbors.Size(); i++)
1298  {
1299  send_deref[neighbors[i]].SetNCMesh(this);
1300  }
1301 
1302  // derefinements that occur next to the processor boundary need to be sent
1303  // to the adjoining neighbors to keep their ghost layers in sync
1304  Array<int> ranks;
1305  ranks.Reserve(64);
1306  for (int i = 0; i < derefs.Size(); i++)
1307  {
1308  const int* fine = derefinements.GetRow(derefs[i]);
1309  int parent = elements[old_elements[fine[0]]].parent;
1310 
1311  // send derefinement to neighbors
1312  ElementNeighborProcessors(parent, ranks);
1313  for (int j = 0; j < ranks.Size(); j++)
1314  {
1315  send_deref[ranks[j]].AddDerefinement(parent, new_ranks[fine[0]]);
1316  }
1317  }
1319 
1320  // restore old (pre-redistribution) element indices, for SetDerefMatrixCodes
1321  for (int i = 0; i < leaf_elements.Size(); i++)
1322  {
1323  elements[leaf_elements[i]].index = -1;
1324  }
1325  for (int i = 0; i < old_elements.Size(); i++)
1326  {
1327  elements[old_elements[i]].index = i;
1328  }
1329 
1330  // do local derefinements
1331  Array<int> coarse;
1332  old_elements.Copy(coarse);
1333  for (int i = 0; i < derefs.Size(); i++)
1334  {
1335  const int* fine = derefinements.GetRow(derefs[i]);
1336  int parent = elements[old_elements[fine[0]]].parent;
1337 
1338  // record the relation of the fine elements to their parent
1339  SetDerefMatrixCodes(parent, coarse);
1340 
1341  NCMesh::DerefineElement(parent);
1342  }
1343 
1344  // receive ghost layer derefinements from all neighbors
1345  for (int j = 0; j < neighbors.Size(); j++)
1346  {
1347  int rank, size;
1349 
1351  msg.SetNCMesh(this);
1352  msg.Recv(rank, size, MyComm);
1353 
1354  // do the ghost derefinements
1355  for (int i = 0; i < msg.Size(); i++)
1356  {
1357  int elem = msg.elements[i];
1358  if (elements[elem].ref_type)
1359  {
1360  SetDerefMatrixCodes(elem, coarse);
1362  }
1363  elements[elem].rank = msg.values[i];
1364  }
1365  }
1366 
1367  // update leaf_elements, Element::index etc.
1368  Update();
1369 
1370  UpdateLayers();
1371 
1372  // link old fine elements to the new coarse elements
1373  for (int i = 0; i < coarse.Size(); i++)
1374  {
1375  int index = elements[coarse[i]].index;
1376  if (element_type[index] == 0)
1377  {
1378  // this coarse element will get pruned, encode who owns it now
1379  index = -1 - elements[coarse[i]].rank;
1380  }
1381  transforms.embeddings[i].parent = index;
1382  }
1383 
1384  leaf_elements.Copy(old_elements);
1385 
1386  Prune();
1387 
1388  // renumber coarse element indices after pruning
1389  for (int i = 0; i < coarse.Size(); i++)
1390  {
1391  int &index = transforms.embeddings[i].parent;
1392  if (index >= 0)
1393  {
1394  index = elements[old_elements[index]].index;
1395  }
1396  }
1397 
1398  // make sure we can delete all send buffers
1400 }
1401 
1402 
1403 template<typename Type>
1405  const Table &deref_table)
1406 {
1407  const MPI_Datatype datatype = MPITypeMap<Type>::mpi_type;
1408 
1409  Array<MPI_Request*> requests;
1410  Array<int> neigh;
1411 
1412  requests.Reserve(64);
1413  neigh.Reserve(8);
1414 
1415  // make room for ghost values (indices beyond NumElements)
1416  elem_data.SetSize(leaf_elements.Size(), 0);
1417 
1418  for (int i = 0; i < deref_table.Size(); i++)
1419  {
1420  const int* fine = deref_table.GetRow(i);
1421  int size = deref_table.RowSize(i);
1422  MFEM_ASSERT(size <= 8, "");
1423 
1424  int ranks[8], min_rank = INT_MAX, max_rank = INT_MIN;
1425  for (int j = 0; j < size; j++)
1426  {
1427  ranks[j] = elements[leaf_elements[fine[j]]].rank;
1428  min_rank = std::min(min_rank, ranks[j]);
1429  max_rank = std::max(max_rank, ranks[j]);
1430  }
1431 
1432  // exchange values for derefinements that straddle processor boundaries
1433  if (min_rank != max_rank)
1434  {
1435  neigh.SetSize(0);
1436  for (int j = 0; j < size; j++)
1437  {
1438  if (ranks[j] != MyRank) { neigh.Append(ranks[j]); }
1439  }
1440  neigh.Sort();
1441  neigh.Unique();
1442 
1443  for (int j = 0; j < size; j++/*pass*/)
1444  {
1445  Type *data = &elem_data[fine[j]];
1446 
1447  int rnk = ranks[j], len = 1; /*j;
1448  do { j++; } while (j < size && ranks[j] == rnk);
1449  len = j - len;*/
1450 
1451  if (rnk == MyRank)
1452  {
1453  for (int k = 0; k < neigh.Size(); k++)
1454  {
1455  MPI_Request* req = new MPI_Request;
1456  MPI_Isend(data, len, datatype, neigh[k], 292, MyComm, req);
1457  requests.Append(req);
1458  }
1459  }
1460  else
1461  {
1462  MPI_Request* req = new MPI_Request;
1463  MPI_Irecv(data, len, datatype, rnk, 292, MyComm, req);
1464  requests.Append(req);
1465  }
1466  }
1467  }
1468  }
1469 
1470  for (int i = 0; i < requests.Size(); i++)
1471  {
1472  MPI_Wait(requests[i], MPI_STATUS_IGNORE);
1473  delete requests[i];
1474  }
1475 }
1476 
1477 // instantiate SynchronizeDerefinementData for int and double
1478 template void
1479 ParNCMesh::SynchronizeDerefinementData<int>(Array<int> &, const Table &);
1480 template void
1481 ParNCMesh::SynchronizeDerefinementData<double>(Array<double> &, const Table &);
1482 
1483 
1485  Array<int> &level_ok, int max_nc_level)
1486 {
1487  Array<int> leaf_ok(leaf_elements.Size());
1488  leaf_ok = 1;
1489 
1490  // check elements that we own
1491  for (int i = 0; i < deref_table.Size(); i++)
1492  {
1493  const int *fine = deref_table.GetRow(i),
1494  size = deref_table.RowSize(i);
1495 
1496  int parent = elements[leaf_elements[fine[0]]].parent;
1497  Element &pa = elements[parent];
1498 
1499  for (int j = 0; j < size; j++)
1500  {
1501  int child = leaf_elements[fine[j]];
1502  if (elements[child].rank == MyRank)
1503  {
1504  int splits[3];
1505  CountSplits(child, splits);
1506 
1507  for (int k = 0; k < Dim; k++)
1508  {
1509  if ((pa.ref_type & (1 << k)) &&
1510  splits[k] >= max_nc_level)
1511  {
1512  leaf_ok[fine[j]] = 0; break;
1513  }
1514  }
1515  }
1516  }
1517  }
1518 
1519  SynchronizeDerefinementData(leaf_ok, deref_table);
1520 
1521  level_ok.SetSize(deref_table.Size());
1522  level_ok = 1;
1523 
1524  for (int i = 0; i < deref_table.Size(); i++)
1525  {
1526  const int* fine = deref_table.GetRow(i),
1527  size = deref_table.RowSize(i);
1528 
1529  for (int j = 0; j < size; j++)
1530  {
1531  if (!leaf_ok[fine[j]])
1532  {
1533  level_ok[i] = 0; break;
1534  }
1535  }
1536  }
1537 }
1538 
1539 
1540 //// Rebalance /////////////////////////////////////////////////////////////////
1541 
1543 {
1544  send_rebalance_dofs.clear();
1545  recv_rebalance_dofs.clear();
1546 
1547  Array<int> old_elements;
1548  leaf_elements.GetSubArray(0, NElements, old_elements);
1549 
1550  // figure out new assignments for Element::rank
1551  long local_elems = NElements, total_elems = 0;
1552  MPI_Allreduce(&local_elems, &total_elems, 1, MPI_LONG, MPI_SUM, MyComm);
1553 
1554  long first_elem_global = 0;
1555  MPI_Scan(&local_elems, &first_elem_global, 1, MPI_LONG, MPI_SUM, MyComm);
1556  first_elem_global -= local_elems;
1557 
1558  Array<int> new_ranks(leaf_elements.Size());
1559  new_ranks = -1;
1560 
1561  for (int i = 0, j = 0; i < leaf_elements.Size(); i++)
1562  {
1563  if (elements[leaf_elements[i]].rank == MyRank)
1564  {
1565  new_ranks[i] = Partition(first_elem_global + (j++), total_elems);
1566  }
1567  }
1568 
1569  int target_elements = PartitionFirstIndex(MyRank+1, total_elems)
1570  - PartitionFirstIndex(MyRank, total_elems);
1571 
1572  // assign the new ranks and send elements (plus ghosts) to new owners
1573  RedistributeElements(new_ranks, target_elements, true);
1574 
1575  // set up the old index array
1577  old_index_or_rank = -1;
1578  for (int i = 0; i < old_elements.Size(); i++)
1579  {
1580  Element &el = elements[old_elements[i]];
1581  if (el.rank == MyRank) { old_index_or_rank[el.index] = i; }
1582  }
1583 
1584  // get rid of elements beyond the new ghost layer
1585  Prune();
1586 }
1587 
1588 struct CompareRanks
1589 {
1590  typedef BlockArray<NCMesh::Element> ElemArray;
1591  const ElemArray &elements;
1592  CompareRanks(const ElemArray &elements) : elements(elements) {}
1593 
1594  inline bool operator()(const int a, const int b)
1595  {
1596  return elements[a].rank < elements[b].rank;
1597  }
1598 };
1599 
1600 void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
1601  bool record_comm)
1602 {
1603  UpdateLayers();
1604 
1605  // *** STEP 1: communicate new rank assignments for the ghost layer ***
1606 
1607  NeighborElementRankMessage::Map send_ghost_ranks, recv_ghost_ranks;
1608 
1610  {
1611  Array<int> rank_neighbors;
1612 
1613  // loop over neighbor ranks and their elements
1614  int begin = 0, end = 0;
1615  while (end < ghost_layer.Size())
1616  {
1617  // find range of elements belonging to one rank
1618  int rank = elements[ghost_layer[begin]].rank;
1619  while (end < ghost_layer.Size() &&
1620  elements[ghost_layer[end]].rank == rank) { end++; }
1621 
1622  Array<int> rank_elems;
1623  rank_elems.MakeRef(&ghost_layer[begin], end - begin);
1624 
1625  // find elements within boundary_layer that are neighbors to 'rank'
1626  rank_neighbors.SetSize(0);
1627  NeighborExpand(rank_elems, rank_neighbors, &boundary_layer);
1628 
1629  // send a message with new rank assignments within 'rank_neighbors'
1630  NeighborElementRankMessage& msg = send_ghost_ranks[rank];
1631  msg.SetNCMesh(this);
1632 
1633  msg.Reserve(rank_neighbors.Size());
1634  for (int i = 0; i < rank_neighbors.Size(); i++)
1635  {
1636  int elem = rank_neighbors[i];
1637  msg.AddElementRank(elem, new_ranks[elements[elem].index]);
1638  }
1639 
1640  msg.Isend(rank, MyComm);
1641 
1642  // prepare to receive a message from the neighbor too, these will
1643  // be new the new rank assignments for our ghost layer
1644  recv_ghost_ranks[rank].SetNCMesh(this);
1645 
1646  begin = end;
1647  }
1648  }
1649 
1650  NeighborElementRankMessage::RecvAll(recv_ghost_ranks, MyComm);
1651 
1652  // read new ranks for the ghost layer from messages received
1653  NeighborElementRankMessage::Map::iterator it;
1654  for (it = recv_ghost_ranks.begin(); it != recv_ghost_ranks.end(); ++it)
1655  {
1656  NeighborElementRankMessage &msg = it->second;
1657  for (int i = 0; i < msg.Size(); i++)
1658  {
1659  int ghost_index = elements[msg.elements[i]].index;
1660  MFEM_ASSERT(element_type[ghost_index] == 2, "");
1661  new_ranks[ghost_index] = msg.values[i];
1662  }
1663  }
1664 
1665  recv_ghost_ranks.clear();
1666 
1667  // *** STEP 2: send elements that no longer belong to us to new assignees ***
1668 
1669  /* The result thus far is just the array 'new_ranks' containing new owners
1670  for elements that we currently own plus new owners for the ghost layer.
1671  Next we keep elements that still belong to us and send ElementSets with
1672  the remaining elements to their new owners. Each batch of elements needs
1673  to be sent together with their neighbors so the receiver also gets a
1674  ghost layer that is up to date (this is why we needed Step 1). */
1675 
1676  int received_elements = 0;
1677  for (int i = 0; i < leaf_elements.Size(); i++)
1678  {
1679  Element &el = elements[leaf_elements[i]];
1680  if (el.rank == MyRank && new_ranks[i] == MyRank)
1681  {
1682  received_elements++; // initialize to number of elements we're keeping
1683  }
1684  el.rank = new_ranks[i];
1685  }
1686 
1687  RebalanceMessage::Map send_elems;
1688  {
1689  // sort elements we own by the new rank
1690  Array<int> owned_elements;
1691  owned_elements.MakeRef(leaf_elements.GetData(), NElements);
1692  owned_elements.Sort(CompareRanks(elements));
1693 
1694  Array<int> batch;
1695  batch.Reserve(1024);
1696 
1697  // send elements to new owners
1698  int begin = 0, end = 0;
1699  while (end < NElements)
1700  {
1701  // find range of elements belonging to one rank
1702  int rank = elements[owned_elements[begin]].rank;
1703  while (end < owned_elements.Size() &&
1704  elements[owned_elements[end]].rank == rank) { end++; }
1705 
1706  if (rank != MyRank)
1707  {
1708  Array<int> rank_elems;
1709  rank_elems.MakeRef(&owned_elements[begin], end - begin);
1710 
1711  // expand the 'rank_elems' set by its neighbor elements (ghosts)
1712  batch.SetSize(0);
1713  NeighborExpand(rank_elems, batch);
1714 
1715  // send the batch
1716  RebalanceMessage &msg = send_elems[rank];
1717  msg.SetNCMesh(this);
1718 
1719  msg.Reserve(batch.Size());
1720  for (int i = 0; i < batch.Size(); i++)
1721  {
1722  int elem = batch[i];
1723  Element &el = elements[elem];
1724 
1725  if ((element_type[el.index] & 1) || el.rank != rank)
1726  {
1727  msg.AddElementRank(elem, el.rank);
1728  }
1729  // NOTE: we skip 'ghosts' that are of the receiver's rank because
1730  // they are not really ghosts and would get sent multiple times,
1731  // disrupting the termination mechanism in Step 4.
1732  }
1733 
1734  msg.Isend(rank, MyComm);
1735 
1736  // also: record what elements we sent (excluding the ghosts)
1737  // so that SendRebalanceDofs can later send data for them
1738  if (record_comm)
1739  {
1740  send_rebalance_dofs[rank].SetElements(rank_elems, this);
1741  }
1742  }
1743 
1744  begin = end;
1745  }
1746  }
1747 
1748  // *** STEP 3: receive elements from others ***
1749 
1750  /* We don't know from whom we're going to receive so we need to probe.
1751  Fortunately, we do know how many elements we're going to own eventually
1752  so the termination condition is easy. */
1753 
1754  RebalanceMessage msg;
1755  msg.SetNCMesh(this);
1756 
1757  while (received_elements < target_elements)
1758  {
1759  int rank, size;
1760  RebalanceMessage::Probe(rank, size, MyComm);
1761 
1762  // receive message; note: elements are created as the message is decoded
1763  msg.Recv(rank, size, MyComm);
1764 
1765  for (int i = 0; i < msg.Size(); i++)
1766  {
1767  int elem_rank = msg.values[i];
1768  elements[msg.elements[i]].rank = elem_rank;
1769 
1770  if (elem_rank == MyRank) { received_elements++; }
1771  }
1772 
1773  // save the ranks we received from, for later use in RecvRebalanceDofs
1774  if (record_comm)
1775  {
1776  recv_rebalance_dofs[rank].SetNCMesh(this);
1777  }
1778  }
1779 
1780  Update();
1781 
1782  // make sure we can delete all send buffers
1783  NeighborElementRankMessage::WaitAllSent(send_ghost_ranks);
1785 }
1786 
1787 
1788 void ParNCMesh::SendRebalanceDofs(int old_ndofs,
1789  const Table &old_element_dofs,
1790  long old_global_offset,
1791  FiniteElementSpace *space)
1792 {
1793  Array<int> dofs;
1794  int vdim = space->GetVDim();
1795 
1796  // fill messages (prepared by Rebalance) with element DOFs
1797  RebalanceDofMessage::Map::iterator it;
1798  for (it = send_rebalance_dofs.begin(); it != send_rebalance_dofs.end(); ++it)
1799  {
1800  RebalanceDofMessage &msg = it->second;
1801  msg.dofs.clear();
1802  int ne = msg.elem_ids.size();
1803  if (ne)
1804  {
1805  msg.dofs.reserve(old_element_dofs.RowSize(msg.elem_ids[0]) * ne * vdim);
1806  }
1807  for (int i = 0; i < ne; i++)
1808  {
1809  old_element_dofs.GetRow(msg.elem_ids[i], dofs);
1810  space->DofsToVDofs(dofs, old_ndofs);
1811  msg.dofs.insert(msg.dofs.end(), dofs.begin(), dofs.end());
1812  }
1813  msg.dof_offset = old_global_offset;
1814  }
1815 
1816  // send the DOFs to element recipients from last Rebalance()
1818 }
1819 
1820 
1822 {
1823  // receive from the same ranks as in last Rebalance()
1825 
1826  // count the size of the result
1827  int ne = 0, nd = 0;
1828  RebalanceDofMessage::Map::iterator it;
1829  for (it = recv_rebalance_dofs.begin(); it != recv_rebalance_dofs.end(); ++it)
1830  {
1831  RebalanceDofMessage &msg = it->second;
1832  ne += msg.elem_ids.size();
1833  nd += msg.dofs.size();
1834  }
1835 
1836  elements.SetSize(ne);
1837  dofs.SetSize(nd);
1838 
1839  // copy element indices and their DOFs
1840  ne = nd = 0;
1841  for (it = recv_rebalance_dofs.begin(); it != recv_rebalance_dofs.end(); ++it)
1842  {
1843  RebalanceDofMessage &msg = it->second;
1844  for (unsigned i = 0; i < msg.elem_ids.size(); i++)
1845  {
1846  elements[ne++] = msg.elem_ids[i];
1847  }
1848  for (unsigned i = 0; i < msg.dofs.size(); i++)
1849  {
1850  dofs[nd++] = msg.dof_offset + msg.dofs[i];
1851  }
1852  }
1853 
1855 }
1856 
1857 
1858 //// ElementSet ////////////////////////////////////////////////////////////////
1859 
1861  : ncmesh(other.ncmesh), include_ref_types(other.include_ref_types)
1862 {
1863  other.data.Copy(data);
1864 }
1865 
1867 {
1868  // helper to put an int to the data array
1869  data.Append(value & 0xff);
1870  data.Append((value >> 8) & 0xff);
1871  data.Append((value >> 16) & 0xff);
1872  data.Append((value >> 24) & 0xff);
1873 }
1874 
1876 {
1877  // helper to get an int from the data array
1878  return (int) data[pos] +
1879  ((int) data[pos+1] << 8) +
1880  ((int) data[pos+2] << 16) +
1881  ((int) data[pos+3] << 24);
1882 }
1883 
1885 {
1886  for (int i = 0; i < elements.Size(); i++)
1887  {
1888  int elem = elements[i];
1889  while (elem >= 0)
1890  {
1891  Element &el = ncmesh->elements[elem];
1892  if (el.flag == flag) { break; }
1893  el.flag = flag;
1894  elem = el.parent;
1895  }
1896  }
1897 }
1898 
1900 {
1901  Element &el = ncmesh->elements[elem];
1902  if (!el.ref_type)
1903  {
1904  // we reached a leaf, mark this as zero child mask
1905  data.Append(0);
1906  }
1907  else
1908  {
1909  // check which subtrees contain marked elements
1910  int mask = 0;
1911  for (int i = 0; i < 8; i++)
1912  {
1913  if (el.child[i] >= 0 && ncmesh->elements[el.child[i]].flag)
1914  {
1915  mask |= 1 << i;
1916  }
1917  }
1918 
1919  // write the bit mask and visit the subtrees
1920  data.Append(mask);
1921  if (include_ref_types)
1922  {
1923  data.Append(el.ref_type);
1924  }
1925 
1926  for (int i = 0; i < 8; i++)
1927  {
1928  if (mask & (1 << i))
1929  {
1930  EncodeTree(el.child[i]);
1931  }
1932  }
1933  }
1934 }
1935 
1937 {
1938  FlagElements(elements, 1);
1939 
1940  // Each refinement tree that contains at least one element from the set
1941  // is encoded as HEADER + TREE, where HEADER is the root element number and
1942  // TREE is the output of EncodeTree().
1943  for (int i = 0; i < ncmesh->root_count; i++)
1944  {
1945  if (ncmesh->elements[i].flag)
1946  {
1947  WriteInt(i);
1948  EncodeTree(i);
1949  }
1950  }
1951  WriteInt(-1); // mark end of data
1952 
1953  FlagElements(elements, 0);
1954 }
1955 
1956 void ParNCMesh::ElementSet::DecodeTree(int elem, int &pos,
1957  Array<int> &elements) const
1958 {
1959  int mask = data[pos++];
1960  if (!mask)
1961  {
1962  elements.Append(elem);
1963  }
1964  else
1965  {
1966  Element &el = ncmesh->elements[elem];
1967  if (include_ref_types)
1968  {
1969  int ref_type = data[pos++];
1970  if (!el.ref_type)
1971  {
1972  ncmesh->RefineElement(elem, ref_type);
1973  }
1974  else { MFEM_ASSERT(ref_type == el.ref_type, "") }
1975  }
1976  else { MFEM_ASSERT(el.ref_type != 0, ""); }
1977 
1978  for (int i = 0; i < 8; i++)
1979  {
1980  if (mask & (1 << i))
1981  {
1982  DecodeTree(el.child[i], pos, elements);
1983  }
1984  }
1985  }
1986 }
1987 
1989 {
1990  int root, pos = 0;
1991  while ((root = GetInt(pos)) >= 0)
1992  {
1993  pos += 4;
1994  DecodeTree(root, pos, elements);
1995  }
1996 }
1997 
1998 void ParNCMesh::ElementSet::Dump(std::ostream &os) const
1999 {
2000  write<int>(os, data.Size());
2001  os.write((const char*) data.GetData(), data.Size());
2002 }
2003 
2004 void ParNCMesh::ElementSet::Load(std::istream &is)
2005 {
2006  data.SetSize(read<int>(is));
2007  is.read((char*) data.GetData(), data.Size());
2008 }
2009 
2010 
2011 //// EncodeMeshIds/DecodeMeshIds ///////////////////////////////////////////////
2012 
2014 {
2016  GetSharedEdges();
2017  GetSharedFaces();
2018 
2019  if (!shared_edges.masters.size() &&
2020  !shared_faces.masters.size()) { return; }
2021 
2022  Array<bool> contains_rank(groups.size());
2023  for (unsigned i = 0; i < groups.size(); i++)
2024  {
2025  contains_rank[i] = GroupContains(i, rank);
2026  }
2027 
2028  Array<Pair<int, int> > find_v(ids[0].Size());
2029  for (int i = 0; i < ids[0].Size(); i++)
2030  {
2031  find_v[i].one = ids[0][i].index;
2032  find_v[i].two = i;
2033  }
2034  find_v.Sort();
2035 
2036  // find vertices of master edges shared with 'rank', and modify their
2037  // MeshIds so their element/local matches the element of the master edge
2038  for (unsigned i = 0; i < shared_edges.masters.size(); i++)
2039  {
2040  const MeshId &edge_id = shared_edges.masters[i];
2041  if (contains_rank[edge_group[edge_id.index]])
2042  {
2043  int v[2], pos, k;
2044  GetEdgeVertices(edge_id, v);
2045  for (int j = 0; j < 2; j++)
2046  {
2047  if ((pos = find_v.FindSorted(Pair<int, int>(v[j], 0))) != -1)
2048  {
2049  // switch to an element/local that is safe for 'rank'
2050  k = find_v[pos].two;
2051  ChangeVertexMeshIdElement(ids[0][k], edge_id.element);
2052  ChangeRemainingMeshIds(ids[0], pos, find_v);
2053  }
2054  }
2055  }
2056  }
2057 
2058  if (!shared_faces.masters.size()) { return; }
2059 
2060  Array<Pair<int, int> > find_e(ids[1].Size());
2061  for (int i = 0; i < ids[1].Size(); i++)
2062  {
2063  find_e[i].one = ids[1][i].index;
2064  find_e[i].two = i;
2065  }
2066  find_e.Sort();
2067 
2068  // find vertices/edges of master faces shared with 'rank', and modify their
2069  // MeshIds so their element/local matches the element of the master face
2070  for (unsigned i = 0; i < shared_faces.masters.size(); i++)
2071  {
2072  const MeshId &face_id = shared_faces.masters[i];
2073  if (contains_rank[face_group[face_id.index]])
2074  {
2075  int v[4], e[4], eo[4], pos, k;
2076  GetFaceVerticesEdges(face_id, v, e, eo);
2077  for (int j = 0; j < 4; j++)
2078  {
2079  if ((pos = find_v.FindSorted(Pair<int, int>(v[j], 0))) != -1)
2080  {
2081  k = find_v[pos].two;
2082  ChangeVertexMeshIdElement(ids[0][k], face_id.element);
2083  ChangeRemainingMeshIds(ids[0], pos, find_v);
2084  }
2085  if ((pos = find_e.FindSorted(Pair<int, int>(e[j], 0))) != -1)
2086  {
2087  k = find_e[pos].two;
2088  ChangeEdgeMeshIdElement(ids[1][k], face_id.element);
2089  ChangeRemainingMeshIds(ids[1], pos, find_e);
2090  }
2091  }
2092  }
2093  }
2094 }
2095 
2097 {
2098  Element &el = elements[elem];
2099  MFEM_ASSERT(el.ref_type == 0, "");
2100 
2101  GeomInfo& gi = GI[(int) el.geom];
2102  for (int i = 0; i < gi.nv; i++)
2103  {
2104  if (nodes[el.node[i]].vert_index == id.index)
2105  {
2106  id.local = i;
2107  id.element = elem;
2108  return;
2109  }
2110  }
2111  MFEM_ABORT("Vertex not found.");
2112 }
2113 
2115 {
2116  Element &old = elements[id.element];
2117  const int *ev = GI[(int) old.geom].edges[id.local];
2118  Node* node = nodes.Find(old.node[ev[0]], old.node[ev[1]]);
2119  MFEM_ASSERT(node != NULL, "Edge not found.");
2120 
2121  Element &el = elements[elem];
2122  MFEM_ASSERT(el.ref_type == 0, "");
2123 
2124  GeomInfo& gi = GI[(int) el.geom];
2125  for (int i = 0; i < gi.ne; i++)
2126  {
2127  const int* ev = gi.edges[i];
2128  if ((el.node[ev[0]] == node->p1 && el.node[ev[1]] == node->p2) ||
2129  (el.node[ev[1]] == node->p1 && el.node[ev[0]] == node->p2))
2130  {
2131  id.local = i;
2132  id.element = elem;
2133  return;
2134  }
2135 
2136  }
2137  MFEM_ABORT("Edge not found.");
2138 }
2139 
2141  const Array<Pair<int, int> > &find)
2142 {
2143  const MeshId &first = ids[find[pos].two];
2144  while (++pos < find.Size() && ids[find[pos].two].index == first.index)
2145  {
2146  MeshId &other = ids[find[pos].two];
2147  other.element = first.element;
2148  other.local = first.local;
2149  }
2150 }
2151 
2152 void ParNCMesh::EncodeMeshIds(std::ostream &os, Array<MeshId> ids[])
2153 {
2154  std::map<int, int> stream_id;
2155 
2156  // get a list of elements involved, dump them to 'os' and create the mapping
2157  // element_id: (Element index -> stream ID)
2158  {
2160  for (int type = 0; type < 3; type++)
2161  {
2162  for (int i = 0; i < ids[type].Size(); i++)
2163  {
2164  elements.Append(ids[type][i].element);
2165  }
2166  }
2167 
2168  ElementSet eset(this);
2169  eset.Encode(elements);
2170  eset.Dump(os);
2171 
2172  Array<int> decoded;
2173  decoded.Reserve(elements.Size());
2174  eset.Decode(decoded);
2175 
2176  for (int i = 0; i < decoded.Size(); i++)
2177  {
2178  stream_id[decoded[i]] = i;
2179  }
2180  }
2181 
2182  // write the IDs as element/local pairs
2183  for (int type = 0; type < 3; type++)
2184  {
2185  write<int>(os, ids[type].Size());
2186  for (int i = 0; i < ids[type].Size(); i++)
2187  {
2188  const MeshId& id = ids[type][i];
2189  write<int>(os, stream_id[id.element]); // TODO: variable 1-4 bytes
2190  write<char>(os, id.local);
2191  }
2192  }
2193 }
2194 
2195 void ParNCMesh::DecodeMeshIds(std::istream &is, Array<MeshId> ids[])
2196 {
2197  // read the list of elements
2198  ElementSet eset(this);
2199  eset.Load(is);
2200 
2201  Array<int> elems;
2202  eset.Decode(elems);
2203 
2204  // read vertex/edge/face IDs
2205  for (int type = 0; type < 3; type++)
2206  {
2207  int ne = read<int>(is);
2208  ids[type].SetSize(ne);
2209 
2210  for (int i = 0; i < ne; i++)
2211  {
2212  int el_num = read<int>(is);
2213  int elem = elems[el_num];
2214  Element &el = elements[elem];
2215 
2216  MFEM_VERIFY(!el.ref_type, "not a leaf element: " << el_num);
2217 
2218  MeshId &id = ids[type][i];
2219  id.element = elem;
2220  id.local = read<char>(is);
2221 
2222  // find vertex/edge/face index
2223  GeomInfo &gi = GI[(int) el.geom];
2224  switch (type)
2225  {
2226  case 0:
2227  {
2228  id.index = nodes[el.node[id.local]].vert_index;
2229  break;
2230  }
2231  case 1:
2232  {
2233  const int* ev = gi.edges[id.local];
2234  Node* node = nodes.Find(el.node[ev[0]], el.node[ev[1]]);
2235  MFEM_ASSERT(node && node->HasEdge(), "edge not found.");
2236  id.index = node->edge_index;
2237  break;
2238  }
2239  default:
2240  {
2241  const int* fv = gi.faces[id.local];
2242  Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
2243  el.node[fv[2]], el.node[fv[3]]);
2244  MFEM_ASSERT(face, "face not found.");
2245  id.index = face->index;
2246  }
2247  }
2248  }
2249  }
2250 }
2251 
2252 void ParNCMesh::EncodeGroups(std::ostream &os, const Array<GroupId> &ids)
2253 {
2254  // get a list of unique GroupIds
2255  std::map<GroupId, GroupId> stream_id;
2256  for (int i = 0; i < ids.Size(); i++)
2257  {
2258  if (i && ids[i] == ids[i-1]) { continue; }
2259  unsigned size = stream_id.size();
2260  GroupId &sid = stream_id[ids[i]];
2261  if (size != stream_id.size()) { sid = size; }
2262  }
2263 
2264  // write the unique groups
2265  write<short>(os, stream_id.size());
2266  for (std::map<GroupId, GroupId>::iterator
2267  it = stream_id.begin(); it != stream_id.end(); ++it)
2268  {
2269  write<GroupId>(os, it->second);
2270  if (it->first >= 0)
2271  {
2272  const CommGroup &group = groups[it->first];
2273  write<short>(os, group.size());
2274  for (unsigned i = 0; i < group.size(); i++)
2275  {
2276  write<int>(os, group[i]);
2277  }
2278  }
2279  else
2280  {
2281  // special "invalid" group, marks forwarded rows
2282  write<short>(os, -1);
2283  }
2284  }
2285 
2286  // write the list of all GroupIds
2287  write<int>(os, ids.Size());
2288  for (int i = 0; i < ids.Size(); i++)
2289  {
2290  write<GroupId>(os, stream_id[ids[i]]);
2291  }
2292 }
2293 
2294 void ParNCMesh::DecodeGroups(std::istream &is, Array<GroupId> &ids)
2295 {
2296  int ngroups = read<short>(is);
2297  Array<GroupId> groups(ngroups);
2298 
2299  // read stream groups, convert to our groups
2300  CommGroup ranks;
2301  ranks.reserve(128);
2302  for (int i = 0; i < ngroups; i++)
2303  {
2304  int id = read<GroupId>(is);
2305  int size = read<short>(is);
2306  if (size >= 0)
2307  {
2308  ranks.resize(size);
2309  for (int i = 0; i < size; i++)
2310  {
2311  ranks[i] = read<int>(is);
2312  }
2313  groups[id] = GetGroupId(ranks);
2314  }
2315  else
2316  {
2317  groups[id] = -1; // forwarded
2318  }
2319  }
2320 
2321  // read the list of IDs
2322  ids.SetSize(read<int>(is));
2323  for (int i = 0; i < ids.Size(); i++)
2324  {
2325  ids[i] = groups[read<GroupId>(is)];
2326  }
2327 }
2328 
2329 
2330 //// Messages //////////////////////////////////////////////////////////////////
2331 
2332 template<class ValueType, bool RefTypes, int Tag>
2334 {
2335  std::ostringstream ostream;
2336 
2337  Array<int> tmp_elements;
2338  tmp_elements.MakeRef(elements.data(), elements.size());
2339 
2340  ElementSet eset(pncmesh, RefTypes);
2341  eset.Encode(tmp_elements);
2342  eset.Dump(ostream);
2343 
2344  // decode the element set to obtain a local numbering of elements
2345  Array<int> decoded;
2346  decoded.Reserve(tmp_elements.Size());
2347  eset.Decode(decoded);
2348 
2349  std::map<int, int> element_index;
2350  for (int i = 0; i < decoded.Size(); i++)
2351  {
2352  element_index[decoded[i]] = i;
2353  }
2354 
2355  write<int>(ostream, values.size());
2356  MFEM_ASSERT(elements.size() == values.size(), "");
2357 
2358  for (unsigned i = 0; i < values.size(); i++)
2359  {
2360  write<int>(ostream, element_index[elements[i]]); // element number
2361  write<ValueType>(ostream, values[i]);
2362  }
2363 
2364  ostream.str().swap(data);
2365 }
2366 
2367 template<class ValueType, bool RefTypes, int Tag>
2369 {
2370  std::istringstream istream(data);
2371 
2372  ElementSet eset(pncmesh, RefTypes);
2373  eset.Load(istream);
2374 
2375  Array<int> tmp_elements;
2376  eset.Decode(tmp_elements);
2377 
2378  int* el = tmp_elements.GetData();
2379  elements.assign(el, el + tmp_elements.Size());
2380  values.resize(elements.size());
2381 
2382  int count = read<int>(istream);
2383  for (int i = 0; i < count; i++)
2384  {
2385  int index = read<int>(istream);
2386  MFEM_ASSERT(index >= 0 && (size_t) index < values.size(), "");
2387  values[index] = read<ValueType>(istream);
2388  }
2389 
2390  // no longer need the raw data
2391  data.clear();
2392 }
2393 
2395  NCMesh *ncmesh)
2396 {
2397  eset.SetNCMesh(ncmesh);
2398  eset.Encode(elems);
2399 
2400  Array<int> decoded;
2401  decoded.Reserve(elems.Size());
2402  eset.Decode(decoded);
2403 
2404  elem_ids.resize(decoded.Size());
2405  for (int i = 0; i < decoded.Size(); i++)
2406  {
2407  elem_ids[i] = eset.GetNCMesh()->elements[decoded[i]].index;
2408  }
2409 }
2410 
2411 static void write_dofs(std::ostream &os, const std::vector<int> &dofs)
2412 {
2413  write<int>(os, dofs.size());
2414  // TODO: we should compress the ints, mostly they are contiguous ranges
2415  os.write((const char*) dofs.data(), dofs.size() * sizeof(int));
2416 }
2417 
2418 static void read_dofs(std::istream &is, std::vector<int> &dofs)
2419 {
2420  dofs.resize(read<int>(is));
2421  is.read((char*) dofs.data(), dofs.size() * sizeof(int));
2422 }
2423 
2425 {
2426  std::ostringstream stream;
2427 
2428  eset.Dump(stream);
2429  write<long>(stream, dof_offset);
2430  write_dofs(stream, dofs);
2431 
2432  stream.str().swap(data);
2433 }
2434 
2436 {
2437  std::istringstream stream(data);
2438 
2439  eset.Load(stream);
2440  dof_offset = read<long>(stream);
2441  read_dofs(stream, dofs);
2442 
2443  data.clear();
2444 
2445  Array<int> elems;
2446  eset.Decode(elems);
2447 
2448  elem_ids.resize(elems.Size());
2449  for (int i = 0; i < elems.Size(); i++)
2450  {
2451  elem_ids[i] = eset.GetNCMesh()->elements[elems[i]].index;
2452  }
2453 }
2454 
2455 
2456 //// Utility ///////////////////////////////////////////////////////////////////
2457 
2458 void ParNCMesh::GetDebugMesh(Mesh &debug_mesh) const
2459 {
2460  // create a serial NCMesh containing all our elements (ghosts and all)
2461  NCMesh* copy = new NCMesh(*this);
2462 
2463  Array<int> &cle = copy->leaf_elements;
2464  for (int i = 0; i < cle.Size(); i++)
2465  {
2466  Element &el = copy->elements[cle[i]];
2467  el.attribute = el.rank + 1;
2468  }
2469 
2470  debug_mesh.InitFromNCMesh(*copy);
2471  debug_mesh.SetAttributes();
2472  debug_mesh.ncmesh = copy;
2473 }
2474 
2476 {
2477  NCMesh::Trim();
2478 
2479  shared_vertices.Clear(true);
2480  shared_edges.Clear(true);
2481  shared_faces.Clear(true);
2482 
2483  send_rebalance_dofs.clear();
2484  recv_rebalance_dofs.clear();
2485 
2487 
2488  ClearAuxPM();
2489 }
2490 
2492 {
2493  return (elem_ids.capacity() + dofs.capacity()) * sizeof(int);
2494 }
2495 
2496 template<typename K, typename V>
2497 static long map_memory_usage(const std::map<K, V> &map)
2498 {
2499  long result = 0;
2500  for (typename std::map<K, V>::const_iterator
2501  it = map.begin(); it != map.end(); ++it)
2502  {
2503  result += it->second.MemoryUsage();
2504  result += sizeof(std::pair<K, V>) + 3*sizeof(void*) + sizeof(bool);
2505  }
2506  return result;
2507 }
2508 
2510 {
2511  long groups_size = groups.capacity() * sizeof(CommGroup);
2512  for (unsigned i = 0; i < groups.size(); i++)
2513  {
2514  groups_size += groups[i].capacity() * sizeof(int);
2515  }
2516  const int approx_node_size =
2517  sizeof(std::pair<CommGroup, GroupId>) + 3*sizeof(void*) + sizeof(bool);
2518  return groups_size + group_id.size() * approx_node_size;
2519 }
2520 
2521 long ParNCMesh::MemoryUsage(bool with_base) const
2522 {
2523  return (with_base ? NCMesh::MemoryUsage() : 0) +
2524  GroupsMemoryUsage() +
2539  index_rank.MemoryUsage() +
2541  map_memory_usage(send_rebalance_dofs) +
2542  map_memory_usage(recv_rebalance_dofs) +
2544  aux_pm_store.MemoryUsage() +
2545  sizeof(ParNCMesh) - sizeof(NCMesh);
2546 }
2547 
2548 int ParNCMesh::PrintMemoryDetail(bool with_base) const
2549 {
2550  if (with_base) { NCMesh::PrintMemoryDetail(); }
2551 
2552  mfem::out << GroupsMemoryUsage() << " groups\n"
2553  << vertex_group.MemoryUsage() << " vertex_group\n"
2554  << vertex_owner.MemoryUsage() << " vertex_owner\n"
2555  << edge_group.MemoryUsage() << " edge_group\n"
2556  << edge_owner.MemoryUsage() << " edge_owner\n"
2557  << face_group.MemoryUsage() << " face_group\n"
2558  << face_owner.MemoryUsage() << " face_owner\n"
2559  << shared_vertices.MemoryUsage() << " shared_vertices\n"
2560  << shared_edges.MemoryUsage() << " shared_edges\n"
2561  << shared_faces.MemoryUsage() << " shared_faces\n"
2562  << face_orient.MemoryUsage() << " face_orient\n"
2563  << element_type.MemoryUsage() << " element_type\n"
2564  << ghost_layer.MemoryUsage() << " ghost_layer\n"
2565  << boundary_layer.MemoryUsage() << " boundary_layer\n"
2566  << tmp_owner.MemoryUsage() << " tmp_owner\n"
2567  << index_rank.MemoryUsage() << " index_rank\n"
2568  << tmp_neighbors.MemoryUsage() << " tmp_neighbors\n"
2569  << map_memory_usage(send_rebalance_dofs) << " send_rebalance_dofs\n"
2570  << map_memory_usage(recv_rebalance_dofs) << " recv_rebalance_dofs\n"
2571  << old_index_or_rank.MemoryUsage() << " old_index_or_rank\n"
2572  << aux_pm_store.MemoryUsage() << " aux_pm_store\n"
2573  << sizeof(ParNCMesh) - sizeof(NCMesh) << " ParNCMesh" << std::endl;
2574 
2575  return leaf_elements.Size();
2576 }
2577 
2578 } // namespace mfem
2579 
2580 #endif // MFEM_USE_MPI
NCList face_list
lazy-initialized list of faces, see GetFaceList
Definition: ncmesh.hpp:430
NCList edge_list
lazy-initialized list of edges, see GetEdgeList
Definition: ncmesh.hpp:431
NCMesh(const Mesh *mesh, std::istream *vertex_parents=NULL)
Definition: ncmesh.cpp:67
NCList shared_edges
Definition: pncmesh.hpp:276
int Partition(long index, long total_elements) const
Return the processor number for a global element number.
Definition: pncmesh.hpp:303
ElementSet(NCMesh *ncmesh=NULL, bool include_ref_types=false)
Definition: pncmesh.hpp:359
void SendRebalanceDofs(int old_ndofs, const Table &old_element_dofs, long old_global_offset, FiniteElementSpace *space)
Use the communication pattern from last Rebalance() to send element DOFs.
Definition: pncmesh.cpp:1788
void DecodeMeshIds(std::istream &is, Array< MeshId > ids[])
Definition: pncmesh.cpp:2195
Array< char > element_type
Definition: pncmesh.hpp:289
int Size() const
Logical size of the array.
Definition: array.hpp:133
std::vector< ValueType > values
Definition: pncmesh.hpp:434
long PartitionFirstIndex(int rank, long total_elements) const
Return the global index of the first element owned by processor &#39;rank&#39;.
Definition: pncmesh.hpp:311
CoarseFineTransformations transforms
storage for data returned by Get[De]RefinementTransforms()
Definition: ncmesh.hpp:672
int elem[2]
up to 2 elements sharing the face
Definition: ncmesh.hpp:358
bool groups_augmented
Definition: pncmesh.hpp:281
virtual void Trim()
Save memory by releasing all non-essential and cached data.
Definition: pncmesh.cpp:2475
char ref_type
refinement XYZ bit mask (7 = full isotropic)
Definition: ncmesh.hpp:37
std::vector< int > CommGroup
Definition: pncmesh.hpp:150
void CalcFaceOrientations()
Definition: pncmesh.cpp:643
TmpVertex * tmp_vertex
Definition: ncmesh.hpp:690
void Unique()
Definition: array.hpp:245
void SetDerefMatrixCodes(int parent, Array< int > &fine_coarse)
Definition: ncmesh.cpp:1353
Helper struct to convert a C++ type to an MPI type.
char flag
generic flag/marker, can be used by algorithms
Definition: ncmesh.hpp:380
Array< char > face_orient
Definition: pncmesh.hpp:279
Array< GroupId > edge_group
Definition: pncmesh.hpp:271
void WriteInt(int value)
Definition: pncmesh.cpp:1866
bool HasEdge() const
Definition: ncmesh.hpp:343
virtual void GetVertices(Array< int > &v) const =0
Returns element&#39;s vertices.
MPI_Comm MyComm
Definition: pncmesh.hpp:258
void GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Return Mesh vertex and edge indices of a face identified by &#39;face_id&#39;.
Definition: ncmesh.cpp:2998
void ChangeRemainingMeshIds(Array< MeshId > &ids, int pos, const Array< Pair< int, int > > &find)
Definition: pncmesh.cpp:2140
virtual void BuildFaceList()
Definition: pncmesh.cpp:215
void Dump(std::ostream &os) const
Definition: pncmesh.cpp:1998
virtual void Trim()
Save memory by releasing all non-essential and cached data.
Definition: ncmesh.cpp:3514
int index
element number in the Mesh, -1 if refined
Definition: ncmesh.hpp:381
Array< Element * > face_nbr_elements
Definition: pmesh.hpp:134
const NCList & GetSharedVertices()
Definition: pncmesh.hpp:100
GroupId GetSingletonGroup(int rank)
Definition: pncmesh.cpp:369
void GetFaceNeighbors(class ParMesh &pmesh)
Definition: pncmesh.cpp:799
void SetNCMesh(ParNCMesh *pncmesh)
Set pointer to ParNCMesh (needed to encode the message).
Definition: pncmesh.hpp:443
std::map< int, NeighborElementRankMessage > Map
Definition: pncmesh.hpp:480
void Copy(Array &copy) const
Create a copy of the current array.
Definition: array.hpp:190
T * GetData()
Returns the data.
Definition: array.hpp:115
int NVertices
Definition: ncmesh.hpp:424
void Load(std::istream &is)
Definition: pncmesh.cpp:2004
Table derefinements
possible derefinements, see GetDerefinementTable
Definition: ncmesh.hpp:455
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
void InitDerefTransforms()
Definition: ncmesh.cpp:1338
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition: table.cpp:189
char geom
Geometry::Type of the element.
Definition: ncmesh.hpp:378
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2]) const
Return Mesh vertex indices of an edge identified by &#39;edge_id&#39;.
Definition: ncmesh.cpp:2968
void SynchronizeDerefinementData(Array< Type > &elem_data, const Table &deref_table)
Definition: pncmesh.cpp:1404
int GetInt(int pos) const
Definition: pncmesh.cpp:1875
virtual void OnMeshUpdated(Mesh *mesh)
Definition: pncmesh.cpp:156
virtual void Derefine(const Array< int > &derefs)
Definition: pncmesh.cpp:1232
void AddMasterSlaveConnections(int nitems, const NCList &list)
Definition: pncmesh.cpp:434
void SetElements(const Array< int > &elems, NCMesh *ncmesh)
Definition: pncmesh.cpp:2394
bool operator<(const Pair< A, B > &p, const Pair< A, B > &q)
Comparison operator for class Pair, based on the first element only.
Definition: sort_pairs.hpp:34
void CountSplits(int elem, int splits[3]) const
Definition: ncmesh.cpp:3199
void ChangeEdgeMeshIdElement(NCMesh::MeshId &id, int elem)
Definition: pncmesh.cpp:2114
T * end() const
Definition: array.hpp:267
Array< int > face_nbr_group
Definition: pmesh.hpp:131
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of &quot;test&quot; relative to &quot;base&quot;.
Definition: mesh.cpp:3548
void GetLimitRefinements(Array< Refinement > &refinements, int max_level)
Definition: ncmesh.cpp:3247
T * begin() const
Definition: array.hpp:266
bool GroupContains(GroupId id, int rank) const
Return true if group &#39;id&#39; contains the given rank.
Definition: pncmesh.cpp:378
int PrintMemoryDetail() const
Definition: ncmesh.cpp:3566
void EncodeTree(int elem)
Definition: pncmesh.cpp:1899
int spaceDim
dimensions of the elements and the vertex coordinates
Definition: ncmesh.hpp:323
virtual void AssignLeafIndices()
Definition: pncmesh.cpp:76
const NCList & GetSharedEdges()
Definition: pncmesh.hpp:112
Array< int > vertex_nodeId
Definition: ncmesh.hpp:428
void DeleteAll()
Delete whole array.
Definition: array.hpp:709
int index
Mesh element number.
Definition: ncmesh.hpp:36
int master
master number (in Mesh numbering)
Definition: ncmesh.hpp:157
void Recv(int rank, int size, MPI_Comm comm)
Post-probe receive from processor &#39;rank&#39; of message size &#39;size&#39;.
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition: ncmesh.hpp:188
Array< NCFaceInfo > nc_faces_info
Definition: mesh.hpp:130
virtual void OnMeshUpdated(Mesh *mesh)
Definition: ncmesh.cpp:1603
ParNCMesh(MPI_Comm comm, const NCMesh &ncmesh)
Definition: pncmesh.cpp:28
RebalanceDofMessage::Map send_rebalance_dofs
Definition: pncmesh.hpp:524
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition: mesh.cpp:3374
void GetSubArray(int offset, int sa_size, Array< T > &sa)
Definition: array.hpp:745
int size
Size of the array.
Definition: array.hpp:34
void FlagElements(const Array< int > &elements, char flag)
Definition: pncmesh.cpp:1884
virtual void Update()
Definition: pncmesh.cpp:54
void Decode(Array< int > &elements) const
Definition: pncmesh.cpp:1988
int root_count
Definition: ncmesh.hpp:403
Face * GetFace(Element &elem, int face_no)
Definition: ncmesh.cpp:344
void AddElementRank(int elem, int rank)
Definition: pncmesh.hpp:490
GroupMap group_id
Definition: pncmesh.hpp:268
Array< int > face_nbr_elements_offset
Definition: pmesh.hpp:132
void InitGroups(int num, Array< GroupId > &entity_group)
Definition: pncmesh.cpp:398
int index
face number in the Mesh
Definition: ncmesh.hpp:357
void MakeFromList(int nrows, const Array< Connection > &list)
Definition: table.cpp:274
Array< DenseMatrix * > aux_pm_store
Stores modified point matrices created by GetFaceNeighbors.
Definition: pncmesh.hpp:533
int local
local number within &#39;element&#39;
Definition: ncmesh.hpp:138
void SetAttributes()
Definition: mesh.cpp:900
int Append(const T &el)
Append element to array, resize if necessary.
Definition: array.hpp:614
void RedistributeElements(Array< int > &new_ranks, int target_elements, bool record_comm)
Definition: pncmesh.cpp:1600
void FindNeighbors(int elem, Array< int > &neighbors, const Array< int > *search_set=NULL)
Definition: ncmesh.cpp:2356
std::vector< Master > masters
Definition: ncmesh.hpp:172
void ElementNeighborProcessors(int elem, Array< int > &ranks)
Definition: pncmesh.cpp:749
A pair of objects.
Definition: sort_pairs.hpp:23
Array< int > old_index_or_rank
Definition: pncmesh.hpp:530
RebalanceDofMessage::Map recv_rebalance_dofs
Definition: pncmesh.hpp:525
int InitialPartition(int index) const
Helper to get the partitioning when the serial mesh gets split initially.
Definition: pncmesh.hpp:307
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition: array.hpp:146
Array< Embedding > embeddings
fine element positions in their parents
Definition: ncmesh.hpp:55
NCList shared_vertices
Definition: pncmesh.hpp:275
virtual void BuildEdgeList()
Definition: ncmesh.cpp:1913
void AugmentMasterGroups()
Definition: pncmesh.cpp:513
Identifies a vertex/edge/face in both Mesh and NCMesh.
Definition: ncmesh.hpp:134
int parent
parent element, -1 if this is a root element, -2 if free
Definition: ncmesh.hpp:389
std::map< int, NeighborRefinementMessage > Map
Definition: pncmesh.hpp:461
long MemoryUsage() const
Definition: array.hpp:269
GroupList groups
Definition: pncmesh.hpp:267
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
Helper to send all messages in a rank-to-message map container.
void Sort()
Sorts the array. This requires operator&lt; to be defined for T.
Definition: array.hpp:237
int FindSorted(const T &el) const
Do bisection search for &#39;el&#39; in a sorted array; return -1 if not found.
Definition: array.hpp:683
int GetVDim() const
Returns vector dimension.
Definition: fespace.hpp:242
int Size() const
Returns the number of TYPE I elements.
Definition: table.hpp:92
A class for non-conforming AMR on higher-order hexahedral, quadrilateral or triangular meshes...
Definition: ncmesh.hpp:83
void ClearAuxPM()
Definition: pncmesh.cpp:1059
NCList shared_faces
Definition: pncmesh.hpp:277
virtual void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges)
Definition: ncmesh.cpp:3100
int element
NCMesh::Element containing this vertex/edge/face.
Definition: ncmesh.hpp:137
virtual void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges)
Definition: pncmesh.cpp:669
void Rebalance()
Definition: pncmesh.cpp:1542
static GeomInfo GI[Geometry::NumGeom]
Definition: ncmesh.hpp:727
virtual void ElementSharesEdge(int elem, int enode)
Definition: pncmesh.cpp:240
void Clear(bool hard=false)
Definition: ncmesh.cpp:2069
int slaves_end
slave faces
Definition: ncmesh.hpp:148
void DecodeGroups(std::istream &is, Array< GroupId > &ids)
Definition: pncmesh.cpp:2294
void AdjustMeshIds(Array< MeshId > ids[], int rank)
Definition: pncmesh.cpp:2013
bool Iso
true if the mesh only contains isotropic refinements
Definition: ncmesh.hpp:324
Array< int > tmp_neighbors
Definition: pncmesh.hpp:404
std::map< int, NeighborDerefinementMessage > Map
Definition: pncmesh.hpp:470
virtual void BuildEdgeList()
Definition: pncmesh.cpp:255
void Encode(const Array< int > &elements)
Definition: pncmesh.cpp:1936
Array< Vertex > face_nbr_vertices
Definition: pmesh.hpp:135
virtual void BuildFaceList()
Definition: ncmesh.cpp:1803
Nonconforming edge/face within a bigger edge/face.
Definition: ncmesh.hpp:155
void DerefineElement(int elem)
Definition: ncmesh.cpp:1119
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:66
void GetDebugMesh(Mesh &debug_mesh) const
Definition: pncmesh.cpp:2458
Array< GroupId > vertex_group
Definition: pncmesh.hpp:271
bool PruneTree(int elem)
Internal. Recursive part of Prune().
Definition: pncmesh.cpp:1071
int Find(const T &el) const
Return the first index where &#39;el&#39; is found; return -1 if not found.
Definition: array.hpp:673
void SetSize(int nsize)
Change logical size of the array, keep existing entries.
Definition: array.hpp:569
mfem::Element * NewMeshElement(int geom) const
Definition: ncmesh.cpp:1487
static void RecvAll(MapT &rank_msg, MPI_Comm comm)
Helper to receive all messages in a rank-to-message map container.
Array< GroupId > edge_owner
Definition: pncmesh.hpp:272
Helper struct for defining a connectivity table, see Table::MakeFromList.
Definition: table.hpp:27
void UpdateLayers()
Definition: pncmesh.cpp:694
long MemoryUsage() const
Definition: ncmesh.cpp:3526
HashTable< Node > nodes
Definition: ncmesh.hpp:396
void RefineElement(int elem, char ref_type)
Definition: ncmesh.cpp:666
virtual void Refine(const Array< Refinement > &refinements)
Definition: pncmesh.cpp:1142
virtual ~ParNCMesh()
Definition: pncmesh.cpp:49
static bool compare_ranks_indices(const Element *a, const Element *b)
Definition: pncmesh.cpp:793
int child[8]
2-8 children (if ref_type != 0)
Definition: ncmesh.hpp:387
GroupId GetGroupId(int entity, int index) const
Return the communication group ID for a vertex/edge/face.
Definition: pncmesh.hpp:164
void RecvRebalanceDofs(Array< int > &elements, Array< long > &dofs)
Receive element DOFs sent by SendRebalanceDofs().
Definition: pncmesh.cpp:1821
Array< int > leaf_elements
Definition: ncmesh.hpp:427
static int find_element_edge(const Element &el, int vn0, int vn1)
Definition: ncmesh.cpp:1689
Array< int > boundary_layer
list of type 3 elements
Definition: pncmesh.hpp:292
void Isend(int rank, MPI_Comm comm)
Non-blocking send to processor &#39;rank&#39;.
virtual void ElementSharesVertex(int elem, int vnode)
Definition: pncmesh.cpp:278
void AddElementRank(int elem, int rank)
Definition: pncmesh.hpp:479
Array< Connection > index_rank
Definition: pncmesh.hpp:342
void InitOwners(int num, Array< GroupId > &entity_owner)
Definition: pncmesh.cpp:389
void ChangeVertexMeshIdElement(NCMesh::MeshId &id, int elem)
Definition: pncmesh.cpp:2096
void DecodeTree(int elem, int &pos, Array< int > &elements) const
Definition: pncmesh.cpp:1956
void GetGroupShared(Array< bool > &group_shared)
Definition: pncmesh.cpp:562
Array< FaceInfo > faces_info
Definition: mesh.hpp:129
void SetAttribute(const int attr)
Set element&#39;s attribute.
Definition: element.hpp:53
virtual void AssignLeafIndices()
Definition: ncmesh.cpp:1478
void EncodeGroups(std::ostream &os, const Array< GroupId > &ids)
Definition: pncmesh.cpp:2252
virtual void BuildVertexList()
Definition: pncmesh.cpp:291
static int find_hex_face(int a, int b, int c)
Definition: ncmesh.cpp:1705
NCMesh * ncmesh
Optional non-conforming mesh extension.
Definition: mesh.hpp:177
T & Last()
Return the last element in the array.
Definition: array.hpp:647
void NeighborExpand(const Array< int > &elems, Array< int > &expanded, const Array< int > *search_set=NULL)
Definition: ncmesh.cpp:2439
BlockArray< Element > elements
Definition: ncmesh.hpp:399
virtual void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level)
Definition: pncmesh.cpp:1484
void FindSetNeighbors(const Array< char > &elem_set, Array< int > *neighbors, Array< char > *neighbor_set=NULL)
Definition: ncmesh.cpp:2253
int GetNEdges() const
Return the number of edges.
Definition: mesh.hpp:624
Array< int > ghost_layer
list of elements whose &#39;element_type&#39; == 2.
Definition: pncmesh.hpp:291
void MakeRef(T *, int)
Make this Array a reference to a pointer.
Definition: array.hpp:720
virtual void Update()
Definition: ncmesh.cpp:188
static void WaitAllSent(MapT &rank_msg)
Helper to wait for all messages in a map container to be sent.
void EncodeMeshIds(std::ostream &os, Array< MeshId > ids[])
Definition: pncmesh.cpp:2152
long MemoryUsage() const
Return total number of bytes allocated.
Definition: ncmesh.cpp:3545
const NCList & GetSharedFaces()
Definition: pncmesh.hpp:123
std::map< int, RebalanceMessage > Map
Definition: pncmesh.hpp:491
static int get_face_orientation(Face &face, Element &e1, Element &e2, int local[2]=NULL)
Definition: pncmesh.cpp:617
virtual void BuildVertexList()
Definition: ncmesh.cpp:2013
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition: ncmesh.hpp:195
friend struct CompareRanks
Definition: ncmesh.hpp:737
void NeighborProcessors(Array< int > &neighbors)
Definition: pncmesh.cpp:781
Array< GroupId > face_owner
Definition: pncmesh.hpp:272
long GroupsMemoryUsage() const
Definition: pncmesh.cpp:2509
int RowSize(int i) const
Definition: table.hpp:108
Table send_face_nbr_elements
Definition: pmesh.hpp:137
char ref_type
bit mask of X,Y,Z refinements (bits 0,1,2 respectively)
Definition: ncmesh.hpp:379
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:64
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition: mesh.cpp:6175
static int find_node(const Element &el, int node)
Definition: ncmesh.cpp:1679
void MakeShared(const Array< GroupId > &entity_group, const NCList &list, NCList &shared)
Definition: pncmesh.cpp:586
bool CheckElementType(int elem, int type)
Definition: pncmesh.cpp:732
virtual void UpdateVertices()
update Vertex::index and vertex_nodeId
Definition: pncmesh.cpp:110
int index
Mesh number.
Definition: ncmesh.hpp:136
Class for parallel meshes.
Definition: pmesh.hpp:32
Array< int > tmp_owner
Definition: pncmesh.hpp:341
Abstract data type element.
Definition: element.hpp:27
virtual void ElementSharesFace(int elem, int face)
Definition: pncmesh.cpp:202
const double * CalcVertexPos(int node) const
Definition: ncmesh.cpp:1499
int rank
processor number (ParNCMesh), -1 if undefined/unknown
Definition: ncmesh.hpp:382
virtual void LimitNCLevel(int max_nc_level)
Parallel version of NCMesh::LimitNCLevel.
Definition: pncmesh.cpp:1214
int node[8]
element corners (if ref_type == 0)
Definition: ncmesh.hpp:386
GroupId JoinGroups(GroupId g1, GroupId g2)
Definition: pncmesh.cpp:352
Array< unsigned char > data
encoded refinement (sub-)trees
Definition: pncmesh.hpp:373
DenseMatrix point_matrix
position within the master edge/face
Definition: ncmesh.hpp:159
static void Probe(int &rank, int &size, MPI_Comm comm)
HashTable< Face > faces
Definition: ncmesh.hpp:397
Array< GroupId > vertex_owner
Definition: pncmesh.hpp:272
Array< GroupId > face_group
Definition: pncmesh.hpp:271
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Definition: fespace.cpp:106