MFEM  v4.1.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
pmesh.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 "../fem/fem.hpp"
18 #include "../general/sets.hpp"
19 #include "../general/sort_pairs.hpp"
20 #include "../general/text.hpp"
21 #include "../general/globals.hpp"
22 
23 #include <iostream>
24 #include <fstream>
25 
26 using namespace std;
27 
28 namespace mfem
29 {
30 
31 ParMesh::ParMesh(const ParMesh &pmesh, bool copy_nodes)
32  : Mesh(pmesh, false),
33  group_svert(pmesh.group_svert),
34  group_sedge(pmesh.group_sedge),
35  group_stria(pmesh.group_stria),
36  group_squad(pmesh.group_squad),
37  gtopo(pmesh.gtopo)
38 {
39  MyComm = pmesh.MyComm;
40  NRanks = pmesh.NRanks;
41  MyRank = pmesh.MyRank;
42 
43  // Duplicate the shared_edges
44  shared_edges.SetSize(pmesh.shared_edges.Size());
45  for (int i = 0; i < shared_edges.Size(); i++)
46  {
47  shared_edges[i] = pmesh.shared_edges[i]->Duplicate(this);
48  }
49 
50  shared_trias = pmesh.shared_trias;
51  shared_quads = pmesh.shared_quads;
52 
53  // Copy the shared-to-local index Arrays
56  sface_lface = pmesh.sface_lface;
57 
58  // Do not copy face-neighbor data (can be generated if needed)
59  have_face_nbr_data = false;
60 
61  // If pmesh has a ParNURBSExtension, it was copied by the Mesh copy ctor, so
62  // there is no need to do anything here.
63 
64  // Copy ParNCMesh, if present
65  if (pmesh.pncmesh)
66  {
67  pncmesh = new ParNCMesh(*pmesh.pncmesh);
68  pncmesh->OnMeshUpdated(this);
69  }
70  else
71  {
72  pncmesh = NULL;
73  }
74  ncmesh = pncmesh;
75 
76  // Copy the Nodes as a ParGridFunction, including the FiniteElementCollection
77  // and the FiniteElementSpace (as a ParFiniteElementSpace)
78  if (pmesh.Nodes && copy_nodes)
79  {
80  FiniteElementSpace *fes = pmesh.Nodes->FESpace();
81  const FiniteElementCollection *fec = fes->FEColl();
82  FiniteElementCollection *fec_copy =
84  ParFiniteElementSpace *pfes_copy =
85  new ParFiniteElementSpace(*fes, *this, fec_copy);
86  Nodes = new ParGridFunction(pfes_copy);
87  Nodes->MakeOwner(fec_copy);
88  *Nodes = *pmesh.Nodes;
89  own_nodes = 1;
90  }
91 }
92 
93 ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
94  int part_method)
95  : gtopo(comm)
96 {
97  int *partitioning = NULL;
98  Array<bool> activeBdrElem;
99 
100  MyComm = comm;
101  MPI_Comm_size(MyComm, &NRanks);
102  MPI_Comm_rank(MyComm, &MyRank);
103 
104  if (mesh.Nonconforming())
105  {
106  if (partitioning_)
107  {
108  partitioning = partitioning_;
109  }
110  ncmesh = pncmesh = new ParNCMesh(comm, *mesh.ncmesh, partitioning);
111  if (!partitioning)
112  {
113  partitioning = new int[mesh.GetNE()];
114  for (int i = 0; i < mesh.GetNE(); i++)
115  {
116  partitioning[i] = pncmesh->InitialPartition(i);
117  }
118  }
119 
120  pncmesh->Prune();
121 
123  pncmesh->OnMeshUpdated(this);
124 
126 
127  // SetMeshGen(); // called by Mesh::InitFromNCMesh(...) above
128  meshgen = mesh.meshgen; // copy the global 'meshgen'
129 
130  mesh.attributes.Copy(attributes);
132 
134  }
135  else // mesh.Conforming()
136  {
137  Dim = mesh.Dim;
138  spaceDim = mesh.spaceDim;
139 
140  ncmesh = pncmesh = NULL;
141 
142  if (partitioning_)
143  {
144  partitioning = partitioning_;
145  }
146  else
147  {
148  partitioning = mesh.GeneratePartitioning(NRanks, part_method);
149  }
150 
151  // re-enumerate the partitions to better map to actual processor
152  // interconnect topology !?
153 
154  Array<int> vert_global_local;
155  NumOfVertices = BuildLocalVertices(mesh, partitioning, vert_global_local);
156  NumOfElements = BuildLocalElements(mesh, partitioning, vert_global_local);
157 
158  Table *edge_element = NULL;
159  NumOfBdrElements = BuildLocalBoundary(mesh, partitioning,
160  vert_global_local,
161  activeBdrElem, edge_element);
162 
163  SetMeshGen();
164  meshgen = mesh.meshgen; // copy the global 'meshgen'
165 
166  mesh.attributes.Copy(attributes);
168 
169  NumOfEdges = NumOfFaces = 0;
170 
171  if (Dim > 1)
172  {
173  el_to_edge = new Table;
175  }
176 
177  STable3D *faces_tbl = NULL;
178  if (Dim == 3)
179  {
180  faces_tbl = GetElementToFaceTable(1);
181  }
182 
183  GenerateFaces();
184 
185  ListOfIntegerSets groups;
186  {
187  // the first group is the local one
188  IntegerSet group;
189  group.Recreate(1, &MyRank);
190  groups.Insert(group);
191  }
192 
193  MFEM_ASSERT(mesh.GetNFaces() == 0 || Dim >= 3, "");
194 
195  Array<int> face_group(mesh.GetNFaces());
196  Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
197 
198  FindSharedFaces(mesh, partitioning, face_group, groups);
199  int nsedges = FindSharedEdges(mesh, partitioning, edge_element, groups);
200  int nsvert = FindSharedVertices(partitioning, vert_element, groups);
201 
202  // build the group communication topology
203  gtopo.Create(groups, 822);
204 
205  // fill out group_sface, group_sedge, group_svert
206  int ngroups = groups.Size()-1, nstris, nsquads;
207  BuildFaceGroup(ngroups, mesh, face_group, nstris, nsquads);
208  BuildEdgeGroup(ngroups, *edge_element);
209  BuildVertexGroup(ngroups, *vert_element);
210 
211  // build shared_faces and sface_lface mapping
212  BuildSharedFaceElems(nstris, nsquads, mesh, partitioning, faces_tbl,
213  face_group, vert_global_local);
214  delete faces_tbl;
215 
216  // build shared_edges and sedge_ledge mapping
217  BuildSharedEdgeElems(nsedges, mesh, vert_global_local, edge_element);
218  delete edge_element;
219 
220  // build svert_lvert mapping
221  BuildSharedVertMapping(nsvert, vert_element, vert_global_local);
222  delete vert_element;
223 
224  SetMeshGen();
225  meshgen = mesh.meshgen; // copy the global 'meshgen'
226  }
227 
228  if (mesh.NURBSext)
229  {
230  MFEM_ASSERT(mesh.GetNodes() &&
231  mesh.GetNodes()->FESpace()->GetNURBSext() == mesh.NURBSext,
232  "invalid NURBS mesh");
233  NURBSext = new ParNURBSExtension(comm, mesh.NURBSext, partitioning,
234  activeBdrElem);
235  }
236 
237  if (mesh.GetNodes()) // curved mesh
238  {
239  if (!NURBSext)
240  {
241  Nodes = new ParGridFunction(this, mesh.GetNodes());
242  }
243  else
244  {
245  const FiniteElementSpace *glob_fes = mesh.GetNodes()->FESpace();
247  FiniteElementCollection::New(glob_fes->FEColl()->Name());
248  ParFiniteElementSpace *pfes =
249  new ParFiniteElementSpace(this, nfec, glob_fes->GetVDim(),
250  glob_fes->GetOrdering());
251  Nodes = new ParGridFunction(pfes);
252  Nodes->MakeOwner(nfec); // Nodes will own nfec and pfes
253  }
254  own_nodes = 1;
255 
256  Array<int> gvdofs, lvdofs;
257  Vector lnodes;
258  int element_counter = 0;
259  for (int i = 0; i < mesh.GetNE(); i++)
260  {
261  if (partitioning[i] == MyRank)
262  {
263  Nodes->FESpace()->GetElementVDofs(element_counter, lvdofs);
264  mesh.GetNodes()->FESpace()->GetElementVDofs(i, gvdofs);
265  mesh.GetNodes()->GetSubVector(gvdofs, lnodes);
266  Nodes->SetSubVector(lvdofs, lnodes);
267  element_counter++;
268  }
269  }
270  }
271 
272  if (partitioning != partitioning_)
273  {
274  delete [] partitioning;
275  }
276 
277  have_face_nbr_data = false;
278 }
279 
280 
282  const int* partitioning,
283  Array<int> &vert_global_local)
284 {
285  vert_global_local.SetSize(mesh.GetNV());
286  vert_global_local = -1;
287 
288  int vert_counter = 0;
289  for (int i = 0; i < mesh.GetNE(); i++)
290  {
291  if (partitioning[i] == MyRank)
292  {
293  Array<int> vert;
294  mesh.GetElementVertices(i, vert);
295  for (int j = 0; j < vert.Size(); j++)
296  {
297  if (vert_global_local[vert[j]] < 0)
298  {
299  vert_global_local[vert[j]] = vert_counter++;
300  }
301  }
302  }
303  }
304 
305  // re-enumerate the local vertices to preserve the global ordering
306  vert_counter = 0;
307  for (int i = 0; i < vert_global_local.Size(); i++)
308  {
309  if (vert_global_local[i] >= 0)
310  {
311  vert_global_local[i] = vert_counter++;
312  }
313  }
314 
315  vertices.SetSize(vert_counter);
316 
317  for (int i = 0; i < vert_global_local.Size(); i++)
318  {
319  if (vert_global_local[i] >= 0)
320  {
321  vertices[vert_global_local[i]].SetCoords(mesh.SpaceDimension(),
322  mesh.GetVertex(i));
323  }
324  }
325 
326  return vert_counter;
327 }
328 
329 int ParMesh::BuildLocalElements(const Mesh& mesh, const int* partitioning,
330  const Array<int>& vert_global_local)
331 {
332  int nelems = 0;
333  for (int i = 0; i < mesh.GetNE(); i++)
334  {
335  if (partitioning[i] == MyRank) { nelems++; }
336  }
337 
338  elements.SetSize(nelems);
339 
340  // Determine elements, enumerating the local elements to preserve the global
341  // order. This is used, e.g. by the ParGridFunction ctor that takes a global
342  // GridFunction as input parameter.
343  int element_counter = 0;
344  for (int i = 0; i < mesh.GetNE(); i++)
345  {
346  if (partitioning[i] == MyRank)
347  {
348  elements[element_counter] = mesh.GetElement(i)->Duplicate(this);
349  int *v = elements[element_counter]->GetVertices();
350  int nv = elements[element_counter]->GetNVertices();
351  for (int j = 0; j < nv; j++)
352  {
353  v[j] = vert_global_local[v[j]];
354  }
355  element_counter++;
356  }
357  }
358 
359  return element_counter;
360 }
361 
362 int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
363  const Array<int>& vert_global_local,
364  Array<bool>& activeBdrElem,
365  Table*& edge_element)
366 {
367  int nbdry = 0;
368 
369  if (mesh.NURBSext)
370  {
371  activeBdrElem.SetSize(mesh.GetNBE());
372  activeBdrElem = false;
373  }
374  // build boundary elements
375  if (Dim == 3)
376  {
377  for (int i = 0; i < mesh.GetNBE(); i++)
378  {
379  int face, o, el1, el2;
380  mesh.GetBdrElementFace(i, &face, &o);
381  mesh.GetFaceElements(face, &el1, &el2);
382  if (partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2] == MyRank)
383  {
384  nbdry++;
385  if (mesh.NURBSext)
386  {
387  activeBdrElem[i] = true;
388  }
389  }
390  }
391 
392  int bdrelem_counter = 0;
393  boundary.SetSize(nbdry);
394  for (int i = 0; i < mesh.GetNBE(); i++)
395  {
396  int face, o, el1, el2;
397  mesh.GetBdrElementFace(i, &face, &o);
398  mesh.GetFaceElements(face, &el1, &el2);
399  if (partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2] == MyRank)
400  {
401  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
402  int *v = boundary[bdrelem_counter]->GetVertices();
403  int nv = boundary[bdrelem_counter]->GetNVertices();
404  for (int j = 0; j < nv; j++)
405  {
406  v[j] = vert_global_local[v[j]];
407  }
408  bdrelem_counter++;
409  }
410  }
411  }
412  else if (Dim == 2)
413  {
414  edge_element = new Table;
415  Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
416 
417  for (int i = 0; i < mesh.GetNBE(); i++)
418  {
419  int edge = mesh.GetBdrElementEdgeIndex(i);
420  int el1 = edge_element->GetRow(edge)[0];
421  if (partitioning[el1] == MyRank)
422  {
423  nbdry++;
424  if (mesh.NURBSext)
425  {
426  activeBdrElem[i] = true;
427  }
428  }
429  }
430 
431  int bdrelem_counter = 0;
432  boundary.SetSize(nbdry);
433  for (int i = 0; i < mesh.GetNBE(); i++)
434  {
435  int edge = mesh.GetBdrElementEdgeIndex(i);
436  int el1 = edge_element->GetRow(edge)[0];
437  if (partitioning[el1] == MyRank)
438  {
439  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
440  int *v = boundary[bdrelem_counter]->GetVertices();
441  int nv = boundary[bdrelem_counter]->GetNVertices();
442  for (int j = 0; j < nv; j++)
443  {
444  v[j] = vert_global_local[v[j]];
445  }
446  bdrelem_counter++;
447  }
448  }
449  }
450  else if (Dim == 1)
451  {
452  for (int i = 0; i < mesh.GetNBE(); i++)
453  {
454  int vert = mesh.boundary[i]->GetVertices()[0];
455  int el1, el2;
456  mesh.GetFaceElements(vert, &el1, &el2);
457  if (partitioning[el1] == MyRank)
458  {
459  nbdry++;
460  }
461  }
462 
463  int bdrelem_counter = 0;
464  boundary.SetSize(nbdry);
465  for (int i = 0; i < mesh.GetNBE(); i++)
466  {
467  int vert = mesh.boundary[i]->GetVertices()[0];
468  int el1, el2;
469  mesh.GetFaceElements(vert, &el1, &el2);
470  if (partitioning[el1] == MyRank)
471  {
472  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
473  int *v = boundary[bdrelem_counter]->GetVertices();
474  v[0] = vert_global_local[v[0]];
475  bdrelem_counter++;
476  }
477  }
478  }
479 
480  return nbdry;
481 }
482 
483 void ParMesh::FindSharedFaces(const Mesh &mesh, const int *partitioning,
484  Array<int> &face_group,
485  ListOfIntegerSets &groups)
486 {
487  IntegerSet group;
488 
489  // determine shared faces
490  face_group.SetSize(mesh.GetNFaces());
491  for (int i = 0; i < face_group.Size(); i++)
492  {
493  int el[2];
494  face_group[i] = -1;
495  mesh.GetFaceElements(i, &el[0], &el[1]);
496  if (el[1] >= 0)
497  {
498  el[0] = partitioning[el[0]];
499  el[1] = partitioning[el[1]];
500  if ((el[0] == MyRank && el[1] != MyRank) ||
501  (el[0] != MyRank && el[1] == MyRank))
502  {
503  group.Recreate(2, el);
504  face_group[i] = groups.Insert(group) - 1;
505  }
506  }
507  }
508 }
509 
510 int ParMesh::FindSharedEdges(const Mesh &mesh, const int *partitioning,
511  Table*& edge_element,
512  ListOfIntegerSets& groups)
513 {
514  IntegerSet group;
515 
516  // determine shared edges
517  int sedge_counter = 0;
518  if (!edge_element)
519  {
520  edge_element = new Table;
521  if (Dim == 1)
522  {
523  edge_element->SetDims(0,0);
524  }
525  else
526  {
527  Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
528  }
529  }
530 
531  for (int i = 0; i < edge_element->Size(); i++)
532  {
533  int me = 0, others = 0;
534  for (int j = edge_element->GetI()[i]; j < edge_element->GetI()[i+1]; j++)
535  {
536  int k = edge_element->GetJ()[j];
537  int rank = partitioning[k];
538  edge_element->GetJ()[j] = rank;
539  if (rank == MyRank)
540  {
541  me = 1;
542  }
543  else
544  {
545  others = 1;
546  }
547  }
548 
549  if (me && others)
550  {
551  sedge_counter++;
552  group.Recreate(edge_element->RowSize(i), edge_element->GetRow(i));
553  edge_element->GetRow(i)[0] = groups.Insert(group) - 1;
554  }
555  else
556  {
557  edge_element->GetRow(i)[0] = -1;
558  }
559  }
560 
561  return sedge_counter;
562 }
563 
564 int ParMesh::FindSharedVertices(const int *partitioning, Table *vert_element,
565  ListOfIntegerSets &groups)
566 {
567  IntegerSet group;
568 
569  // determine shared vertices
570  int svert_counter = 0;
571  for (int i = 0; i < vert_element->Size(); i++)
572  {
573  int me = 0, others = 0;
574  for (int j = vert_element->GetI()[i]; j < vert_element->GetI()[i+1]; j++)
575  {
576  vert_element->GetJ()[j] = partitioning[vert_element->GetJ()[j]];
577  if (vert_element->GetJ()[j] == MyRank)
578  {
579  me = 1;
580  }
581  else
582  {
583  others = 1;
584  }
585  }
586 
587  if (me && others)
588  {
589  svert_counter++;
590  group.Recreate(vert_element->RowSize(i), vert_element->GetRow(i));
591  vert_element->GetI()[i] = groups.Insert(group) - 1;
592  }
593  else
594  {
595  vert_element->GetI()[i] = -1;
596  }
597  }
598  return svert_counter;
599 }
600 
601 void ParMesh::BuildFaceGroup(int ngroups, const Mesh &mesh,
602  const Array<int> &face_group,
603  int &nstria, int &nsquad)
604 {
605  // build group_stria and group_squad
606  group_stria.MakeI(ngroups);
607  group_squad.MakeI(ngroups);
608 
609  for (int i = 0; i < face_group.Size(); i++)
610  {
611  if (face_group[i] >= 0)
612  {
613  if (mesh.GetFace(i)->GetType() == Element::TRIANGLE)
614  {
615  group_stria.AddAColumnInRow(face_group[i]);
616  }
617  else
618  {
619  group_squad.AddAColumnInRow(face_group[i]);
620  }
621  }
622  }
623 
624  group_stria.MakeJ();
625  group_squad.MakeJ();
626 
627  nstria = nsquad = 0;
628  for (int i = 0; i < face_group.Size(); i++)
629  {
630  if (face_group[i] >= 0)
631  {
632  if (mesh.GetFace(i)->GetType() == Element::TRIANGLE)
633  {
634  group_stria.AddConnection(face_group[i], nstria++);
635  }
636  else
637  {
638  group_squad.AddConnection(face_group[i], nsquad++);
639  }
640  }
641  }
642 
645 }
646 
647 void ParMesh::BuildEdgeGroup(int ngroups, const Table &edge_element)
648 {
649  group_sedge.MakeI(ngroups);
650 
651  for (int i = 0; i < edge_element.Size(); i++)
652  {
653  if (edge_element.GetRow(i)[0] >= 0)
654  {
655  group_sedge.AddAColumnInRow(edge_element.GetRow(i)[0]);
656  }
657  }
658 
659  group_sedge.MakeJ();
660 
661  int sedge_counter = 0;
662  for (int i = 0; i < edge_element.Size(); i++)
663  {
664  if (edge_element.GetRow(i)[0] >= 0)
665  {
666  group_sedge.AddConnection(edge_element.GetRow(i)[0], sedge_counter++);
667  }
668  }
669 
671 }
672 
673 void ParMesh::BuildVertexGroup(int ngroups, const Table &vert_element)
674 {
675  group_svert.MakeI(ngroups);
676 
677  for (int i = 0; i < vert_element.Size(); i++)
678  {
679  if (vert_element.GetI()[i] >= 0)
680  {
681  group_svert.AddAColumnInRow(vert_element.GetI()[i]);
682  }
683  }
684 
685  group_svert.MakeJ();
686 
687  int svert_counter = 0;
688  for (int i = 0; i < vert_element.Size(); i++)
689  {
690  if (vert_element.GetI()[i] >= 0)
691  {
692  group_svert.AddConnection(vert_element.GetI()[i], svert_counter++);
693  }
694  }
695 
697 }
698 
699 void ParMesh::BuildSharedFaceElems(int ntri_faces, int nquad_faces,
700  const Mesh& mesh, int *partitioning,
701  const STable3D *faces_tbl,
702  const Array<int> &face_group,
703  const Array<int> &vert_global_local)
704 {
705  shared_trias.SetSize(ntri_faces);
706  shared_quads.SetSize(nquad_faces);
707  sface_lface. SetSize(ntri_faces + nquad_faces);
708 
709  if (Dim < 3) { return; }
710 
711  int stria_counter = 0;
712  int squad_counter = 0;
713  for (int i = 0; i < face_group.Size(); i++)
714  {
715  if (face_group[i] < 0) { continue; }
716 
717  const Element *face = mesh.GetFace(i);
718  const int *fv = face->GetVertices();
719  switch (face->GetType())
720  {
721  case Element::TRIANGLE:
722  {
723  shared_trias[stria_counter].Set(fv);
724  int *v = shared_trias[stria_counter].v;
725  for (int j = 0; j < 3; j++)
726  {
727  v[j] = vert_global_local[v[j]];
728  }
729  const int lface = (*faces_tbl)(v[0], v[1], v[2]);
730  sface_lface[stria_counter] = lface;
731  if (meshgen == 1) // Tet-only mesh
732  {
733  Tetrahedron *tet = dynamic_cast<Tetrahedron *>
734  (elements[faces_info[lface].Elem1No]);
735  // mark the shared face for refinement by reorienting
736  // it according to the refinement flag in the tetrahedron
737  // to which this shared face belongs to.
738  if (tet->GetRefinementFlag())
739  {
740  tet->GetMarkedFace(faces_info[lface].Elem1Inf/64, v);
741  // flip the shared face in the processor that owns the
742  // second element (in 'mesh')
743  int gl_el1, gl_el2;
744  mesh.GetFaceElements(i, &gl_el1, &gl_el2);
745  if (MyRank == partitioning[gl_el2])
746  {
747  std::swap(v[0], v[1]);
748  }
749  }
750  }
751  stria_counter++;
752  break;
753  }
754 
756  {
757  shared_quads[squad_counter].Set(fv);
758  int *v = shared_quads[squad_counter].v;
759  for (int j = 0; j < 4; j++)
760  {
761  v[j] = vert_global_local[v[j]];
762  }
763  sface_lface[shared_trias.Size() + squad_counter] =
764  (*faces_tbl)(v[0], v[1], v[2], v[3]);
765  squad_counter++;
766  break;
767  }
768 
769  default:
770  MFEM_ABORT("unknown face type: " << face->GetType());
771  break;
772  }
773  }
774 }
775 
776 void ParMesh::BuildSharedEdgeElems(int nedges, Mesh& mesh,
777  const Array<int>& vert_global_local,
778  const Table* edge_element)
779 {
780  // The passed in mesh is still the global mesh. "this" mesh is the
781  // local partitioned mesh.
782 
783  shared_edges.SetSize(nedges);
784  sedge_ledge. SetSize(nedges);
785 
786  {
787  DSTable v_to_v(NumOfVertices);
788  GetVertexToVertexTable(v_to_v);
789 
790  int sedge_counter = 0;
791  for (int i = 0; i < edge_element->Size(); i++)
792  {
793  if (edge_element->GetRow(i)[0] >= 0)
794  {
795  Array<int> vert;
796  mesh.GetEdgeVertices(i, vert);
797 
798  shared_edges[sedge_counter] =
799  new Segment(vert_global_local[vert[0]],
800  vert_global_local[vert[1]], 1);
801 
802  sedge_ledge[sedge_counter] = v_to_v(vert_global_local[vert[0]],
803  vert_global_local[vert[1]]);
804 
805  MFEM_VERIFY(sedge_ledge[sedge_counter] >= 0, "Error in v_to_v.");
806 
807  sedge_counter++;
808  }
809  }
810  }
811 }
812 
814  const mfem::Table *vert_element,
815  const Array<int> &vert_global_local)
816 {
817  // build svert_lvert
818  svert_lvert.SetSize(nvert);
819 
820  int svert_counter = 0;
821  for (int i = 0; i < vert_element->Size(); i++)
822  {
823  if (vert_element->GetI()[i] >= 0)
824  {
825  svert_lvert[svert_counter++] = vert_global_local[i];
826  }
827  }
828 }
829 
830 
831 // protected method, used by Nonconforming(De)Refinement and Rebalance
833  : MyComm(pncmesh.MyComm)
834  , NRanks(pncmesh.NRanks)
835  , MyRank(pncmesh.MyRank)
836  , gtopo(MyComm)
837  , pncmesh(NULL)
838 {
839  Mesh::InitFromNCMesh(pncmesh);
840  ReduceMeshGen();
841  have_face_nbr_data = false;
842 }
843 
845 {
846  int loc_meshgen = meshgen;
847  MPI_Allreduce(&loc_meshgen, &meshgen, 1, MPI_INT, MPI_BOR, MyComm);
848 }
849 
851 {
852  // Determine sedge_ledge
854  if (shared_edges.Size())
855  {
856  DSTable v_to_v(NumOfVertices);
857  GetVertexToVertexTable(v_to_v);
858  for (int se = 0; se < shared_edges.Size(); se++)
859  {
860  const int *v = shared_edges[se]->GetVertices();
861  const int l_edge = v_to_v(v[0], v[1]);
862  MFEM_ASSERT(l_edge >= 0, "invalid shared edge");
863  sedge_ledge[se] = l_edge;
864  }
865  }
866 
867  // Determine sface_lface
868  const int nst = shared_trias.Size();
869  sface_lface.SetSize(nst + shared_quads.Size());
870  if (sface_lface.Size())
871  {
872  STable3D *faces_tbl = GetFacesTable();
873  for (int st = 0; st < nst; st++)
874  {
875  const int *v = shared_trias[st].v;
876  sface_lface[st] = (*faces_tbl)(v[0], v[1], v[2]);
877  }
878  for (int sq = 0; sq < shared_quads.Size(); sq++)
879  {
880  const int *v = shared_quads[sq].v;
881  sface_lface[nst+sq] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
882  }
883  delete faces_tbl;
884  }
885 }
886 
887 ParMesh::ParMesh(MPI_Comm comm, istream &input, bool refine)
888  : gtopo(comm)
889 {
890  MyComm = comm;
891  MPI_Comm_size(MyComm, &NRanks);
892  MPI_Comm_rank(MyComm, &MyRank);
893 
894  have_face_nbr_data = false;
895  pncmesh = NULL;
896 
897  string ident;
898 
899  // read the serial part of the mesh
900  const int gen_edges = 1;
901 
902  // Tell Loader() to read up to 'mfem_serial_mesh_end' instead of
903  // 'mfem_mesh_end', as we have additional parallel mesh data to load in from
904  // the stream.
905  Loader(input, gen_edges, "mfem_serial_mesh_end");
906 
907  ReduceMeshGen(); // determine the global 'meshgen'
908 
909  skip_comment_lines(input, '#');
910 
911  // read the group topology
912  input >> ident;
913  MFEM_VERIFY(ident == "communication_groups",
914  "input stream is not a parallel MFEM mesh");
915  gtopo.Load(input);
916 
917  skip_comment_lines(input, '#');
918 
919  // read and set the sizes of svert_lvert, group_svert
920  {
921  int num_sverts;
922  input >> ident >> num_sverts; // total_shared_vertices
923  svert_lvert.SetSize(num_sverts);
924  group_svert.SetDims(GetNGroups()-1, num_sverts);
925  }
926  // read and set the sizes of sedge_ledge, group_sedge
927  if (Dim >= 2)
928  {
929  skip_comment_lines(input, '#');
930  int num_sedges;
931  input >> ident >> num_sedges; // total_shared_edges
932  sedge_ledge.SetSize(num_sedges);
933  shared_edges.SetSize(num_sedges);
934  group_sedge.SetDims(GetNGroups()-1, num_sedges);
935  }
936  else
937  {
938  group_sedge.SetSize(GetNGroups()-1, 0); // create empty group_sedge
939  }
940  // read and set the sizes of sface_lface, group_{stria,squad}
941  if (Dim >= 3)
942  {
943  skip_comment_lines(input, '#');
944  int num_sface;
945  input >> ident >> num_sface; // total_shared_faces
946  sface_lface.SetSize(num_sface);
949  }
950  else
951  {
952  group_stria.SetSize(GetNGroups()-1, 0); // create empty group_stria
953  group_squad.SetSize(GetNGroups()-1, 0); // create empty group_squad
954  }
955 
956  // read, group by group, the contents of group_svert, svert_lvert,
957  // group_sedge, shared_edges, group_{stria,squad}, shared_{trias,quads}
958  int svert_counter = 0, sedge_counter = 0;
959  for (int gr = 1; gr < GetNGroups(); gr++)
960  {
961  skip_comment_lines(input, '#');
962 #if 0
963  // implementation prior to prism-dev merge
964  int g;
965  input >> ident >> g; // group
966  if (g != gr)
967  {
968  mfem::err << "ParMesh::ParMesh : expecting group " << gr
969  << ", read group " << g << endl;
970  mfem_error();
971  }
972 #endif
973 
974  {
975  int nv;
976  input >> ident >> nv; // shared_vertices (in this group)
977  nv += svert_counter;
978  MFEM_VERIFY(nv <= group_svert.Size_of_connections(),
979  "incorrect number of total_shared_vertices");
980  group_svert.GetI()[gr] = nv;
981  for ( ; svert_counter < nv; svert_counter++)
982  {
983  group_svert.GetJ()[svert_counter] = svert_counter;
984  input >> svert_lvert[svert_counter];
985  }
986  }
987  if (Dim >= 2)
988  {
989  int ne, v[2];
990  input >> ident >> ne; // shared_edges (in this group)
991  ne += sedge_counter;
992  MFEM_VERIFY(ne <= group_sedge.Size_of_connections(),
993  "incorrect number of total_shared_edges");
994  group_sedge.GetI()[gr] = ne;
995  for ( ; sedge_counter < ne; sedge_counter++)
996  {
997  group_sedge.GetJ()[sedge_counter] = sedge_counter;
998  input >> v[0] >> v[1];
999  shared_edges[sedge_counter] = new Segment(v[0], v[1], 1);
1000  }
1001  }
1002  if (Dim >= 3)
1003  {
1004  int nf, tstart = shared_trias.Size(), qstart = shared_quads.Size();
1005  input >> ident >> nf; // shared_faces (in this group)
1006  for (int i = 0; i < nf; i++)
1007  {
1008  int geom, *v;
1009  input >> geom;
1010  switch (geom)
1011  {
1012  case Geometry::TRIANGLE:
1013  shared_trias.SetSize(shared_trias.Size()+1);
1014  v = shared_trias.Last().v;
1015  for (int i = 0; i < 3; i++) { input >> v[i]; }
1016  break;
1017  case Geometry::SQUARE:
1018  shared_quads.SetSize(shared_quads.Size()+1);
1019  v = shared_quads.Last().v;
1020  for (int i = 0; i < 4; i++) { input >> v[i]; }
1021  break;
1022  default:
1023  MFEM_ABORT("invalid shared face geometry: " << geom);
1024  }
1025  }
1026  group_stria.AddColumnsInRow(gr-1, shared_trias.Size()-tstart);
1027  group_squad.AddColumnsInRow(gr-1, shared_quads.Size()-qstart);
1028  }
1029  }
1030  if (Dim >= 3)
1031  {
1032  MFEM_VERIFY(shared_trias.Size() + shared_quads.Size()
1033  == sface_lface.Size(),
1034  "incorrect number of total_shared_faces");
1035  // Define the J arrays of group_stria and group_squad -- they just contain
1036  // consecutive numbers starting from 0 up to shared_trias.Size()-1 and
1037  // shared_quads.Size()-1, respectively.
1038  group_stria.MakeJ();
1039  for (int i = 0; i < shared_trias.Size(); i++)
1040  {
1041  group_stria.GetJ()[i] = i;
1042  }
1043  group_squad.MakeJ();
1044  for (int i = 0; i < shared_quads.Size(); i++)
1045  {
1046  group_squad.GetJ()[i] = i;
1047  }
1048  }
1049 
1050  const bool fix_orientation = false;
1051  Finalize(refine, fix_orientation);
1052 
1053  // If the mesh has Nodes, convert them from GridFunction to ParGridFunction?
1054 
1055  // note: attributes and bdr_attributes are local lists
1056 
1057  // TODO: AMR meshes, NURBS meshes?
1058 }
1059 
1060 ParMesh::ParMesh(ParMesh *orig_mesh, int ref_factor, int ref_type)
1061  : Mesh(orig_mesh, ref_factor, ref_type),
1062  MyComm(orig_mesh->GetComm()),
1063  NRanks(orig_mesh->GetNRanks()),
1064  MyRank(orig_mesh->GetMyRank()),
1065  gtopo(orig_mesh->gtopo),
1066  have_face_nbr_data(false),
1067  pncmesh(NULL)
1068 {
1069  // Need to initialize:
1070  // - shared_edges, shared_{trias,quads}
1071  // - group_svert, group_sedge, group_{stria,squad}
1072  // - svert_lvert, sedge_ledge, sface_lface
1073 
1074  meshgen = orig_mesh->meshgen; // copy the global 'meshgen'
1075 
1076  H1_FECollection rfec(ref_factor, Dim, ref_type);
1077  ParFiniteElementSpace rfes(orig_mesh, &rfec);
1078 
1079  // count the number of entries in each row of group_s{vert,edge,face}
1080  group_svert.MakeI(GetNGroups()-1); // exclude the local group 0
1084  for (int gr = 1; gr < GetNGroups(); gr++)
1085  {
1086  // orig vertex -> vertex
1087  group_svert.AddColumnsInRow(gr-1, orig_mesh->GroupNVertices(gr));
1088  // orig edge -> (ref_factor-1) vertices and (ref_factor) edges
1089  const int orig_ne = orig_mesh->GroupNEdges(gr);
1090  group_svert.AddColumnsInRow(gr-1, (ref_factor-1)*orig_ne);
1091  group_sedge.AddColumnsInRow(gr-1, ref_factor*orig_ne);
1092  // orig face -> (?) vertices, (?) edges, and (?) faces
1093  const int orig_nt = orig_mesh->GroupNTriangles(gr);
1094  if (orig_nt > 0)
1095  {
1097  const int nvert = Geometry::NumVerts[geom];
1098  RefinedGeometry &RG =
1099  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1100 
1101  // count internal vertices
1102  group_svert.AddColumnsInRow(gr-1, orig_nt*rfec.DofForGeometry(geom));
1103  // count internal edges
1104  group_sedge.AddColumnsInRow(gr-1, orig_nt*(RG.RefEdges.Size()/2-
1105  RG.NumBdrEdges));
1106  // count refined faces
1107  group_stria.AddColumnsInRow(gr-1, orig_nt*(RG.RefGeoms.Size()/nvert));
1108  }
1109  const int orig_nq = orig_mesh->GroupNQuadrilaterals(gr);
1110  if (orig_nq > 0)
1111  {
1113  const int nvert = Geometry::NumVerts[geom];
1114  RefinedGeometry &RG =
1115  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1116 
1117  // count internal vertices
1118  group_svert.AddColumnsInRow(gr-1, orig_nq*rfec.DofForGeometry(geom));
1119  // count internal edges
1120  group_sedge.AddColumnsInRow(gr-1, orig_nq*(RG.RefEdges.Size()/2-
1121  RG.NumBdrEdges));
1122  // count refined faces
1123  group_squad.AddColumnsInRow(gr-1, orig_nq*(RG.RefGeoms.Size()/nvert));
1124  }
1125  }
1126 
1127  group_svert.MakeJ();
1129 
1130  group_sedge.MakeJ();
1133 
1134  group_stria.MakeJ();
1135  group_squad.MakeJ();
1138  sface_lface.SetSize(shared_trias.Size() + shared_quads.Size());
1139 
1140  Array<int> rdofs;
1141  for (int gr = 1; gr < GetNGroups(); gr++)
1142  {
1143  // add shared vertices from original shared vertices
1144  const int orig_n_verts = orig_mesh->GroupNVertices(gr);
1145  for (int j = 0; j < orig_n_verts; j++)
1146  {
1147  rfes.GetVertexDofs(orig_mesh->GroupVertex(gr, j), rdofs);
1148  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[0])-1);
1149  }
1150 
1151  // add refined shared edges; add shared vertices from refined shared edges
1152  const int orig_n_edges = orig_mesh->GroupNEdges(gr);
1153  if (orig_n_edges > 0)
1154  {
1156  const int nvert = Geometry::NumVerts[geom];
1157  RefinedGeometry &RG = *GlobGeometryRefiner.Refine(geom, ref_factor);
1158  const int *c2h_map = rfec.GetDofMap(geom);
1159 
1160  for (int e = 0; e < orig_n_edges; e++)
1161  {
1162  rfes.GetSharedEdgeDofs(gr, e, rdofs);
1163  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1164  // add the internal edge 'rdofs' as shared vertices
1165  for (int j = 2; j < rdofs.Size(); j++)
1166  {
1167  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1168  }
1169  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1170  {
1171  Element *elem = NewElement(geom);
1172  int *v = elem->GetVertices();
1173  for (int k = 0; k < nvert; k++)
1174  {
1175  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1176  v[k] = rdofs[c2h_map[cid]];
1177  }
1178  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1179  }
1180  }
1181  }
1182  // add refined shared faces; add shared edges and shared vertices from
1183  // refined shared faces
1184  const int orig_nt = orig_mesh->GroupNTriangles(gr);
1185  if (orig_nt > 0)
1186  {
1188  const int nvert = Geometry::NumVerts[geom];
1189  RefinedGeometry &RG =
1190  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1191  const int num_int_verts = rfec.DofForGeometry(geom);
1192  const int *c2h_map = rfec.GetDofMap(geom);
1193 
1194  for (int f = 0; f < orig_nt; f++)
1195  {
1196  rfes.GetSharedTriangleDofs(gr, f, rdofs);
1197  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1198  // add the internal face 'rdofs' as shared vertices
1199  for (int j = rdofs.Size()-num_int_verts; j < rdofs.Size(); j++)
1200  {
1201  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1202  }
1203  // add the internal (for the shared face) edges as shared edges
1204  for (int j = 2*RG.NumBdrEdges; j < RG.RefEdges.Size(); j += 2)
1205  {
1207  int *v = elem->GetVertices();
1208  for (int k = 0; k < 2; k++)
1209  {
1210  v[k] = rdofs[c2h_map[RG.RefEdges[j+k]]];
1211  }
1212  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1213  }
1214  // add refined shared faces
1215  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1216  {
1217  shared_trias.SetSize(shared_trias.Size()+1);
1218  int *v = shared_trias.Last().v;
1219  for (int k = 0; k < nvert; k++)
1220  {
1221  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1222  v[k] = rdofs[c2h_map[cid]];
1223  }
1224  group_stria.AddConnection(gr-1, shared_trias.Size()-1);
1225  }
1226  }
1227  }
1228  const int orig_nq = orig_mesh->GroupNQuadrilaterals(gr);
1229  if (orig_nq > 0)
1230  {
1232  const int nvert = Geometry::NumVerts[geom];
1233  RefinedGeometry &RG =
1234  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1235  const int num_int_verts = rfec.DofForGeometry(geom);
1236  const int *c2h_map = rfec.GetDofMap(geom);
1237 
1238  for (int f = 0; f < orig_nq; f++)
1239  {
1240  rfes.GetSharedQuadrilateralDofs(gr, f, rdofs);
1241  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1242  // add the internal face 'rdofs' as shared vertices
1243  for (int j = rdofs.Size()-num_int_verts; j < rdofs.Size(); j++)
1244  {
1245  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1246  }
1247  // add the internal (for the shared face) edges as shared edges
1248  for (int j = 2*RG.NumBdrEdges; j < RG.RefEdges.Size(); j += 2)
1249  {
1251  int *v = elem->GetVertices();
1252  for (int k = 0; k < 2; k++)
1253  {
1254  v[k] = rdofs[c2h_map[RG.RefEdges[j+k]]];
1255  }
1256  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1257  }
1258  // add refined shared faces
1259  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1260  {
1261  shared_quads.SetSize(shared_quads.Size()+1);
1262  int *v = shared_quads.Last().v;
1263  for (int k = 0; k < nvert; k++)
1264  {
1265  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1266  v[k] = rdofs[c2h_map[cid]];
1267  }
1268  group_squad.AddConnection(gr-1, shared_quads.Size()-1);
1269  }
1270  }
1271  }
1272  }
1277 
1278  FinalizeParTopo();
1279 }
1280 
1281 void ParMesh::Finalize(bool refine, bool fix_orientation)
1282 {
1283  const int meshgen_save = meshgen; // Mesh::Finalize() may call SetMeshGen()
1284 
1285  Mesh::Finalize(refine, fix_orientation);
1286 
1287  meshgen = meshgen_save;
1288  // Note: if Mesh::Finalize() calls MarkTetMeshForRefinement() then the
1289  // shared_trias have been rotated as necessary.
1290 
1291  // Setup secondary parallel mesh data: sedge_ledge, sface_lface
1292  FinalizeParTopo();
1293 }
1294 
1296 {
1297  // Determine the largest attribute number across all processors
1298  int max_attr = attr.Max();
1299  int glb_max_attr = -1;
1300  MPI_Allreduce(&max_attr, &glb_max_attr, 1, MPI_INT, MPI_MAX, MyComm);
1301 
1302  // Create marker arrays to indicate which attributes are present
1303  // assuming attribute numbers are in the range [1,glb_max_attr].
1304  bool * attr_marker = new bool[glb_max_attr];
1305  bool * glb_attr_marker = new bool[glb_max_attr];
1306  for (int i=0; i<glb_max_attr; i++)
1307  {
1308  attr_marker[i] = false;
1309  }
1310  for (int i=0; i<attr.Size(); i++)
1311  {
1312  attr_marker[attr[i] - 1] = true;
1313  }
1314  MPI_Allreduce(attr_marker, glb_attr_marker, glb_max_attr,
1315  MPI_C_BOOL, MPI_LOR, MyComm);
1316  delete [] attr_marker;
1317 
1318  // Translate from the marker array to a unique, sorted list of attributes
1319  Array<int> glb_attr;
1320  glb_attr.SetSize(glb_max_attr);
1321  glb_attr = glb_max_attr;
1322  int o = 0;
1323  for (int i=0; i<glb_max_attr; i++)
1324  {
1325  if (glb_attr_marker[i])
1326  {
1327  glb_attr[o++] = i + 1;
1328  }
1329  }
1330  delete [] glb_attr_marker;
1331 
1332  glb_attr.Sort();
1333  glb_attr.Unique();
1334  glb_attr.Copy(attr);
1335 }
1336 
1338 {
1339  // Determine the attributes occurring in local interior and boundary elements
1341 
1343  if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1344  {
1345  MFEM_WARNING("Non-positive boundary element attributes found!");
1346  }
1347 
1349  if (attributes.Size() > 0 && attributes[0] <= 0)
1350  {
1351  MFEM_WARNING("Non-positive element attributes found!");
1352  }
1353 }
1354 
1355 void ParMesh::GroupEdge(int group, int i, int &edge, int &o)
1356 {
1357  int sedge = group_sedge.GetRow(group-1)[i];
1358  edge = sedge_ledge[sedge];
1359  int *v = shared_edges[sedge]->GetVertices();
1360  o = (v[0] < v[1]) ? (+1) : (-1);
1361 }
1362 
1363 void ParMesh::GroupTriangle(int group, int i, int &face, int &o)
1364 {
1365  int stria = group_stria.GetRow(group-1)[i];
1366  face = sface_lface[stria];
1367  // face gives the base orientation
1368  MFEM_ASSERT(faces[face]->GetType() == Element::TRIANGLE,
1369  "Expecting a triangular face.");
1370 
1371  o = GetTriOrientation(faces[face]->GetVertices(), shared_trias[stria].v);
1372 }
1373 
1374 void ParMesh::GroupQuadrilateral(int group, int i, int &face, int &o)
1375 {
1376  int squad = group_squad.GetRow(group-1)[i];
1377  face = sface_lface[shared_trias.Size()+squad];
1378  // face gives the base orientation
1379  MFEM_ASSERT(faces[face]->GetType() == Element::QUADRILATERAL,
1380  "Expecting a quadrilateral face.");
1381 
1382  o = GetQuadOrientation(faces[face]->GetVertices(), shared_quads[squad].v);
1383 }
1384 
1386 {
1387  Array<int> order;
1388  GetEdgeOrdering(v_to_v, order); // local edge ordering
1389 
1390  // create a GroupCommunicator on the shared edges
1391  GroupCommunicator sedge_comm(gtopo);
1392  {
1393  // initialize sedge_comm
1394  Table &gr_sedge = sedge_comm.GroupLDofTable(); // differs from group_sedge
1395  gr_sedge.SetDims(GetNGroups(), shared_edges.Size());
1396  gr_sedge.GetI()[0] = 0;
1397  for (int gr = 1; gr <= GetNGroups(); gr++)
1398  {
1399  gr_sedge.GetI()[gr] = group_sedge.GetI()[gr-1];
1400  }
1401  for (int k = 0; k < shared_edges.Size(); k++)
1402  {
1403  gr_sedge.GetJ()[k] = group_sedge.GetJ()[k];
1404  }
1405  sedge_comm.Finalize();
1406  }
1407 
1408  Array<int> sedge_ord(shared_edges.Size());
1409  Array<Pair<int,int> > sedge_ord_map(shared_edges.Size());
1410  for (int k = 0; k < shared_edges.Size(); k++)
1411  {
1412  // sedge_ledge may be undefined -- use shared_edges and v_to_v instead
1413  const int sedge = group_sedge.GetJ()[k];
1414  const int *v = shared_edges[sedge]->GetVertices();
1415  sedge_ord[k] = order[v_to_v(v[0], v[1])];
1416  }
1417 
1418  sedge_comm.Bcast<int>(sedge_ord, 1);
1419 
1420  for (int k = 0, gr = 1; gr < GetNGroups(); gr++)
1421  {
1422  const int n = group_sedge.RowSize(gr-1);
1423  if (n == 0) { continue; }
1424  sedge_ord_map.SetSize(n);
1425  for (int j = 0; j < n; j++)
1426  {
1427  sedge_ord_map[j].one = sedge_ord[k+j];
1428  sedge_ord_map[j].two = j;
1429  }
1430  SortPairs<int, int>(sedge_ord_map, n);
1431  for (int j = 0; j < n; j++)
1432  {
1433  const int sedge_from = group_sedge.GetJ()[k+j];
1434  const int *v = shared_edges[sedge_from]->GetVertices();
1435  sedge_ord[k+j] = order[v_to_v(v[0], v[1])];
1436  }
1437  std::sort(&sedge_ord[k], &sedge_ord[k] + n);
1438  for (int j = 0; j < n; j++)
1439  {
1440  const int sedge_to = group_sedge.GetJ()[k+sedge_ord_map[j].two];
1441  const int *v = shared_edges[sedge_to]->GetVertices();
1442  order[v_to_v(v[0], v[1])] = sedge_ord[k+j];
1443  }
1444  k += n;
1445  }
1446 
1447 #ifdef MFEM_DEBUG
1448  {
1449  Array<Pair<int, double> > ilen_len(order.Size());
1450 
1451  for (int i = 0; i < NumOfVertices; i++)
1452  {
1453  for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
1454  {
1455  int j = it.Index();
1456  ilen_len[j].one = order[j];
1457  ilen_len[j].two = GetLength(i, it.Column());
1458  }
1459  }
1460 
1461  SortPairs<int, double>(ilen_len, order.Size());
1462 
1463  double d_max = 0.;
1464  for (int i = 1; i < order.Size(); i++)
1465  {
1466  d_max = std::max(d_max, ilen_len[i-1].two-ilen_len[i].two);
1467  }
1468 
1469 #if 0
1470  // Debug message from every MPI rank.
1471  mfem::out << "proc. " << MyRank << '/' << NRanks << ": d_max = " << d_max
1472  << endl;
1473 #else
1474  // Debug message just from rank 0.
1475  double glob_d_max;
1476  MPI_Reduce(&d_max, &glob_d_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
1477  if (MyRank == 0)
1478  {
1479  mfem::out << "glob_d_max = " << glob_d_max << endl;
1480  }
1481 #endif
1482  }
1483 #endif
1484 
1485  // use 'order' to mark the tets, the boundary triangles, and the shared
1486  // triangle faces
1487  for (int i = 0; i < NumOfElements; i++)
1488  {
1489  if (elements[i]->GetType() == Element::TETRAHEDRON)
1490  {
1491  elements[i]->MarkEdge(v_to_v, order);
1492  }
1493  }
1494 
1495  for (int i = 0; i < NumOfBdrElements; i++)
1496  {
1497  if (boundary[i]->GetType() == Element::TRIANGLE)
1498  {
1499  boundary[i]->MarkEdge(v_to_v, order);
1500  }
1501  }
1502 
1503  for (int i = 0; i < shared_trias.Size(); i++)
1504  {
1505  Triangle::MarkEdge(shared_trias[i].v, v_to_v, order);
1506  }
1507 }
1508 
1509 // For a line segment with vertices v[0] and v[1], return a number with
1510 // the following meaning:
1511 // 0 - the edge was not refined
1512 // 1 - the edge e was refined once by splitting v[0],v[1]
1513 int ParMesh::GetEdgeSplittings(Element *edge, const DSTable &v_to_v,
1514  int *middle)
1515 {
1516  int m, *v = edge->GetVertices();
1517 
1518  if ((m = v_to_v(v[0], v[1])) != -1 && middle[m] != -1)
1519  {
1520  return 1;
1521  }
1522  else
1523  {
1524  return 0;
1525  }
1526 }
1527 
1528 void ParMesh::GetFaceSplittings(const int *fv, const HashTable<Hashed2> &v_to_v,
1529  Array<unsigned> &codes)
1530 {
1531  typedef Triple<int,int,int> face_t;
1532  Array<face_t> face_stack;
1533 
1534  unsigned code = 0;
1535  face_stack.Append(face_t(fv[0], fv[1], fv[2]));
1536  for (unsigned bit = 0; face_stack.Size() > 0; bit++)
1537  {
1538  if (bit == 8*sizeof(unsigned))
1539  {
1540  codes.Append(code);
1541  code = bit = 0;
1542  }
1543 
1544  const face_t &f = face_stack.Last();
1545  int mid = v_to_v.FindId(f.one, f.two);
1546  if (mid == -1)
1547  {
1548  // leave a 0 at bit 'bit'
1549  face_stack.DeleteLast();
1550  }
1551  else
1552  {
1553  code += (1 << bit); // set bit 'bit' to 1
1554  mid += NumOfVertices;
1555  face_stack.Append(face_t(f.three, f.one, mid));
1556  face_t &r = face_stack[face_stack.Size()-2];
1557  r = face_t(r.two, r.three, mid);
1558  }
1559  }
1560  codes.Append(code);
1561 }
1562 
1564  const Array<unsigned> &codes, int &pos)
1565 {
1566  typedef Triple<int,int,int> face_t;
1567  Array<face_t> face_stack;
1568 
1569  bool need_refinement = 0;
1570  face_stack.Append(face_t(v[0], v[1], v[2]));
1571  for (unsigned bit = 0, code = codes[pos++]; face_stack.Size() > 0; bit++)
1572  {
1573  if (bit == 8*sizeof(unsigned))
1574  {
1575  code = codes[pos++];
1576  bit = 0;
1577  }
1578 
1579  if ((code & (1 << bit)) == 0) { face_stack.DeleteLast(); continue; }
1580 
1581  const face_t &f = face_stack.Last();
1582  int mid = v_to_v.FindId(f.one, f.two);
1583  if (mid == -1)
1584  {
1585  mid = v_to_v.GetId(f.one, f.two);
1586  int ind[2] = { f.one, f.two };
1587  vertices.Append(Vertex());
1588  AverageVertices(ind, 2, vertices.Size()-1);
1589  need_refinement = 1;
1590  }
1591  mid += NumOfVertices;
1592  face_stack.Append(face_t(f.three, f.one, mid));
1593  face_t &r = face_stack[face_stack.Size()-2];
1594  r = face_t(r.two, r.three, mid);
1595  }
1596  return need_refinement;
1597 }
1598 
1599 void ParMesh::GenerateOffsets(int N, HYPRE_Int loc_sizes[],
1600  Array<HYPRE_Int> *offsets[]) const
1601 {
1602  if (HYPRE_AssumedPartitionCheck())
1603  {
1604  Array<HYPRE_Int> temp(N);
1605  MPI_Scan(loc_sizes, temp.GetData(), N, HYPRE_MPI_INT, MPI_SUM, MyComm);
1606  for (int i = 0; i < N; i++)
1607  {
1608  offsets[i]->SetSize(3);
1609  (*offsets[i])[0] = temp[i] - loc_sizes[i];
1610  (*offsets[i])[1] = temp[i];
1611  }
1612  MPI_Bcast(temp.GetData(), N, HYPRE_MPI_INT, NRanks-1, MyComm);
1613  for (int i = 0; i < N; i++)
1614  {
1615  (*offsets[i])[2] = temp[i];
1616  // check for overflow
1617  MFEM_VERIFY((*offsets[i])[0] >= 0 && (*offsets[i])[1] >= 0,
1618  "overflow in offsets");
1619  }
1620  }
1621  else
1622  {
1623  Array<HYPRE_Int> temp(N*NRanks);
1624  MPI_Allgather(loc_sizes, N, HYPRE_MPI_INT, temp.GetData(), N,
1625  HYPRE_MPI_INT, MyComm);
1626  for (int i = 0; i < N; i++)
1627  {
1628  Array<HYPRE_Int> &offs = *offsets[i];
1629  offs.SetSize(NRanks+1);
1630  offs[0] = 0;
1631  for (int j = 0; j < NRanks; j++)
1632  {
1633  offs[j+1] = offs[j] + temp[i+N*j];
1634  }
1635  // Check for overflow
1636  MFEM_VERIFY(offs[MyRank] >= 0 && offs[MyRank+1] >= 0,
1637  "overflow in offsets");
1638  }
1639  }
1640 }
1641 
1643  int i, IsoparametricTransformation *ElTr)
1644 {
1645  DenseMatrix &pointmat = ElTr->GetPointMat();
1646  Element *elem = face_nbr_elements[i];
1647 
1648  ElTr->Attribute = elem->GetAttribute();
1649  ElTr->ElementNo = NumOfElements + i;
1650 
1651  if (Nodes == NULL)
1652  {
1653  const int nv = elem->GetNVertices();
1654  const int *v = elem->GetVertices();
1655 
1656  pointmat.SetSize(spaceDim, nv);
1657  for (int k = 0; k < spaceDim; k++)
1658  {
1659  for (int j = 0; j < nv; j++)
1660  {
1661  pointmat(k, j) = face_nbr_vertices[v[j]](k);
1662  }
1663  }
1664 
1666  }
1667  else
1668  {
1669  Array<int> vdofs;
1670  ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
1671  if (pNodes)
1672  {
1673  pNodes->ParFESpace()->GetFaceNbrElementVDofs(i, vdofs);
1674  int n = vdofs.Size()/spaceDim;
1675  pointmat.SetSize(spaceDim, n);
1676  for (int k = 0; k < spaceDim; k++)
1677  {
1678  for (int j = 0; j < n; j++)
1679  {
1680  pointmat(k,j) = (pNodes->FaceNbrData())(vdofs[n*k+j]);
1681  }
1682  }
1683 
1684  ElTr->SetFE(pNodes->ParFESpace()->GetFaceNbrFE(i));
1685  }
1686  else
1687  {
1688  MFEM_ABORT("Nodes are not ParGridFunction!");
1689  }
1690  }
1691  ElTr->FinalizeTransformation();
1692 }
1693 
1695 {
1696  if (!have_face_nbr_data)
1697  {
1698  return;
1699  }
1700 
1701  have_face_nbr_data = false;
1705  for (int i = 0; i < face_nbr_elements.Size(); i++)
1706  {
1708  }
1709  face_nbr_elements.DeleteAll();
1710  face_nbr_vertices.DeleteAll();
1713 }
1714 
1715 void ParMesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
1716 {
1717  space_dim = (space_dim == -1) ? spaceDim : space_dim;
1719  if (discont)
1720  {
1721  nfec = new L2_FECollection(order, Dim, BasisType::GaussLobatto);
1722  }
1723  else
1724  {
1725  nfec = new H1_FECollection(order, Dim);
1726  }
1727  ParFiniteElementSpace* nfes = new ParFiniteElementSpace(this, nfec, space_dim,
1728  ordering);
1729  auto pnodes = new ParGridFunction(nfes);
1730  GetNodes(*pnodes);
1731  NewNodes(*pnodes, true);
1732  Nodes->MakeOwner(nfec);
1733 }
1734 
1736 {
1737  if (have_face_nbr_data)
1738  {
1739  return;
1740  }
1741 
1742  if (Nonconforming())
1743  {
1744  // with ParNCMesh we can set up face neighbors without communication
1745  pncmesh->GetFaceNeighbors(*this);
1746  have_face_nbr_data = true;
1747 
1749  return;
1750  }
1751 
1752  Table *gr_sface;
1753  int *s2l_face;
1754  bool del_tables = false;
1755  if (Dim == 1)
1756  {
1757  gr_sface = &group_svert;
1758  s2l_face = svert_lvert;
1759  }
1760  else if (Dim == 2)
1761  {
1762  gr_sface = &group_sedge;
1763  s2l_face = sedge_ledge;
1764  }
1765  else
1766  {
1767  s2l_face = sface_lface;
1768  if (shared_trias.Size() == sface_lface.Size())
1769  {
1770  // All shared faces are Triangular
1771  gr_sface = &group_stria;
1772  }
1773  else if (shared_quads.Size() == sface_lface.Size())
1774  {
1775  // All shared faced are Quadrilateral
1776  gr_sface = &group_squad;
1777  }
1778  else
1779  {
1780  // Shared faces contain a mixture of triangles and quads
1781  gr_sface = new Table;
1782  del_tables = true;
1783 
1784  // Merge the Tables group_stria and group_squad
1785  gr_sface->MakeI(group_stria.Size());
1786  for (int gr=0; gr<group_stria.Size(); gr++)
1787  {
1788  gr_sface->AddColumnsInRow(gr,
1789  group_stria.RowSize(gr) +
1790  group_squad.RowSize(gr));
1791  }
1792  gr_sface->MakeJ();
1793  const int nst = shared_trias.Size();
1794  for (int gr=0; gr<group_stria.Size(); gr++)
1795  {
1796  gr_sface->AddConnections(gr,
1797  group_stria.GetRow(gr),
1798  group_stria.RowSize(gr));
1799  for (int c=0; c<group_squad.RowSize(gr); c++)
1800  {
1801  gr_sface->AddConnection(gr,
1802  nst + group_squad.GetRow(gr)[c]);
1803  }
1804  }
1805  gr_sface->ShiftUpI();
1806  }
1807  }
1808 
1809  ExchangeFaceNbrData(gr_sface, s2l_face);
1810 
1811  if (del_tables) { delete gr_sface; }
1812 
1813  if ( have_face_nbr_data ) { return; }
1814 
1815  have_face_nbr_data = true;
1816 
1818 }
1819 
1820 void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
1821 {
1822  int num_face_nbrs = 0;
1823  for (int g = 1; g < GetNGroups(); g++)
1824  {
1825  if (gr_sface->RowSize(g-1) > 0)
1826  {
1827  num_face_nbrs++;
1828  }
1829  }
1830 
1831  face_nbr_group.SetSize(num_face_nbrs);
1832 
1833  if (num_face_nbrs == 0)
1834  {
1835  have_face_nbr_data = true;
1836  return;
1837  }
1838 
1839  {
1840  // sort face-neighbors by processor rank
1841  Array<Pair<int, int> > rank_group(num_face_nbrs);
1842 
1843  for (int g = 1, counter = 0; g < GetNGroups(); g++)
1844  {
1845  if (gr_sface->RowSize(g-1) > 0)
1846  {
1847  MFEM_ASSERT(gtopo.GetGroupSize(g) == 2, "group size is not 2!");
1848 
1849  const int *nbs = gtopo.GetGroup(g);
1850  int lproc = (nbs[0]) ? nbs[0] : nbs[1];
1851  rank_group[counter].one = gtopo.GetNeighborRank(lproc);
1852  rank_group[counter].two = g;
1853  counter++;
1854  }
1855  }
1856 
1857  SortPairs<int, int>(rank_group, rank_group.Size());
1858 
1859  for (int fn = 0; fn < num_face_nbrs; fn++)
1860  {
1861  face_nbr_group[fn] = rank_group[fn].two;
1862  }
1863  }
1864 
1865  MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
1866  MPI_Request *send_requests = requests;
1867  MPI_Request *recv_requests = requests + num_face_nbrs;
1868  MPI_Status *statuses = new MPI_Status[num_face_nbrs];
1869 
1870  int *nbr_data = new int[6*num_face_nbrs];
1871  int *nbr_send_data = nbr_data;
1872  int *nbr_recv_data = nbr_data + 3*num_face_nbrs;
1873 
1874  Array<int> el_marker(GetNE());
1875  Array<int> vertex_marker(GetNV());
1876  el_marker = -1;
1877  vertex_marker = -1;
1878 
1879  Table send_face_nbr_elemdata, send_face_nbr_facedata;
1880 
1881  send_face_nbr_elements.MakeI(num_face_nbrs);
1882  send_face_nbr_vertices.MakeI(num_face_nbrs);
1883  send_face_nbr_elemdata.MakeI(num_face_nbrs);
1884  send_face_nbr_facedata.MakeI(num_face_nbrs);
1885  for (int fn = 0; fn < num_face_nbrs; fn++)
1886  {
1887  int nbr_group = face_nbr_group[fn];
1888  int num_sfaces = gr_sface->RowSize(nbr_group-1);
1889  int *sface = gr_sface->GetRow(nbr_group-1);
1890  for (int i = 0; i < num_sfaces; i++)
1891  {
1892  int lface = s2l_face[sface[i]];
1893  int el = faces_info[lface].Elem1No;
1894  if (el_marker[el] != fn)
1895  {
1896  el_marker[el] = fn;
1898 
1899  const int nv = elements[el]->GetNVertices();
1900  const int *v = elements[el]->GetVertices();
1901  for (int j = 0; j < nv; j++)
1902  if (vertex_marker[v[j]] != fn)
1903  {
1904  vertex_marker[v[j]] = fn;
1906  }
1907 
1908  send_face_nbr_elemdata.AddColumnsInRow(fn, nv + 2);
1909  }
1910  }
1911  send_face_nbr_facedata.AddColumnsInRow(fn, 2*num_sfaces);
1912 
1913  nbr_send_data[3*fn ] = send_face_nbr_elements.GetI()[fn];
1914  nbr_send_data[3*fn+1] = send_face_nbr_vertices.GetI()[fn];
1915  nbr_send_data[3*fn+2] = send_face_nbr_elemdata.GetI()[fn];
1916 
1917  int nbr_rank = GetFaceNbrRank(fn);
1918  int tag = 0;
1919 
1920  MPI_Isend(&nbr_send_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
1921  &send_requests[fn]);
1922  MPI_Irecv(&nbr_recv_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
1923  &recv_requests[fn]);
1924  }
1927  send_face_nbr_elemdata.MakeJ();
1928  send_face_nbr_facedata.MakeJ();
1929  el_marker = -1;
1930  vertex_marker = -1;
1931  const int nst = shared_trias.Size();
1932  for (int fn = 0; fn < num_face_nbrs; fn++)
1933  {
1934  int nbr_group = face_nbr_group[fn];
1935  int num_sfaces = gr_sface->RowSize(nbr_group-1);
1936  int *sface = gr_sface->GetRow(nbr_group-1);
1937  for (int i = 0; i < num_sfaces; i++)
1938  {
1939  const int sf = sface[i];
1940  int lface = s2l_face[sf];
1941  int el = faces_info[lface].Elem1No;
1942  if (el_marker[el] != fn)
1943  {
1944  el_marker[el] = fn;
1946 
1947  const int nv = elements[el]->GetNVertices();
1948  const int *v = elements[el]->GetVertices();
1949  for (int j = 0; j < nv; j++)
1950  if (vertex_marker[v[j]] != fn)
1951  {
1952  vertex_marker[v[j]] = fn;
1954  }
1955 
1956  send_face_nbr_elemdata.AddConnection(fn, GetAttribute(el));
1957  send_face_nbr_elemdata.AddConnection(
1958  fn, GetElementBaseGeometry(el));
1959  send_face_nbr_elemdata.AddConnections(fn, v, nv);
1960  }
1961  send_face_nbr_facedata.AddConnection(fn, el);
1962  int info = faces_info[lface].Elem1Inf;
1963  // change the orientation in info to be relative to the shared face
1964  // in 1D and 2D keep the orientation equal to 0
1965  if (Dim == 3)
1966  {
1967  const int *lf_v = faces[lface]->GetVertices();
1968  if (sf < nst) // triangle shared face
1969  {
1970  info += GetTriOrientation(shared_trias[sf].v, lf_v);
1971  }
1972  else // quad shared face
1973  {
1974  info += GetQuadOrientation(shared_quads[sf-nst].v, lf_v);
1975  }
1976  }
1977  send_face_nbr_facedata.AddConnection(fn, info);
1978  }
1979  }
1982  send_face_nbr_elemdata.ShiftUpI();
1983  send_face_nbr_facedata.ShiftUpI();
1984 
1985  // convert the vertex indices in send_face_nbr_elemdata
1986  // convert the element indices in send_face_nbr_facedata
1987  for (int fn = 0; fn < num_face_nbrs; fn++)
1988  {
1989  int num_elems = send_face_nbr_elements.RowSize(fn);
1990  int *elems = send_face_nbr_elements.GetRow(fn);
1991  int num_verts = send_face_nbr_vertices.RowSize(fn);
1992  int *verts = send_face_nbr_vertices.GetRow(fn);
1993  int *elemdata = send_face_nbr_elemdata.GetRow(fn);
1994  int num_sfaces = send_face_nbr_facedata.RowSize(fn)/2;
1995  int *facedata = send_face_nbr_facedata.GetRow(fn);
1996 
1997  for (int i = 0; i < num_verts; i++)
1998  {
1999  vertex_marker[verts[i]] = i;
2000  }
2001 
2002  for (int el = 0; el < num_elems; el++)
2003  {
2004  const int nv = elements[elems[el]]->GetNVertices();
2005  elemdata += 2; // skip the attribute and the geometry type
2006  for (int j = 0; j < nv; j++)
2007  {
2008  elemdata[j] = vertex_marker[elemdata[j]];
2009  }
2010  elemdata += nv;
2011 
2012  el_marker[elems[el]] = el;
2013  }
2014 
2015  for (int i = 0; i < num_sfaces; i++)
2016  {
2017  facedata[2*i] = el_marker[facedata[2*i]];
2018  }
2019  }
2020 
2021  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2022 
2023  Array<int> recv_face_nbr_facedata;
2024  Table recv_face_nbr_elemdata;
2025 
2026  // fill-in face_nbr_elements_offset, face_nbr_vertices_offset
2027  face_nbr_elements_offset.SetSize(num_face_nbrs + 1);
2028  face_nbr_vertices_offset.SetSize(num_face_nbrs + 1);
2029  recv_face_nbr_elemdata.MakeI(num_face_nbrs);
2030  face_nbr_elements_offset[0] = 0;
2031  face_nbr_vertices_offset[0] = 0;
2032  for (int fn = 0; fn < num_face_nbrs; fn++)
2033  {
2034  face_nbr_elements_offset[fn+1] =
2035  face_nbr_elements_offset[fn] + nbr_recv_data[3*fn];
2036  face_nbr_vertices_offset[fn+1] =
2037  face_nbr_vertices_offset[fn] + nbr_recv_data[3*fn+1];
2038  recv_face_nbr_elemdata.AddColumnsInRow(fn, nbr_recv_data[3*fn+2]);
2039  }
2040  recv_face_nbr_elemdata.MakeJ();
2041 
2042  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2043 
2044  // send and receive the element data
2045  for (int fn = 0; fn < num_face_nbrs; fn++)
2046  {
2047  int nbr_rank = GetFaceNbrRank(fn);
2048  int tag = 0;
2049 
2050  MPI_Isend(send_face_nbr_elemdata.GetRow(fn),
2051  send_face_nbr_elemdata.RowSize(fn),
2052  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2053 
2054  MPI_Irecv(recv_face_nbr_elemdata.GetRow(fn),
2055  recv_face_nbr_elemdata.RowSize(fn),
2056  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2057  }
2058 
2059  // convert the element data into face_nbr_elements
2060  face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
2061  while (true)
2062  {
2063  int fn;
2064  MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2065 
2066  if (fn == MPI_UNDEFINED)
2067  {
2068  break;
2069  }
2070 
2071  int vert_off = face_nbr_vertices_offset[fn];
2072  int elem_off = face_nbr_elements_offset[fn];
2073  int num_elems = face_nbr_elements_offset[fn+1] - elem_off;
2074  int *recv_elemdata = recv_face_nbr_elemdata.GetRow(fn);
2075 
2076  for (int i = 0; i < num_elems; i++)
2077  {
2078  Element *el = NewElement(recv_elemdata[1]);
2079  el->SetAttribute(recv_elemdata[0]);
2080  recv_elemdata += 2;
2081  int nv = el->GetNVertices();
2082  for (int j = 0; j < nv; j++)
2083  {
2084  recv_elemdata[j] += vert_off;
2085  }
2086  el->SetVertices(recv_elemdata);
2087  recv_elemdata += nv;
2088  face_nbr_elements[elem_off++] = el;
2089  }
2090  }
2091 
2092  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2093 
2094  // send and receive the face data
2095  recv_face_nbr_facedata.SetSize(
2096  send_face_nbr_facedata.Size_of_connections());
2097  for (int fn = 0; fn < num_face_nbrs; fn++)
2098  {
2099  int nbr_rank = GetFaceNbrRank(fn);
2100  int tag = 0;
2101 
2102  MPI_Isend(send_face_nbr_facedata.GetRow(fn),
2103  send_face_nbr_facedata.RowSize(fn),
2104  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2105 
2106  // the size of the send and receive face data is the same
2107  MPI_Irecv(&recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]],
2108  send_face_nbr_facedata.RowSize(fn),
2109  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2110  }
2111 
2112  // transfer the received face data into faces_info
2113  while (true)
2114  {
2115  int fn;
2116  MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2117 
2118  if (fn == MPI_UNDEFINED)
2119  {
2120  break;
2121  }
2122 
2123  int elem_off = face_nbr_elements_offset[fn];
2124  int nbr_group = face_nbr_group[fn];
2125  int num_sfaces = gr_sface->RowSize(nbr_group-1);
2126  int *sface = gr_sface->GetRow(nbr_group-1);
2127  int *facedata =
2128  &recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]];
2129 
2130  for (int i = 0; i < num_sfaces; i++)
2131  {
2132  const int sf = sface[i];
2133  int lface = s2l_face[sf];
2134  FaceInfo &face_info = faces_info[lface];
2135  face_info.Elem2No = -1 - (facedata[2*i] + elem_off);
2136  int info = facedata[2*i+1];
2137  // change the orientation in info to be relative to the local face
2138  if (Dim < 3)
2139  {
2140  info++; // orientation 0 --> orientation 1
2141  }
2142  else
2143  {
2144  int nbr_ori = info%64, nbr_v[4];
2145  const int *lf_v = faces[lface]->GetVertices();
2146 
2147  if (sf < nst) // triangle shared face
2148  {
2149  // apply the nbr_ori to sf_v to get nbr_v
2150  const int *perm = tri_t::Orient[nbr_ori];
2151  const int *sf_v = shared_trias[sf].v;
2152  for (int j = 0; j < 3; j++)
2153  {
2154  nbr_v[perm[j]] = sf_v[j];
2155  }
2156  // get the orientation of nbr_v w.r.t. the local face
2157  nbr_ori = GetTriOrientation(lf_v, nbr_v);
2158  }
2159  else // quad shared face
2160  {
2161  // apply the nbr_ori to sf_v to get nbr_v
2162  const int *perm = quad_t::Orient[nbr_ori];
2163  const int *sf_v = shared_quads[sf-nst].v;
2164  for (int j = 0; j < 4; j++)
2165  {
2166  nbr_v[perm[j]] = sf_v[j];
2167  }
2168  // get the orientation of nbr_v w.r.t. the local face
2169  nbr_ori = GetQuadOrientation(lf_v, nbr_v);
2170  }
2171 
2172  info = 64*(info/64) + nbr_ori;
2173  }
2174  face_info.Elem2Inf = info;
2175  }
2176  }
2177 
2178  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2179 
2180  // allocate the face_nbr_vertices
2181  face_nbr_vertices.SetSize(face_nbr_vertices_offset[num_face_nbrs]);
2182 
2183  delete [] nbr_data;
2184 
2185  delete [] statuses;
2186  delete [] requests;
2187 }
2188 
2190 {
2191  if (!have_face_nbr_data)
2192  {
2193  ExchangeFaceNbrData(); // calls this method at the end
2194  }
2195  else if (Nodes == NULL)
2196  {
2197  if (Nonconforming())
2198  {
2199  // with ParNCMesh we already have the vertices
2200  return;
2201  }
2202 
2203  int num_face_nbrs = GetNFaceNeighbors();
2204 
2205  if (!num_face_nbrs) { return; }
2206 
2207  MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2208  MPI_Request *send_requests = requests;
2209  MPI_Request *recv_requests = requests + num_face_nbrs;
2210  MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2211 
2212  // allocate buffer and copy the vertices to be sent
2214  for (int i = 0; i < send_vertices.Size(); i++)
2215  {
2216  send_vertices[i] = vertices[send_face_nbr_vertices.GetJ()[i]];
2217  }
2218 
2219  // send and receive the vertices
2220  for (int fn = 0; fn < num_face_nbrs; fn++)
2221  {
2222  int nbr_rank = GetFaceNbrRank(fn);
2223  int tag = 0;
2224 
2225  MPI_Isend(send_vertices[send_face_nbr_vertices.GetI()[fn]](),
2227  MPI_DOUBLE, nbr_rank, tag, MyComm, &send_requests[fn]);
2228 
2230  3*(face_nbr_vertices_offset[fn+1] -
2232  MPI_DOUBLE, nbr_rank, tag, MyComm, &recv_requests[fn]);
2233  }
2234 
2235  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2236  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2237 
2238  delete [] statuses;
2239  delete [] requests;
2240  }
2241  else
2242  {
2243  ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
2244  MFEM_VERIFY(pNodes != NULL, "Nodes are not ParGridFunction!");
2245  pNodes->ExchangeFaceNbrData();
2246  }
2247 }
2248 
2249 int ParMesh::GetFaceNbrRank(int fn) const
2250 {
2251  if (Conforming())
2252  {
2253  int nbr_group = face_nbr_group[fn];
2254  const int *nbs = gtopo.GetGroup(nbr_group);
2255  int nbr_lproc = (nbs[0]) ? nbs[0] : nbs[1];
2256  int nbr_rank = gtopo.GetNeighborRank(nbr_lproc);
2257  return nbr_rank;
2258  }
2259  else
2260  {
2261  // NC: simplified handling of face neighbor ranks
2262  return face_nbr_group[fn];
2263  }
2264 }
2265 
2267 {
2268  const Array<int> *s2l_face;
2269  if (Dim == 1)
2270  {
2271  s2l_face = &svert_lvert;
2272  }
2273  else if (Dim == 2)
2274  {
2275  s2l_face = &sedge_ledge;
2276  }
2277  else
2278  {
2279  s2l_face = &sface_lface;
2280  }
2281 
2282  Table *face_elem = new Table;
2283 
2284  face_elem->MakeI(faces_info.Size());
2285 
2286  for (int i = 0; i < faces_info.Size(); i++)
2287  {
2288  if (faces_info[i].Elem2No >= 0)
2289  {
2290  face_elem->AddColumnsInRow(i, 2);
2291  }
2292  else
2293  {
2294  face_elem->AddAColumnInRow(i);
2295  }
2296  }
2297  for (int i = 0; i < s2l_face->Size(); i++)
2298  {
2299  face_elem->AddAColumnInRow((*s2l_face)[i]);
2300  }
2301 
2302  face_elem->MakeJ();
2303 
2304  for (int i = 0; i < faces_info.Size(); i++)
2305  {
2306  face_elem->AddConnection(i, faces_info[i].Elem1No);
2307  if (faces_info[i].Elem2No >= 0)
2308  {
2309  face_elem->AddConnection(i, faces_info[i].Elem2No);
2310  }
2311  }
2312  for (int i = 0; i < s2l_face->Size(); i++)
2313  {
2314  int lface = (*s2l_face)[i];
2315  int nbr_elem_idx = -1 - faces_info[lface].Elem2No;
2316  face_elem->AddConnection(lface, NumOfElements + nbr_elem_idx);
2317  }
2318 
2319  face_elem->ShiftUpI();
2320 
2321  return face_elem;
2322 }
2323 
2325  FaceElementTransformations* FETr, Element::Type face_type,
2326  Geometry::Type face_geom)
2327 {
2328  // calculate composition of FETr->Loc1 and FETr->Elem1
2330  if (Nodes == NULL)
2331  {
2332  FETr->Elem1->Transform(FETr->Loc1.Transf.GetPointMat(), face_pm);
2334  }
2335  else
2336  {
2337  const FiniteElement* face_el =
2338  Nodes->FESpace()->GetTraceElement(FETr->Elem1No, face_geom);
2339 
2340 #if 0 // TODO: handle the case of non-interpolatory Nodes
2341  DenseMatrix I;
2342  face_el->Project(Transformation.GetFE(), FETr->Loc1.Transf, I);
2343  MultABt(Transformation.GetPointMat(), I, pm_face);
2344 #else
2345  IntegrationRule eir(face_el->GetDof());
2346  FETr->Loc1.Transform(face_el->GetNodes(), eir);
2347  Nodes->GetVectorValues(*FETr->Elem1, eir, face_pm);
2348 #endif
2349  FaceTransformation.SetFE(face_el);
2350  }
2352  return &FaceTransformation;
2353 }
2354 
2356 GetSharedFaceTransformations(int sf, bool fill2)
2357 {
2358  int FaceNo = GetSharedFace(sf);
2359 
2360  FaceInfo &face_info = faces_info[FaceNo];
2361 
2362  bool is_slave = Nonconforming() && IsSlaveFace(face_info);
2363  bool is_ghost = Nonconforming() && FaceNo >= GetNumFaces();
2364 
2365  NCFaceInfo* nc_info = NULL;
2366  if (is_slave) { nc_info = &nc_faces_info[face_info.NCFace]; }
2367 
2368  int local_face = is_ghost ? nc_info->MasterFace : FaceNo;
2369  Element::Type face_type = GetFaceElementType(local_face);
2370  Geometry::Type face_geom = GetFaceGeometryType(local_face);
2371 
2372  // setup the transformation for the first element
2373  FaceElemTr.Elem1No = face_info.Elem1No;
2376 
2377  // setup the transformation for the second (neighbor) element
2378  if (fill2)
2379  {
2380  FaceElemTr.Elem2No = -1 - face_info.Elem2No;
2383  }
2384  else
2385  {
2386  FaceElemTr.Elem2No = -1;
2387  }
2388 
2389  // setup the face transformation if the face is not a ghost
2390  FaceElemTr.FaceGeom = face_geom;
2391  if (!is_ghost)
2392  {
2394  // NOTE: The above call overwrites FaceElemTr.Loc1
2395  }
2396 
2397  // setup Loc1 & Loc2
2398  int elem_type = GetElementType(face_info.Elem1No);
2399  GetLocalFaceTransformation(face_type, elem_type, FaceElemTr.Loc1.Transf,
2400  face_info.Elem1Inf);
2401 
2402  if (fill2)
2403  {
2404  elem_type = face_nbr_elements[FaceElemTr.Elem2No]->GetType();
2405  GetLocalFaceTransformation(face_type, elem_type, FaceElemTr.Loc2.Transf,
2406  face_info.Elem2Inf);
2407  }
2408 
2409  // adjust Loc1 or Loc2 of the master face if this is a slave face
2410  if (is_slave)
2411  {
2412  // is a ghost slave? -> master not a ghost -> choose Elem1 local transf
2413  // not a ghost slave? -> master is a ghost -> choose Elem2 local transf
2415  is_ghost ? FaceElemTr.Loc1.Transf : FaceElemTr.Loc2.Transf;
2416 
2417  if (is_ghost || fill2)
2418  {
2419  ApplyLocalSlaveTransformation(loctr, face_info);
2420  }
2421 
2422  if (face_type == Element::SEGMENT && fill2)
2423  {
2424  // fix slave orientation in 2D: flip Loc2 to match Loc1 and Face
2426  std::swap(pm(0,0), pm(0,1));
2427  std::swap(pm(1,0), pm(1,1));
2428  }
2429  }
2430 
2431  // for ghost faces we need a special version of GetFaceTransformation
2432  if (is_ghost)
2433  {
2434  FaceElemTr.Face =
2435  GetGhostFaceTransformation(&FaceElemTr, face_type, face_geom);
2436  }
2437 
2438  return &FaceElemTr;
2439 }
2440 
2442 {
2443  if (Conforming())
2444  {
2445  switch (Dim)
2446  {
2447  case 1: return svert_lvert.Size();
2448  case 2: return sedge_ledge.Size();
2449  default: return sface_lface.Size();
2450  }
2451  }
2452  else
2453  {
2454  MFEM_ASSERT(Dim > 1, "");
2455  const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
2456  return shared.conforming.size() + shared.slaves.size();
2457  }
2458 }
2459 
2460 int ParMesh::GetSharedFace(int sface) const
2461 {
2462  if (Conforming())
2463  {
2464  switch (Dim)
2465  {
2466  case 1: return svert_lvert[sface];
2467  case 2: return sedge_ledge[sface];
2468  default: return sface_lface[sface];
2469  }
2470  }
2471  else
2472  {
2473  MFEM_ASSERT(Dim > 1, "");
2474  const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
2475  int csize = (int) shared.conforming.size();
2476  return sface < csize
2477  ? shared.conforming[sface].index
2478  : shared.slaves[sface - csize].index;
2479  }
2480 }
2481 
2482 // shift cyclically 3 integers a, b, c, so that the smallest of
2483 // order[a], order[b], order[c] is first
2484 static inline
2485 void Rotate3Indirect(int &a, int &b, int &c,
2486  const Array<std::int64_t> &order)
2487 {
2488  if (order[a] < order[b])
2489  {
2490  if (order[a] > order[c])
2491  {
2492  ShiftRight(a, b, c);
2493  }
2494  }
2495  else
2496  {
2497  if (order[b] < order[c])
2498  {
2499  ShiftRight(c, b, a);
2500  }
2501  else
2502  {
2503  ShiftRight(a, b, c);
2504  }
2505  }
2506 }
2507 
2509 {
2510  if (Dim != 3 || !(meshgen & 1))
2511  {
2512  return;
2513  }
2514 
2515  ResetLazyData();
2516 
2517  DSTable *old_v_to_v = NULL;
2518  Table *old_elem_vert = NULL;
2519 
2520  if (Nodes)
2521  {
2522  PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
2523  }
2524 
2525  // create a GroupCommunicator over shared vertices
2526  GroupCommunicator svert_comm(gtopo);
2527  {
2528  // initialize svert_comm
2529  Table &gr_svert = svert_comm.GroupLDofTable();
2530  // gr_svert differs from group_svert - the latter does not store gr. 0
2531  gr_svert.SetDims(GetNGroups(), svert_lvert.Size());
2532  gr_svert.GetI()[0] = 0;
2533  for (int gr = 1; gr <= GetNGroups(); gr++)
2534  {
2535  gr_svert.GetI()[gr] = group_svert.GetI()[gr-1];
2536  }
2537  for (int k = 0; k < svert_lvert.Size(); k++)
2538  {
2539  gr_svert.GetJ()[k] = group_svert.GetJ()[k];
2540  }
2541  svert_comm.Finalize();
2542  }
2543 
2544  // communicate the local index of each shared vertex from the group master to
2545  // other ranks in the group
2546  Array<int> svert_master_rank(svert_lvert.Size());
2547  Array<int> svert_master_index(svert_lvert);
2548  {
2549  for (int i = 0; i < group_svert.Size(); i++)
2550  {
2551  int rank = gtopo.GetGroupMasterRank(i+1);
2552  for (int j = 0; j < group_svert.RowSize(i); j++)
2553  {
2554  svert_master_rank[group_svert.GetRow(i)[j]] = rank;
2555  }
2556  }
2557  svert_comm.Bcast(svert_master_index);
2558  }
2559 
2560  // the pairs (master rank, master local index) define a globally consistent
2561  // vertex ordering
2562  Array<std::int64_t> glob_vert_order(vertices.Size());
2563  {
2564  Array<int> lvert_svert(vertices.Size());
2565  lvert_svert = -1;
2566  for (int i = 0; i < svert_lvert.Size(); i++)
2567  {
2568  lvert_svert[svert_lvert[i]] = i;
2569  }
2570 
2571  for (int i = 0; i < vertices.Size(); i++)
2572  {
2573  int s = lvert_svert[i];
2574  if (s >= 0)
2575  {
2576  glob_vert_order[i] =
2577  (std::int64_t(svert_master_rank[s]) << 32) + svert_master_index[s];
2578  }
2579  else
2580  {
2581  glob_vert_order[i] = (std::int64_t(MyRank) << 32) + i;
2582  }
2583  }
2584  }
2585 
2586  // rotate tetrahedra so that vertex zero is the lowest (global) index vertex,
2587  // vertex 1 is the second lowest (global) index and vertices 2 and 3 preserve
2588  // positive orientation of the element
2589  for (int i = 0; i < NumOfElements; i++)
2590  {
2592  {
2593  int *v = elements[i]->GetVertices();
2594 
2595  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
2596 
2597  if (glob_vert_order[v[0]] < glob_vert_order[v[3]])
2598  {
2599  Rotate3Indirect(v[1], v[2], v[3], glob_vert_order);
2600  }
2601  else
2602  {
2603  ShiftRight(v[0], v[1], v[3]);
2604  }
2605  }
2606  }
2607 
2608  // rotate also boundary triangles
2609  for (int i = 0; i < NumOfBdrElements; i++)
2610  {
2612  {
2613  int *v = boundary[i]->GetVertices();
2614 
2615  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
2616  }
2617  }
2618 
2619  const bool check_consistency = true;
2620  if (check_consistency)
2621  {
2622  // create a GroupCommunicator on the shared triangles
2623  GroupCommunicator stria_comm(gtopo);
2624  {
2625  // initialize stria_comm
2626  Table &gr_stria = stria_comm.GroupLDofTable();
2627  // gr_stria differs from group_stria - the latter does not store gr. 0
2628  gr_stria.SetDims(GetNGroups(), shared_trias.Size());
2629  gr_stria.GetI()[0] = 0;
2630  for (int gr = 1; gr <= GetNGroups(); gr++)
2631  {
2632  gr_stria.GetI()[gr] = group_stria.GetI()[gr-1];
2633  }
2634  for (int k = 0; k < shared_trias.Size(); k++)
2635  {
2636  gr_stria.GetJ()[k] = group_stria.GetJ()[k];
2637  }
2638  stria_comm.Finalize();
2639  }
2640  Array<int> stria_flag(shared_trias.Size());
2641  for (int i = 0; i < stria_flag.Size(); i++)
2642  {
2643  const int *v = shared_trias[i].v;
2644  if (glob_vert_order[v[0]] < glob_vert_order[v[1]])
2645  {
2646  stria_flag[i] = (glob_vert_order[v[0]] < glob_vert_order[v[2]]) ? 0 : 2;
2647  }
2648  else // v[1] < v[0]
2649  {
2650  stria_flag[i] = (glob_vert_order[v[1]] < glob_vert_order[v[2]]) ? 1 : 2;
2651  }
2652  }
2653 
2654  Array<int> stria_master_flag(stria_flag);
2655  stria_comm.Bcast(stria_master_flag);
2656  for (int i = 0; i < stria_flag.Size(); i++)
2657  {
2658  const int *v = shared_trias[i].v;
2659  MFEM_VERIFY(stria_flag[i] == stria_master_flag[i],
2660  "inconsistent vertex ordering found, shared triangle "
2661  << i << ": ("
2662  << v[0] << ", " << v[1] << ", " << v[2] << "), "
2663  << "local flag: " << stria_flag[i]
2664  << ", master flag: " << stria_master_flag[i]);
2665  }
2666  }
2667 
2668  // rotate shared triangle faces
2669  for (int i = 0; i < shared_trias.Size(); i++)
2670  {
2671  int *v = shared_trias[i].v;
2672 
2673  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
2674  }
2675 
2676  // finalize
2677  if (!Nodes)
2678  {
2680  GenerateFaces();
2681  if (el_to_edge)
2682  {
2684  }
2685  }
2686  else
2687  {
2688  DoNodeReorder(old_v_to_v, old_elem_vert);
2689  delete old_elem_vert;
2690  delete old_v_to_v;
2691  }
2692 
2693  // the local edge and face numbering is changed therefore we need to
2694  // update sedge_ledge and sface_lface.
2695  FinalizeParTopo();
2696 }
2697 
2698 void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
2699 {
2700  if (pncmesh)
2701  {
2702  MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
2703  }
2704 
2706 
2708 
2709  if (Dim == 3)
2710  {
2711  int uniform_refinement = 0;
2712  if (type < 0)
2713  {
2714  type = -type;
2715  uniform_refinement = 1;
2716  }
2717 
2718  // 1. Hash table of vertex to vertex connections corresponding to refined
2719  // edges.
2720  HashTable<Hashed2> v_to_v;
2721 
2722  // 2. Do the red refinement.
2723  switch (type)
2724  {
2725  case 1:
2726  for (int i = 0; i < marked_el.Size(); i++)
2727  {
2728  Bisection(marked_el[i], v_to_v);
2729  }
2730  break;
2731  case 2:
2732  for (int i = 0; i < marked_el.Size(); i++)
2733  {
2734  Bisection(marked_el[i], v_to_v);
2735 
2736  Bisection(NumOfElements - 1, v_to_v);
2737  Bisection(marked_el[i], v_to_v);
2738  }
2739  break;
2740  case 3:
2741  for (int i = 0; i < marked_el.Size(); i++)
2742  {
2743  Bisection(marked_el[i], v_to_v);
2744 
2745  int j = NumOfElements - 1;
2746  Bisection(j, v_to_v);
2747  Bisection(NumOfElements - 1, v_to_v);
2748  Bisection(j, v_to_v);
2749 
2750  Bisection(marked_el[i], v_to_v);
2751  Bisection(NumOfElements-1, v_to_v);
2752  Bisection(marked_el[i], v_to_v);
2753  }
2754  break;
2755  }
2756 
2757  // 3. Do the green refinement (to get conforming mesh).
2758  int need_refinement;
2759  int max_faces_in_group = 0;
2760  // face_splittings identify how the shared faces have been split
2761  Array<unsigned> *face_splittings = new Array<unsigned>[GetNGroups()-1];
2762  for (int i = 0; i < GetNGroups()-1; i++)
2763  {
2764  const int faces_in_group = GroupNTriangles(i+1);
2765  face_splittings[i].Reserve(faces_in_group);
2766  if (faces_in_group > max_faces_in_group)
2767  {
2768  max_faces_in_group = faces_in_group;
2769  }
2770  }
2771  int neighbor;
2772  Array<unsigned> iBuf(max_faces_in_group);
2773 
2774  MPI_Request *requests = new MPI_Request[GetNGroups()-1];
2775  MPI_Status status;
2776 
2777 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
2778  int ref_loops_all = 0, ref_loops_par = 0;
2779 #endif
2780  do
2781  {
2782  need_refinement = 0;
2783  for (int i = 0; i < NumOfElements; i++)
2784  {
2785  if (elements[i]->NeedRefinement(v_to_v))
2786  {
2787  need_refinement = 1;
2788  Bisection(i, v_to_v);
2789  }
2790  }
2791 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
2792  ref_loops_all++;
2793 #endif
2794 
2795  if (uniform_refinement)
2796  {
2797  continue;
2798  }
2799 
2800  // if the mesh is locally conforming start making it globally
2801  // conforming
2802  if (need_refinement == 0)
2803  {
2804 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
2805  ref_loops_par++;
2806 #endif
2807  // MPI_Barrier(MyComm);
2808  const int tag = 293;
2809 
2810  // (a) send the type of interface splitting
2811  int req_count = 0;
2812  for (int i = 0; i < GetNGroups()-1; i++)
2813  {
2814  const int *group_faces = group_stria.GetRow(i);
2815  const int faces_in_group = group_stria.RowSize(i);
2816  // it is enough to communicate through the faces
2817  if (faces_in_group == 0) { continue; }
2818 
2819  face_splittings[i].SetSize(0);
2820  for (int j = 0; j < faces_in_group; j++)
2821  {
2822  GetFaceSplittings(shared_trias[group_faces[j]].v, v_to_v,
2823  face_splittings[i]);
2824  }
2825  const int *nbs = gtopo.GetGroup(i+1);
2826  neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
2827  MPI_Isend(face_splittings[i], face_splittings[i].Size(),
2828  MPI_UNSIGNED, neighbor, tag, MyComm,
2829  &requests[req_count++]);
2830  }
2831 
2832  // (b) receive the type of interface splitting
2833  for (int i = 0; i < GetNGroups()-1; i++)
2834  {
2835  const int *group_faces = group_stria.GetRow(i);
2836  const int faces_in_group = group_stria.RowSize(i);
2837  if (faces_in_group == 0) { continue; }
2838 
2839  const int *nbs = gtopo.GetGroup(i+1);
2840  neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
2841  MPI_Probe(neighbor, tag, MyComm, &status);
2842  int count;
2843  MPI_Get_count(&status, MPI_UNSIGNED, &count);
2844  iBuf.SetSize(count);
2845  MPI_Recv(iBuf, count, MPI_UNSIGNED, neighbor, tag, MyComm,
2846  MPI_STATUS_IGNORE);
2847 
2848  for (int j = 0, pos = 0; j < faces_in_group; j++)
2849  {
2850  const int *v = shared_trias[group_faces[j]].v;
2851  need_refinement |= DecodeFaceSplittings(v_to_v, v, iBuf, pos);
2852  }
2853  }
2854 
2855  int nr = need_refinement;
2856  MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
2857 
2858  MPI_Waitall(req_count, requests, MPI_STATUSES_IGNORE);
2859  }
2860  }
2861  while (need_refinement == 1);
2862 
2863 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
2864  {
2865  int i = ref_loops_all;
2866  MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
2867  if (MyRank == 0)
2868  {
2869  mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
2870  << ref_loops_all << ", ref_loops_par = " << ref_loops_par
2871  << '\n' << endl;
2872  }
2873  }
2874 #endif
2875 
2876  delete [] requests;
2877  iBuf.DeleteAll();
2878  delete [] face_splittings;
2879 
2880  // 4. Update the boundary elements.
2881  do
2882  {
2883  need_refinement = 0;
2884  for (int i = 0; i < NumOfBdrElements; i++)
2885  {
2886  if (boundary[i]->NeedRefinement(v_to_v))
2887  {
2888  need_refinement = 1;
2889  BdrBisection(i, v_to_v);
2890  }
2891  }
2892  }
2893  while (need_refinement == 1);
2894 
2895  if (NumOfBdrElements != boundary.Size())
2896  {
2897  mfem_error("ParMesh::LocalRefinement :"
2898  " (NumOfBdrElements != boundary.Size())");
2899  }
2900 
2901  ResetLazyData();
2902 
2903  const int old_nv = NumOfVertices;
2904  NumOfVertices = vertices.Size();
2905 
2906  RefineGroups(old_nv, v_to_v);
2907 
2908  // 5. Update the groups after refinement.
2909  if (el_to_face != NULL)
2910  {
2912  GenerateFaces();
2913  }
2914 
2915  // 6. Update element-to-edge relations.
2916  if (el_to_edge != NULL)
2917  {
2919  }
2920  } // 'if (Dim == 3)'
2921 
2922 
2923  if (Dim == 2)
2924  {
2925  int uniform_refinement = 0;
2926  if (type < 0)
2927  {
2928  // type = -type; // not used
2929  uniform_refinement = 1;
2930  }
2931 
2932  // 1. Get table of vertex to vertex connections.
2933  DSTable v_to_v(NumOfVertices);
2934  GetVertexToVertexTable(v_to_v);
2935 
2936  // 2. Get edge to element connections in arrays edge1 and edge2
2937  int nedges = v_to_v.NumberOfEntries();
2938  int *edge1 = new int[nedges];
2939  int *edge2 = new int[nedges];
2940  int *middle = new int[nedges];
2941 
2942  for (int i = 0; i < nedges; i++)
2943  {
2944  edge1[i] = edge2[i] = middle[i] = -1;
2945  }
2946 
2947  for (int i = 0; i < NumOfElements; i++)
2948  {
2949  int *v = elements[i]->GetVertices();
2950  for (int j = 0; j < 3; j++)
2951  {
2952  int ind = v_to_v(v[j], v[(j+1)%3]);
2953  (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
2954  }
2955  }
2956 
2957  // 3. Do the red refinement.
2958  for (int i = 0; i < marked_el.Size(); i++)
2959  {
2960  RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
2961  }
2962 
2963  // 4. Do the green refinement (to get conforming mesh).
2964  int need_refinement;
2965  int edges_in_group, max_edges_in_group = 0;
2966  // edge_splittings identify how the shared edges have been split
2967  int **edge_splittings = new int*[GetNGroups()-1];
2968  for (int i = 0; i < GetNGroups()-1; i++)
2969  {
2970  edges_in_group = GroupNEdges(i+1);
2971  edge_splittings[i] = new int[edges_in_group];
2972  if (edges_in_group > max_edges_in_group)
2973  {
2974  max_edges_in_group = edges_in_group;
2975  }
2976  }
2977  int neighbor, *iBuf = new int[max_edges_in_group];
2978 
2979  Array<int> group_edges;
2980 
2981  MPI_Request request;
2982  MPI_Status status;
2983  Vertex V;
2984  V(2) = 0.0;
2985 
2986 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
2987  int ref_loops_all = 0, ref_loops_par = 0;
2988 #endif
2989  do
2990  {
2991  need_refinement = 0;
2992  for (int i = 0; i < nedges; i++)
2993  {
2994  if (middle[i] != -1 && edge1[i] != -1)
2995  {
2996  need_refinement = 1;
2997  GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
2998  }
2999  }
3000 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3001  ref_loops_all++;
3002 #endif
3003 
3004  if (uniform_refinement)
3005  {
3006  continue;
3007  }
3008 
3009  // if the mesh is locally conforming start making it globally
3010  // conforming
3011  if (need_refinement == 0)
3012  {
3013 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3014  ref_loops_par++;
3015 #endif
3016  // MPI_Barrier(MyComm);
3017 
3018  // (a) send the type of interface splitting
3019  for (int i = 0; i < GetNGroups()-1; i++)
3020  {
3021  group_sedge.GetRow(i, group_edges);
3022  edges_in_group = group_edges.Size();
3023  // it is enough to communicate through the edges
3024  if (edges_in_group != 0)
3025  {
3026  for (int j = 0; j < edges_in_group; j++)
3027  {
3028  edge_splittings[i][j] =
3029  GetEdgeSplittings(shared_edges[group_edges[j]], v_to_v,
3030  middle);
3031  }
3032  const int *nbs = gtopo.GetGroup(i+1);
3033  if (nbs[0] == 0)
3034  {
3035  neighbor = gtopo.GetNeighborRank(nbs[1]);
3036  }
3037  else
3038  {
3039  neighbor = gtopo.GetNeighborRank(nbs[0]);
3040  }
3041  MPI_Isend(edge_splittings[i], edges_in_group, MPI_INT,
3042  neighbor, 0, MyComm, &request);
3043  }
3044  }
3045 
3046  // (b) receive the type of interface splitting
3047  for (int i = 0; i < GetNGroups()-1; i++)
3048  {
3049  group_sedge.GetRow(i, group_edges);
3050  edges_in_group = group_edges.Size();
3051  if (edges_in_group != 0)
3052  {
3053  const int *nbs = gtopo.GetGroup(i+1);
3054  if (nbs[0] == 0)
3055  {
3056  neighbor = gtopo.GetNeighborRank(nbs[1]);
3057  }
3058  else
3059  {
3060  neighbor = gtopo.GetNeighborRank(nbs[0]);
3061  }
3062  MPI_Recv(iBuf, edges_in_group, MPI_INT, neighbor,
3063  MPI_ANY_TAG, MyComm, &status);
3064 
3065  for (int j = 0; j < edges_in_group; j++)
3066  {
3067  if (iBuf[j] == 1 && edge_splittings[i][j] == 0)
3068  {
3069  int *v = shared_edges[group_edges[j]]->GetVertices();
3070  int ii = v_to_v(v[0], v[1]);
3071 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3072  if (middle[ii] != -1)
3073  {
3074  mfem_error("ParMesh::LocalRefinement (triangles) : "
3075  "Oops!");
3076  }
3077 #endif
3078  need_refinement = 1;
3079  middle[ii] = NumOfVertices++;
3080  for (int c = 0; c < 2; c++)
3081  {
3082  V(c) = 0.5 * (vertices[v[0]](c) + vertices[v[1]](c));
3083  }
3084  vertices.Append(V);
3085  }
3086  }
3087  }
3088  }
3089 
3090  int nr = need_refinement;
3091  MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3092  }
3093  }
3094  while (need_refinement == 1);
3095 
3096 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3097  {
3098  int i = ref_loops_all;
3099  MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3100  if (MyRank == 0)
3101  {
3102  mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3103  << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3104  << '\n' << endl;
3105  }
3106  }
3107 #endif
3108 
3109  for (int i = 0; i < GetNGroups()-1; i++)
3110  {
3111  delete [] edge_splittings[i];
3112  }
3113  delete [] edge_splittings;
3114 
3115  delete [] iBuf;
3116 
3117  // 5. Update the boundary elements.
3118  int v1[2], v2[2], bisect, temp;
3119  temp = NumOfBdrElements;
3120  for (int i = 0; i < temp; i++)
3121  {
3122  int *v = boundary[i]->GetVertices();
3123  bisect = v_to_v(v[0], v[1]);
3124  if (middle[bisect] != -1)
3125  {
3126  // the element was refined (needs updating)
3127  if (boundary[i]->GetType() == Element::SEGMENT)
3128  {
3129  v1[0] = v[0]; v1[1] = middle[bisect];
3130  v2[0] = middle[bisect]; v2[1] = v[1];
3131 
3132  boundary[i]->SetVertices(v1);
3133  boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
3134  }
3135  else
3136  {
3137  mfem_error("Only bisection of segment is implemented for bdr"
3138  " elem.");
3139  }
3140  }
3141  }
3142  NumOfBdrElements = boundary.Size();
3143 
3144  ResetLazyData();
3145 
3146  // 5a. Update the groups after refinement.
3147  RefineGroups(v_to_v, middle);
3148 
3149  // 6. Free the allocated memory.
3150  delete [] edge1;
3151  delete [] edge2;
3152  delete [] middle;
3153 
3154  if (el_to_edge != NULL)
3155  {
3157  GenerateFaces();
3158  }
3159  } // 'if (Dim == 2)'
3160 
3161  if (Dim == 1) // --------------------------------------------------------
3162  {
3163  int cne = NumOfElements, cnv = NumOfVertices;
3164  NumOfVertices += marked_el.Size();
3165  NumOfElements += marked_el.Size();
3166  vertices.SetSize(NumOfVertices);
3167  elements.SetSize(NumOfElements);
3169 
3170  for (int j = 0; j < marked_el.Size(); j++)
3171  {
3172  int i = marked_el[j];
3173  Segment *c_seg = (Segment *)elements[i];
3174  int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
3175  int new_v = cnv + j, new_e = cne + j;
3176  AverageVertices(vert, 2, new_v);
3177  elements[new_e] = new Segment(new_v, vert[1], attr);
3178  vert[1] = new_v;
3179 
3180  CoarseFineTr.embeddings[i] = Embedding(i, 1);
3181  CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
3182  }
3183 
3184  static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
3186  UseExternalData(seg_children, 1, 2, 3);
3187 
3188  GenerateFaces();
3189  } // end of 'if (Dim == 1)'
3190 
3192  sequence++;
3193 
3194  UpdateNodes();
3195 
3196 #ifdef MFEM_DEBUG
3197  CheckElementOrientation(false);
3199 #endif
3200 }
3201 
3203  int nc_limit)
3204 {
3205  if (NURBSext)
3206  {
3207  MFEM_ABORT("ParMesh::NonconformingRefinement: NURBS meshes are not "
3208  "supported. Project the NURBS to Nodes first.");
3209  }
3210 
3211  if (!pncmesh)
3212  {
3213  MFEM_ABORT("Can't convert conforming ParMesh to nonconforming ParMesh "
3214  "(you need to initialize the ParMesh from a nonconforming "
3215  "serial Mesh)");
3216  }
3217 
3219 
3220  // NOTE: no check of !refinements.Size(), in parallel we would have to reduce
3221 
3222  // do the refinements
3224  pncmesh->Refine(refinements);
3225 
3226  if (nc_limit > 0)
3227  {
3228  pncmesh->LimitNCLevel(nc_limit);
3229  }
3230 
3231  // create a second mesh containing the finest elements from 'pncmesh'
3232  ParMesh* pmesh2 = new ParMesh(*pncmesh);
3233  pncmesh->OnMeshUpdated(pmesh2);
3234 
3235  attributes.Copy(pmesh2->attributes);
3237 
3238  // now swap the meshes, the second mesh will become the old coarse mesh
3239  // and this mesh will be the new fine mesh
3240  Swap(*pmesh2, false);
3241 
3242  delete pmesh2; // NOTE: old face neighbors destroyed here
3243 
3245 
3247 
3249  sequence++;
3250 
3251  UpdateNodes();
3252 }
3253 
3255  double threshold, int nc_limit, int op)
3256 {
3257  MFEM_VERIFY(pncmesh, "Only supported for non-conforming meshes.");
3258  MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
3259  "Project the NURBS to Nodes first.");
3260 
3261  const Table &dt = pncmesh->GetDerefinementTable();
3262 
3263  pncmesh->SynchronizeDerefinementData(elem_error, dt);
3264 
3265  Array<int> level_ok;
3266  if (nc_limit > 0)
3267  {
3268  pncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
3269  }
3270 
3271  Array<int> derefs;
3272  for (int i = 0; i < dt.Size(); i++)
3273  {
3274  if (nc_limit > 0 && !level_ok[i]) { continue; }
3275 
3276  double error =
3277  AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
3278 
3279  if (error < threshold) { derefs.Append(i); }
3280  }
3281 
3282  long glob_size = ReduceInt(derefs.Size());
3283  if (!glob_size) { return false; }
3284 
3285  // Destroy face-neighbor data only when actually de-refining.
3287 
3288  pncmesh->Derefine(derefs);
3289 
3290  ParMesh* mesh2 = new ParMesh(*pncmesh);
3291  pncmesh->OnMeshUpdated(mesh2);
3292 
3293  attributes.Copy(mesh2->attributes);
3295 
3296  Swap(*mesh2, false);
3297  delete mesh2;
3298 
3300 
3302 
3304  sequence++;
3305 
3306  UpdateNodes();
3307 
3308  return true;
3309 }
3310 
3311 
3313 {
3314  RebalanceImpl(NULL); // default SFC-based partition
3315 }
3316 
3317 void ParMesh::Rebalance(const Array<int> &partition)
3318 {
3319  RebalanceImpl(&partition);
3320 }
3321 
3322 void ParMesh::RebalanceImpl(const Array<int> *partition)
3323 {
3324  if (Conforming())
3325  {
3326  MFEM_ABORT("Load balancing is currently not supported for conforming"
3327  " meshes.");
3328  }
3329 
3330  // Make sure the Nodes use a ParFiniteElementSpace
3331  if (Nodes && dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace()) == NULL)
3332  {
3333  ParFiniteElementSpace *pfes =
3334  new ParFiniteElementSpace(*Nodes->FESpace(), *this);
3335  ParGridFunction *new_nodes = new ParGridFunction(pfes);
3336  *new_nodes = *Nodes;
3337  if (Nodes->OwnFEC())
3338  {
3339  new_nodes->MakeOwner(Nodes->OwnFEC());
3340  Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
3341  delete Nodes->FESpace();
3342  }
3343  delete Nodes;
3344  Nodes = new_nodes;
3345  }
3346 
3348 
3349  pncmesh->Rebalance(partition);
3350 
3351  ParMesh* pmesh2 = new ParMesh(*pncmesh);
3352  pncmesh->OnMeshUpdated(pmesh2);
3353 
3354  attributes.Copy(pmesh2->attributes);
3356 
3357  Swap(*pmesh2, false);
3358  delete pmesh2;
3359 
3361 
3363 
3365  sequence++;
3366 
3367  UpdateNodes();
3368 }
3369 
3370 void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
3371 {
3372  // Refine groups after LocalRefinement in 2D (triangle meshes)
3373 
3374  MFEM_ASSERT(Dim == 2 && meshgen == 1, "internal error");
3375 
3376  Array<int> group_verts, group_edges;
3377 
3378  // To update the groups after a refinement, we observe that:
3379  // - every (new and old) vertex, edge and face belongs to exactly one group
3380  // - the refinement does not create new groups
3381  // - a new vertex appears only as the middle of a refined edge
3382  // - a face can be refined 2, 3 or 4 times producing new edges and faces
3383 
3384  int *I_group_svert, *J_group_svert;
3385  int *I_group_sedge, *J_group_sedge;
3386 
3387  I_group_svert = Memory<int>(GetNGroups()+1);
3388  I_group_sedge = Memory<int>(GetNGroups()+1);
3389 
3390  I_group_svert[0] = I_group_svert[1] = 0;
3391  I_group_sedge[0] = I_group_sedge[1] = 0;
3392 
3393  // overestimate the size of the J arrays
3394  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
3396  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
3397 
3398  for (int group = 0; group < GetNGroups()-1; group++)
3399  {
3400  // Get the group shared objects
3401  group_svert.GetRow(group, group_verts);
3402  group_sedge.GetRow(group, group_edges);
3403 
3404  // Check which edges have been refined
3405  for (int i = 0; i < group_sedge.RowSize(group); i++)
3406  {
3407  int *v = shared_edges[group_edges[i]]->GetVertices();
3408  const int ind = middle[v_to_v(v[0], v[1])];
3409  if (ind != -1)
3410  {
3411  // add a vertex
3412  group_verts.Append(svert_lvert.Append(ind)-1);
3413  // update the edges
3414  const int attr = shared_edges[group_edges[i]]->GetAttribute();
3415  shared_edges.Append(new Segment(v[1], ind, attr));
3416  group_edges.Append(sedge_ledge.Append(-1)-1);
3417  v[1] = ind;
3418  }
3419  }
3420 
3421  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
3422  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
3423 
3424  int *J;
3425  J = J_group_svert+I_group_svert[group];
3426  for (int i = 0; i < group_verts.Size(); i++)
3427  {
3428  J[i] = group_verts[i];
3429  }
3430  J = J_group_sedge+I_group_sedge[group];
3431  for (int i = 0; i < group_edges.Size(); i++)
3432  {
3433  J[i] = group_edges[i];
3434  }
3435  }
3436 
3437  FinalizeParTopo();
3438 
3439  group_svert.SetIJ(I_group_svert, J_group_svert);
3440  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
3441 }
3442 
3443 void ParMesh::RefineGroups(int old_nv, const HashTable<Hashed2> &v_to_v)
3444 {
3445  // Refine groups after LocalRefinement in 3D (tetrahedral meshes)
3446 
3447  MFEM_ASSERT(Dim == 3 && meshgen == 1, "internal error");
3448 
3449  Array<int> group_verts, group_edges, group_trias;
3450 
3451  // To update the groups after a refinement, we observe that:
3452  // - every (new and old) vertex, edge and face belongs to exactly one group
3453  // - the refinement does not create new groups
3454  // - a new vertex appears only as the middle of a refined edge
3455  // - a face can be refined multiple times producing new edges and faces
3456 
3457  Array<Segment *> sedge_stack;
3458  Array<Vert3> sface_stack;
3459 
3460  Array<int> I_group_svert, J_group_svert;
3461  Array<int> I_group_sedge, J_group_sedge;
3462  Array<int> I_group_stria, J_group_stria;
3463 
3464  I_group_svert.SetSize(GetNGroups());
3465  I_group_sedge.SetSize(GetNGroups());
3466  I_group_stria.SetSize(GetNGroups());
3467 
3468  I_group_svert[0] = 0;
3469  I_group_sedge[0] = 0;
3470  I_group_stria[0] = 0;
3471 
3472  for (int group = 0; group < GetNGroups()-1; group++)
3473  {
3474  // Get the group shared objects
3475  group_svert.GetRow(group, group_verts);
3476  group_sedge.GetRow(group, group_edges);
3477  group_stria.GetRow(group, group_trias);
3478 
3479  // Check which edges have been refined
3480  for (int i = 0; i < group_sedge.RowSize(group); i++)
3481  {
3482  int *v = shared_edges[group_edges[i]]->GetVertices();
3483  int ind = v_to_v.FindId(v[0], v[1]);
3484  if (ind == -1) { continue; }
3485 
3486  // This shared edge is refined: walk the whole refinement tree
3487  const int attr = shared_edges[group_edges[i]]->GetAttribute();
3488  do
3489  {
3490  ind += old_nv;
3491  // Add new shared vertex
3492  group_verts.Append(svert_lvert.Append(ind)-1);
3493  // Put the right sub-edge on top of the stack
3494  sedge_stack.Append(new Segment(ind, v[1], attr));
3495  // The left sub-edge replaces the original edge
3496  v[1] = ind;
3497  ind = v_to_v.FindId(v[0], ind);
3498  }
3499  while (ind != -1);
3500  // Process all edges in the edge stack
3501  do
3502  {
3503  Segment *se = sedge_stack.Last();
3504  v = se->GetVertices();
3505  ind = v_to_v.FindId(v[0], v[1]);
3506  if (ind == -1)
3507  {
3508  // The edge 'se' is not refined
3509  sedge_stack.DeleteLast();
3510  // Add new shared edge
3511  shared_edges.Append(se);
3512  group_edges.Append(sedge_ledge.Append(-1)-1);
3513  }
3514  else
3515  {
3516  // The edge 'se' is refined
3517  ind += old_nv;
3518  // Add new shared vertex
3519  group_verts.Append(svert_lvert.Append(ind)-1);
3520  // Put the left sub-edge on top of the stack
3521  sedge_stack.Append(new Segment(v[0], ind, attr));
3522  // The right sub-edge replaces the original edge
3523  v[0] = ind;
3524  }
3525  }
3526  while (sedge_stack.Size() > 0);
3527  }
3528 
3529  // Check which triangles have been refined
3530  for (int i = 0; i < group_stria.RowSize(group); i++)
3531  {
3532  int *v = shared_trias[group_trias[i]].v;
3533  int ind = v_to_v.FindId(v[0], v[1]);
3534  if (ind == -1) { continue; }
3535 
3536  // This shared face is refined: walk the whole refinement tree
3537  const int edge_attr = 1;
3538  do
3539  {
3540  ind += old_nv;
3541  // Add the refinement edge to the edge stack
3542  sedge_stack.Append(new Segment(v[2], ind, edge_attr));
3543  // Put the right sub-triangle on top of the face stack
3544  sface_stack.Append(Vert3(v[1], v[2], ind));
3545  // The left sub-triangle replaces the original one
3546  v[1] = v[0]; v[0] = v[2]; v[2] = ind;
3547  ind = v_to_v.FindId(v[0], v[1]);
3548  }
3549  while (ind != -1);
3550  // Process all faces (triangles) in the face stack
3551  do
3552  {
3553  Vert3 &st = sface_stack.Last();
3554  v = st.v;
3555  ind = v_to_v.FindId(v[0], v[1]);
3556  if (ind == -1)
3557  {
3558  // The triangle 'st' is not refined
3559  // Add new shared face
3560  shared_trias.Append(st);
3561  group_trias.Append(sface_lface.Append(-1)-1);
3562  sface_stack.DeleteLast();
3563  }
3564  else
3565  {
3566  // The triangle 'st' is refined
3567  ind += old_nv;
3568  // Add the refinement edge to the edge stack
3569  sedge_stack.Append(new Segment(v[2], ind, edge_attr));
3570  // Put the left sub-triangle on top of the face stack
3571  sface_stack.Append(Vert3(v[2], v[0], ind));
3572  // Note that the above Append() may invalidate 'v'
3573  v = sface_stack[sface_stack.Size()-2].v;
3574  // The right sub-triangle replaces the original one
3575  v[0] = v[1]; v[1] = v[2]; v[2] = ind;
3576  }
3577  }
3578  while (sface_stack.Size() > 0);
3579  // Process all edges in the edge stack (same code as above)
3580  do
3581  {
3582  Segment *se = sedge_stack.Last();
3583  v = se->GetVertices();
3584  ind = v_to_v.FindId(v[0], v[1]);
3585  if (ind == -1)
3586  {
3587  // The edge 'se' is not refined
3588  sedge_stack.DeleteLast();
3589  // Add new shared edge
3590  shared_edges.Append(se);
3591  group_edges.Append(sedge_ledge.Append(-1)-1);
3592  }
3593  else
3594  {
3595  // The edge 'se' is refined
3596  ind += old_nv;
3597  // Add new shared vertex
3598  group_verts.Append(svert_lvert.Append(ind)-1);
3599  // Put the left sub-edge on top of the stack
3600  sedge_stack.Append(new Segment(v[0], ind, edge_attr));
3601  // The right sub-edge replaces the original edge
3602  v[0] = ind;
3603  }
3604  }
3605  while (sedge_stack.Size() > 0);
3606  }
3607 
3608  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
3609  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
3610  I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
3611 
3612  J_group_svert.Append(group_verts);
3613  J_group_sedge.Append(group_edges);
3614  J_group_stria.Append(group_trias);
3615  }
3616 
3617  FinalizeParTopo();
3618 
3619  group_svert.SetIJ(I_group_svert, J_group_svert);
3620  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
3621  group_stria.SetIJ(I_group_stria, J_group_stria);
3622  I_group_svert.LoseData(); J_group_svert.LoseData();
3623  I_group_sedge.LoseData(); J_group_sedge.LoseData();
3624  I_group_stria.LoseData(); J_group_stria.LoseData();
3625 }
3626 
3628 {
3629  Array<int> sverts, sedges;
3630 
3631  int *I_group_svert, *J_group_svert;
3632  int *I_group_sedge, *J_group_sedge;
3633 
3634  I_group_svert = Memory<int>(GetNGroups());
3635  I_group_sedge = Memory<int>(GetNGroups());
3636 
3637  I_group_svert[0] = 0;
3638  I_group_sedge[0] = 0;
3639 
3640  // compute the size of the J arrays
3641  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
3643  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
3644 
3645  for (int group = 0; group < GetNGroups()-1; group++)
3646  {
3647  // Get the group shared objects
3648  group_svert.GetRow(group, sverts);
3649  group_sedge.GetRow(group, sedges);
3650 
3651  // Process all the edges
3652  for (int i = 0; i < group_sedge.RowSize(group); i++)
3653  {
3654  int *v = shared_edges[sedges[i]]->GetVertices();
3655  const int ind = old_nv + sedge_ledge[sedges[i]];
3656  // add a vertex
3657  sverts.Append(svert_lvert.Append(ind)-1);
3658  // update the edges
3659  const int attr = shared_edges[sedges[i]]->GetAttribute();
3660  shared_edges.Append(new Segment(v[1], ind, attr));
3661  sedges.Append(sedge_ledge.Append(-1)-1);
3662  v[1] = ind;
3663  }
3664 
3665  I_group_svert[group+1] = I_group_svert[group] + sverts.Size();
3666  I_group_sedge[group+1] = I_group_sedge[group] + sedges.Size();
3667 
3668  sverts.CopyTo(J_group_svert + I_group_svert[group]);
3669  sedges.CopyTo(J_group_sedge + I_group_sedge[group]);
3670  }
3671 
3672  FinalizeParTopo();
3673 
3674  group_svert.SetIJ(I_group_svert, J_group_svert);
3675  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
3676 }
3677 
3678 void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
3679  const DSTable &old_v_to_v,
3680  const STable3D &old_faces,
3681  Array<int> *f2qf)
3682 {
3683  // f2qf can be NULL if all faces are quads or there are no quad faces
3684 
3685  Array<int> group_verts, group_edges, group_trias, group_quads;
3686 
3687  int *I_group_svert, *J_group_svert;
3688  int *I_group_sedge, *J_group_sedge;
3689  int *I_group_stria, *J_group_stria;
3690  int *I_group_squad, *J_group_squad;
3691 
3692  I_group_svert = Memory<int>(GetNGroups());
3693  I_group_sedge = Memory<int>(GetNGroups());
3694  I_group_stria = Memory<int>(GetNGroups());
3695  I_group_squad = Memory<int>(GetNGroups());
3696 
3697  I_group_svert[0] = 0;
3698  I_group_sedge[0] = 0;
3699  I_group_stria[0] = 0;
3700  I_group_squad[0] = 0;
3701 
3702  // compute the size of the J arrays
3703  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
3706  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections() +
3709  J_group_stria = Memory<int>(4*group_stria.Size_of_connections());
3710  J_group_squad = Memory<int>(4*group_squad.Size_of_connections());
3711 
3712  const int oface = old_nv + old_nedges;
3713 
3714  for (int group = 0; group < GetNGroups()-1; group++)
3715  {
3716  // Get the group shared objects
3717  group_svert.GetRow(group, group_verts);
3718  group_sedge.GetRow(group, group_edges);
3719  group_stria.GetRow(group, group_trias);
3720  group_squad.GetRow(group, group_quads);
3721 
3722  // Process the edges that have been refined
3723  for (int i = 0; i < group_sedge.RowSize(group); i++)
3724  {
3725  int *v = shared_edges[group_edges[i]]->GetVertices();
3726  const int ind = old_nv + old_v_to_v(v[0], v[1]);
3727  // add a vertex
3728  group_verts.Append(svert_lvert.Append(ind)-1);
3729  // update the edges
3730  const int attr = shared_edges[group_edges[i]]->GetAttribute();
3731  shared_edges.Append(new Segment(v[1], ind, attr));
3732  group_edges.Append(sedge_ledge.Append(-1)-1);
3733  v[1] = ind; // v[0] remains the same
3734  }
3735 
3736  // Process the triangles that have been refined
3737  for (int i = 0; i < group_stria.RowSize(group); i++)
3738  {
3739  int m[3];
3740  const int stria = group_trias[i];
3741  int *v = shared_trias[stria].v;
3742  // add the refinement edges
3743  m[0] = old_nv + old_v_to_v(v[0], v[1]);
3744  m[1] = old_nv + old_v_to_v(v[1], v[2]);
3745  m[2] = old_nv + old_v_to_v(v[2], v[0]);
3746  const int edge_attr = 1;
3747  shared_edges.Append(new Segment(m[0], m[1], edge_attr));
3748  group_edges.Append(sedge_ledge.Append(-1)-1);
3749  shared_edges.Append(new Segment(m[1], m[2], edge_attr));
3750  group_edges.Append(sedge_ledge.Append(-1)-1);
3751  shared_edges.Append(new Segment(m[0], m[2], edge_attr));
3752  group_edges.Append(sedge_ledge.Append(-1)-1);
3753  // update faces
3754  const int nst = shared_trias.Size();
3755  shared_trias.SetSize(nst+3);
3756  // The above SetSize() may invalidate 'v'
3757  v = shared_trias[stria].v;
3758  shared_trias[nst+0].Set(m[1],m[2],m[0]);
3759  shared_trias[nst+1].Set(m[0],v[1],m[1]);
3760  shared_trias[nst+2].Set(m[2],m[1],v[2]);
3761  v[1] = m[0]; v[2] = m[2]; // v[0] remains the same
3762  group_trias.Append(nst+0);
3763  group_trias.Append(nst+1);
3764  group_trias.Append(nst+2);
3765  // sface_lface is set later
3766  }
3767 
3768  // Process the quads that have been refined
3769  for (int i = 0; i < group_squad.RowSize(group); i++)
3770  {
3771  int m[5];
3772  const int squad = group_quads[i];
3773  int *v = shared_quads[squad].v;
3774  const int olf = old_faces(v[0], v[1], v[2], v[3]);
3775  // f2qf can be NULL if all faces are quads
3776  m[0] = oface + (f2qf ? (*f2qf)[olf] : olf);
3777  // add a vertex
3778  group_verts.Append(svert_lvert.Append(m[0])-1);
3779  // add the refinement edges
3780  m[1] = old_nv + old_v_to_v(v[0], v[1]);
3781  m[2] = old_nv + old_v_to_v(v[1], v[2]);
3782  m[3] = old_nv + old_v_to_v(v[2], v[3]);
3783  m[4] = old_nv + old_v_to_v(v[3], v[0]);
3784  const int edge_attr = 1;
3785  shared_edges.Append(new Segment(m[1], m[0], edge_attr));
3786  group_edges.Append(sedge_ledge.Append(-1)-1);
3787  shared_edges.Append(new Segment(m[2], m[0], edge_attr));
3788  group_edges.Append(sedge_ledge.Append(-1)-1);
3789  shared_edges.Append(new Segment(m[3], m[0], edge_attr));
3790  group_edges.Append(sedge_ledge.Append(-1)-1);
3791  shared_edges.Append(new Segment(m[4], m[0], edge_attr));
3792  group_edges.Append(sedge_ledge.Append(-1)-1);
3793  // update faces
3794  const int nsq = shared_quads.Size();
3795  shared_quads.SetSize(nsq+3);
3796  // The above SetSize() may invalidate 'v'
3797  v = shared_quads[squad].v;
3798  shared_quads[nsq+0].Set(m[1],v[1],m[2],m[0]);
3799  shared_quads[nsq+1].Set(m[0],m[2],v[2],m[3]);
3800  shared_quads[nsq+2].Set(m[4],m[0],m[3],v[3]);
3801  v[1] = m[1]; v[2] = m[0]; v[3] = m[4]; // v[0] remains the same
3802  group_quads.Append(nsq+0);
3803  group_quads.Append(nsq+1);
3804  group_quads.Append(nsq+2);
3805  // sface_lface is set later
3806  }
3807 
3808  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
3809  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
3810  I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
3811  I_group_squad[group+1] = I_group_squad[group] + group_quads.Size();
3812 
3813  group_verts.CopyTo(J_group_svert + I_group_svert[group]);
3814  group_edges.CopyTo(J_group_sedge + I_group_sedge[group]);
3815  group_trias.CopyTo(J_group_stria + I_group_stria[group]);
3816  group_quads.CopyTo(J_group_squad + I_group_squad[group]);
3817  }
3818 
3819  FinalizeParTopo();
3820 
3821  group_svert.SetIJ(I_group_svert, J_group_svert);
3822  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
3823  group_stria.SetIJ(I_group_stria, J_group_stria);
3824  group_squad.SetIJ(I_group_squad, J_group_squad);
3825 }
3826 
3828 {
3830 
3831  const int old_nv = NumOfVertices;
3832 
3833  // call Mesh::UniformRefinement2D so that it won't update the nodes
3834  {
3835  const bool update_nodes = false;
3836  Mesh::UniformRefinement2D_base(update_nodes);
3837  }
3838 
3839  // update the groups
3840  UniformRefineGroups2D(old_nv);
3841 
3842  UpdateNodes();
3843 
3844 #ifdef MFEM_DEBUG
3845  // If there are no Nodes, the orientation is checked in the call to
3846  // UniformRefinement2D_base() above.
3847  if (Nodes) { CheckElementOrientation(false); }
3848 #endif
3849 }
3850 
3852 {
3854 
3855  const int old_nv = NumOfVertices;
3856  const int old_nedges = NumOfEdges;
3857 
3858  DSTable v_to_v(NumOfVertices);
3859  GetVertexToVertexTable(v_to_v);
3860  STable3D *faces_tbl = GetFacesTable();
3861 
3862  // call Mesh::UniformRefinement3D_base so that it won't update the nodes
3863  Array<int> f2qf;
3864  {
3865  const bool update_nodes = false;
3866  UniformRefinement3D_base(&f2qf, &v_to_v, update_nodes);
3867  // Note: for meshes that have triangular faces, v_to_v is modified by the
3868  // above call to return different edge indices - this is used when
3869  // updating the groups. This is needed by ReorientTetMesh().
3870  }
3871 
3872  // update the groups
3873  UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
3874  f2qf.Size() ? &f2qf : NULL);
3875  delete faces_tbl;
3876 
3877  UpdateNodes();
3878 }
3879 
3881 {
3882  if (MyRank == 0)
3883  {
3884  mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
3885  }
3886 }
3887 
3888 void ParMesh::PrintXG(std::ostream &out) const
3889 {
3890  MFEM_ASSERT(Dim == spaceDim, "2D manifolds not supported");
3891  if (Dim == 3 && meshgen == 1)
3892  {
3893  int i, j, nv;
3894  const int *ind;
3895 
3896  out << "NETGEN_Neutral_Format\n";
3897  // print the vertices
3898  out << NumOfVertices << '\n';
3899  for (i = 0; i < NumOfVertices; i++)
3900  {
3901  for (j = 0; j < Dim; j++)
3902  {
3903  out << " " << vertices[i](j);
3904  }
3905  out << '\n';
3906  }
3907 
3908  // print the elements
3909  out << NumOfElements << '\n';
3910  for (i = 0; i < NumOfElements; i++)
3911  {
3912  nv = elements[i]->GetNVertices();
3913  ind = elements[i]->GetVertices();
3914  out << elements[i]->GetAttribute();
3915  for (j = 0; j < nv; j++)
3916  {
3917  out << " " << ind[j]+1;
3918  }
3919  out << '\n';
3920  }
3921 
3922  // print the boundary + shared faces information
3923  out << NumOfBdrElements + sface_lface.Size() << '\n';
3924  // boundary
3925  for (i = 0; i < NumOfBdrElements; i++)
3926  {
3927  nv = boundary[i]->GetNVertices();
3928  ind = boundary[i]->GetVertices();
3929  out << boundary[i]->GetAttribute();
3930  for (j = 0; j < nv; j++)
3931  {
3932  out << " " << ind[j]+1;
3933  }
3934  out << '\n';
3935  }
3936  // shared faces
3937  const int sf_attr =
3938  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
3939  for (i = 0; i < shared_trias.Size(); i++)
3940  {
3941  ind = shared_trias[i].v;
3942  out << sf_attr;
3943  for (j = 0; j < 3; j++)
3944  {
3945  out << ' ' << ind[j]+1;
3946  }
3947  out << '\n';
3948  }
3949  // There are no quad shared faces
3950  }
3951 
3952  if (Dim == 3 && meshgen == 2)
3953  {
3954  int i, j, nv;
3955  const int *ind;
3956 
3957  out << "TrueGrid\n"
3958  << "1 " << NumOfVertices << " " << NumOfElements << " 0 0 0 0 0 0 0\n"
3959  << "0 0 0 1 0 0 0 0 0 0 0\n"
3960  << "0 0 " << NumOfBdrElements+sface_lface.Size()
3961  << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
3962  << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
3963  << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
3964 
3965  // print the vertices
3966  for (i = 0; i < NumOfVertices; i++)
3967  {
3968  out << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
3969  << " " << vertices[i](2) << " 0.0\n";
3970  }
3971 
3972  // print the elements
3973  for (i = 0; i < NumOfElements; i++)
3974  {
3975  nv = elements[i]->GetNVertices();
3976  ind = elements[i]->GetVertices();
3977  out << i+1 << " " << elements[i]->GetAttribute();
3978  for (j = 0; j < nv; j++)
3979  {
3980  out << " " << ind[j]+1;
3981  }
3982  out << '\n';
3983  }
3984 
3985  // print the boundary information
3986  for (i = 0; i < NumOfBdrElements; i++)
3987  {
3988  nv = boundary[i]->GetNVertices();
3989  ind = boundary[i]->GetVertices();
3990  out << boundary[i]->GetAttribute();
3991  for (j = 0; j < nv; j++)
3992  {
3993  out << " " << ind[j]+1;
3994  }
3995  out << " 1.0 1.0 1.0 1.0\n";
3996  }
3997 
3998  // print the shared faces information
3999  const int sf_attr =
4000  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4001  // There are no shared triangle faces
4002  for (i = 0; i < shared_quads.Size(); i++)
4003  {
4004  ind = shared_quads[i].v;
4005  out << sf_attr;
4006  for (j = 0; j < 4; j++)
4007  {
4008  out << ' ' << ind[j]+1;
4009  }
4010  out << " 1.0 1.0 1.0 1.0\n";
4011  }
4012  }
4013 
4014  if (Dim == 2)
4015  {
4016  int i, j, attr;
4017  Array<int> v;
4018 
4019  out << "areamesh2\n\n";
4020 
4021  // print the boundary + shared edges information
4022  out << NumOfBdrElements + shared_edges.Size() << '\n';
4023  // boundary
4024  for (i = 0; i < NumOfBdrElements; i++)
4025  {
4026  attr = boundary[i]->GetAttribute();
4027  boundary[i]->GetVertices(v);
4028  out << attr << " ";
4029  for (j = 0; j < v.Size(); j++)
4030  {
4031  out << v[j] + 1 << " ";
4032  }
4033  out << '\n';
4034  }
4035  // shared edges
4036  for (i = 0; i < shared_edges.Size(); i++)
4037  {
4038  attr = shared_edges[i]->GetAttribute();
4039  shared_edges[i]->GetVertices(v);
4040  out << attr << " ";
4041  for (j = 0; j < v.Size(); j++)
4042  {
4043  out << v[j] + 1 << " ";
4044  }
4045  out << '\n';
4046  }
4047 
4048  // print the elements
4049  out << NumOfElements << '\n';
4050  for (i = 0; i < NumOfElements; i++)
4051  {
4052  attr = elements[i]->GetAttribute();
4053  elements[i]->GetVertices(v);
4054 
4055  out << attr << " ";
4056  if ((j = GetElementType(i)) == Element::TRIANGLE)
4057  {
4058  out << 3 << " ";
4059  }
4060  else if (j == Element::QUADRILATERAL)
4061  {
4062  out << 4 << " ";
4063  }
4064  else if (j == Element::SEGMENT)
4065  {
4066  out << 2 << " ";
4067  }
4068  for (j = 0; j < v.Size(); j++)
4069  {
4070  out << v[j] + 1 << " ";
4071  }
4072  out << '\n';
4073  }
4074 
4075  // print the vertices
4076  out << NumOfVertices << '\n';
4077  for (i = 0; i < NumOfVertices; i++)
4078  {
4079  for (j = 0; j < Dim; j++)
4080  {
4081  out << vertices[i](j) << " ";
4082  }
4083  out << '\n';
4084  }
4085  }
4086  out.flush();
4087 }
4088 
4090 {
4091  // In 2D, this is a workaround for a CPU boundary rendering artifact. We need
4092  // to skip a shared master edge if one of its slaves has the same rank.
4093 
4094  const NCMesh::NCList &list = pncmesh->GetEdgeList();
4095  for (int i = master.slaves_begin; i < master.slaves_end; i++)
4096  {
4097  if (!pncmesh->IsGhost(1, list.slaves[i].index)) { return true; }
4098  }
4099  return false;
4100 }
4101 
4102 void ParMesh::Print(std::ostream &out) const
4103 {
4104  bool print_shared = true;
4105  int i, j, shared_bdr_attr;
4106  Array<int> nc_shared_faces;
4107 
4108  if (NURBSext)
4109  {
4110  Printer(out); // does not print shared boundary
4111  return;
4112  }
4113 
4114  const Array<int>* s2l_face;
4115  if (!pncmesh)
4116  {
4117  s2l_face = ((Dim == 1) ? &svert_lvert :
4118  ((Dim == 2) ? &sedge_ledge : &sface_lface));
4119  }
4120  else
4121  {
4122  s2l_face = &nc_shared_faces;
4123  if (Dim >= 2)
4124  {
4125  // get a list of all shared non-ghost faces
4126  const NCMesh::NCList& sfaces =
4127  (Dim == 3) ? pncmesh->GetSharedFaces() : pncmesh->GetSharedEdges();
4128  const int nfaces = GetNumFaces();
4129  for (unsigned i = 0; i < sfaces.conforming.size(); i++)
4130  {
4131  int index = sfaces.conforming[i].index;
4132  if (index < nfaces) { nc_shared_faces.Append(index); }
4133  }
4134  for (unsigned i = 0; i < sfaces.masters.size(); i++)
4135  {
4136  if (Dim == 2 && WantSkipSharedMaster(sfaces.masters[i])) { continue; }
4137  int index = sfaces.masters[i].index;
4138  if (index < nfaces) { nc_shared_faces.Append(index); }
4139  }
4140  for (unsigned i = 0; i < sfaces.slaves.size(); i++)
4141  {
4142  int index = sfaces.slaves[i].index;
4143  if (index < nfaces) { nc_shared_faces.Append(index); }
4144  }
4145  }
4146  }
4147 
4148  out << "MFEM mesh v1.0\n";
4149 
4150  // optional
4151  out <<
4152  "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
4153  "# POINT = 0\n"
4154  "# SEGMENT = 1\n"
4155  "# TRIANGLE = 2\n"
4156  "# SQUARE = 3\n"
4157  "# TETRAHEDRON = 4\n"
4158  "# CUBE = 5\n"
4159  "# PRISM = 6\n"
4160  "#\n";
4161 
4162  out << "\ndimension\n" << Dim
4163  << "\n\nelements\n" << NumOfElements << '\n';
4164  for (i = 0; i < NumOfElements; i++)
4165  {
4166  PrintElement(elements[i], out);
4167  }
4168 
4169  int num_bdr_elems = NumOfBdrElements;
4170  if (print_shared && Dim > 1)
4171  {
4172  num_bdr_elems += s2l_face->Size();
4173  }
4174  out << "\nboundary\n" << num_bdr_elems << '\n';
4175  for (i = 0; i < NumOfBdrElements; i++)
4176  {
4177  PrintElement(boundary[i], out);
4178  }
4179 
4180  if (print_shared && Dim > 1)
4181  {
4182  if (bdr_attributes.Size())
4183  {
4184  shared_bdr_attr = bdr_attributes.Max() + MyRank + 1;
4185  }
4186  else
4187  {
4188  shared_bdr_attr = MyRank + 1;
4189  }
4190  for (i = 0; i < s2l_face->Size(); i++)
4191  {
4192  // Modify the attributes of the faces (not used otherwise?)
4193  faces[(*s2l_face)[i]]->SetAttribute(shared_bdr_attr);
4194  PrintElement(faces[(*s2l_face)[i]], out);
4195  }
4196  }
4197  out << "\nvertices\n" << NumOfVertices << '\n';
4198  if (Nodes == NULL)
4199  {
4200  out << spaceDim << '\n';
4201  for (i = 0; i < NumOfVertices; i++)
4202  {
4203  out << vertices[i](0);
4204  for (j = 1; j < spaceDim; j++)
4205  {
4206  out << ' ' << vertices[i](j);
4207  }
4208  out << '\n';
4209  }
4210  out.flush();
4211  }
4212  else
4213  {
4214  out << "\nnodes\n";
4215  Nodes->Save(out);
4216  }
4217 }
4218 
4219 static void dump_element(const Element* elem, Array<int> &data)
4220 {
4221  data.Append(elem->GetGeometryType());
4222 
4223  int nv = elem->GetNVertices();
4224  const int *v = elem->GetVertices();
4225  for (int i = 0; i < nv; i++)
4226  {
4227  data.Append(v[i]);
4228  }
4229 }
4230 
4231 void ParMesh::PrintAsOne(std::ostream &out)
4232 {
4233  int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc;
4234  const int *v;
4235  MPI_Status status;
4236  Array<double> vert;
4237  Array<int> ints;
4238 
4239  if (MyRank == 0)
4240  {
4241  out << "MFEM mesh v1.0\n";
4242 
4243  // optional
4244  out <<
4245  "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
4246  "# POINT = 0\n"
4247  "# SEGMENT = 1\n"
4248  "# TRIANGLE = 2\n"
4249  "# SQUARE = 3\n"
4250  "# TETRAHEDRON = 4\n"
4251  "# CUBE = 5\n"
4252  "# PRISM = 6\n"
4253  "#\n";
4254 
4255  out << "\ndimension\n" << Dim;
4256  }
4257 
4258  nv = NumOfElements;
4259  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4260  if (MyRank == 0)
4261  {
4262  out << "\n\nelements\n" << ne << '\n';
4263  for (i = 0; i < NumOfElements; i++)
4264  {
4265  // processor number + 1 as attribute and geometry type
4266  out << 1 << ' ' << elements[i]->GetGeometryType();
4267  // vertices
4268  nv = elements[i]->GetNVertices();
4269  v = elements[i]->GetVertices();
4270  for (j = 0; j < nv; j++)
4271  {
4272  out << ' ' << v[j];
4273  }
4274  out << '\n';
4275  }
4276  vc = NumOfVertices;
4277  for (p = 1; p < NRanks; p++)
4278  {
4279  MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
4280  ints.SetSize(ne);
4281  if (ne)
4282  {
4283  MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
4284  }
4285  for (i = 0; i < ne; )
4286  {
4287  // processor number + 1 as attribute and geometry type
4288  out << p+1 << ' ' << ints[i];
4289  // vertices
4290  k = Geometries.GetVertices(ints[i++])->GetNPoints();
4291  for (j = 0; j < k; j++)
4292  {
4293  out << ' ' << vc + ints[i++];
4294  }
4295  out << '\n';
4296  }
4297  vc += nv;
4298  }
4299  }
4300  else
4301  {
4302  // for each element send its geometry type and its vertices
4303  ne = 0;
4304  for (i = 0; i < NumOfElements; i++)
4305  {
4306  ne += 1 + elements[i]->GetNVertices();
4307  }
4308  nv = NumOfVertices;
4309  MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
4310 
4311  ints.Reserve(ne);
4312  ints.SetSize(0);
4313  for (i = 0; i < NumOfElements; i++)
4314  {
4315  dump_element(elements[i], ints);
4316  }
4317  MFEM_ASSERT(ints.Size() == ne, "");
4318  if (ne)
4319  {
4320  MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm);
4321  }
4322  }
4323 
4324  // boundary + shared boundary
4325  ne = NumOfBdrElements;
4326  if (!pncmesh)
4327  {
4328  ne += GetNSharedFaces();
4329  }
4330  else if (Dim > 1)
4331  {
4332  const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
4333  ne += list.conforming.size() + list.masters.size() + list.slaves.size();
4334  // In addition to the number returned by GetNSharedFaces(), include the
4335  // the master shared faces as well.
4336  }
4337  ints.Reserve(ne * (1 + 2*(Dim-1))); // just an upper bound
4338  ints.SetSize(0);
4339 
4340  // for each boundary and shared boundary element send its geometry type
4341  // and its vertices
4342  ne = 0;
4343  for (i = j = 0; i < NumOfBdrElements; i++)
4344  {
4345  dump_element(boundary[i], ints); ne++;
4346  }
4347  if (!pncmesh)
4348  {
4349  switch (Dim)
4350  {
4351  case 1:
4352  for (i = 0; i < svert_lvert.Size(); i++)
4353  {
4354  ints.Append(Geometry::POINT);
4355  ints.Append(svert_lvert[i]);
4356  ne++;
4357  }
4358  break;
4359 
4360  case 2:
4361  for (i = 0; i < shared_edges.Size(); i++)
4362  {
4363  dump_element(shared_edges[i], ints); ne++;
4364  }
4365  break;
4366 
4367  case 3:
4368  for (i = 0; i < shared_trias.Size(); i++)
4369  {
4370  ints.Append(Geometry::TRIANGLE);
4371  ints.Append(shared_trias[i].v, 3);
4372  ne++;
4373  }
4374  for (i = 0; i < shared_quads.Size(); i++)
4375  {
4376  ints.Append(Geometry::SQUARE);
4377  ints.Append(shared_quads[i].v, 4);
4378  ne++;
4379  }
4380  break;
4381 
4382  default:
4383  MFEM_ABORT("invalid dimension: " << Dim);
4384  }
4385  }
4386  else if (Dim > 1)
4387  {
4388  const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
4389  const int nfaces = GetNumFaces();
4390  for (i = 0; i < (int) list.conforming.size(); i++)
4391  {
4392  int index = list.conforming[i].index;
4393  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
4394  }
4395  for (i = 0; i < (int) list.masters.size(); i++)
4396  {
4397  int index = list.masters[i].index;
4398  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
4399  }
4400  for (i = 0; i < (int) list.slaves.size(); i++)
4401  {
4402  int index = list.slaves[i].index;
4403  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
4404  }
4405  }
4406 
4407  MPI_Reduce(&ne, &k, 1, MPI_INT, MPI_SUM, 0, MyComm);
4408  if (MyRank == 0)
4409  {
4410  out << "\nboundary\n" << k << '\n';
4411  vc = 0;
4412  for (p = 0; p < NRanks; p++)
4413  {
4414  if (p)
4415  {
4416  MPI_Recv(nv_ne, 2, MPI_INT, p, 446, MyComm, &status);
4417  ints.SetSize(ne);
4418  if (ne)
4419  {
4420  MPI_Recv(ints.GetData(), ne, MPI_INT, p, 447, MyComm, &status);
4421  }
4422  }
4423  else
4424  {
4425  ne = ints.Size();
4426  nv = NumOfVertices;
4427  }
4428  for (i = 0; i < ne; )
4429  {
4430  // processor number + 1 as bdr. attr. and bdr. geometry type
4431  out << p+1 << ' ' << ints[i];
4432  k = Geometries.NumVerts[ints[i++]];
4433  // vertices
4434  for (j = 0; j < k; j++)
4435  {
4436  out << ' ' << vc + ints[i++];
4437  }
4438  out << '\n';
4439  }
4440  vc += nv;
4441  }
4442  }
4443  else
4444  {
4445  nv = NumOfVertices;
4446  ne = ints.Size();
4447  MPI_Send(nv_ne, 2, MPI_INT, 0, 446, MyComm);
4448  if (ne)
4449  {
4450  MPI_Send(ints.GetData(), ne, MPI_INT, 0, 447, MyComm);
4451  }
4452  }
4453 
4454  // vertices / nodes
4455  MPI_Reduce(&NumOfVertices, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4456  if (MyRank == 0)
4457  {
4458  out << "\nvertices\n" << nv << '\n';
4459  }
4460  if (Nodes == NULL)
4461  {
4462  if (MyRank == 0)
4463  {
4464  out << spaceDim << '\n';
4465  for (i = 0; i < NumOfVertices; i++)
4466  {
4467  out << vertices[i](0);
4468  for (j = 1; j < spaceDim; j++)
4469  {
4470  out << ' ' << vertices[i](j);
4471  }
4472  out << '\n';
4473  }
4474  for (p = 1; p < NRanks; p++)
4475  {
4476  MPI_Recv(&nv, 1, MPI_INT, p, 448, MyComm, &status);
4477  vert.SetSize(nv*spaceDim);
4478  if (nv)
4479  {
4480  MPI_Recv(&vert[0], nv*spaceDim, MPI_DOUBLE, p, 449, MyComm, &status);
4481  }
4482  for (i = 0; i < nv; i++)
4483  {
4484  out << vert[i*spaceDim];
4485  for (j = 1; j < spaceDim; j++)
4486  {
4487  out << ' ' << vert[i*spaceDim+j];
4488  }
4489  out << '\n';
4490  }
4491  }
4492  out.flush();
4493  }
4494  else
4495  {
4496  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 448, MyComm);
4498  for (i = 0; i < NumOfVertices; i++)
4499  {
4500  for (j = 0; j < spaceDim; j++)
4501  {
4502  vert[i*spaceDim+j] = vertices[i](j);
4503  }
4504  }
4505  if (NumOfVertices)
4506  {
4507  MPI_Send(&vert[0], NumOfVertices*spaceDim, MPI_DOUBLE, 0, 449, MyComm);
4508  }
4509  }
4510  }
4511  else
4512  {
4513  if (MyRank == 0)
4514  {
4515  out << "\nnodes\n";
4516  }
4517  ParGridFunction *pnodes = dynamic_cast<ParGridFunction *>(Nodes);
4518  if (pnodes)
4519  {
4520  pnodes->SaveAsOne(out);
4521  }
4522  else
4523  {
4524  ParFiniteElementSpace *pfes =
4525  dynamic_cast<ParFiniteElementSpace *>(Nodes->FESpace());
4526  if (pfes)
4527  {
4528  // create a wrapper ParGridFunction
4529  ParGridFunction ParNodes(pfes, Nodes);
4530  ParNodes.SaveAsOne(out);
4531  }
4532  else
4533  {
4534  mfem_error("ParMesh::PrintAsOne : Nodes have no parallel info!");
4535  }
4536  }
4537  }
4538 }
4539 
4540 void ParMesh::PrintAsOneXG(std::ostream &out)
4541 {
4542  MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported.");
4543  if (Dim == 3 && meshgen == 1)
4544  {
4545  int i, j, k, nv, ne, p;
4546  const int *ind, *v;
4547  MPI_Status status;
4548  Array<double> vert;
4549  Array<int> ints;
4550 
4551  if (MyRank == 0)
4552  {
4553  out << "NETGEN_Neutral_Format\n";
4554  // print the vertices
4555  ne = NumOfVertices;
4556  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4557  out << nv << '\n';
4558  for (i = 0; i < NumOfVertices; i++)
4559  {
4560  for (j = 0; j < Dim; j++)
4561  {
4562  out << " " << vertices[i](j);
4563  }
4564  out << '\n';
4565  }
4566  for (p = 1; p < NRanks; p++)
4567  {
4568  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4569  vert.SetSize(Dim*nv);
4570  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
4571  for (i = 0; i < nv; i++)
4572  {
4573  for (j = 0; j < Dim; j++)
4574  {
4575  out << " " << vert[Dim*i+j];
4576  }
4577  out << '\n';
4578  }
4579  }
4580 
4581  // print the elements
4582  nv = NumOfElements;
4583  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4584  out << ne << '\n';
4585  for (i = 0; i < NumOfElements; i++)
4586  {
4587  nv = elements[i]->GetNVertices();
4588  ind = elements[i]->GetVertices();
4589  out << 1;
4590  for (j = 0; j < nv; j++)
4591  {
4592  out << " " << ind[j]+1;
4593  }
4594  out << '\n';
4595  }
4596  k = NumOfVertices;
4597  for (p = 1; p < NRanks; p++)
4598  {
4599  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4600  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4601  ints.SetSize(4*ne);
4602  MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
4603  for (i = 0; i < ne; i++)
4604  {
4605  out << p+1;
4606  for (j = 0; j < 4; j++)
4607  {
4608  out << " " << k+ints[i*4+j]+1;
4609  }
4610  out << '\n';
4611  }
4612  k += nv;
4613  }
4614  // print the boundary + shared faces information
4616  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4617  out << ne << '\n';
4618  // boundary
4619  for (i = 0; i < NumOfBdrElements; i++)
4620  {
4621  nv = boundary[i]->GetNVertices();
4622  ind = boundary[i]->GetVertices();
4623  out << 1;
4624  for (j = 0; j < nv; j++)
4625  {
4626  out << " " << ind[j]+1;
4627  }
4628  out << '\n';
4629  }
4630  // shared faces
4631  const int sf_attr =
4632  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4633  for (i = 0; i < shared_trias.Size(); i++)
4634  {
4635  ind = shared_trias[i].v;
4636  out << sf_attr;
4637  for (j = 0; j < 3; j++)
4638  {
4639  out << ' ' << ind[j]+1;
4640  }
4641  out << '\n';
4642  }
4643  // There are no quad shared faces
4644  k = NumOfVertices;
4645  for (p = 1; p < NRanks; p++)
4646  {
4647  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4648  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4649  ints.SetSize(3*ne);
4650  MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
4651  for (i = 0; i < ne; i++)
4652  {
4653  out << p+1;
4654  for (j = 0; j < 3; j++)
4655  {
4656  out << ' ' << k+ints[i*3+j]+1;
4657  }
4658  out << '\n';
4659  }
4660  k += nv;
4661  }
4662  }
4663  else
4664  {
4665  ne = NumOfVertices;
4666  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4667  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4668  vert.SetSize(Dim*NumOfVertices);
4669  for (i = 0; i < NumOfVertices; i++)
4670  for (j = 0; j < Dim; j++)
4671  {
4672  vert[Dim*i+j] = vertices[i](j);
4673  }
4674  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
4675  0, 445, MyComm);
4676  // elements
4677  ne = NumOfElements;
4678  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4679  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4680  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
4681  ints.SetSize(NumOfElements*4);
4682  for (i = 0; i < NumOfElements; i++)
4683  {
4684  v = elements[i]->GetVertices();
4685  for (j = 0; j < 4; j++)
4686  {
4687  ints[4*i+j] = v[j];
4688  }
4689  }
4690  MPI_Send(&ints[0], 4*NumOfElements, MPI_INT, 0, 447, MyComm);
4691  // boundary + shared faces
4693  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4694  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4696  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
4697  ints.SetSize(3*ne);
4698  for (i = 0; i < NumOfBdrElements; i++)
4699  {
4700  v = boundary[i]->GetVertices();
4701  for (j = 0; j < 3; j++)
4702  {
4703  ints[3*i+j] = v[j];
4704  }
4705  }
4706  for ( ; i < ne; i++)
4707  {
4708  v = shared_trias[i-NumOfBdrElements].v; // tet mesh
4709  for (j = 0; j < 3; j++)
4710  {
4711  ints[3*i+j] = v[j];
4712  }
4713  }
4714  MPI_Send(&ints[0], 3*ne, MPI_INT, 0, 447, MyComm);
4715  }
4716  }
4717 
4718  if (Dim == 3 && meshgen == 2)
4719  {
4720  int i, j, k, nv, ne, p;
4721  const int *ind, *v;
4722  MPI_Status status;
4723  Array<double> vert;
4724  Array<int> ints;
4725 
4726  int TG_nv, TG_ne, TG_nbe;
4727 
4728  if (MyRank == 0)
4729  {
4730  MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4731  MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4733  MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
4734 
4735  out << "TrueGrid\n"
4736  << "1 " << TG_nv << " " << TG_ne << " 0 0 0 0 0 0 0\n"
4737  << "0 0 0 1 0 0 0 0 0 0 0\n"
4738  << "0 0 " << TG_nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
4739  << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
4740  << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
4741 
4742  // print the vertices
4743  nv = TG_nv;
4744  for (i = 0; i < NumOfVertices; i++)
4745  {
4746  out << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
4747  << " " << vertices[i](2) << " 0.0\n";
4748  }
4749  for (p = 1; p < NRanks; p++)
4750  {
4751  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4752  vert.SetSize(Dim*nv);
4753  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
4754  for (i = 0; i < nv; i++)
4755  {
4756  out << i+1 << " 0.0 " << vert[Dim*i] << " " << vert[Dim*i+1]
4757  << " " << vert[Dim*i+2] << " 0.0\n";
4758  }
4759  }
4760 
4761  // print the elements
4762  ne = TG_ne;
4763  for (i = 0; i < NumOfElements; i++)
4764  {
4765  nv = elements[i]->GetNVertices();
4766  ind = elements[i]->GetVertices();
4767  out << i+1 << " " << 1;
4768  for (j = 0; j < nv; j++)
4769  {
4770  out << " " << ind[j]+1;
4771  }
4772  out << '\n';
4773  }
4774  k = NumOfVertices;
4775  for (p = 1; p < NRanks; p++)
4776  {
4777  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4778  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4779  ints.SetSize(8*ne);
4780  MPI_Recv(&ints[0], 8*ne, MPI_INT, p, 447, MyComm, &status);
4781  for (i = 0; i < ne; i++)
4782  {
4783  out << i+1 << " " << p+1;
4784  for (j = 0; j < 8; j++)
4785  {
4786  out << " " << k+ints[i*8+j]+1;
4787  }
4788  out << '\n';
4789  }
4790  k += nv;
4791  }
4792 
4793  // print the boundary + shared faces information
4794  ne = TG_nbe;
4795  // boundary
4796  for (i = 0; i < NumOfBdrElements; i++)
4797  {
4798  nv = boundary[i]->GetNVertices();
4799  ind = boundary[i]->GetVertices();
4800  out << 1;
4801  for (j = 0; j < nv; j++)
4802  {
4803  out << " " << ind[j]+1;
4804  }
4805  out << " 1.0 1.0 1.0 1.0\n";
4806  }
4807  // shared faces
4808  const int sf_attr =
4809  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4810  // There are no shared triangle faces
4811  for (i = 0; i < shared_quads.Size(); i++)
4812  {
4813  ind = shared_quads[i].v;
4814  out << sf_attr;
4815  for (j = 0; j < 4; j++)
4816  {
4817  out << ' ' << ind[j]+1;
4818  }
4819  out << " 1.0 1.0 1.0 1.0\n";
4820  }
4821  k = NumOfVertices;
4822  for (p = 1; p < NRanks; p++)
4823  {
4824  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4825  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4826  ints.SetSize(4*ne);
4827  MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
4828  for (i = 0; i < ne; i++)
4829  {
4830  out << p+1;
4831  for (j = 0; j < 4; j++)
4832  {
4833  out << " " << k+ints[i*4+j]+1;
4834  }
4835  out << " 1.0 1.0 1.0 1.0\n";
4836  }
4837  k += nv;
4838  }
4839  }
4840  else
4841  {
4842  MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4843  MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4845  MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
4846 
4847  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4848  vert.SetSize(Dim*NumOfVertices);
4849  for (i = 0; i < NumOfVertices; i++)
4850  for (j = 0; j < Dim; j++)
4851  {
4852  vert[Dim*i+j] = vertices[i](j);
4853  }
4854  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE, 0, 445, MyComm);
4855  // elements
4856  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4857  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
4858  ints.SetSize(NumOfElements*8);
4859  for (i = 0; i < NumOfElements; i++)
4860  {
4861  v = elements[i]->GetVertices();
4862  for (j = 0; j < 8; j++)
4863  {
4864  ints[8*i+j] = v[j];
4865  }
4866  }
4867  MPI_Send(&ints[0], 8*NumOfElements, MPI_INT, 0, 447, MyComm);
4868  // boundary + shared faces
4869  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
4871  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
4872  ints.SetSize(4*ne);
4873  for (i = 0; i < NumOfBdrElements; i++)
4874  {
4875  v = boundary[i]->GetVertices();
4876  for (j = 0; j < 4; j++)
4877  {
4878  ints[4*i+j] = v[j];
4879  }
4880  }
4881  for ( ; i < ne; i++)
4882  {
4883  v = shared_quads[i-NumOfBdrElements].v; // hex mesh
4884  for (j = 0; j < 4; j++)
4885  {
4886  ints[4*i+j] = v[j];
4887  }
4888  }
4889  MPI_Send(&ints[0], 4*ne, MPI_INT, 0, 447, MyComm);
4890  }
4891  }
4892 
4893  if (Dim == 2)
4894  {
4895  int i, j, k, attr, nv, ne, p;
4896  Array<int> v;
4897  MPI_Status status;
4898  Array<double> vert;
4899  Array<int> ints;
4900 
4901  if (MyRank == 0)
4902  {
4903  out << "areamesh2\n\n";
4904 
4905  // print the boundary + shared edges information
4906  nv = NumOfBdrElements + shared_edges.Size();
4907  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4908  out << ne << '\n';
4909  // boundary
4910  for (i = 0; i < NumOfBdrElements; i++)
4911  {
4912  attr = boundary[i]->GetAttribute();
4913  boundary[i]->GetVertices(v);
4914  out << attr << " ";
4915  for (j = 0; j < v.Size(); j++)
4916  {
4917  out << v[j] + 1 << " ";
4918  }
4919  out << '\n';
4920  }
4921  // shared edges
4922  for (i = 0; i < shared_edges.Size(); i++)
4923  {
4924  attr = shared_edges[i]->GetAttribute();
4925  shared_edges[i]->GetVertices(v);
4926  out << attr << " ";
4927  for (j = 0; j < v.Size(); j++)
4928  {
4929  out << v[j] + 1 << " ";
4930  }
4931  out << '\n';
4932  }
4933  k = NumOfVertices;
4934  for (p = 1; p < NRanks; p++)
4935  {
4936  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4937  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4938  ints.SetSize(2*ne);
4939  MPI_Recv(&ints[0], 2*ne, MPI_INT, p, 447, MyComm, &status);
4940  for (i = 0; i < ne; i++)
4941  {
4942  out << p+1;
4943  for (j = 0; j < 2; j++)
4944  {
4945  out << " " << k+ints[i*2+j]+1;
4946  }
4947  out << '\n';
4948  }
4949  k += nv;
4950  }
4951 
4952  // print the elements
4953  nv = NumOfElements;
4954  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
4955  out << ne << '\n';
4956  for (i = 0; i < NumOfElements; i++)
4957  {
4958  // attr = elements[i]->GetAttribute(); // not used
4959  elements[i]->GetVertices(v);
4960  out << 1 << " " << 3 << " ";
4961  for (j = 0; j < v.Size(); j++)
4962  {
4963  out << v[j] + 1 << " ";
4964  }
4965  out << '\n';
4966  }
4967  k = NumOfVertices;
4968  for (p = 1; p < NRanks; p++)
4969  {
4970  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
4971  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
4972  ints.SetSize(3*ne);
4973  MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
4974  for (i = 0; i < ne; i++)
4975  {
4976  out << p+1 << " " << 3;
4977  for (j = 0; j < 3; j++)
4978  {
4979  out << " " << k+ints[i*3+j]+1;
4980  }
4981  out << '\n';
4982  }
4983  k += nv;
4984  }
4985 
4986  // print the vertices
4987  ne = NumOfVertices;
4988  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4989  out << nv << '\n';
4990  for (i = 0; i < NumOfVertices; i++)
4991  {
4992  for (j = 0; j < Dim; j++)
4993  {
4994  out << vertices[i](j) << " ";
4995  }
4996  out << '\n';
4997  }
4998  for (p = 1; p < NRanks; p++)
4999  {
5000  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5001  vert.SetSize(Dim*nv);
5002  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
5003  for (i = 0; i < nv; i++)
5004  {
5005  for (j = 0; j < Dim; j++)
5006  {
5007  out << " " << vert[Dim*i+j];
5008  }
5009  out << '\n';
5010  }
5011  }
5012  }
5013  else
5014  {
5015  // boundary + shared faces
5016  nv = NumOfBdrElements + shared_edges.Size();
5017  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5018  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5019  ne = NumOfBdrElements + shared_edges.Size();
5020  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
5021  ints.SetSize(2*ne);
5022  for (i = 0; i < NumOfBdrElements; i++)
5023  {
5024  boundary[i]->GetVertices(v);
5025  for (j = 0; j < 2; j++)
5026  {
5027  ints[2*i+j] = v[j];
5028  }
5029  }
5030  for ( ; i < ne; i++)
5031  {
5032  shared_edges[i-NumOfBdrElements]->GetVertices(v);
5033  for (j = 0; j < 2; j++)
5034  {
5035  ints[2*i+j] = v[j];
5036  }
5037  }
5038  MPI_Send(&ints[0], 2*ne, MPI_INT, 0, 447, MyComm);
5039  // elements
5040  ne = NumOfElements;
5041  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5042  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5043  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
5044  ints.SetSize(NumOfElements*3);
5045  for (i = 0; i < NumOfElements; i++)
5046  {
5047  elements[i]->GetVertices(v);
5048  for (j = 0; j < 3; j++)
5049  {
5050  ints[3*i+j] = v[j];
5051  }
5052  }
5053  MPI_Send(&ints[0], 3*NumOfElements, MPI_INT, 0, 447, MyComm);
5054  // vertices
5055  ne = NumOfVertices;
5056  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5057  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5058  vert.SetSize(Dim*NumOfVertices);
5059  for (i = 0; i < NumOfVertices; i++)
5060  for (j = 0; j < Dim; j++)
5061  {
5062  vert[Dim*i+j] = vertices[i](j);
5063  }
5064  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
5065  0, 445, MyComm);
5066  }
5067  }
5068 }
5069 
5070 void ParMesh::GetBoundingBox(Vector &gp_min, Vector &gp_max, int ref)
5071 {
5072  int sdim;
5073  Vector p_min, p_max;
5074 
5075  this->Mesh::GetBoundingBox(p_min, p_max, ref);
5076 
5077  sdim = SpaceDimension();
5078 
5079  gp_min.SetSize(sdim);
5080  gp_max.SetSize(sdim);
5081 
5082  MPI_Allreduce(p_min.GetData(), gp_min, sdim, MPI_DOUBLE, MPI_MIN, MyComm);
5083  MPI_Allreduce(p_max.GetData(), gp_max, sdim, MPI_DOUBLE, MPI_MAX, MyComm);
5084 }
5085 
5086 void ParMesh::GetCharacteristics(double &gh_min, double &gh_max,
5087  double &gk_min, double &gk_max)
5088 {
5089  double h_min, h_max, kappa_min, kappa_max;
5090 
5091  this->Mesh::GetCharacteristics(h_min, h_max, kappa_min, kappa_max);
5092 
5093  MPI_Allreduce(&h_min, &gh_min, 1, MPI_DOUBLE, MPI_MIN, MyComm);
5094  MPI_Allreduce(&h_max, &gh_max, 1, MPI_DOUBLE, MPI_MAX, MyComm);
5095  MPI_Allreduce(&kappa_min, &gk_min, 1, MPI_DOUBLE, MPI_MIN, MyComm);
5096  MPI_Allreduce(&kappa_max, &gk_max, 1, MPI_DOUBLE, MPI_MAX, MyComm);
5097 }
5098 
5099 void ParMesh::PrintInfo(std::ostream &out)
5100 {
5101  int i;
5102  DenseMatrix J(Dim);
5103  double h_min, h_max, kappa_min, kappa_max, h, kappa;
5104 
5105  if (MyRank == 0)
5106  {
5107  out << "Parallel Mesh Stats:" << '\n';
5108  }
5109 
5110  for (i = 0; i < NumOfElements; i++)
5111  {
5112  GetElementJacobian(i, J);
5113  h = pow(fabs(J.Weight()), 1.0/double(Dim));
5114  kappa = (Dim == spaceDim) ?
5115  J.CalcSingularvalue(0) / J.CalcSingularvalue(Dim-1) : -1.0;
5116  if (i == 0)
5117  {
5118  h_min = h_max = h;
5119  kappa_min = kappa_max = kappa;
5120  }
5121  else
5122  {
5123  if (h < h_min) { h_min = h; }
5124  if (h > h_max) { h_max = h; }
5125  if (kappa < kappa_min) { kappa_min = kappa; }
5126  if (kappa > kappa_max) { kappa_max = kappa; }
5127  }
5128  }
5129 
5130  double gh_min, gh_max, gk_min, gk_max;
5131  MPI_Reduce(&h_min, &gh_min, 1, MPI_DOUBLE, MPI_MIN, 0, MyComm);
5132  MPI_Reduce(&h_max, &gh_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
5133  MPI_Reduce(&kappa_min, &gk_min, 1, MPI_DOUBLE, MPI_MIN, 0, MyComm);
5134  MPI_Reduce(&kappa_max, &gk_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
5135 
5136  // TODO: collect and print stats by geometry
5137 
5138  long ldata[5]; // vert, edge, face, elem, neighbors;
5139  long mindata[5], maxdata[5], sumdata[5];
5140 
5141  // count locally owned vertices, edges, and faces
5142  ldata[0] = GetNV();
5143  ldata[1] = GetNEdges();
5144  ldata[2] = GetNFaces();
5145  ldata[3] = GetNE();
5146  ldata[4] = gtopo.GetNumNeighbors()-1;
5147  for (int gr = 1; gr < GetNGroups(); gr++)
5148  {
5149  if (!gtopo.IAmMaster(gr)) // we are not the master
5150  {
5151  ldata[0] -= group_svert.RowSize(gr-1);
5152  ldata[1] -= group_sedge.RowSize(gr-1);
5153  ldata[2] -= group_stria.RowSize(gr-1);
5154  ldata[2] -= group_squad.RowSize(gr-1);
5155  }
5156  }
5157 
5158  MPI_Reduce(ldata, mindata, 5, MPI_LONG, MPI_MIN, 0, MyComm);
5159  MPI_Reduce(ldata, sumdata, 5, MPI_LONG, MPI_SUM, 0, MyComm);
5160  MPI_Reduce(ldata, maxdata, 5, MPI_LONG, MPI_MAX, 0, MyComm);
5161 
5162  if (MyRank == 0)
5163  {
5164  out << '\n'
5165  << " "
5166  << setw(12) << "minimum"
5167  << setw(12) << "average"
5168  << setw(12) << "maximum"
5169  << setw(12) << "total" << '\n';
5170  out << " vertices "
5171  << setw(12) << mindata[0]
5172  << setw(12) << sumdata[0]/NRanks
5173  << setw(12) << maxdata[0]
5174  << setw(12) << sumdata[0] << '\n';
5175  out << " edges "
5176  << setw(12) << mindata[1]
5177  << setw(12) << sumdata[1]/NRanks
5178  << setw(12) << maxdata[1]
5179  << setw(12) << sumdata[1] << '\n';
5180  if (Dim == 3)
5181  {
5182  out << " faces "
5183  << setw(12) << mindata[2]
5184  << setw(12) << sumdata[2]/NRanks
5185  << setw(12) << maxdata[2]
5186  << setw(12) << sumdata[2] << '\n';
5187  }
5188  out << " elements "
5189  << setw(12) << mindata[3]
5190  << setw(12) << sumdata[3]/NRanks
5191  << setw(12) << maxdata[3]
5192  << setw(12) << sumdata[3] << '\n';
5193  out << " neighbors "
5194  << setw(12) << mindata[4]
5195  << setw(12) << sumdata[4]/NRanks
5196  << setw(12) << maxdata[4] << '\n';
5197  out << '\n'
5198  << " "
5199  << setw(12) << "minimum"
5200  << setw(12) << "maximum" << '\n';
5201  out << " h "
5202  << setw(12) << gh_min
5203  << setw(12) << gh_max << '\n';
5204  out << " kappa "
5205  << setw(12) << gk_min
5206  << setw(12) << gk_max << '\n';
5207  out << std::flush;
5208  }
5209 }
5210 
5211 long ParMesh::ReduceInt(int value) const
5212 {
5213  long local = value, global;
5214  MPI_Allreduce(&local, &global, 1, MPI_LONG, MPI_SUM, MyComm);
5215  return global;
5216 }
5217 
5218 void ParMesh::ParPrint(ostream &out) const
5219 {
5220  if (NURBSext || pncmesh)
5221  {
5222  // TODO: AMR meshes, NURBS meshes.
5223  Print(out);
5224  return;
5225  }
5226 
5227  // Write out serial mesh. Tell serial mesh to deliniate the end of it's
5228  // output with 'mfem_serial_mesh_end' instead of 'mfem_mesh_end', as we will
5229  // be adding additional parallel mesh information.
5230  Printer(out, "mfem_serial_mesh_end");
5231 
5232  // write out group topology info.
5233  gtopo.Save(out);
5234 
5235  out << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
5236  if (Dim >= 2)
5237  {
5238  out << "total_shared_edges " << shared_edges.Size() << '\n';
5239  }
5240  if (Dim >= 3)
5241  {
5242  out << "total_shared_faces " << sface_lface.Size() << '\n';
5243  }
5244  for (int gr = 1; gr < GetNGroups(); gr++)
5245  {
5246  {
5247  const int nv = group_svert.RowSize(gr-1);
5248  const int *sv = group_svert.GetRow(gr-1);
5249  out << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
5250  for (int i = 0; i < nv; i++)
5251  {
5252  out << svert_lvert[sv[i]] << '\n';
5253  }
5254  }
5255  if (Dim >= 2)
5256  {
5257  const int ne = group_sedge.RowSize(gr-1);
5258  const int *se = group_sedge.GetRow(gr-1);
5259  out << "\nshared_edges " << ne << '\n';
5260  for (int i = 0; i < ne; i++)
5261  {
5262  const int *v = shared_edges[se[i]]->GetVertices();
5263  out << v[0] << ' ' << v[1] << '\n';
5264  }
5265  }
5266  if (Dim >= 3)
5267  {
5268  const int nt = group_stria.RowSize(gr-1);
5269  const int *st = group_stria.GetRow(gr-1);
5270  const int nq = group_squad.RowSize(gr-1);
5271  const int *sq = group_squad.GetRow(gr-1);
5272  out << "\nshared_faces " << nt+nq << '\n';
5273  for (int i = 0; i < nt; i++)
5274  {
5275  out << Geometry::TRIANGLE;
5276  const int *v = shared_trias[st[i]].v;
5277  for (int j = 0; j < 3; j++) { out << ' ' << v[j]; }
5278  out << '\n';
5279  }
5280  for (int i = 0; i < nq; i++)
5281  {
5282  out << Geometry::SQUARE;
5283  const int *v = shared_quads[sq[i]].v;
5284  for (int j = 0; j < 4; j++) { out << ' ' << v[j]; }
5285  out << '\n';
5286  }
5287  }
5288  }
5289 
5290  // Write out section end tag for mesh.
5291  out << "\nmfem_mesh_end" << endl;
5292 }
5293 
5294 int ParMesh::FindPoints(DenseMatrix& point_mat, Array<int>& elem_id,
5295  Array<IntegrationPoint>& ip, bool warn,
5296  InverseElementTransformation *inv_trans)
5297 {
5298  const int npts = point_mat.Width();
5299  if (npts == 0) { return 0; }
5300 
5301  const bool no_warn = false;
5302  Mesh::FindPoints(point_mat, elem_id, ip, no_warn, inv_trans);
5303 
5304  // If multiple processors find the same point, we need to choose only one of
5305  // the processors to mark that point as found.
5306  // Here, we choose the processor with the minimal rank.
5307 
5308  Array<int> my_point_rank(npts), glob_point_rank(npts);
5309  for (int k = 0; k < npts; k++)
5310  {
5311  my_point_rank[k] = (elem_id[k] == -1) ? NRanks : MyRank;
5312  }
5313 
5314  MPI_Allreduce(my_point_rank.GetData(), glob_point_rank.GetData(), npts,
5315  MPI_INT, MPI_MIN, MyComm);
5316 
5317  int pts_found = 0;
5318  for (int k = 0; k < npts; k++)
5319  {
5320  if (glob_point_rank[k] == NRanks) { elem_id[k] = -1; }
5321  else
5322  {
5323  pts_found++;
5324  if (glob_point_rank[k] != MyRank) { elem_id[k] = -2; }
5325  }
5326  }
5327  if (warn && pts_found != npts && MyRank == 0)
5328  {
5329  MFEM_WARNING((npts-pts_found) << " points were not found");
5330  }
5331  return pts_found;
5332 }
5333 
5334 static void PrintVertex(const Vertex &v, int space_dim, ostream &out)
5335 {
5336  out << v(0);
5337  for (int d = 1; d < space_dim; d++)
5338  {
5339  out << ' ' << v(d);
5340  }
5341 }
5342 
5343 void ParMesh::PrintSharedEntities(const char *fname_prefix) const
5344 {
5345  stringstream out_name;
5346  out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
5347  << ".shared_entities";
5348  ofstream out(out_name.str().c_str());
5349  out.precision(16);
5350 
5351  gtopo.Save(out);
5352 
5353  out << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
5354  if (Dim >= 2)
5355  {
5356  out << "total_shared_edges " << shared_edges.Size() << '\n';
5357  }
5358  if (Dim >= 3)
5359  {
5360  out << "total_shared_faces " << sface_lface.Size() << '\n';
5361  }
5362  for (int gr = 1; gr < GetNGroups(); gr++)
5363  {
5364  {
5365  const int nv = group_svert.RowSize(gr-1);
5366  const int *sv = group_svert.GetRow(gr-1);
5367  out << "\n# group " << gr << "\n\nshared_vertices " << nv << '\n';
5368  for (int i = 0; i < nv; i++)
5369  {
5370  const int lvi = svert_lvert[sv[i]];
5371  // out << lvi << '\n';
5372  PrintVertex(vertices[lvi], spaceDim, out);
5373  out << '\n';
5374  }
5375  }
5376  if (Dim >= 2)
5377  {
5378  const int ne = group_sedge.RowSize(gr-1);
5379  const int *se = group_sedge.GetRow(gr-1);
5380  out << "\nshared_edges " << ne << '\n';
5381  for (int i = 0; i < ne; i++)
5382  {
5383  const int *v = shared_edges[se[i]]->GetVertices();
5384  // out << v[0] << ' ' << v[1] << '\n';
5385  PrintVertex(vertices[v[0]], spaceDim, out);
5386  out << " | ";
5387  PrintVertex(vertices[v[1]], spaceDim, out);
5388  out << '\n';
5389  }
5390  }
5391  if (Dim >= 3)
5392  {
5393  const int nt = group_stria.RowSize(gr-1);
5394  const int *st = group_stria.GetRow(gr-1);
5395  const int nq = group_squad.RowSize(gr-1);
5396  const int *sq = group_squad.GetRow(gr-1);
5397  out << "\nshared_faces " << nt+nq << '\n';
5398  for (int i = 0; i < nt; i++)
5399  {
5400  const int *v = shared_trias[st[i]].v;
5401 #if 0
5402  out << Geometry::TRIANGLE;
5403  for (int j = 0; j < 3; j++) { out << ' ' << v[j]; }
5404  out << '\n';
5405 #endif
5406  for (int j = 0; j < 3; j++)
5407  {
5408  PrintVertex(vertices[v[j]], spaceDim, out);
5409  (j < 2) ? out << " | " : out << '\n';
5410  }
5411  }
5412  for (int i = 0; i < nq; i++)
5413  {
5414  const int *v = shared_quads[sq[i]].v;
5415 #if 0
5416  out << Geometry::SQUARE;
5417  for (int j = 0; j < 4; j++) { out << ' ' << v[j]; }
5418  out << '\n';
5419 #endif
5420  for (int j = 0; j < 4; j++)
5421  {
5422  PrintVertex(vertices[v[j]], spaceDim, out);
5423  (j < 3) ? out << " | " : out << '\n';
5424  }
5425  }
5426  }
5427  }
5428 }
5429 
5431 {
5432  delete pncmesh;
5433  ncmesh = pncmesh = NULL;
5434 
5436 
5437  for (int i = 0; i < shared_edges.Size(); i++)
5438  {
5440  }
5441 
5442  // The Mesh destructor is called automatically
5443 }
5444 
5445 }
5446 
5447 #endif
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition: intrules.hpp:245
Abstract class for Finite Elements.
Definition: fe.hpp:232
Geometry::Type GetGeometryType() const
Definition: element.hpp:52
int GetGroupMasterRank(int g) const
int GetNFaceNeighbors() const
Definition: pmesh.hpp:273
void PrintAsOneXG(std::ostream &out=mfem::out)
Old mesh format (Netgen/Truegrid) version of &#39;PrintAsOne&#39;.
Definition: pmesh.cpp:4540
void Create(ListOfIntegerSets &groups, int mpitag)
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition: mesh.cpp:3179
Ordering::Type GetOrdering() const
Return the ordering method.
Definition: fespace.hpp:393
void MultABt(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &ABt)
Multiply a matrix A with the transpose of a matrix B: A*Bt.
Definition: densemat.cpp:2391
int Size() const
Logical size of the array.
Definition: array.hpp:124
void SetSubVector(const Array< int > &dofs, const double value)
Set the entries listed in dofs to the given value.
Definition: vector.cpp:498
void GetConformingSharedStructures(class ParMesh &pmesh)
Definition: pncmesh.cpp:930
void Recreate(const int n, const int *p)
Definition: sets.cpp:59
ElementTransformation * Face
Definition: eltrans.hpp:356
int * GetJ()
Definition: table.hpp:114
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:90
DenseMatrix & GetPointMat()
Read and write access to the underlying point matrix describing the transformation.
Definition: eltrans.hpp:321
const double * GetVertex(int i) const
Return pointer to vertex i&#39;s coordinates.
Definition: mesh.hpp:750
virtual ~ParMesh()
Definition: pmesh.cpp:5430
void Unique()
Definition: array.hpp:237
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
void UniformRefineGroups2D(int old_nv)
Definition: pmesh.cpp:3627
void FreeElement(Element *E)
Definition: mesh.cpp:10108
int CheckElementOrientation(bool fix_it=true)
Check the orientation of the elements.
Definition: mesh.cpp:3981
int NRanks
Definition: pmesh.hpp:39
void SetSize(int dim, int connections_per_row)
Set the size and the number of connections for the table.
Definition: table.cpp:128
virtual Element * Duplicate(Mesh *m) const =0
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition: mesh.cpp:4431
int FindSharedVertices(const int *partition, Table *vertex_element, ListOfIntegerSets &groups)
Definition: pmesh.cpp:564
void AddColumnsInRow(int r, int ncol)
Definition: table.hpp:78
void UniformRefinement3D_base(Array< int > *f2qf=NULL, DSTable *v_to_v_p=NULL, bool update_nodes=true)
Definition: mesh.cpp:6440
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition: table.cpp:85
virtual void Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
Definition: fe.cpp:130
virtual void GetVertices(Array< int > &v) const =0
Returns element&#39;s vertices.
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Definition: pmesh.cpp:1715
Array< Element * > boundary
Definition: mesh.hpp:84
const FiniteElement * GetTraceElement(int i, Geometry::Type geom_type) const
Return the trace element from element &#39;i&#39; to the given &#39;geom_type&#39;.
Definition: fespace.cpp:1936
friend class ParNCMesh
Definition: pmesh.hpp:347
int * GeneratePartitioning(int nparts, int part_method=1)
Definition: mesh.cpp:5351
int BuildLocalVertices(const Mesh &global_mesh, const int *partitioning, Array< int > &vert_global_local)
Fills out partitioned Mesh::vertices.
Definition: pmesh.cpp:281
int GetNGroups() const
Definition: pmesh.hpp:249
CoarseFineTransformations CoarseFineTr
Definition: mesh.hpp:159
virtual void ReorientTetMesh()
See the remarks for the serial version in mesh.hpp.
Definition: pmesh.cpp:2508
int own_nodes
Definition: mesh.hpp:165
bool Conforming() const
Definition: mesh.hpp:1167
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:407
int GetNBE() const
Returns number of boundary elements.
Definition: mesh.hpp:696
IsoparametricTransformation Transformation
Definition: mesh.hpp:153
void GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: fespace.cpp:172
void GetCharacteristics(double &h_min, double &h_max, double &kappa_min, double &kappa_max, Vector *Vh=NULL, Vector *Vk=NULL)
Definition: mesh.cpp:184
void MakeOwner(FiniteElementCollection *_fec)
Make the GridFunction the owner of fec and fes.
Definition: gridfunc.hpp:112
Array< Element * > face_nbr_elements
Definition: pmesh.hpp:241
const Geometry::Type geom
Definition: ex1.cpp:40
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Definition: mesh.cpp:318
void NewNodes(GridFunction &nodes, bool make_owner=false)
Replace the internal node GridFunction with the given GridFunction.
Definition: mesh.cpp:6227
void GetFaceNeighbors(class ParMesh &pmesh)
Definition: pncmesh.cpp:1031
int NumOfEdges
Definition: mesh.hpp:63
Lists all edges/faces in the nonconforming mesh.
Definition: ncmesh.hpp:193
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition: operator.hpp:63
int GetGroupSize(int g) const
int GetNSharedFaces() const
Return the number of shared faces (3D), edges (2D), vertices (1D)
Definition: pmesh.cpp:2441
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition: mesh.cpp:4656
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition: mesh.cpp:120
void ShiftRight(int &a, int &b, int &c)
Definition: mesh.hpp:1451
Array< int > sface_lface
Definition: pmesh.hpp:79
void SetDims(int rows, int nnz)
Definition: table.cpp:144
void BuildSharedFaceElems(int ntri_faces, int nquad_faces, const Mesh &mesh, int *partitioning, const STable3D *faces_tbl, const Array< int > &face_group, const Array< int > &vert_global_local)
Definition: pmesh.cpp:699
const int * GetGroup(int g) const
Element::Type GetBdrElementType(int i) const
Returns the type of boundary element i.
Definition: mesh.cpp:4661
void Copy(Array &copy) const
Create a copy of the current array.
Definition: array.hpp:812
T * GetData()
Returns the data.
Definition: array.hpp:98
void SaveAsOne(std::ostream &out=mfem::out)
Merge the local grid functions.
Definition: pgridfunc.cpp:501
virtual void LocalRefinement(const Array< int > &marked_el, int type=3)
This function is not public anymore. Use GeneralRefinement instead.
Definition: pmesh.cpp:2698
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
GridFunction * Nodes
Definition: mesh.hpp:164
int NumOfElements
Definition: mesh.hpp:62
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition: table.cpp:191
IntegrationPointTransformation Loc2
Definition: eltrans.hpp:357
int GetBdrElementEdgeIndex(int i) const
Definition: mesh.cpp:4624
void GetCharacteristics(double &h_min, double &h_max, double &kappa_min, double &kappa_max)
Definition: pmesh.cpp:5086
void AverageVertices(const int *indexes, int n, int result)
Averages the vertices with given indexes and saves the result in vertices[result].
Definition: mesh.cpp:6251
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:693
void SynchronizeDerefinementData(Array< Type > &elem_data, const Table &deref_table)
Definition: pncmesh.cpp:1648
const Element * GetFace(int i) const
Definition: mesh.hpp:783
virtual void SetVertices(const int *ind)
Set the indices the element according to the input.
Definition: element.cpp:17
void UniformRefineGroups3D(int old_nv, int old_nedges, const DSTable &old_v_to_v, const STable3D &old_faces, Array< int > *f2qf)
Definition: pmesh.cpp:3678
virtual void OnMeshUpdated(Mesh *mesh)
Definition: pncmesh.cpp:186
virtual void Derefine(const Array< int > &derefs)
Definition: pncmesh.cpp:1476
Abstract parallel finite element space.
Definition: pfespace.hpp:28
bool have_face_nbr_data
Definition: pmesh.hpp:237
Array< Vert3 > shared_trias
Definition: pmesh.hpp:66
const FiniteElement * GetFE() const
Definition: eltrans.hpp:308
virtual int DofForGeometry(Geometry::Type GeomType) const
Definition: fe_coll.hpp:100
Array< int > face_nbr_vertices_offset
Definition: pmesh.hpp:240
std::vector< MeshId > conforming
Definition: ncmesh.hpp:195
Data type for vertex.
Definition: vertex.hpp:21
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition: mesh.cpp:3135
Array< int > RefEdges
Definition: geom.hpp:240
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 GetVertices(Vector &vert_coord) const
Definition: mesh.cpp:6132
virtual void SetAttributes()
Definition: pmesh.cpp:1337
The inverse transformation of a given ElementTransformation.
Definition: eltrans.hpp:115
void RebalanceImpl(const Array< int > *partition)
Definition: pmesh.cpp:3322
ElementTransformation * Elem2
Definition: eltrans.hpp:356
void BuildFaceGroup(int ngroups, const Mesh &mesh, const Array< int > &face_group, int &nstria, int &nsquad)
Definition: pmesh.cpp:601
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:166
virtual void MarkTetMeshForRefinement(DSTable &v_to_v)
Definition: pmesh.cpp:1385
int GroupNQuadrilaterals(int group)
Definition: pmesh.hpp:255
Geometry::Type GetElementBaseGeometry(int i) const
Definition: mesh.hpp:790
int Size_of_connections() const
Definition: table.hpp:98
Array< Element * > faces
Definition: mesh.hpp:85
const IntegrationRule * GetVertices(int GeomType)
Return an IntegrationRule consisting of all vertices of the given Geometry::Type, GeomType...
Definition: geom.cpp:228
const NCList & GetSharedEdges()
Definition: pncmesh.hpp:110
void GetVertexToVertexTable(DSTable &) const
Definition: mesh.cpp:4735
Array< int > sedge_ledge
Definition: pmesh.hpp:77
void RedRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition: mesh.hpp:268
Operation last_operation
Definition: mesh.hpp:203
void skip_comment_lines(std::istream &is, const char comment_char)
Definition: text.hpp:27
void DeleteAll()
Delete whole array.
Definition: array.hpp:802
Table group_stria
Definition: pmesh.hpp:72
void AddConnections(int r, const int *c, int nc)
Definition: table.cpp:108
void InitRefinementTransforms()
Definition: mesh.cpp:8072
IsoparametricTransformation FaceTransformation
Definition: mesh.hpp:155
const NCList & GetSharedList(int entity)
Helper to get shared vertices/edges/faces (&#39;entity&#39; == 0/1/2 resp.).
Definition: pncmesh.hpp:114
void GroupTriangle(int group, int i, int &face, int &o)
Definition: pmesh.cpp:1363
void UniformRefinement2D_base(bool update_nodes=true)
Definition: mesh.cpp:6281
Array< NCFaceInfo > nc_faces_info
Definition: mesh.hpp:142
bool IAmMaster(int g) const
bool WantSkipSharedMaster(const NCMesh::Master &master) const
Definition: pmesh.cpp:4089
A parallel extension of the NCMesh class.
Definition: pncmesh.hpp:64
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
Definition: pfespace.cpp:526
Element * NewElement(int geom)
Definition: mesh.cpp:3064
Table * el_to_face
Definition: mesh.hpp:145
FaceElementTransformations FaceElemTr
Definition: mesh.hpp:156
ParNCMesh * pncmesh
Definition: pmesh.hpp:247
Element::Type GetFaceElementType(int Face) const
Definition: mesh.cpp:991
void CopyTo(U *dest)
Definition: array.hpp:258
int FindSharedEdges(const Mesh &mesh, const int *partition, Table *&edge_element, ListOfIntegerSets &groups)
Definition: pmesh.cpp:510
int GroupVertex(int group, int i)
Definition: pmesh.hpp:257
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition: mesh.cpp:3943
virtual int FindPoints(DenseMatrix &point_mat, Array< int > &elem_ids, Array< IntegrationPoint > &ips, bool warn=true, InverseElementTransformation *inv_trans=NULL)
Find the ids of the elements that contain the given points, and their corresponding reference coordin...
Definition: pmesh.cpp:5294
void GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
Definition: pfespace.cpp:1160
void ExchangeFaceNbrData()
Definition: pmesh.cpp:1735
void BuildSharedVertMapping(int nvert, const Table *vert_element, const Array< int > &vert_global_local)
Definition: pmesh.cpp:813
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Definition: gridfunc.cpp:2731
void ReduceMeshGen()
Definition: pmesh.cpp:844
double Weight() const
Definition: densemat.cpp:506
virtual void GetVertices(Array< int > &v) const
Returns the indices of the element&#39;s vertices.
Definition: segment.cpp:40
virtual void UniformRefinement2D()
Refine a mixed 2D mesh uniformly.
Definition: pmesh.cpp:3827
void GetElementJacobian(int i, DenseMatrix &J)
Definition: mesh.cpp:60
Geometry Geometries
Definition: fe.cpp:12638
void Rebalance()
Definition: pmesh.cpp:3312
void GetVertexDofs(int i, Array< int > &dofs) const
Definition: fespace.cpp:1830
void MarkEdge(DenseMatrix &pmat)
Definition: triangle.cpp:53
const int * GetDofMap(Geometry::Type GeomType) const
Get the Cartesian to local H1 dof map.
Definition: fe_coll.cpp:1756
bool Nonconforming() const
Definition: mesh.hpp:1168
int BuildLocalBoundary(const Mesh &global_mesh, const int *partitioning, const Array< int > &vert_global_local, Array< bool > &activeBdrElem, Table *&edge_element)
Fills out partitioned Mesh::boundary.
Definition: pmesh.cpp:362
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
const FiniteElement * GetFaceNbrFE(int i) const
Definition: pfespace.cpp:1194
Array< int > face_nbr_elements_offset
Definition: pmesh.hpp:239
void GetEdgeOrdering(DSTable &v_to_v, Array< int > &order)
Definition: mesh.cpp:1788
MPI_Comm MyComm
Definition: pmesh.hpp:38
Symmetric 3D Table.
Definition: stable3d.hpp:29
void GetBoundingBox(Vector &p_min, Vector &p_max, int ref=2)
Definition: pmesh.cpp:5070
const Table & GetDerefinementTable()
Definition: ncmesh.cpp:1733
DenseTensor point_matrices[Geometry::NumGeom]
Matrices for IsoparametricTransformation organized by Geometry::Type.
Definition: ncmesh.hpp:63
Array< Element * > shared_edges
Definition: pmesh.hpp:62
virtual void SetAttributes()
Definition: mesh.cpp:1107
int Append(const T &el)
Append element to array, resize if necessary.
Definition: array.hpp:707
virtual void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0)
This function is not public anymore. Use GeneralRefinement instead.
Definition: pmesh.cpp:3202
IntegrationPointTransformation Loc1
Definition: eltrans.hpp:357
int GetSharedFace(int sface) const
Return the local face index for the given shared face.
Definition: pmesh.cpp:2460
int GetNumNeighbors() const
void Clear()
Definition: table.cpp:381
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
Definition: error.cpp:153
void BuildSharedEdgeElems(int nedges, Mesh &mesh, const Array< int > &vert_global_local, const Table *edge_element)
Definition: pmesh.cpp:776
double b
Definition: lissajous.cpp:42
void PrintAsOne(std::ostream &out=mfem::out)
Definition: pmesh.cpp:4231
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
void GetFaceTransformation(int i, IsoparametricTransformation *FTr)
Returns the transformation defining the given face element in a user-defined variable.
Definition: mesh.cpp:478
std::vector< Master > masters
Definition: ncmesh.hpp:196
static const int NumVerts[NumGeom]
Definition: geom.hpp:48
void AddConnection(int r, int c)
Definition: table.hpp:80
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition: mesh.cpp:5137
FaceElementTransformations * GetSharedFaceTransformations(int sf, bool fill2=true)
Definition: pmesh.cpp:2356
void LoseData()
NULL-ifies the data.
Definition: array.hpp:118
Table send_face_nbr_vertices
Definition: pmesh.hpp:245
Type
Constants for the classes derived from Element.
Definition: element.hpp:41
T Max() const
Find the maximal element in the array, using the comparison operator &lt; for class T.
Definition: array.cpp:68
int InitialPartition(int index) const
Helper to get the partitioning when the serial mesh gets split initially.
Definition: pncmesh.hpp:305
const IntegrationRule & GetNodes() const
Definition: fe.hpp:367
int Insert(IntegerSet &s)
Definition: sets.cpp:82
virtual void Print(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:4102
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition: array.hpp:142
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
Definition: pfespace.cpp:573
Array< Embedding > embeddings
Fine element positions in their parents.
Definition: ncmesh.hpp:65
void Transpose(const Table &A, Table &At, int _ncols_A)
Transpose a Table.
Definition: table.cpp:414
GeometryRefiner GlobGeometryRefiner
Definition: geom.cpp:1395
int GetAttribute() const
Return element&#39;s attribute.
Definition: element.hpp:55
int GetElementToEdgeTable(Table &, Array< int > &)
Definition: mesh.cpp:4760
void MarkCoarseLevel()
Definition: ncmesh.cpp:3857
const Element * GetElement(int i) const
Definition: mesh.hpp:775
void ResetLazyData()
Definition: mesh.cpp:1097
IntegrationRule RefPts
Definition: geom.hpp:239
void Sort()
Sorts the array. This requires operator&lt; to be defined for T.
Definition: array.hpp:229
int GroupNVertices(int group)
Definition: pmesh.hpp:252
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
Definition: geom.cpp:925
void GetLocalFaceTransformation(int face_type, int elem_type, IsoparametricTransformation &Transf, int info)
A helper method that constructs a transformation from the reference space of a face to the reference ...
Definition: mesh.cpp:817
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
Definition: pfespace.cpp:549
IsoparametricTransformation Transformation2
Definition: mesh.hpp:153
FiniteElementCollection * OwnFEC()
Definition: gridfunc.hpp:114
int Size() const
Returns the number of TYPE I elements.
Definition: table.hpp:92
int GetVDim() const
Returns vector dimension.
Definition: fespace.hpp:370
FiniteElementSpace * FESpace()
Definition: gridfunc.hpp:442
int NumOfBdrElements
Definition: mesh.hpp:62
Table * el_to_edge
Definition: mesh.hpp:144
Array< Vert4 > shared_quads
Definition: pmesh.hpp:67
void Rebalance(const Array< int > *custom_partition=NULL)
Definition: pncmesh.cpp:1786
int slaves_end
slave faces
Definition: ncmesh.hpp:170
Data type tetrahedron element.
Definition: tetrahedron.hpp:22
Vector & FaceNbrData()
Definition: pgridfunc.hpp:198
int CheckBdrElementOrientation(bool fix_it=true)
Check the orientation of the boundary elements.
Definition: mesh.cpp:4212
int SpaceDimension() const
Definition: mesh.hpp:745
void GetBdrElementFace(int i, int *, int *) const
Return the index and the orientation of the face of bdr element i. (3D)
Definition: mesh.cpp:4601
void GroupQuadrilateral(int group, int i, int &face, int &o)
Definition: pmesh.cpp:1374
void Save(std::ostream &out) const
Save the data in a stream.
int GroupNTriangles(int group)
Definition: pmesh.hpp:254
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Definition: mesh.cpp:974
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition: mesh.hpp:189
void FindSharedFaces(const Mesh &mesh, const int *partition, Array< int > &face_group, ListOfIntegerSets &groups)
Definition: pmesh.cpp:483
void RefineGroups(const DSTable &v_to_v, int *middle)
Update the groups after triangle refinement.
Definition: pmesh.cpp:3370
virtual bool NonconformingDerefinement(Array< double > &elem_error, double threshold, int nc_limit=0, int op=1)
NC version of GeneralDerefinement.
Definition: pmesh.cpp:3254
Array< Vertex > face_nbr_vertices
Definition: pmesh.hpp:242
std::vector< Slave > slaves
Definition: ncmesh.hpp:197
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:87
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition: fe.hpp:314
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 ApplyLocalSlaveTransformation(IsoparametricTransformation &transf, const FaceInfo &fi)
Definition: mesh.cpp:934
static void PrintElement(const Element *, std::ostream &)
Definition: mesh.cpp:3129
int GetNeighborRank(int i) const
void AddAColumnInRow(int r)
Definition: table.hpp:77
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition: mesh.hpp:1059
void SetSize(int nsize)
Change logical size of the array, keep existing entries.
Definition: array.hpp:635
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition: mesh.cpp:1836
void PrintSharedEntities(const char *fname_prefix) const
Debugging method.
Definition: pmesh.cpp:5343
virtual long ReduceInt(int value) const
Utility function: sum integers from all processors (Allreduce).
Definition: pmesh.cpp:5211
void Transform(const IntegrationPoint &, IntegrationPoint &)
Definition: eltrans.cpp:537
bool IsSlaveFace(const FaceInfo &fi) const
Definition: mesh.cpp:929
Array< Vertex > vertices
Definition: mesh.hpp:83
void DeleteLast()
Delete the last entry.
Definition: array.hpp:177
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
Array< Element * > elements
Definition: mesh.hpp:78
double GetLength(int i, int j) const
Return the length of the segment from node i to node j.
Definition: mesh.cpp:4698
void ShiftUpI()
Definition: table.cpp:119
int meshgen
Definition: mesh.hpp:70
void BuildVertexGroup(int ngroups, const Table &vert_element)
Definition: pmesh.cpp:673
static FiniteElementCollection * New(const char *name)
Factory method: return a newly allocated FiniteElementCollection according to the given name...
Definition: fe_coll.cpp:47
void GetFaceNbrElementTransformation(int i, IsoparametricTransformation *ElTr)
Definition: pmesh.cpp:1642
double a
Definition: lissajous.cpp:41
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:191
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition: mesh.hpp:690
Array< int > be_to_edge
Definition: mesh.hpp:147
virtual const char * Name() const
Definition: fe_coll.hpp:53
void DistributeAttributes(Array< int > &attr)
Ensure that bdr_attributes and attributes agree across processors.
Definition: pmesh.cpp:1295
virtual void Refine(const Array< Refinement > &refinements)
Definition: pncmesh.cpp:1380
void Swap(Mesh &other, bool non_geometry)
Definition: mesh.cpp:7464
void BuildEdgeGroup(int ngroups, const Table &edge_element)
Definition: pmesh.cpp:647
void GetFaceSplittings(const int *fv, const HashTable< Hashed2 > &v_to_v, Array< unsigned > &codes)
Append codes identifying how the given face has been split to codes.
Definition: pmesh.cpp:1528
void GetElementTransformation(int i, IsoparametricTransformation *ElTr)
Definition: mesh.cpp:338
Table group_sedge
Definition: pmesh.hpp:71
void ExchangeFaceNbrNodes()
Definition: pmesh.cpp:2189
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition: mesh.cpp:2316
static const int Orient[NumOrient][NumVert]
Definition: geom.hpp:173
A set of integers.
Definition: sets.hpp:23
int BuildLocalElements(const Mesh &global_mesh, const int *partitioning, const Array< int > &vert_global_local)
Fills out partitioned Mesh::elements.
Definition: pmesh.cpp:329
void SetIJ(int *newI, int *newJ, int newsize=-1)
Replace the I and J arrays with the given newI and newJ arrays.
Definition: table.cpp:208
void MakeJ()
Definition: table.cpp:95
Table group_svert
Shared objects in each group.
Definition: pmesh.hpp:70
bool DecodeFaceSplittings(HashTable< Hashed2 > &v_to_v, const int *v, const Array< unsigned > &codes, int &pos)
Definition: pmesh.cpp:1563
ElementTransformation * GetGhostFaceTransformation(FaceElementTransformations *FETr, Element::Type face_type, Geometry::Type face_geom)
Definition: pmesh.cpp:2324
void SetFE(const FiniteElement *FE)
Definition: eltrans.hpp:307
long sequence
Definition: mesh.hpp:76
virtual void UniformRefinement3D()
Refine a mixed 3D mesh uniformly.
Definition: pmesh.cpp:3851
IsoparametricTransformation Transf
Definition: eltrans.hpp:347
int index(int i, int j, int nx, int ny)
Definition: life.cpp:241
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
Definition: pncmesh.hpp:170
ElementTransformation * Elem1
Definition: eltrans.hpp:356
double CalcSingularvalue(const int i) const
Return the i-th singular value (decreasing order) of NxN matrix, N=1,2,3.
Definition: densemat.cpp:1224
void GetVectorValues(ElementTransformation &T, const IntegrationRule &ir, DenseMatrix &vals) const
Definition: gridfunc.cpp:620
Array< FaceInfo > faces_info
Definition: mesh.hpp:141
virtual int GetNVertices() const =0
void SetAttribute(const int attr)
Set element&#39;s attribute.
Definition: element.hpp:58
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition: pmesh.cpp:1281
STable3D * GetFacesTable()
Definition: mesh.cpp:5088
NCMesh * ncmesh
Optional non-conforming mesh extension.
Definition: mesh.hpp:192
T & Last()
Return the last element in the array.
Definition: array.hpp:740
int Dim
Definition: mesh.hpp:59
virtual void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level)
Definition: pncmesh.cpp:1728
int GetEdgeSplittings(Element *edge, const DSTable &v_to_v, int *middle)
Return a number(0-1) identifying how the given edge has been split.
Definition: pmesh.cpp:1513
Geometry::Type GetFaceGeometryType(int Face) const
Definition: mesh.cpp:986
int GetNEdges() const
Return the number of edges.
Definition: mesh.hpp:699
virtual void PrintXG(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:3888
double AggregateError(const Array< double > &elem_error, const int *fine, int nfine, int op)
Derefinement helper.
Definition: mesh.cpp:7324
void GetMarkedFace(const int face, int *fv)
void GroupEdge(int group, int i, int &edge, int &o)
Definition: pmesh.cpp:1355
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition: mesh.hpp:702
virtual int FindPoints(DenseMatrix &point_mat, Array< int > &elem_ids, Array< IntegrationPoint > &ips, bool warn=true, InverseElementTransformation *inv_trans=NULL)
Find the ids of the elements that contain the given points, and their corresponding reference coordin...
Definition: mesh.cpp:10133
double kappa
Definition: ex3.cpp:54
Vector data type.
Definition: vector.hpp:48
const FiniteElementCollection * FEColl() const
Definition: fespace.hpp:395
virtual void Transform(const IntegrationPoint &, Vector &)=0
void GetNodes(Vector &node_coord) const
Definition: mesh.cpp:6203
void GenerateOffsets(int N, HYPRE_Int loc_sizes[], Array< HYPRE_Int > *offsets[]) const
Definition: pmesh.cpp:1599
int NumberOfEntries() const
Definition: table.hpp:241
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
int * GetI()
Definition: table.hpp:113
int GetId(int p1, int p2)
Get id of item whose parents are p1, p2... Create it if it doesn&#39;t exist.
Definition: hash.hpp:279
Arbitrary order H1-conforming (continuous) finite elements.
Definition: fe_coll.hpp:83
Array< int > svert_lvert
Shared to local index mapping.
Definition: pmesh.hpp:76
void GenerateFaces()
Definition: mesh.cpp:4950
const NCList & GetSharedFaces()
Definition: pncmesh.hpp:111
int MyRank
Definition: pmesh.hpp:39
int spaceDim
Definition: mesh.hpp:60
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition: ncmesh.hpp:219
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition: mesh.cpp:1902
int RowSize(int i) const
Definition: table.hpp:108
Class for parallel grid function.
Definition: pgridfunc.hpp:32
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition: densemat.hpp:90
Table send_face_nbr_elements
Definition: pmesh.hpp:244
virtual void PrintInfo(std::ostream &out=mfem::out)
Print various parallel mesh stats.
Definition: pmesh.cpp:5099
List of integer sets.
Definition: sets.hpp:54
int NumOfVertices
Definition: mesh.hpp:62
Table * GetFaceToAllElementTable() const
Definition: pmesh.cpp:2266
void FinalizeParTopo()
Definition: pmesh.cpp:850
void DeleteFaceNbrData()
Definition: pmesh.cpp:1694
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
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition: mesh.cpp:7737
int FindId(int p1, int p2) const
Find id of item whose parents are p1, p2... Return -1 if it doesn&#39;t exist.
Definition: hash.hpp:370
void ParPrint(std::ostream &out) const
Save the mesh in a parallel mesh format.
Definition: pmesh.cpp:5218
Class for parallel meshes.
Definition: pmesh.hpp:32
Abstract data type element.
Definition: element.hpp:28
int GetAttribute(int i) const
Return the attribute of element i.
Definition: mesh.hpp:1001
int GetFaceNbrRank(int fn) const
Definition: pmesh.cpp:2249
const Table & ElementToEdgeTable() const
Definition: mesh.cpp:4844
Data type line segment element.
Definition: segment.hpp:22
void BdrBisection(int i, const HashTable< Hashed2 > &)
Bisect a boundary triangle: boundary element with index i is bisected.
Definition: mesh.cpp:7945
Table group_squad
Definition: pmesh.hpp:73
void GenerateNCFaceInfo()
Definition: mesh.cpp:5035
Array< int > RefGeoms
Definition: geom.hpp:240
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition: mesh.hpp:834
virtual Type GetType() const =0
Returns element&#39;s type.
virtual void LimitNCLevel(int max_nc_level)
Parallel version of NCMesh::LimitNCLevel.
Definition: pncmesh.cpp:1458
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition: mesh.hpp:187
Table * GetVertexToElementTable()
The returned Table must be destroyed by the caller.
Definition: mesh.cpp:4494
const Element * GetBdrElement(int i) const
Definition: mesh.hpp:779
static const int Orient[NumOrient][NumVert]
Definition: geom.hpp:152
void Load(std::istream &in)
Load the data from a stream.
void UpdateNodes()
Update the nodes of a curved mesh after refinement.
Definition: mesh.cpp:6272
Defines the position of a fine element within a coarse element.
Definition: ncmesh.hpp:46
int GroupNEdges(int group)
Definition: pmesh.hpp:253
void GreenRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition: mesh.hpp:274
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:143
void Printer(std::ostream &out=mfem::out, std::string section_delimiter="") const
Definition: mesh.cpp:8321
ParFiniteElementSpace * ParFESpace() const
Definition: pgridfunc.hpp:103
virtual void NURBSUniformRefinement()
Refine NURBS mesh.
Definition: pmesh.cpp:3880
int NumOfFaces
Definition: mesh.hpp:63