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