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