MFEM  v4.5.2
Finite element discretization library
pmesh.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, 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  face_nbr_el_to_face(NULL),
38  glob_elem_offset(-1),
39  glob_offset_sequence(-1),
40  gtopo(pmesh.gtopo)
41 {
42  MyComm = pmesh.MyComm;
43  NRanks = pmesh.NRanks;
44  MyRank = pmesh.MyRank;
45 
46  // Duplicate the shared_edges
47  shared_edges.SetSize(pmesh.shared_edges.Size());
48  for (int i = 0; i < shared_edges.Size(); i++)
49  {
50  shared_edges[i] = pmesh.shared_edges[i]->Duplicate(this);
51  }
52 
53  shared_trias = pmesh.shared_trias;
54  shared_quads = pmesh.shared_quads;
55 
56  // Copy the shared-to-local index Arrays
59  sface_lface = pmesh.sface_lface;
60 
61  // Do not copy face-neighbor data (can be generated if needed)
62  have_face_nbr_data = false;
63 
64  // If pmesh has a ParNURBSExtension, it was copied by the Mesh copy ctor, so
65  // there is no need to do anything here.
66 
67  // Copy ParNCMesh, if present
68  if (pmesh.pncmesh)
69  {
70  pncmesh = new ParNCMesh(*pmesh.pncmesh);
71  pncmesh->OnMeshUpdated(this);
72  }
73  else
74  {
75  pncmesh = NULL;
76  }
77  ncmesh = pncmesh;
78 
79  // Copy the Nodes as a ParGridFunction, including the FiniteElementCollection
80  // and the FiniteElementSpace (as a ParFiniteElementSpace)
81  if (pmesh.Nodes && copy_nodes)
82  {
83  FiniteElementSpace *fes = pmesh.Nodes->FESpace();
84  const FiniteElementCollection *fec = fes->FEColl();
85  FiniteElementCollection *fec_copy =
87  ParFiniteElementSpace *pfes_copy =
88  new ParFiniteElementSpace(*fes, *this, fec_copy);
89  Nodes = new ParGridFunction(pfes_copy);
90  Nodes->MakeOwner(fec_copy);
91  *Nodes = *pmesh.Nodes;
92  own_nodes = 1;
93  }
94 }
95 
97 {
98  Swap(mesh);
99 }
100 
102 {
103  Swap(mesh);
104  return *this;
105 }
106 
107 ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
108  int part_method)
109  : face_nbr_el_to_face(NULL)
110  , glob_elem_offset(-1)
111  , glob_offset_sequence(-1)
112  , gtopo(comm)
113 {
114  int *partitioning = NULL;
115  Array<bool> activeBdrElem;
116 
117  MyComm = comm;
118  MPI_Comm_size(MyComm, &NRanks);
119  MPI_Comm_rank(MyComm, &MyRank);
120 
121  if (mesh.Nonconforming())
122  {
123  if (partitioning_)
124  {
125  partitioning = partitioning_;
126  }
127  ncmesh = pncmesh = new ParNCMesh(comm, *mesh.ncmesh, partitioning);
128  if (!partitioning)
129  {
130  partitioning = new int[mesh.GetNE()];
131  for (int i = 0; i < mesh.GetNE(); i++)
132  {
133  partitioning[i] = pncmesh->InitialPartition(i);
134  }
135  }
136 
137  pncmesh->Prune();
138 
140  pncmesh->OnMeshUpdated(this);
141 
143 
144  // SetMeshGen(); // called by Mesh::InitFromNCMesh(...) above
145  meshgen = mesh.meshgen; // copy the global 'meshgen'
146 
147  mesh.attributes.Copy(attributes);
149 
151  }
152  else // mesh.Conforming()
153  {
154  Dim = mesh.Dim;
155  spaceDim = mesh.spaceDim;
156 
157  ncmesh = pncmesh = NULL;
158 
159  if (partitioning_)
160  {
161  partitioning = partitioning_;
162  }
163  else
164  {
165  partitioning = mesh.GeneratePartitioning(NRanks, part_method);
166  }
167 
168  // re-enumerate the partitions to better map to actual processor
169  // interconnect topology !?
170 
171  Array<int> vert_global_local;
172  NumOfVertices = BuildLocalVertices(mesh, partitioning, vert_global_local);
173  NumOfElements = BuildLocalElements(mesh, partitioning, vert_global_local);
174 
175  Table *edge_element = NULL;
176  NumOfBdrElements = BuildLocalBoundary(mesh, partitioning,
177  vert_global_local,
178  activeBdrElem, edge_element);
179 
180  SetMeshGen();
181  meshgen = mesh.meshgen; // copy the global 'meshgen'
182 
183  mesh.attributes.Copy(attributes);
185 
186  NumOfEdges = NumOfFaces = 0;
187 
188  if (Dim > 1)
189  {
190  el_to_edge = new Table;
192  }
193 
194  STable3D *faces_tbl = NULL;
195  if (Dim == 3)
196  {
197  faces_tbl = GetElementToFaceTable(1);
198  }
199 
200  GenerateFaces();
201 
202  ListOfIntegerSets groups;
203  {
204  // the first group is the local one
205  IntegerSet group;
206  group.Recreate(1, &MyRank);
207  groups.Insert(group);
208  }
209 
210  MFEM_ASSERT(mesh.GetNFaces() == 0 || Dim >= 3, "");
211 
212  Array<int> face_group(mesh.GetNFaces());
213  Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
214 
215  FindSharedFaces(mesh, partitioning, face_group, groups);
216  int nsedges = FindSharedEdges(mesh, partitioning, edge_element, groups);
217  int nsvert = FindSharedVertices(partitioning, vert_element, groups);
218 
219  // build the group communication topology
220  gtopo.Create(groups, 822);
221 
222  // fill out group_sface, group_sedge, group_svert
223  int ngroups = groups.Size()-1, nstris, nsquads;
224  BuildFaceGroup(ngroups, mesh, face_group, nstris, nsquads);
225  BuildEdgeGroup(ngroups, *edge_element);
226  BuildVertexGroup(ngroups, *vert_element);
227 
228  // build shared_faces and sface_lface mapping
229  BuildSharedFaceElems(nstris, nsquads, mesh, partitioning, faces_tbl,
230  face_group, vert_global_local);
231  delete faces_tbl;
232 
233  // build shared_edges and sedge_ledge mapping
234  BuildSharedEdgeElems(nsedges, mesh, vert_global_local, edge_element);
235  delete edge_element;
236 
237  // build svert_lvert mapping
238  BuildSharedVertMapping(nsvert, vert_element, vert_global_local);
239  delete vert_element;
240 
241  SetMeshGen();
242  meshgen = mesh.meshgen; // copy the global 'meshgen'
243  }
244 
245  if (mesh.NURBSext)
246  {
247  MFEM_ASSERT(mesh.GetNodes() &&
248  mesh.GetNodes()->FESpace()->GetNURBSext() == mesh.NURBSext,
249  "invalid NURBS mesh");
250  NURBSext = new ParNURBSExtension(comm, mesh.NURBSext, partitioning,
251  activeBdrElem);
252  }
253 
254  if (mesh.GetNodes()) // curved mesh
255  {
256  if (!NURBSext)
257  {
258  Nodes = new ParGridFunction(this, mesh.GetNodes());
259  }
260  else
261  {
262  const FiniteElementSpace *glob_fes = mesh.GetNodes()->FESpace();
264  FiniteElementCollection::New(glob_fes->FEColl()->Name());
265  ParFiniteElementSpace *pfes =
266  new ParFiniteElementSpace(this, nfec, glob_fes->GetVDim(),
267  glob_fes->GetOrdering());
268  Nodes = new ParGridFunction(pfes);
269  Nodes->MakeOwner(nfec); // Nodes will own nfec and pfes
270  }
271  own_nodes = 1;
272 
273  Array<int> gvdofs, lvdofs;
274  Vector lnodes;
275  int element_counter = 0;
276  for (int i = 0; i < mesh.GetNE(); i++)
277  {
278  if (partitioning[i] == MyRank)
279  {
280  Nodes->FESpace()->GetElementVDofs(element_counter, lvdofs);
281  mesh.GetNodes()->FESpace()->GetElementVDofs(i, gvdofs);
282  mesh.GetNodes()->GetSubVector(gvdofs, lnodes);
283  Nodes->SetSubVector(lvdofs, lnodes);
284  element_counter++;
285  }
286  }
287 
288  // set meaningful values to 'vertices' even though we have Nodes,
289  // for compatibility (e.g., Mesh::GetVertex())
291  }
292 
293  if (partitioning != partitioning_)
294  {
295  delete [] partitioning;
296  }
297 
298  have_face_nbr_data = false;
299 }
300 
301 
303  const int* partitioning,
304  Array<int> &vert_global_local)
305 {
306  vert_global_local.SetSize(mesh.GetNV());
307  vert_global_local = -1;
308 
309  int vert_counter = 0;
310  for (int i = 0; i < mesh.GetNE(); i++)
311  {
312  if (partitioning[i] == MyRank)
313  {
314  Array<int> vert;
315  mesh.GetElementVertices(i, vert);
316  for (int j = 0; j < vert.Size(); j++)
317  {
318  if (vert_global_local[vert[j]] < 0)
319  {
320  vert_global_local[vert[j]] = vert_counter++;
321  }
322  }
323  }
324  }
325 
326  // re-enumerate the local vertices to preserve the global ordering
327  vert_counter = 0;
328  for (int i = 0; i < vert_global_local.Size(); i++)
329  {
330  if (vert_global_local[i] >= 0)
331  {
332  vert_global_local[i] = vert_counter++;
333  }
334  }
335 
336  vertices.SetSize(vert_counter);
337 
338  for (int i = 0; i < vert_global_local.Size(); i++)
339  {
340  if (vert_global_local[i] >= 0)
341  {
342  vertices[vert_global_local[i]].SetCoords(mesh.SpaceDimension(),
343  mesh.GetVertex(i));
344  }
345  }
346 
347  return vert_counter;
348 }
349 
350 int ParMesh::BuildLocalElements(const Mesh& mesh, const int* partitioning,
351  const Array<int>& vert_global_local)
352 {
353  int nelems = 0;
354  for (int i = 0; i < mesh.GetNE(); i++)
355  {
356  if (partitioning[i] == MyRank) { nelems++; }
357  }
358 
359  elements.SetSize(nelems);
360 
361  // Determine elements, enumerating the local elements to preserve the global
362  // order. This is used, e.g. by the ParGridFunction ctor that takes a global
363  // GridFunction as input parameter.
364  int element_counter = 0;
365  for (int i = 0; i < mesh.GetNE(); i++)
366  {
367  if (partitioning[i] == MyRank)
368  {
369  elements[element_counter] = mesh.GetElement(i)->Duplicate(this);
370  int *v = elements[element_counter]->GetVertices();
371  int nv = elements[element_counter]->GetNVertices();
372  for (int j = 0; j < nv; j++)
373  {
374  v[j] = vert_global_local[v[j]];
375  }
376  element_counter++;
377  }
378  }
379 
380  return element_counter;
381 }
382 
383 int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
384  const Array<int>& vert_global_local,
385  Array<bool>& activeBdrElem,
386  Table*& edge_element)
387 {
388  int nbdry = 0;
389 
390  if (mesh.NURBSext)
391  {
392  activeBdrElem.SetSize(mesh.GetNBE());
393  activeBdrElem = false;
394  }
395  // build boundary elements
396  if (Dim == 3)
397  {
398  for (int i = 0; i < mesh.GetNBE(); i++)
399  {
400  int face, o, el1, el2;
401  mesh.GetBdrElementFace(i, &face, &o);
402  mesh.GetFaceElements(face, &el1, &el2);
403  if (partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2] == MyRank)
404  {
405  nbdry++;
406  if (mesh.NURBSext)
407  {
408  activeBdrElem[i] = true;
409  }
410  }
411  }
412 
413  int bdrelem_counter = 0;
414  boundary.SetSize(nbdry);
415  for (int i = 0; i < mesh.GetNBE(); i++)
416  {
417  int face, o, el1, el2;
418  mesh.GetBdrElementFace(i, &face, &o);
419  mesh.GetFaceElements(face, &el1, &el2);
420  if (partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2] == MyRank)
421  {
422  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
423  int *v = boundary[bdrelem_counter]->GetVertices();
424  int nv = boundary[bdrelem_counter]->GetNVertices();
425  for (int j = 0; j < nv; j++)
426  {
427  v[j] = vert_global_local[v[j]];
428  }
429  bdrelem_counter++;
430  }
431  }
432  }
433  else if (Dim == 2)
434  {
435  edge_element = new Table;
436  Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
437 
438  for (int i = 0; i < mesh.GetNBE(); i++)
439  {
440  int edge = mesh.GetBdrElementEdgeIndex(i);
441  int el1 = edge_element->GetRow(edge)[0];
442  if (partitioning[el1] == MyRank)
443  {
444  nbdry++;
445  if (mesh.NURBSext)
446  {
447  activeBdrElem[i] = true;
448  }
449  }
450  }
451 
452  int bdrelem_counter = 0;
453  boundary.SetSize(nbdry);
454  for (int i = 0; i < mesh.GetNBE(); i++)
455  {
456  int edge = mesh.GetBdrElementEdgeIndex(i);
457  int el1 = edge_element->GetRow(edge)[0];
458  if (partitioning[el1] == MyRank)
459  {
460  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
461  int *v = boundary[bdrelem_counter]->GetVertices();
462  int nv = boundary[bdrelem_counter]->GetNVertices();
463  for (int j = 0; j < nv; j++)
464  {
465  v[j] = vert_global_local[v[j]];
466  }
467  bdrelem_counter++;
468  }
469  }
470  }
471  else if (Dim == 1)
472  {
473  for (int i = 0; i < mesh.GetNBE(); i++)
474  {
475  int vert = mesh.boundary[i]->GetVertices()[0];
476  int el1, el2;
477  mesh.GetFaceElements(vert, &el1, &el2);
478  if (partitioning[el1] == MyRank)
479  {
480  nbdry++;
481  }
482  }
483 
484  int bdrelem_counter = 0;
485  boundary.SetSize(nbdry);
486  for (int i = 0; i < mesh.GetNBE(); i++)
487  {
488  int vert = mesh.boundary[i]->GetVertices()[0];
489  int el1, el2;
490  mesh.GetFaceElements(vert, &el1, &el2);
491  if (partitioning[el1] == MyRank)
492  {
493  boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate(this);
494  int *v = boundary[bdrelem_counter]->GetVertices();
495  v[0] = vert_global_local[v[0]];
496  bdrelem_counter++;
497  }
498  }
499  }
500 
501  return nbdry;
502 }
503 
504 void ParMesh::FindSharedFaces(const Mesh &mesh, const int *partitioning,
505  Array<int> &face_group,
506  ListOfIntegerSets &groups)
507 {
508  IntegerSet group;
509 
510  // determine shared faces
511  face_group.SetSize(mesh.GetNFaces());
512  for (int i = 0; i < face_group.Size(); i++)
513  {
514  int el[2];
515  face_group[i] = -1;
516  mesh.GetFaceElements(i, &el[0], &el[1]);
517  if (el[1] >= 0)
518  {
519  el[0] = partitioning[el[0]];
520  el[1] = partitioning[el[1]];
521  if ((el[0] == MyRank && el[1] != MyRank) ||
522  (el[0] != MyRank && el[1] == MyRank))
523  {
524  group.Recreate(2, el);
525  face_group[i] = groups.Insert(group) - 1;
526  }
527  }
528  }
529 }
530 
531 int ParMesh::FindSharedEdges(const Mesh &mesh, const int *partitioning,
532  Table*& edge_element,
533  ListOfIntegerSets& groups)
534 {
535  IntegerSet group;
536 
537  // determine shared edges
538  int sedge_counter = 0;
539  if (!edge_element)
540  {
541  edge_element = new Table;
542  if (Dim == 1)
543  {
544  edge_element->SetDims(0,0);
545  }
546  else
547  {
548  Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
549  }
550  }
551 
552  for (int i = 0; i < edge_element->Size(); i++)
553  {
554  int me = 0, others = 0;
555  for (int j = edge_element->GetI()[i]; j < edge_element->GetI()[i+1]; j++)
556  {
557  int k = edge_element->GetJ()[j];
558  int rank = partitioning[k];
559  edge_element->GetJ()[j] = rank;
560  if (rank == MyRank)
561  {
562  me = 1;
563  }
564  else
565  {
566  others = 1;
567  }
568  }
569 
570  if (me && others)
571  {
572  sedge_counter++;
573  group.Recreate(edge_element->RowSize(i), edge_element->GetRow(i));
574  edge_element->GetRow(i)[0] = groups.Insert(group) - 1;
575  }
576  else
577  {
578  edge_element->GetRow(i)[0] = -1;
579  }
580  }
581 
582  return sedge_counter;
583 }
584 
585 int ParMesh::FindSharedVertices(const int *partitioning, Table *vert_element,
586  ListOfIntegerSets &groups)
587 {
588  IntegerSet group;
589 
590  // determine shared vertices
591  int svert_counter = 0;
592  for (int i = 0; i < vert_element->Size(); i++)
593  {
594  int me = 0, others = 0;
595  for (int j = vert_element->GetI()[i]; j < vert_element->GetI()[i+1]; j++)
596  {
597  vert_element->GetJ()[j] = partitioning[vert_element->GetJ()[j]];
598  if (vert_element->GetJ()[j] == MyRank)
599  {
600  me = 1;
601  }
602  else
603  {
604  others = 1;
605  }
606  }
607 
608  if (me && others)
609  {
610  svert_counter++;
611  group.Recreate(vert_element->RowSize(i), vert_element->GetRow(i));
612  vert_element->GetI()[i] = groups.Insert(group) - 1;
613  }
614  else
615  {
616  vert_element->GetI()[i] = -1;
617  }
618  }
619  return svert_counter;
620 }
621 
622 void ParMesh::BuildFaceGroup(int ngroups, const Mesh &mesh,
623  const Array<int> &face_group,
624  int &nstria, int &nsquad)
625 {
626  // build group_stria and group_squad
627  group_stria.MakeI(ngroups);
628  group_squad.MakeI(ngroups);
629 
630  for (int i = 0; i < face_group.Size(); i++)
631  {
632  if (face_group[i] >= 0)
633  {
634  if (mesh.GetFace(i)->GetType() == Element::TRIANGLE)
635  {
636  group_stria.AddAColumnInRow(face_group[i]);
637  }
638  else
639  {
640  group_squad.AddAColumnInRow(face_group[i]);
641  }
642  }
643  }
644 
645  group_stria.MakeJ();
646  group_squad.MakeJ();
647 
648  nstria = nsquad = 0;
649  for (int i = 0; i < face_group.Size(); i++)
650  {
651  if (face_group[i] >= 0)
652  {
653  if (mesh.GetFace(i)->GetType() == Element::TRIANGLE)
654  {
655  group_stria.AddConnection(face_group[i], nstria++);
656  }
657  else
658  {
659  group_squad.AddConnection(face_group[i], nsquad++);
660  }
661  }
662  }
663 
666 }
667 
668 void ParMesh::BuildEdgeGroup(int ngroups, const Table &edge_element)
669 {
670  group_sedge.MakeI(ngroups);
671 
672  for (int i = 0; i < edge_element.Size(); i++)
673  {
674  if (edge_element.GetRow(i)[0] >= 0)
675  {
676  group_sedge.AddAColumnInRow(edge_element.GetRow(i)[0]);
677  }
678  }
679 
680  group_sedge.MakeJ();
681 
682  int sedge_counter = 0;
683  for (int i = 0; i < edge_element.Size(); i++)
684  {
685  if (edge_element.GetRow(i)[0] >= 0)
686  {
687  group_sedge.AddConnection(edge_element.GetRow(i)[0], sedge_counter++);
688  }
689  }
690 
692 }
693 
694 void ParMesh::BuildVertexGroup(int ngroups, const Table &vert_element)
695 {
696  group_svert.MakeI(ngroups);
697 
698  for (int i = 0; i < vert_element.Size(); i++)
699  {
700  if (vert_element.GetI()[i] >= 0)
701  {
702  group_svert.AddAColumnInRow(vert_element.GetI()[i]);
703  }
704  }
705 
706  group_svert.MakeJ();
707 
708  int svert_counter = 0;
709  for (int i = 0; i < vert_element.Size(); i++)
710  {
711  if (vert_element.GetI()[i] >= 0)
712  {
713  group_svert.AddConnection(vert_element.GetI()[i], svert_counter++);
714  }
715  }
716 
718 }
719 
720 void ParMesh::BuildSharedFaceElems(int ntri_faces, int nquad_faces,
721  const Mesh& mesh, int *partitioning,
722  const STable3D *faces_tbl,
723  const Array<int> &face_group,
724  const Array<int> &vert_global_local)
725 {
726  shared_trias.SetSize(ntri_faces);
727  shared_quads.SetSize(nquad_faces);
728  sface_lface. SetSize(ntri_faces + nquad_faces);
729 
730  if (Dim < 3) { return; }
731 
732  int stria_counter = 0;
733  int squad_counter = 0;
734  for (int i = 0; i < face_group.Size(); i++)
735  {
736  if (face_group[i] < 0) { continue; }
737 
738  const Element *face = mesh.GetFace(i);
739  const int *fv = face->GetVertices();
740  switch (face->GetType())
741  {
742  case Element::TRIANGLE:
743  {
744  shared_trias[stria_counter].Set(fv);
745  int *v = shared_trias[stria_counter].v;
746  for (int j = 0; j < 3; j++)
747  {
748  v[j] = vert_global_local[v[j]];
749  }
750  const int lface = (*faces_tbl)(v[0], v[1], v[2]);
751  sface_lface[stria_counter] = lface;
752  if (meshgen == 1) // Tet-only mesh
753  {
754  Tetrahedron *tet = dynamic_cast<Tetrahedron *>
755  (elements[faces_info[lface].Elem1No]);
756  // mark the shared face for refinement by reorienting
757  // it according to the refinement flag in the tetrahedron
758  // to which this shared face belongs to.
759  if (tet->GetRefinementFlag())
760  {
761  tet->GetMarkedFace(faces_info[lface].Elem1Inf/64, v);
762  // flip the shared face in the processor that owns the
763  // second element (in 'mesh')
764  int gl_el1, gl_el2;
765  mesh.GetFaceElements(i, &gl_el1, &gl_el2);
766  if (MyRank == partitioning[gl_el2])
767  {
768  std::swap(v[0], v[1]);
769  }
770  }
771  }
772  stria_counter++;
773  break;
774  }
775 
777  {
778  shared_quads[squad_counter].Set(fv);
779  int *v = shared_quads[squad_counter].v;
780  for (int j = 0; j < 4; j++)
781  {
782  v[j] = vert_global_local[v[j]];
783  }
784  sface_lface[shared_trias.Size() + squad_counter] =
785  (*faces_tbl)(v[0], v[1], v[2], v[3]);
786  squad_counter++;
787  break;
788  }
789 
790  default:
791  MFEM_ABORT("unknown face type: " << face->GetType());
792  break;
793  }
794  }
795 }
796 
797 void ParMesh::BuildSharedEdgeElems(int nedges, Mesh& mesh,
798  const Array<int>& vert_global_local,
799  const Table* edge_element)
800 {
801  // The passed in mesh is still the global mesh. "this" mesh is the
802  // local partitioned mesh.
803 
804  shared_edges.SetSize(nedges);
805  sedge_ledge. SetSize(nedges);
806 
807  {
808  DSTable v_to_v(NumOfVertices);
809  GetVertexToVertexTable(v_to_v);
810 
811  int sedge_counter = 0;
812  for (int i = 0; i < edge_element->Size(); i++)
813  {
814  if (edge_element->GetRow(i)[0] >= 0)
815  {
816  Array<int> vert;
817  mesh.GetEdgeVertices(i, vert);
818 
819  shared_edges[sedge_counter] =
820  new Segment(vert_global_local[vert[0]],
821  vert_global_local[vert[1]], 1);
822 
823  sedge_ledge[sedge_counter] = v_to_v(vert_global_local[vert[0]],
824  vert_global_local[vert[1]]);
825 
826  MFEM_VERIFY(sedge_ledge[sedge_counter] >= 0, "Error in v_to_v.");
827 
828  sedge_counter++;
829  }
830  }
831  }
832 }
833 
835  const mfem::Table *vert_element,
836  const Array<int> &vert_global_local)
837 {
838  // build svert_lvert
839  svert_lvert.SetSize(nvert);
840 
841  int svert_counter = 0;
842  for (int i = 0; i < vert_element->Size(); i++)
843  {
844  if (vert_element->GetI()[i] >= 0)
845  {
846  svert_lvert[svert_counter++] = vert_global_local[i];
847  }
848  }
849 }
850 
851 
852 // protected method, used by Nonconforming(De)Refinement and Rebalance
854  : MyComm(pncmesh.MyComm)
855  , NRanks(pncmesh.NRanks)
856  , MyRank(pncmesh.MyRank)
857  , face_nbr_el_to_face(NULL)
858  , glob_elem_offset(-1)
859  , glob_offset_sequence(-1)
860  , gtopo(MyComm)
861  , pncmesh(NULL)
862 {
864  ReduceMeshGen();
865  have_face_nbr_data = false;
866 }
867 
869 {
870  if (glob_offset_sequence != sequence) // mesh has changed
871  {
872  long long local_elems = NumOfElements;
873  MPI_Scan(&local_elems, &glob_elem_offset, 1, MPI_LONG_LONG, MPI_SUM,
874  MyComm);
875  glob_elem_offset -= local_elems;
876 
877  glob_offset_sequence = sequence; // don't recalculate until refinement etc.
878  }
879 }
880 
882 {
883  int loc_meshgen = meshgen;
884  MPI_Allreduce(&loc_meshgen, &meshgen, 1, MPI_INT, MPI_BOR, MyComm);
885 }
886 
888 {
889  // Determine sedge_ledge
891  if (shared_edges.Size())
892  {
893  DSTable v_to_v(NumOfVertices);
894  GetVertexToVertexTable(v_to_v);
895  for (int se = 0; se < shared_edges.Size(); se++)
896  {
897  const int *v = shared_edges[se]->GetVertices();
898  const int l_edge = v_to_v(v[0], v[1]);
899  MFEM_ASSERT(l_edge >= 0, "invalid shared edge");
900  sedge_ledge[se] = l_edge;
901  }
902  }
903 
904  // Determine sface_lface
905  const int nst = shared_trias.Size();
906  sface_lface.SetSize(nst + shared_quads.Size());
907  if (sface_lface.Size())
908  {
909  STable3D *faces_tbl = GetFacesTable();
910  for (int st = 0; st < nst; st++)
911  {
912  const int *v = shared_trias[st].v;
913  sface_lface[st] = (*faces_tbl)(v[0], v[1], v[2]);
914  }
915  for (int sq = 0; sq < shared_quads.Size(); sq++)
916  {
917  const int *v = shared_quads[sq].v;
918  sface_lface[nst+sq] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
919  }
920  delete faces_tbl;
921  }
922 }
923 
924 ParMesh::ParMesh(MPI_Comm comm, istream &input, bool refine)
925  : face_nbr_el_to_face(NULL)
926  , glob_elem_offset(-1)
927  , glob_offset_sequence(-1)
928  , gtopo(comm)
929 {
930  MyComm = comm;
931  MPI_Comm_size(MyComm, &NRanks);
932  MPI_Comm_rank(MyComm, &MyRank);
933 
934  have_face_nbr_data = false;
935  pncmesh = NULL;
936 
937  const int gen_edges = 1;
938 
939  Load(input, gen_edges, refine, true);
940 }
941 
942 void ParMesh::Load(istream &input, int generate_edges, int refine,
943  bool fix_orientation)
944 {
946 
947  // Tell Loader() to read up to 'mfem_serial_mesh_end' instead of
948  // 'mfem_mesh_end', as we have additional parallel mesh data to load in from
949  // the stream.
950  Loader(input, generate_edges, "mfem_serial_mesh_end");
951 
952  ReduceMeshGen(); // determine the global 'meshgen'
953 
954  if (Conforming())
955  {
956  LoadSharedEntities(input);
957  }
958  else
959  {
960  // the ParNCMesh instance was already constructed in 'Loader'
961  pncmesh = dynamic_cast<ParNCMesh*>(ncmesh);
962  MFEM_ASSERT(pncmesh, "internal error");
963 
964  // in the NC case we don't need to load extra data from the file,
965  // as the shared entities can be constructed from the ghost layer
967  }
968 
969  Finalize(refine, fix_orientation);
970 
971  EnsureParNodes();
972 
973  // note: attributes and bdr_attributes are local lists
974 
975  // TODO: NURBS meshes?
976 }
977 
978 void ParMesh::LoadSharedEntities(istream &input)
979 {
980  string ident;
981  skip_comment_lines(input, '#');
982 
983  // read the group topology
984  input >> ident;
985  MFEM_VERIFY(ident == "communication_groups",
986  "input stream is not a parallel MFEM mesh");
987  gtopo.Load(input);
988 
989  skip_comment_lines(input, '#');
990 
991  // read and set the sizes of svert_lvert, group_svert
992  {
993  int num_sverts;
994  input >> ident >> num_sverts;
995  MFEM_VERIFY(ident == "total_shared_vertices", "invalid mesh file");
996  svert_lvert.SetSize(num_sverts);
997  group_svert.SetDims(GetNGroups()-1, num_sverts);
998  }
999  // read and set the sizes of sedge_ledge, group_sedge
1000  if (Dim >= 2)
1001  {
1002  skip_comment_lines(input, '#');
1003  int num_sedges;
1004  input >> ident >> num_sedges;
1005  MFEM_VERIFY(ident == "total_shared_edges", "invalid mesh file");
1006  sedge_ledge.SetSize(num_sedges);
1007  shared_edges.SetSize(num_sedges);
1008  group_sedge.SetDims(GetNGroups()-1, num_sedges);
1009  }
1010  else
1011  {
1012  group_sedge.SetSize(GetNGroups()-1, 0); // create empty group_sedge
1013  }
1014  // read and set the sizes of sface_lface, group_{stria,squad}
1015  if (Dim >= 3)
1016  {
1017  skip_comment_lines(input, '#');
1018  int num_sface;
1019  input >> ident >> num_sface;
1020  MFEM_VERIFY(ident == "total_shared_faces", "invalid mesh file");
1021  sface_lface.SetSize(num_sface);
1024  }
1025  else
1026  {
1027  group_stria.SetSize(GetNGroups()-1, 0); // create empty group_stria
1028  group_squad.SetSize(GetNGroups()-1, 0); // create empty group_squad
1029  }
1030 
1031  // read, group by group, the contents of group_svert, svert_lvert,
1032  // group_sedge, shared_edges, group_{stria,squad}, shared_{trias,quads}
1033  int svert_counter = 0, sedge_counter = 0;
1034  for (int gr = 1; gr < GetNGroups(); gr++)
1035  {
1036  skip_comment_lines(input, '#');
1037 #if 0
1038  // implementation prior to prism-dev merge
1039  int g;
1040  input >> ident >> g; // group
1041  if (g != gr)
1042  {
1043  mfem::err << "ParMesh::ParMesh : expecting group " << gr
1044  << ", read group " << g << endl;
1045  mfem_error();
1046  }
1047 #endif
1048  {
1049  int nv;
1050  input >> ident >> nv; // shared_vertices (in this group)
1051  MFEM_VERIFY(ident == "shared_vertices", "invalid mesh file");
1052  nv += svert_counter;
1053  MFEM_VERIFY(nv <= group_svert.Size_of_connections(),
1054  "incorrect number of total_shared_vertices");
1055  group_svert.GetI()[gr] = nv;
1056  for ( ; svert_counter < nv; svert_counter++)
1057  {
1058  group_svert.GetJ()[svert_counter] = svert_counter;
1059  input >> svert_lvert[svert_counter];
1060  }
1061  }
1062  if (Dim >= 2)
1063  {
1064  int ne, v[2];
1065  input >> ident >> ne; // shared_edges (in this group)
1066  MFEM_VERIFY(ident == "shared_edges", "invalid mesh file");
1067  ne += sedge_counter;
1068  MFEM_VERIFY(ne <= group_sedge.Size_of_connections(),
1069  "incorrect number of total_shared_edges");
1070  group_sedge.GetI()[gr] = ne;
1071  for ( ; sedge_counter < ne; sedge_counter++)
1072  {
1073  group_sedge.GetJ()[sedge_counter] = sedge_counter;
1074  input >> v[0] >> v[1];
1075  shared_edges[sedge_counter] = new Segment(v[0], v[1], 1);
1076  }
1077  }
1078  if (Dim >= 3)
1079  {
1080  int nf, tstart = shared_trias.Size(), qstart = shared_quads.Size();
1081  input >> ident >> nf; // shared_faces (in this group)
1082  for (int i = 0; i < nf; i++)
1083  {
1084  int geom, *v;
1085  input >> geom;
1086  switch (geom)
1087  {
1088  case Geometry::TRIANGLE:
1089  shared_trias.SetSize(shared_trias.Size()+1);
1090  v = shared_trias.Last().v;
1091  for (int ii = 0; ii < 3; ii++) { input >> v[ii]; }
1092  break;
1093  case Geometry::SQUARE:
1094  shared_quads.SetSize(shared_quads.Size()+1);
1095  v = shared_quads.Last().v;
1096  for (int ii = 0; ii < 4; ii++) { input >> v[ii]; }
1097  break;
1098  default:
1099  MFEM_ABORT("invalid shared face geometry: " << geom);
1100  }
1101  }
1102  group_stria.AddColumnsInRow(gr-1, shared_trias.Size()-tstart);
1103  group_squad.AddColumnsInRow(gr-1, shared_quads.Size()-qstart);
1104  }
1105  }
1106  if (Dim >= 3)
1107  {
1108  MFEM_VERIFY(shared_trias.Size() + shared_quads.Size()
1109  == sface_lface.Size(),
1110  "incorrect number of total_shared_faces");
1111  // Define the J arrays of group_stria and group_squad -- they just contain
1112  // consecutive numbers starting from 0 up to shared_trias.Size()-1 and
1113  // shared_quads.Size()-1, respectively.
1114  group_stria.MakeJ();
1115  for (int i = 0; i < shared_trias.Size(); i++)
1116  {
1117  group_stria.GetJ()[i] = i;
1118  }
1119  group_squad.MakeJ();
1120  for (int i = 0; i < shared_quads.Size(); i++)
1121  {
1122  group_squad.GetJ()[i] = i;
1123  }
1124  }
1125 }
1126 
1127 ParMesh::ParMesh(ParMesh *orig_mesh, int ref_factor, int ref_type)
1128 {
1129  MakeRefined_(*orig_mesh, ref_factor, ref_type);
1130 }
1131 
1132 void ParMesh::MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type)
1133 {
1134  MyComm = orig_mesh.GetComm();
1135  NRanks = orig_mesh.GetNRanks();
1136  MyRank = orig_mesh.GetMyRank();
1137  face_nbr_el_to_face = NULL;
1138  glob_elem_offset = -1;
1139  glob_offset_sequence = -1;
1140  gtopo = orig_mesh.gtopo;
1141  have_face_nbr_data = false;
1142  pncmesh = NULL;
1143 
1144  Array<int> ref_factors(orig_mesh.GetNE());
1145  ref_factors = ref_factor;
1146  Mesh::MakeRefined_(orig_mesh, ref_factors, ref_type);
1147 
1148  // Need to initialize:
1149  // - shared_edges, shared_{trias,quads}
1150  // - group_svert, group_sedge, group_{stria,squad}
1151  // - svert_lvert, sedge_ledge, sface_lface
1152 
1153  meshgen = orig_mesh.meshgen; // copy the global 'meshgen'
1154 
1155  H1_FECollection rfec(ref_factor, Dim, ref_type);
1156  ParFiniteElementSpace rfes(&orig_mesh, &rfec);
1157 
1158  // count the number of entries in each row of group_s{vert,edge,face}
1159  group_svert.MakeI(GetNGroups()-1); // exclude the local group 0
1163  for (int gr = 1; gr < GetNGroups(); gr++)
1164  {
1165  // orig vertex -> vertex
1166  group_svert.AddColumnsInRow(gr-1, orig_mesh.GroupNVertices(gr));
1167  // orig edge -> (ref_factor-1) vertices and (ref_factor) edges
1168  const int orig_ne = orig_mesh.GroupNEdges(gr);
1169  group_svert.AddColumnsInRow(gr-1, (ref_factor-1)*orig_ne);
1170  group_sedge.AddColumnsInRow(gr-1, ref_factor*orig_ne);
1171  // orig face -> (?) vertices, (?) edges, and (?) faces
1172  const int orig_nt = orig_mesh.GroupNTriangles(gr);
1173  if (orig_nt > 0)
1174  {
1175  const Geometry::Type geom = Geometry::TRIANGLE;
1176  const int nvert = Geometry::NumVerts[geom];
1177  RefinedGeometry &RG =
1178  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1179 
1180  // count internal vertices
1181  group_svert.AddColumnsInRow(gr-1, orig_nt*rfec.DofForGeometry(geom));
1182  // count internal edges
1183  group_sedge.AddColumnsInRow(gr-1, orig_nt*(RG.RefEdges.Size()/2-
1184  RG.NumBdrEdges));
1185  // count refined faces
1186  group_stria.AddColumnsInRow(gr-1, orig_nt*(RG.RefGeoms.Size()/nvert));
1187  }
1188  const int orig_nq = orig_mesh.GroupNQuadrilaterals(gr);
1189  if (orig_nq > 0)
1190  {
1191  const Geometry::Type geom = Geometry::SQUARE;
1192  const int nvert = Geometry::NumVerts[geom];
1193  RefinedGeometry &RG =
1194  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1195 
1196  // count internal vertices
1197  group_svert.AddColumnsInRow(gr-1, orig_nq*rfec.DofForGeometry(geom));
1198  // count internal edges
1199  group_sedge.AddColumnsInRow(gr-1, orig_nq*(RG.RefEdges.Size()/2-
1200  RG.NumBdrEdges));
1201  // count refined faces
1202  group_squad.AddColumnsInRow(gr-1, orig_nq*(RG.RefGeoms.Size()/nvert));
1203  }
1204  }
1205 
1206  group_svert.MakeJ();
1208 
1209  group_sedge.MakeJ();
1212 
1213  group_stria.MakeJ();
1214  group_squad.MakeJ();
1217  sface_lface.SetSize(shared_trias.Size() + shared_quads.Size());
1218 
1219  Array<int> rdofs;
1220  for (int gr = 1; gr < GetNGroups(); gr++)
1221  {
1222  // add shared vertices from original shared vertices
1223  const int orig_n_verts = orig_mesh.GroupNVertices(gr);
1224  for (int j = 0; j < orig_n_verts; j++)
1225  {
1226  rfes.GetVertexDofs(orig_mesh.GroupVertex(gr, j), rdofs);
1227  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[0])-1);
1228  }
1229 
1230  // add refined shared edges; add shared vertices from refined shared edges
1231  const int orig_n_edges = orig_mesh.GroupNEdges(gr);
1232  if (orig_n_edges > 0)
1233  {
1234  const Geometry::Type geom = Geometry::SEGMENT;
1235  const int nvert = Geometry::NumVerts[geom];
1236  RefinedGeometry &RG = *GlobGeometryRefiner.Refine(geom, ref_factor);
1237  const int *c2h_map = rfec.GetDofMap(geom, ref_factor); // FIXME hp
1238 
1239  for (int e = 0; e < orig_n_edges; e++)
1240  {
1241  rfes.GetSharedEdgeDofs(gr, e, rdofs);
1242  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1243  // add the internal edge 'rdofs' as shared vertices
1244  for (int j = 2; j < rdofs.Size(); j++)
1245  {
1246  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1247  }
1248  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1249  {
1250  Element *elem = NewElement(geom);
1251  int *v = elem->GetVertices();
1252  for (int k = 0; k < nvert; k++)
1253  {
1254  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1255  v[k] = rdofs[c2h_map[cid]];
1256  }
1257  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1258  }
1259  }
1260  }
1261  // add refined shared faces; add shared edges and shared vertices from
1262  // refined shared faces
1263  const int orig_nt = orig_mesh.GroupNTriangles(gr);
1264  if (orig_nt > 0)
1265  {
1266  const Geometry::Type geom = Geometry::TRIANGLE;
1267  const int nvert = Geometry::NumVerts[geom];
1268  RefinedGeometry &RG =
1269  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1270  const int num_int_verts = rfec.DofForGeometry(geom);
1271  const int *c2h_map = rfec.GetDofMap(geom, ref_factor); // FIXME hp
1272 
1273  for (int f = 0; f < orig_nt; f++)
1274  {
1275  rfes.GetSharedTriangleDofs(gr, f, rdofs);
1276  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1277  // add the internal face 'rdofs' as shared vertices
1278  for (int j = rdofs.Size()-num_int_verts; j < rdofs.Size(); j++)
1279  {
1280  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1281  }
1282  // add the internal (for the shared face) edges as shared edges
1283  for (int j = 2*RG.NumBdrEdges; j < RG.RefEdges.Size(); j += 2)
1284  {
1286  int *v = elem->GetVertices();
1287  for (int k = 0; k < 2; k++)
1288  {
1289  v[k] = rdofs[c2h_map[RG.RefEdges[j+k]]];
1290  }
1291  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1292  }
1293  // add refined shared faces
1294  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1295  {
1296  shared_trias.SetSize(shared_trias.Size()+1);
1297  int *v = shared_trias.Last().v;
1298  for (int k = 0; k < nvert; k++)
1299  {
1300  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1301  v[k] = rdofs[c2h_map[cid]];
1302  }
1303  group_stria.AddConnection(gr-1, shared_trias.Size()-1);
1304  }
1305  }
1306  }
1307  const int orig_nq = orig_mesh.GroupNQuadrilaterals(gr);
1308  if (orig_nq > 0)
1309  {
1310  const Geometry::Type geom = Geometry::SQUARE;
1311  const int nvert = Geometry::NumVerts[geom];
1312  RefinedGeometry &RG =
1313  *GlobGeometryRefiner.Refine(geom, ref_factor, ref_factor);
1314  const int num_int_verts = rfec.DofForGeometry(geom);
1315  const int *c2h_map = rfec.GetDofMap(geom, ref_factor); // FIXME hp
1316 
1317  for (int f = 0; f < orig_nq; f++)
1318  {
1319  rfes.GetSharedQuadrilateralDofs(gr, f, rdofs);
1320  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
1321  // add the internal face 'rdofs' as shared vertices
1322  for (int j = rdofs.Size()-num_int_verts; j < rdofs.Size(); j++)
1323  {
1324  group_svert.AddConnection(gr-1, svert_lvert.Append(rdofs[j])-1);
1325  }
1326  // add the internal (for the shared face) edges as shared edges
1327  for (int j = 2*RG.NumBdrEdges; j < RG.RefEdges.Size(); j += 2)
1328  {
1330  int *v = elem->GetVertices();
1331  for (int k = 0; k < 2; k++)
1332  {
1333  v[k] = rdofs[c2h_map[RG.RefEdges[j+k]]];
1334  }
1335  group_sedge.AddConnection(gr-1, shared_edges.Append(elem)-1);
1336  }
1337  // add refined shared faces
1338  for (int j = 0; j < RG.RefGeoms.Size(); j += nvert)
1339  {
1340  shared_quads.SetSize(shared_quads.Size()+1);
1341  int *v = shared_quads.Last().v;
1342  for (int k = 0; k < nvert; k++)
1343  {
1344  int cid = RG.RefGeoms[j+k]; // local Cartesian index
1345  v[k] = rdofs[c2h_map[cid]];
1346  }
1347  group_squad.AddConnection(gr-1, shared_quads.Size()-1);
1348  }
1349  }
1350  }
1351  }
1356 
1357  FinalizeParTopo();
1358 
1359  if (Nodes != NULL)
1360  {
1361  // This call will turn the Nodes into a ParGridFunction
1362  SetCurvature(1, GetNodalFESpace()->IsDGSpace(), spaceDim,
1363  GetNodalFESpace()->GetOrdering());
1364  }
1365 }
1366 
1367 ParMesh ParMesh::MakeRefined(ParMesh &orig_mesh, int ref_factor, int ref_type)
1368 {
1369  ParMesh mesh;
1370  mesh.MakeRefined_(orig_mesh, ref_factor, ref_type);
1371  return mesh;
1372 }
1373 
1375 {
1376  ParMesh mesh;
1377 
1378  mesh.MyComm = orig_mesh.GetComm();
1379  mesh.NRanks = orig_mesh.GetNRanks();
1380  mesh.MyRank = orig_mesh.GetMyRank();
1381  mesh.glob_elem_offset = -1;
1382  mesh.glob_offset_sequence = -1;
1383  mesh.gtopo = orig_mesh.gtopo;
1384  mesh.have_face_nbr_data = false;
1385  mesh.pncmesh = NULL;
1386  mesh.meshgen = orig_mesh.meshgen;
1387 
1388  H1_FECollection fec(1, orig_mesh.Dimension());
1389  ParFiniteElementSpace fes(&orig_mesh, &fec);
1390 
1391  Array<int> vglobal(orig_mesh.GetNV());
1392  for (int iv=0; iv<orig_mesh.GetNV(); ++iv)
1393  {
1394  vglobal[iv] = fes.GetGlobalTDofNumber(iv);
1395  }
1396  mesh.MakeSimplicial_(orig_mesh, vglobal);
1397 
1398  // count the number of entries in each row of group_s{vert,edge,face}
1399  mesh.group_svert.MakeI(mesh.GetNGroups()-1); // exclude the local group 0
1400  mesh.group_sedge.MakeI(mesh.GetNGroups()-1);
1401  mesh.group_stria.MakeI(mesh.GetNGroups()-1);
1402  mesh.group_squad.MakeI(mesh.GetNGroups()-1);
1403  for (int gr = 1; gr < mesh.GetNGroups(); gr++)
1404  {
1405  mesh.group_svert.AddColumnsInRow(gr-1, orig_mesh.GroupNVertices(gr));
1406  mesh.group_sedge.AddColumnsInRow(gr-1, orig_mesh.GroupNEdges(gr));
1407  // Every quad gives an extra edge
1408  const int orig_nq = orig_mesh.GroupNQuadrilaterals(gr);
1409  mesh.group_sedge.AddColumnsInRow(gr-1, orig_nq);
1410  // Every quad is subdivided into two triangles
1411  mesh.group_stria.AddColumnsInRow(gr-1, 2*orig_nq);
1412  // Existing triangles remain unchanged
1413  const int orig_nt = orig_mesh.GroupNTriangles(gr);
1414  mesh.group_stria.AddColumnsInRow(gr-1, orig_nt);
1415  }
1416  mesh.group_svert.MakeJ();
1418 
1419  mesh.group_sedge.MakeJ();
1420  mesh.shared_edges.Reserve(mesh.group_sedge.Size_of_connections());
1422 
1423  mesh.group_stria.MakeJ();
1424  mesh.shared_trias.Reserve(mesh.group_stria.Size_of_connections());
1425  mesh.sface_lface.SetSize(mesh.shared_trias.Size());
1426 
1427  mesh.group_squad.MakeJ();
1428 
1429  constexpr int ntris = 2, nv_tri = 3, nv_quad = 4;
1430 
1431  Array<int> dofs;
1432  for (int gr = 1; gr < mesh.GetNGroups(); gr++)
1433  {
1434  // add shared vertices from original shared vertices
1435  const int orig_n_verts = orig_mesh.GroupNVertices(gr);
1436  for (int j = 0; j < orig_n_verts; j++)
1437  {
1438  fes.GetVertexDofs(orig_mesh.GroupVertex(gr, j), dofs);
1439  mesh.group_svert.AddConnection(gr-1, mesh.svert_lvert.Append(dofs[0])-1);
1440  }
1441 
1442  // add original shared edges
1443  const int orig_n_edges = orig_mesh.GroupNEdges(gr);
1444  for (int e = 0; e < orig_n_edges; e++)
1445  {
1446  int iedge, o;
1447  orig_mesh.GroupEdge(gr, e, iedge, o);
1448  Element *elem = mesh.NewElement(Geometry::SEGMENT);
1449  Array<int> edge_verts;
1450  orig_mesh.GetEdgeVertices(iedge, edge_verts);
1451  elem->SetVertices(edge_verts);
1452  mesh.group_sedge.AddConnection(gr-1, mesh.shared_edges.Append(elem)-1);
1453  }
1454  // add original shared triangles
1455  const int orig_nt = orig_mesh.GroupNTriangles(gr);
1456  for (int e = 0; e < orig_nt; e++)
1457  {
1458  int itri, o;
1459  orig_mesh.GroupTriangle(gr, e, itri, o);
1460  const int *v = orig_mesh.GetFace(itri)->GetVertices();
1461  mesh.shared_trias.SetSize(mesh.shared_trias.Size()+1);
1462  int *v2 = mesh.shared_trias.Last().v;
1463  for (int iv=0; iv<nv_tri; ++iv) { v2[iv] = v[iv]; }
1464  mesh.group_stria.AddConnection(gr-1, mesh.shared_trias.Size()-1);
1465  }
1466  // add triangles from split quads and add resulting diagonal edge
1467  const int orig_nq = orig_mesh.GroupNQuadrilaterals(gr);
1468  if (orig_nq > 0)
1469  {
1470  static const int trimap[12] =
1471  {
1472  0, 0, 0, 1,
1473  1, 2, 1, 2,
1474  2, 3, 3, 3
1475  };
1476  static const int diagmap[4] = { 0, 2, 1, 3 };
1477  for (int f = 0; f < orig_nq; ++f)
1478  {
1479  int iquad, o;
1480  orig_mesh.GroupQuadrilateral(gr, f, iquad, o);
1481  const int *v = orig_mesh.GetFace(iquad)->GetVertices();
1482  // Split quad according the smallest (global) vertex
1483  int vg[nv_quad];
1484  for (int iv=0; iv<nv_quad; ++iv) { vg[iv] = vglobal[v[iv]]; }
1485  int iv_min = std::min_element(vg, vg+nv_quad) - vg;
1486  int isplit = (iv_min == 0 || iv_min == 2) ? 0 : 1;
1487  // Add diagonal
1488  Element *diag = mesh.NewElement(Geometry::SEGMENT);
1489  int *v_diag = diag->GetVertices();
1490  v_diag[0] = v[diagmap[0 + isplit*2]];
1491  v_diag[1] = v[diagmap[1 + isplit*2]];
1492  mesh.group_sedge.AddConnection(gr-1, mesh.shared_edges.Append(diag)-1);
1493  // Add two new triangles
1494  for (int itri=0; itri<ntris; ++itri)
1495  {
1496  mesh.shared_trias.SetSize(mesh.shared_trias.Size()+1);
1497  int *v2 = mesh.shared_trias.Last().v;
1498  for (int iv=0; iv<nv_tri; ++iv)
1499  {
1500  v2[iv] = v[trimap[itri + isplit*2 + iv*ntris*2]];
1501  }
1502  mesh.group_stria.AddConnection(gr-1, mesh.shared_trias.Size()-1);
1503  }
1504  }
1505  }
1506  }
1507  mesh.group_svert.ShiftUpI();
1508  mesh.group_sedge.ShiftUpI();
1509  mesh.group_stria.ShiftUpI();
1510 
1511  mesh.FinalizeParTopo();
1512 
1513  return mesh;
1514 }
1515 
1516 void ParMesh::Finalize(bool refine, bool fix_orientation)
1517 {
1518  const int meshgen_save = meshgen; // Mesh::Finalize() may call SetMeshGen()
1519 
1520  Mesh::Finalize(refine, fix_orientation);
1521 
1522  meshgen = meshgen_save;
1523  // Note: if Mesh::Finalize() calls MarkTetMeshForRefinement() then the
1524  // shared_trias have been rotated as necessary.
1525 
1526  // Setup secondary parallel mesh data: sedge_ledge, sface_lface
1527  FinalizeParTopo();
1528 }
1529 
1530 int ParMesh::GetLocalElementNum(long long global_element_num) const
1531 {
1533  long long local = global_element_num - glob_elem_offset;
1534  if (local < 0 || local >= NumOfElements) { return -1; }
1535  return local;
1536 }
1537 
1538 long long ParMesh::GetGlobalElementNum(int local_element_num) const
1539 {
1541  return glob_elem_offset + local_element_num;
1542 }
1543 
1545 {
1546  // Determine the largest attribute number across all processors
1547  int max_attr = attr.Size() ? attr.Max() : 1 /*allow empty ranks*/;
1548  int glb_max_attr = -1;
1549  MPI_Allreduce(&max_attr, &glb_max_attr, 1, MPI_INT, MPI_MAX, MyComm);
1550 
1551  // Create marker arrays to indicate which attributes are present
1552  // assuming attribute numbers are in the range [1,glb_max_attr].
1553  bool *attr_marker = new bool[glb_max_attr];
1554  bool *glb_attr_marker = new bool[glb_max_attr];
1555  for (int i = 0; i < glb_max_attr; i++)
1556  {
1557  attr_marker[i] = false;
1558  }
1559  for (int i = 0; i < attr.Size(); i++)
1560  {
1561  attr_marker[attr[i] - 1] = true;
1562  }
1563  MPI_Allreduce(attr_marker, glb_attr_marker, glb_max_attr,
1564  MPI_C_BOOL, MPI_LOR, MyComm);
1565  delete [] attr_marker;
1566 
1567  // Translate from the marker array to a unique, sorted list of attributes
1568  attr.SetSize(0);
1569  attr.Reserve(glb_max_attr);
1570  for (int i = 0; i < glb_max_attr; i++)
1571  {
1572  if (glb_attr_marker[i])
1573  {
1574  attr.Append(i + 1);
1575  }
1576  }
1577  delete [] glb_attr_marker;
1578 }
1579 
1581 {
1582  // Determine the attributes occurring in local interior and boundary elements
1584 
1586  if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1587  {
1588  MFEM_WARNING("Non-positive boundary element attributes found!");
1589  }
1590 
1592  if (attributes.Size() > 0 && attributes[0] <= 0)
1593  {
1594  MFEM_WARNING("Non-positive element attributes found!");
1595  }
1596 }
1597 
1599 {
1600  // maximum number of boundary elements over all ranks
1601  int maxNumOfBdrElements;
1602  MPI_Allreduce(&NumOfBdrElements, &maxNumOfBdrElements, 1,
1603  MPI_INT, MPI_MAX, MyComm);
1604  return (maxNumOfBdrElements > 0);
1605 }
1606 
1607 void ParMesh::GroupEdge(int group, int i, int &edge, int &o) const
1608 {
1609  int sedge = group_sedge.GetRow(group-1)[i];
1610  edge = sedge_ledge[sedge];
1611  int *v = shared_edges[sedge]->GetVertices();
1612  o = (v[0] < v[1]) ? (+1) : (-1);
1613 }
1614 
1615 void ParMesh::GroupTriangle(int group, int i, int &face, int &o) const
1616 {
1617  int stria = group_stria.GetRow(group-1)[i];
1618  face = sface_lface[stria];
1619  // face gives the base orientation
1620  MFEM_ASSERT(faces[face]->GetType() == Element::TRIANGLE,
1621  "Expecting a triangular face.");
1622 
1623  o = GetTriOrientation(faces[face]->GetVertices(), shared_trias[stria].v);
1624 }
1625 
1626 void ParMesh::GroupQuadrilateral(int group, int i, int &face, int &o) const
1627 {
1628  int squad = group_squad.GetRow(group-1)[i];
1629  face = sface_lface[shared_trias.Size()+squad];
1630  // face gives the base orientation
1631  MFEM_ASSERT(faces[face]->GetType() == Element::QUADRILATERAL,
1632  "Expecting a quadrilateral face.");
1633 
1634  o = GetQuadOrientation(faces[face]->GetVertices(), shared_quads[squad].v);
1635 }
1636 
1638  GroupCommunicator& sedge_comm) const
1639 {
1640  Table &gr_sedge = sedge_comm.GroupLDofTable();
1641  gr_sedge.SetDims(GetNGroups(), shared_edges.Size());
1642  gr_sedge.GetI()[0] = 0;
1643  for (int gr = 1; gr <= GetNGroups(); gr++)
1644  {
1645  gr_sedge.GetI()[gr] = group_sedge.GetI()[gr-1];
1646  }
1647  for (int k = 0; k < shared_edges.Size(); k++)
1648  {
1649  if (ordering == 1)
1650  {
1651  gr_sedge.GetJ()[k] =k;
1652  }
1653  else
1654  {
1655  gr_sedge.GetJ()[k] = group_sedge.GetJ()[k];
1656  }
1657  }
1658  sedge_comm.Finalize();
1659 }
1660 
1662  GroupCommunicator& svert_comm) const
1663 {
1664  Table &gr_svert = svert_comm.GroupLDofTable();
1665  gr_svert.SetDims(GetNGroups(), svert_lvert.Size());
1666  gr_svert.GetI()[0] = 0;
1667  for (int gr = 1; gr <= GetNGroups(); gr++)
1668  {
1669  gr_svert.GetI()[gr] = group_svert.GetI()[gr-1];
1670  }
1671  for (int k = 0; k < svert_lvert.Size(); k++)
1672  {
1673  if (ordering == 1)
1674  {
1675  gr_svert.GetJ()[k] = k;
1676  }
1677  else
1678  {
1679  gr_svert.GetJ()[k] = group_svert.GetJ()[k];
1680  }
1681  }
1682  svert_comm.Finalize();
1683 }
1684 
1686  GroupCommunicator& squad_comm) const
1687 {
1688  Table &gr_squad = squad_comm.GroupLDofTable();
1689  gr_squad.SetDims(GetNGroups(), shared_quads.Size());
1690  gr_squad.GetI()[0] = 0;
1691  for (int gr = 1; gr <= GetNGroups(); gr++)
1692  {
1693  gr_squad.GetI()[gr] = group_squad.GetI()[gr-1];
1694  }
1695  for (int k = 0; k < shared_quads.Size(); k++)
1696  {
1697  if (ordering == 1)
1698  {
1699  gr_squad.GetJ()[k] = k;
1700  }
1701  else
1702  {
1703  gr_squad.GetJ()[k] = group_squad.GetJ()[k];
1704  }
1705  }
1706  squad_comm.Finalize();
1707 }
1708 
1710  GroupCommunicator& stria_comm) const
1711 {
1712  Table &gr_stria = stria_comm.GroupLDofTable();
1713  gr_stria.SetDims(GetNGroups(), shared_trias.Size());
1714  gr_stria.GetI()[0] = 0;
1715  for (int gr = 1; gr <= GetNGroups(); gr++)
1716  {
1717  gr_stria.GetI()[gr] = group_stria.GetI()[gr-1];
1718  }
1719  for (int k = 0; k < shared_trias.Size(); k++)
1720  {
1721  if (ordering == 1)
1722  {
1723  gr_stria.GetJ()[k] = k;
1724  }
1725  else
1726  {
1727  gr_stria.GetJ()[k] = group_stria.GetJ()[k];
1728  }
1729  }
1730  stria_comm.Finalize();
1731 }
1732 
1734 {
1735  Array<int> order;
1736  GetEdgeOrdering(v_to_v, order); // local edge ordering
1737 
1738  // create a GroupCommunicator on the shared edges
1739  GroupCommunicator sedge_comm(gtopo);
1740  GetSharedEdgeCommunicator(sedge_comm);
1741 
1742  Array<int> sedge_ord(shared_edges.Size());
1743  Array<Pair<int,int> > sedge_ord_map(shared_edges.Size());
1744  for (int k = 0; k < shared_edges.Size(); k++)
1745  {
1746  // sedge_ledge may be undefined -- use shared_edges and v_to_v instead
1747  const int sedge = group_sedge.GetJ()[k];
1748  const int *v = shared_edges[sedge]->GetVertices();
1749  sedge_ord[k] = order[v_to_v(v[0], v[1])];
1750  }
1751 
1752  sedge_comm.Bcast<int>(sedge_ord, 1);
1753 
1754  for (int k = 0, gr = 1; gr < GetNGroups(); gr++)
1755  {
1756  const int n = group_sedge.RowSize(gr-1);
1757  if (n == 0) { continue; }
1758  sedge_ord_map.SetSize(n);
1759  for (int j = 0; j < n; j++)
1760  {
1761  sedge_ord_map[j].one = sedge_ord[k+j];
1762  sedge_ord_map[j].two = j;
1763  }
1764  SortPairs<int, int>(sedge_ord_map, n);
1765  for (int j = 0; j < n; j++)
1766  {
1767  const int sedge_from = group_sedge.GetJ()[k+j];
1768  const int *v = shared_edges[sedge_from]->GetVertices();
1769  sedge_ord[k+j] = order[v_to_v(v[0], v[1])];
1770  }
1771  std::sort(&sedge_ord[k], &sedge_ord[k] + n);
1772  for (int j = 0; j < n; j++)
1773  {
1774  const int sedge_to = group_sedge.GetJ()[k+sedge_ord_map[j].two];
1775  const int *v = shared_edges[sedge_to]->GetVertices();
1776  order[v_to_v(v[0], v[1])] = sedge_ord[k+j];
1777  }
1778  k += n;
1779  }
1780 
1781 #ifdef MFEM_DEBUG
1782  {
1783  Array<Pair<int, double> > ilen_len(order.Size());
1784 
1785  for (int i = 0; i < NumOfVertices; i++)
1786  {
1787  for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
1788  {
1789  int j = it.Index();
1790  ilen_len[j].one = order[j];
1791  ilen_len[j].two = GetLength(i, it.Column());
1792  }
1793  }
1794 
1795  SortPairs<int, double>(ilen_len, order.Size());
1796 
1797  double d_max = 0.;
1798  for (int i = 1; i < order.Size(); i++)
1799  {
1800  d_max = std::max(d_max, ilen_len[i-1].two-ilen_len[i].two);
1801  }
1802 
1803 #if 0
1804  // Debug message from every MPI rank.
1805  mfem::out << "proc. " << MyRank << '/' << NRanks << ": d_max = " << d_max
1806  << endl;
1807 #else
1808  // Debug message just from rank 0.
1809  double glob_d_max;
1810  MPI_Reduce(&d_max, &glob_d_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
1811  if (MyRank == 0)
1812  {
1813  mfem::out << "glob_d_max = " << glob_d_max << endl;
1814  }
1815 #endif
1816  }
1817 #endif
1818 
1819  // use 'order' to mark the tets, the boundary triangles, and the shared
1820  // triangle faces
1821  for (int i = 0; i < NumOfElements; i++)
1822  {
1823  if (elements[i]->GetType() == Element::TETRAHEDRON)
1824  {
1825  elements[i]->MarkEdge(v_to_v, order);
1826  }
1827  }
1828 
1829  for (int i = 0; i < NumOfBdrElements; i++)
1830  {
1831  if (boundary[i]->GetType() == Element::TRIANGLE)
1832  {
1833  boundary[i]->MarkEdge(v_to_v, order);
1834  }
1835  }
1836 
1837  for (int i = 0; i < shared_trias.Size(); i++)
1838  {
1839  Triangle::MarkEdge(shared_trias[i].v, v_to_v, order);
1840  }
1841 }
1842 
1843 // For a line segment with vertices v[0] and v[1], return a number with
1844 // the following meaning:
1845 // 0 - the edge was not refined
1846 // 1 - the edge e was refined once by splitting v[0],v[1]
1847 int ParMesh::GetEdgeSplittings(Element *edge, const DSTable &v_to_v,
1848  int *middle)
1849 {
1850  int m, *v = edge->GetVertices();
1851 
1852  if ((m = v_to_v(v[0], v[1])) != -1 && middle[m] != -1)
1853  {
1854  return 1;
1855  }
1856  else
1857  {
1858  return 0;
1859  }
1860 }
1861 
1862 void ParMesh::GetFaceSplittings(const int *fv, const HashTable<Hashed2> &v_to_v,
1863  Array<unsigned> &codes)
1864 {
1865  typedef Triple<int,int,int> face_t;
1866  Array<face_t> face_stack;
1867 
1868  unsigned code = 0;
1869  face_stack.Append(face_t(fv[0], fv[1], fv[2]));
1870  for (unsigned bit = 0; face_stack.Size() > 0; bit++)
1871  {
1872  if (bit == 8*sizeof(unsigned))
1873  {
1874  codes.Append(code);
1875  code = bit = 0;
1876  }
1877 
1878  const face_t &f = face_stack.Last();
1879  int mid = v_to_v.FindId(f.one, f.two);
1880  if (mid == -1)
1881  {
1882  // leave a 0 at bit 'bit'
1883  face_stack.DeleteLast();
1884  }
1885  else
1886  {
1887  code += (1 << bit); // set bit 'bit' to 1
1888  mid += NumOfVertices;
1889  face_stack.Append(face_t(f.three, f.one, mid));
1890  face_t &r = face_stack[face_stack.Size()-2];
1891  r = face_t(r.two, r.three, mid);
1892  }
1893  }
1894  codes.Append(code);
1895 }
1896 
1898  const Array<unsigned> &codes, int &pos)
1899 {
1900  typedef Triple<int,int,int> face_t;
1901  Array<face_t> face_stack;
1902 
1903  bool need_refinement = 0;
1904  face_stack.Append(face_t(v[0], v[1], v[2]));
1905  for (unsigned bit = 0, code = codes[pos++]; face_stack.Size() > 0; bit++)
1906  {
1907  if (bit == 8*sizeof(unsigned))
1908  {
1909  code = codes[pos++];
1910  bit = 0;
1911  }
1912 
1913  if ((code & (1 << bit)) == 0) { face_stack.DeleteLast(); continue; }
1914 
1915  const face_t &f = face_stack.Last();
1916  int mid = v_to_v.FindId(f.one, f.two);
1917  if (mid == -1)
1918  {
1919  mid = v_to_v.GetId(f.one, f.two);
1920  int ind[2] = { f.one, f.two };
1921  vertices.Append(Vertex());
1922  AverageVertices(ind, 2, vertices.Size()-1);
1923  need_refinement = 1;
1924  }
1925  mid += NumOfVertices;
1926  face_stack.Append(face_t(f.three, f.one, mid));
1927  face_t &r = face_stack[face_stack.Size()-2];
1928  r = face_t(r.two, r.three, mid);
1929  }
1930  return need_refinement;
1931 }
1932 
1933 void ParMesh::GenerateOffsets(int N, HYPRE_BigInt loc_sizes[],
1934  Array<HYPRE_BigInt> *offsets[]) const
1935 {
1936  if (HYPRE_AssumedPartitionCheck())
1937  {
1938  Array<HYPRE_BigInt> temp(N);
1939  MPI_Scan(loc_sizes, temp.GetData(), N, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
1940  for (int i = 0; i < N; i++)
1941  {
1942  offsets[i]->SetSize(3);
1943  (*offsets[i])[0] = temp[i] - loc_sizes[i];
1944  (*offsets[i])[1] = temp[i];
1945  }
1946  MPI_Bcast(temp.GetData(), N, HYPRE_MPI_BIG_INT, NRanks-1, MyComm);
1947  for (int i = 0; i < N; i++)
1948  {
1949  (*offsets[i])[2] = temp[i];
1950  // check for overflow
1951  MFEM_VERIFY((*offsets[i])[0] >= 0 && (*offsets[i])[1] >= 0,
1952  "overflow in offsets");
1953  }
1954  }
1955  else
1956  {
1957  Array<HYPRE_BigInt> temp(N*NRanks);
1958  MPI_Allgather(loc_sizes, N, HYPRE_MPI_BIG_INT, temp.GetData(), N,
1959  HYPRE_MPI_BIG_INT, MyComm);
1960  for (int i = 0; i < N; i++)
1961  {
1962  Array<HYPRE_BigInt> &offs = *offsets[i];
1963  offs.SetSize(NRanks+1);
1964  offs[0] = 0;
1965  for (int j = 0; j < NRanks; j++)
1966  {
1967  offs[j+1] = offs[j] + temp[i+N*j];
1968  }
1969  // Check for overflow
1970  MFEM_VERIFY(offs[MyRank] >= 0 && offs[MyRank+1] >= 0,
1971  "overflow in offsets");
1972  }
1973  }
1974 }
1975 
1977  int i, IsoparametricTransformation *ElTr)
1978 {
1979  DenseMatrix &pointmat = ElTr->GetPointMat();
1980  Element *elem = face_nbr_elements[i];
1981 
1982  ElTr->Attribute = elem->GetAttribute();
1983  ElTr->ElementNo = NumOfElements + i;
1985  ElTr->mesh = this;
1986  ElTr->Reset();
1987 
1988  if (Nodes == NULL)
1989  {
1990  const int nv = elem->GetNVertices();
1991  const int *v = elem->GetVertices();
1992 
1993  pointmat.SetSize(spaceDim, nv);
1994  for (int k = 0; k < spaceDim; k++)
1995  {
1996  for (int j = 0; j < nv; j++)
1997  {
1998  pointmat(k, j) = face_nbr_vertices[v[j]](k);
1999  }
2000  }
2001 
2003  }
2004  else
2005  {
2006  Array<int> vdofs;
2007  ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
2008  if (pNodes)
2009  {
2010  pNodes->ParFESpace()->GetFaceNbrElementVDofs(i, vdofs);
2011  int n = vdofs.Size()/spaceDim;
2012  pointmat.SetSize(spaceDim, n);
2013  for (int k = 0; k < spaceDim; k++)
2014  {
2015  for (int j = 0; j < n; j++)
2016  {
2017  pointmat(k,j) = (pNodes->FaceNbrData())(vdofs[n*k+j]);
2018  }
2019  }
2020 
2021  ElTr->SetFE(pNodes->ParFESpace()->GetFaceNbrFE(i));
2022  }
2023  else
2024  {
2025  MFEM_ABORT("Nodes are not ParGridFunction!");
2026  }
2027  }
2028 }
2029 
2030 double ParMesh::GetFaceNbrElementSize(int i, int type)
2031 {
2033 }
2034 
2036 {
2037  if (!have_face_nbr_data)
2038  {
2039  return;
2040  }
2041 
2042  have_face_nbr_data = false;
2046  for (int i = 0; i < face_nbr_elements.Size(); i++)
2047  {
2049  }
2050  face_nbr_elements.DeleteAll();
2051  face_nbr_vertices.DeleteAll();
2054 }
2055 
2056 void ParMesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
2057 {
2058  space_dim = (space_dim == -1) ? spaceDim : space_dim;
2060  if (discont)
2061  {
2062  nfec = new L2_FECollection(order, Dim, BasisType::GaussLobatto);
2063  }
2064  else
2065  {
2066  nfec = new H1_FECollection(order, Dim);
2067  }
2068  ParFiniteElementSpace* nfes = new ParFiniteElementSpace(this, nfec, space_dim,
2069  ordering);
2070  auto pnodes = new ParGridFunction(nfes);
2071  GetNodes(*pnodes);
2072  NewNodes(*pnodes, true);
2073  Nodes->MakeOwner(nfec);
2074 }
2075 
2077 {
2078  ParFiniteElementSpace *npfes = dynamic_cast<ParFiniteElementSpace*>(nfes);
2079  if (npfes)
2080  {
2081  SetNodalFESpace(npfes);
2082  }
2083  else
2084  {
2085  Mesh::SetNodalFESpace(nfes);
2086  }
2087 }
2088 
2090 {
2091  ParGridFunction *nodes = new ParGridFunction(npfes);
2092  SetNodalGridFunction(nodes, true);
2093 }
2094 
2096 {
2097  if (Nodes && dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace()) == NULL)
2098  {
2099  ParFiniteElementSpace *pfes =
2100  new ParFiniteElementSpace(*Nodes->FESpace(), *this);
2101  ParGridFunction *new_nodes = new ParGridFunction(pfes);
2102 
2103  *new_nodes = *Nodes;
2104 
2105  if (Nodes->OwnFEC())
2106  {
2107  new_nodes->MakeOwner(Nodes->OwnFEC());
2108  Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
2109  delete Nodes->FESpace();
2110  }
2111 
2112  delete Nodes;
2113  Nodes = new_nodes;
2114  }
2115 }
2116 
2118 {
2119  if (have_face_nbr_data)
2120  {
2121  return;
2122  }
2123 
2124  if (Nonconforming())
2125  {
2126  // with ParNCMesh we can set up face neighbors without communication
2127  pncmesh->GetFaceNeighbors(*this);
2128  have_face_nbr_data = true;
2129 
2131  return;
2132  }
2133 
2134  Table *gr_sface;
2135  int *s2l_face;
2136  bool del_tables = false;
2137  if (Dim == 1)
2138  {
2139  gr_sface = &group_svert;
2140  s2l_face = svert_lvert;
2141  }
2142  else if (Dim == 2)
2143  {
2144  gr_sface = &group_sedge;
2145  s2l_face = sedge_ledge;
2146  }
2147  else
2148  {
2149  s2l_face = sface_lface;
2150  if (shared_trias.Size() == sface_lface.Size())
2151  {
2152  // All shared faces are Triangular
2153  gr_sface = &group_stria;
2154  }
2155  else if (shared_quads.Size() == sface_lface.Size())
2156  {
2157  // All shared faced are Quadrilateral
2158  gr_sface = &group_squad;
2159  }
2160  else
2161  {
2162  // Shared faces contain a mixture of triangles and quads
2163  gr_sface = new Table;
2164  del_tables = true;
2165 
2166  // Merge the Tables group_stria and group_squad
2167  gr_sface->MakeI(group_stria.Size());
2168  for (int gr=0; gr<group_stria.Size(); gr++)
2169  {
2170  gr_sface->AddColumnsInRow(gr,
2171  group_stria.RowSize(gr) +
2172  group_squad.RowSize(gr));
2173  }
2174  gr_sface->MakeJ();
2175  const int nst = shared_trias.Size();
2176  for (int gr=0; gr<group_stria.Size(); gr++)
2177  {
2178  gr_sface->AddConnections(gr,
2179  group_stria.GetRow(gr),
2180  group_stria.RowSize(gr));
2181  for (int c=0; c<group_squad.RowSize(gr); c++)
2182  {
2183  gr_sface->AddConnection(gr,
2184  nst + group_squad.GetRow(gr)[c]);
2185  }
2186  }
2187  gr_sface->ShiftUpI();
2188  }
2189  }
2190 
2191  ExchangeFaceNbrData(gr_sface, s2l_face);
2192 
2193  if (Dim == 3)
2194  {
2196  }
2197 
2198  if (del_tables) { delete gr_sface; }
2199 
2200  if ( have_face_nbr_data ) { return; }
2201 
2202  have_face_nbr_data = true;
2203 
2205 }
2206 
2207 void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
2208 {
2209  int num_face_nbrs = 0;
2210  for (int g = 1; g < GetNGroups(); g++)
2211  {
2212  if (gr_sface->RowSize(g-1) > 0)
2213  {
2214  num_face_nbrs++;
2215  }
2216  }
2217 
2218  face_nbr_group.SetSize(num_face_nbrs);
2219 
2220  if (num_face_nbrs == 0)
2221  {
2222  have_face_nbr_data = true;
2223  return;
2224  }
2225 
2226  {
2227  // sort face-neighbors by processor rank
2228  Array<Pair<int, int> > rank_group(num_face_nbrs);
2229 
2230  for (int g = 1, counter = 0; g < GetNGroups(); g++)
2231  {
2232  if (gr_sface->RowSize(g-1) > 0)
2233  {
2234  MFEM_ASSERT(gtopo.GetGroupSize(g) == 2, "group size is not 2!");
2235 
2236  const int *nbs = gtopo.GetGroup(g);
2237  int lproc = (nbs[0]) ? nbs[0] : nbs[1];
2238  rank_group[counter].one = gtopo.GetNeighborRank(lproc);
2239  rank_group[counter].two = g;
2240  counter++;
2241  }
2242  }
2243 
2244  SortPairs<int, int>(rank_group, rank_group.Size());
2245 
2246  for (int fn = 0; fn < num_face_nbrs; fn++)
2247  {
2248  face_nbr_group[fn] = rank_group[fn].two;
2249  }
2250  }
2251 
2252  MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2253  MPI_Request *send_requests = requests;
2254  MPI_Request *recv_requests = requests + num_face_nbrs;
2255  MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2256 
2257  int *nbr_data = new int[6*num_face_nbrs];
2258  int *nbr_send_data = nbr_data;
2259  int *nbr_recv_data = nbr_data + 3*num_face_nbrs;
2260 
2261  Array<int> el_marker(GetNE());
2262  Array<int> vertex_marker(GetNV());
2263  el_marker = -1;
2264  vertex_marker = -1;
2265 
2266  Array<int> fcs, cor;
2267 
2268  Table send_face_nbr_elemdata, send_face_nbr_facedata;
2269 
2270  send_face_nbr_elements.MakeI(num_face_nbrs);
2271  send_face_nbr_vertices.MakeI(num_face_nbrs);
2272  send_face_nbr_elemdata.MakeI(num_face_nbrs);
2273  send_face_nbr_facedata.MakeI(num_face_nbrs);
2274  for (int fn = 0; fn < num_face_nbrs; fn++)
2275  {
2276  int nbr_group = face_nbr_group[fn];
2277  int num_sfaces = gr_sface->RowSize(nbr_group-1);
2278  int *sface = gr_sface->GetRow(nbr_group-1);
2279  for (int i = 0; i < num_sfaces; i++)
2280  {
2281  int lface = s2l_face[sface[i]];
2282  int el = faces_info[lface].Elem1No;
2283  if (el_marker[el] != fn)
2284  {
2285  el_marker[el] = fn;
2287 
2288  const int nv = elements[el]->GetNVertices();
2289  const int *v = elements[el]->GetVertices();
2290  for (int j = 0; j < nv; j++)
2291  if (vertex_marker[v[j]] != fn)
2292  {
2293  vertex_marker[v[j]] = fn;
2295  }
2296 
2297  const int nf = elements[el]->GetNFaces();
2298 
2299  send_face_nbr_elemdata.AddColumnsInRow(fn, nv + nf + 2);
2300  }
2301  }
2302  send_face_nbr_facedata.AddColumnsInRow(fn, 2*num_sfaces);
2303 
2304  nbr_send_data[3*fn ] = send_face_nbr_elements.GetI()[fn];
2305  nbr_send_data[3*fn+1] = send_face_nbr_vertices.GetI()[fn];
2306  nbr_send_data[3*fn+2] = send_face_nbr_elemdata.GetI()[fn];
2307 
2308  int nbr_rank = GetFaceNbrRank(fn);
2309  int tag = 0;
2310 
2311  MPI_Isend(&nbr_send_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2312  &send_requests[fn]);
2313  MPI_Irecv(&nbr_recv_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2314  &recv_requests[fn]);
2315  }
2318  send_face_nbr_elemdata.MakeJ();
2319  send_face_nbr_facedata.MakeJ();
2320  el_marker = -1;
2321  vertex_marker = -1;
2322  const int nst = shared_trias.Size();
2323  for (int fn = 0; fn < num_face_nbrs; fn++)
2324  {
2325  int nbr_group = face_nbr_group[fn];
2326  int num_sfaces = gr_sface->RowSize(nbr_group-1);
2327  int *sface = gr_sface->GetRow(nbr_group-1);
2328  for (int i = 0; i < num_sfaces; i++)
2329  {
2330  const int sf = sface[i];
2331  int lface = s2l_face[sf];
2332  int el = faces_info[lface].Elem1No;
2333  if (el_marker[el] != fn)
2334  {
2335  el_marker[el] = fn;
2337 
2338  const int nv = elements[el]->GetNVertices();
2339  const int *v = elements[el]->GetVertices();
2340  for (int j = 0; j < nv; j++)
2341  if (vertex_marker[v[j]] != fn)
2342  {
2343  vertex_marker[v[j]] = fn;
2345  }
2346 
2347  send_face_nbr_elemdata.AddConnection(fn, GetAttribute(el));
2348  send_face_nbr_elemdata.AddConnection(
2349  fn, GetElementBaseGeometry(el));
2350  send_face_nbr_elemdata.AddConnections(fn, v, nv);
2351 
2352  if (Dim == 3)
2353  {
2354  const int nf = elements[el]->GetNFaces();
2355  GetElementFaces(el, fcs, cor);
2356  send_face_nbr_elemdata.AddConnections(fn, cor, nf);
2357  }
2358  }
2359  send_face_nbr_facedata.AddConnection(fn, el);
2360  int info = faces_info[lface].Elem1Inf;
2361  // change the orientation in info to be relative to the shared face
2362  // in 1D and 2D keep the orientation equal to 0
2363  if (Dim == 3)
2364  {
2365  const int *lf_v = faces[lface]->GetVertices();
2366  if (sf < nst) // triangle shared face
2367  {
2368  info += GetTriOrientation(shared_trias[sf].v, lf_v);
2369  }
2370  else // quad shared face
2371  {
2372  info += GetQuadOrientation(shared_quads[sf-nst].v, lf_v);
2373  }
2374  }
2375  send_face_nbr_facedata.AddConnection(fn, info);
2376  }
2377  }
2380  send_face_nbr_elemdata.ShiftUpI();
2381  send_face_nbr_facedata.ShiftUpI();
2382 
2383  // convert the vertex indices in send_face_nbr_elemdata
2384  // convert the element indices in send_face_nbr_facedata
2385  for (int fn = 0; fn < num_face_nbrs; fn++)
2386  {
2387  int num_elems = send_face_nbr_elements.RowSize(fn);
2388  int *elems = send_face_nbr_elements.GetRow(fn);
2389  int num_verts = send_face_nbr_vertices.RowSize(fn);
2390  int *verts = send_face_nbr_vertices.GetRow(fn);
2391  int *elemdata = send_face_nbr_elemdata.GetRow(fn);
2392  int num_sfaces = send_face_nbr_facedata.RowSize(fn)/2;
2393  int *facedata = send_face_nbr_facedata.GetRow(fn);
2394 
2395  for (int i = 0; i < num_verts; i++)
2396  {
2397  vertex_marker[verts[i]] = i;
2398  }
2399 
2400  for (int el = 0; el < num_elems; el++)
2401  {
2402  const int nv = elements[elems[el]]->GetNVertices();
2403  const int nf = (Dim == 3) ? elements[elems[el]]->GetNFaces() : 0;
2404  elemdata += 2; // skip the attribute and the geometry type
2405  for (int j = 0; j < nv; j++)
2406  {
2407  elemdata[j] = vertex_marker[elemdata[j]];
2408  }
2409  elemdata += nv + nf;
2410 
2411  el_marker[elems[el]] = el;
2412  }
2413 
2414  for (int i = 0; i < num_sfaces; i++)
2415  {
2416  facedata[2*i] = el_marker[facedata[2*i]];
2417  }
2418  }
2419 
2420  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2421 
2422  Array<int> recv_face_nbr_facedata;
2423  Table recv_face_nbr_elemdata;
2424 
2425  // fill-in face_nbr_elements_offset, face_nbr_vertices_offset
2426  face_nbr_elements_offset.SetSize(num_face_nbrs + 1);
2427  face_nbr_vertices_offset.SetSize(num_face_nbrs + 1);
2428  recv_face_nbr_elemdata.MakeI(num_face_nbrs);
2429  face_nbr_elements_offset[0] = 0;
2430  face_nbr_vertices_offset[0] = 0;
2431  for (int fn = 0; fn < num_face_nbrs; fn++)
2432  {
2433  face_nbr_elements_offset[fn+1] =
2434  face_nbr_elements_offset[fn] + nbr_recv_data[3*fn];
2435  face_nbr_vertices_offset[fn+1] =
2436  face_nbr_vertices_offset[fn] + nbr_recv_data[3*fn+1];
2437  recv_face_nbr_elemdata.AddColumnsInRow(fn, nbr_recv_data[3*fn+2]);
2438  }
2439  recv_face_nbr_elemdata.MakeJ();
2440 
2441  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2442 
2443  // send and receive the element data
2444  for (int fn = 0; fn < num_face_nbrs; fn++)
2445  {
2446  int nbr_rank = GetFaceNbrRank(fn);
2447  int tag = 0;
2448 
2449  MPI_Isend(send_face_nbr_elemdata.GetRow(fn),
2450  send_face_nbr_elemdata.RowSize(fn),
2451  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2452 
2453  MPI_Irecv(recv_face_nbr_elemdata.GetRow(fn),
2454  recv_face_nbr_elemdata.RowSize(fn),
2455  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2456  }
2457 
2458  // convert the element data into face_nbr_elements
2459  face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
2462  while (true)
2463  {
2464  int fn;
2465  MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2466 
2467  if (fn == MPI_UNDEFINED)
2468  {
2469  break;
2470  }
2471 
2472  int vert_off = face_nbr_vertices_offset[fn];
2473  int elem_off = face_nbr_elements_offset[fn];
2474  int num_elems = face_nbr_elements_offset[fn+1] - elem_off;
2475  int *recv_elemdata = recv_face_nbr_elemdata.GetRow(fn);
2476 
2477  for (int i = 0; i < num_elems; i++)
2478  {
2479  Element *el = NewElement(recv_elemdata[1]);
2480  el->SetAttribute(recv_elemdata[0]);
2481  recv_elemdata += 2;
2482  int nv = el->GetNVertices();
2483  for (int j = 0; j < nv; j++)
2484  {
2485  recv_elemdata[j] += vert_off;
2486  }
2487  el->SetVertices(recv_elemdata);
2488  recv_elemdata += nv;
2489  if (Dim == 3)
2490  {
2491  int nf = el->GetNFaces();
2492  int * fn_ori = face_nbr_el_ori.GetRow(elem_off);
2493  for (int j = 0; j < nf; j++)
2494  {
2495  fn_ori[j] = recv_elemdata[j];
2496  }
2497  recv_elemdata += nf;
2498  }
2499  face_nbr_elements[elem_off++] = el;
2500  }
2501  }
2503 
2504  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2505 
2506  // send and receive the face data
2507  recv_face_nbr_facedata.SetSize(
2508  send_face_nbr_facedata.Size_of_connections());
2509  for (int fn = 0; fn < num_face_nbrs; fn++)
2510  {
2511  int nbr_rank = GetFaceNbrRank(fn);
2512  int tag = 0;
2513 
2514  MPI_Isend(send_face_nbr_facedata.GetRow(fn),
2515  send_face_nbr_facedata.RowSize(fn),
2516  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2517 
2518  // the size of the send and receive face data is the same
2519  MPI_Irecv(&recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]],
2520  send_face_nbr_facedata.RowSize(fn),
2521  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2522  }
2523 
2524  // transfer the received face data into faces_info
2525  while (true)
2526  {
2527  int fn;
2528  MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2529 
2530  if (fn == MPI_UNDEFINED)
2531  {
2532  break;
2533  }
2534 
2535  int elem_off = face_nbr_elements_offset[fn];
2536  int nbr_group = face_nbr_group[fn];
2537  int num_sfaces = gr_sface->RowSize(nbr_group-1);
2538  int *sface = gr_sface->GetRow(nbr_group-1);
2539  int *facedata =
2540  &recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]];
2541 
2542  for (int i = 0; i < num_sfaces; i++)
2543  {
2544  const int sf = sface[i];
2545  int lface = s2l_face[sf];
2546  FaceInfo &face_info = faces_info[lface];
2547  face_info.Elem2No = -1 - (facedata[2*i] + elem_off);
2548  int info = facedata[2*i+1];
2549  // change the orientation in info to be relative to the local face
2550  if (Dim < 3)
2551  {
2552  info++; // orientation 0 --> orientation 1
2553  }
2554  else
2555  {
2556  int nbr_ori = info%64, nbr_v[4];
2557  const int *lf_v = faces[lface]->GetVertices();
2558 
2559  if (sf < nst) // triangle shared face
2560  {
2561  // apply the nbr_ori to sf_v to get nbr_v
2562  const int *perm = tri_t::Orient[nbr_ori];
2563  const int *sf_v = shared_trias[sf].v;
2564  for (int j = 0; j < 3; j++)
2565  {
2566  nbr_v[perm[j]] = sf_v[j];
2567  }
2568  // get the orientation of nbr_v w.r.t. the local face
2569  nbr_ori = GetTriOrientation(lf_v, nbr_v);
2570  }
2571  else // quad shared face
2572  {
2573  // apply the nbr_ori to sf_v to get nbr_v
2574  const int *perm = quad_t::Orient[nbr_ori];
2575  const int *sf_v = shared_quads[sf-nst].v;
2576  for (int j = 0; j < 4; j++)
2577  {
2578  nbr_v[perm[j]] = sf_v[j];
2579  }
2580  // get the orientation of nbr_v w.r.t. the local face
2581  nbr_ori = GetQuadOrientation(lf_v, nbr_v);
2582  }
2583 
2584  info = 64*(info/64) + nbr_ori;
2585  }
2586  face_info.Elem2Inf = info;
2587  }
2588  }
2589 
2590  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2591 
2592  // allocate the face_nbr_vertices
2593  face_nbr_vertices.SetSize(face_nbr_vertices_offset[num_face_nbrs]);
2594 
2595  delete [] nbr_data;
2596 
2597  delete [] statuses;
2598  delete [] requests;
2599 }
2600 
2602 {
2603  if (!have_face_nbr_data)
2604  {
2605  ExchangeFaceNbrData(); // calls this method at the end
2606  }
2607  else if (Nodes == NULL)
2608  {
2609  if (Nonconforming())
2610  {
2611  // with ParNCMesh we already have the vertices
2612  return;
2613  }
2614 
2615  int num_face_nbrs = GetNFaceNeighbors();
2616 
2617  if (!num_face_nbrs) { return; }
2618 
2619  MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2620  MPI_Request *send_requests = requests;
2621  MPI_Request *recv_requests = requests + num_face_nbrs;
2622  MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2623 
2624  // allocate buffer and copy the vertices to be sent
2626  for (int i = 0; i < send_vertices.Size(); i++)
2627  {
2628  send_vertices[i] = vertices[send_face_nbr_vertices.GetJ()[i]];
2629  }
2630 
2631  // send and receive the vertices
2632  for (int fn = 0; fn < num_face_nbrs; fn++)
2633  {
2634  int nbr_rank = GetFaceNbrRank(fn);
2635  int tag = 0;
2636 
2637  MPI_Isend(send_vertices[send_face_nbr_vertices.GetI()[fn]](),
2639  MPI_DOUBLE, nbr_rank, tag, MyComm, &send_requests[fn]);
2640 
2642  3*(face_nbr_vertices_offset[fn+1] -
2644  MPI_DOUBLE, nbr_rank, tag, MyComm, &recv_requests[fn]);
2645  }
2646 
2647  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2648  MPI_Waitall(num_face_nbrs, send_requests, statuses);
2649 
2650  delete [] statuses;
2651  delete [] requests;
2652  }
2653  else
2654  {
2655  ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
2656  MFEM_VERIFY(pNodes != NULL, "Nodes are not ParGridFunction!");
2657  pNodes->ExchangeFaceNbrData();
2658  }
2659 }
2660 
2662 {
2663  STable3D *sfaces_tbl = new STable3D(face_nbr_vertices.Size());
2664  for (int i = 0; i < face_nbr_elements.Size(); i++)
2665  {
2666  const int *v = face_nbr_elements[i]->GetVertices();
2667  switch (face_nbr_elements[i]->GetType())
2668  {
2669  case Element::TETRAHEDRON:
2670  {
2671  for (int j = 0; j < 4; j++)
2672  {
2673  const int *fv = tet_t::FaceVert[j];
2674  sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2675  }
2676  break;
2677  }
2678  case Element::WEDGE:
2679  {
2680  for (int j = 0; j < 2; j++)
2681  {
2682  const int *fv = pri_t::FaceVert[j];
2683  sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2684  }
2685  for (int j = 2; j < 5; j++)
2686  {
2687  const int *fv = pri_t::FaceVert[j];
2688  sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2689  }
2690  break;
2691  }
2692  case Element::PYRAMID:
2693  {
2694  for (int j = 0; j < 1; j++)
2695  {
2696  const int *fv = pyr_t::FaceVert[j];
2697  sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2698  }
2699  for (int j = 1; j < 5; j++)
2700  {
2701  const int *fv = pyr_t::FaceVert[j];
2702  sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2703  }
2704  break;
2705  }
2706  case Element::HEXAHEDRON:
2707  {
2708  // find the face by the vertices with the smallest 3 numbers
2709  // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
2710  for (int j = 0; j < 6; j++)
2711  {
2712  const int *fv = hex_t::FaceVert[j];
2713  sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2714  }
2715  break;
2716  }
2717  default:
2718  MFEM_ABORT("Unexpected type of Element.");
2719  }
2720  }
2721  return sfaces_tbl;
2722 }
2723 
2725 {
2726  int i, *v;
2727  STable3D * faces_tbl = GetFacesTable();
2728  STable3D * sfaces_tbl = GetSharedFacesTable();
2729 
2730  if (face_nbr_el_to_face != NULL)
2731  {
2732  delete face_nbr_el_to_face;
2733  }
2734  face_nbr_el_to_face = new Table(face_nbr_elements.Size(), 6);
2735  for (i = 0; i < face_nbr_elements.Size(); i++)
2736  {
2737  v = face_nbr_elements[i]->GetVertices();
2738  switch (face_nbr_elements[i]->GetType())
2739  {
2740  case Element::TETRAHEDRON:
2741  {
2742  for (int j = 0; j < 4; j++)
2743  {
2744  const int *fv = tet_t::FaceVert[j];
2745  int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2746  if (lf < 0)
2747  {
2748  lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2749  if (lf >= 0)
2750  {
2751  lf += NumOfFaces;
2752  }
2753  }
2754  face_nbr_el_to_face->Push(i, lf);
2755  }
2756  break;
2757  }
2758  case Element::WEDGE:
2759  {
2760  for (int j = 0; j < 2; j++)
2761  {
2762  const int *fv = pri_t::FaceVert[j];
2763  int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2764  if (lf < 0)
2765  {
2766  lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2767  if (lf >= 0)
2768  {
2769  lf += NumOfFaces;
2770  }
2771  }
2772  face_nbr_el_to_face->Push(i, lf);
2773  }
2774  for (int j = 2; j < 5; j++)
2775  {
2776  const int *fv = pri_t::FaceVert[j];
2777  int k = 0;
2778  int max = v[fv[0]];
2779 
2780  if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
2781  if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
2782  if (max < v[fv[3]]) { k = 3; }
2783 
2784  int v0 = -1, v1 = -1, v2 = -1;
2785  switch (k)
2786  {
2787  case 0:
2788  v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2789  break;
2790  case 1:
2791  v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2792  break;
2793  case 2:
2794  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
2795  break;
2796  case 3:
2797  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
2798  break;
2799  }
2800  int lf = faces_tbl->Index(v0, v1, v2);
2801  if (lf < 0)
2802  {
2803  lf = sfaces_tbl->Index(v0, v1, v2);
2804  if (lf >= 0)
2805  {
2806  lf += NumOfFaces;
2807  }
2808  }
2809  face_nbr_el_to_face->Push(i, lf);
2810  }
2811  break;
2812  }
2813  case Element::PYRAMID:
2814  {
2815  for (int j = 0; j < 1; j++)
2816  {
2817  const int *fv = pyr_t::FaceVert[j];
2818  int k = 0;
2819  int max = v[fv[0]];
2820 
2821  if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
2822  if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
2823  if (max < v[fv[3]]) { k = 3; }
2824 
2825  int v0 = -1, v1 = -1, v2 = -1;
2826  switch (k)
2827  {
2828  case 0:
2829  v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2830  break;
2831  case 1:
2832  v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2833  break;
2834  case 2:
2835  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
2836  break;
2837  case 3:
2838  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
2839  break;
2840  }
2841  int lf = faces_tbl->Index(v0, v1, v2);
2842  if (lf < 0)
2843  {
2844  lf = sfaces_tbl->Index(v0, v1, v2);
2845  if (lf >= 0)
2846  {
2847  lf += NumOfFaces;
2848  }
2849  }
2850  face_nbr_el_to_face->Push(i, lf);
2851  }
2852  for (int j = 1; j < 5; j++)
2853  {
2854  const int *fv = pyr_t::FaceVert[j];
2855  int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2856  if (lf < 0)
2857  {
2858  lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
2859  if (lf >= 0)
2860  {
2861  lf += NumOfFaces;
2862  }
2863  }
2864  face_nbr_el_to_face->Push(i, lf);
2865  }
2866  break;
2867  }
2868  case Element::HEXAHEDRON:
2869  {
2870  // find the face by the vertices with the smallest 3 numbers
2871  // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
2872  for (int j = 0; j < 6; j++)
2873  {
2874  const int *fv = hex_t::FaceVert[j];
2875  int k = 0;
2876  int max = v[fv[0]];
2877 
2878  if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
2879  if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
2880  if (max < v[fv[3]]) { k = 3; }
2881 
2882  int v0 = -1, v1 = -1, v2 = -1;
2883  switch (k)
2884  {
2885  case 0:
2886  v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2887  break;
2888  case 1:
2889  v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2890  break;
2891  case 2:
2892  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
2893  break;
2894  case 3:
2895  v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
2896  break;
2897  }
2898  int lf = faces_tbl->Index(v0, v1, v2);
2899  if (lf < 0)
2900  {
2901  lf = sfaces_tbl->Index(v0, v1, v2);
2902  if (lf >= 0)
2903  {
2904  lf += NumOfFaces;
2905  }
2906  }
2907  face_nbr_el_to_face->Push(i, lf);
2908  }
2909  break;
2910  }
2911  default:
2912  MFEM_ABORT("Unexpected type of Element.");
2913  }
2914  }
2916 
2917  delete sfaces_tbl;
2918  if (ret_ftbl)
2919  {
2920  return faces_tbl;
2921  }
2922  delete faces_tbl;
2923  return NULL;
2924 }
2925 
2926 int ParMesh::GetFaceNbrRank(int fn) const
2927 {
2928  if (Conforming())
2929  {
2930  int nbr_group = face_nbr_group[fn];
2931  const int *nbs = gtopo.GetGroup(nbr_group);
2932  int nbr_lproc = (nbs[0]) ? nbs[0] : nbs[1];
2933  int nbr_rank = gtopo.GetNeighborRank(nbr_lproc);
2934  return nbr_rank;
2935  }
2936  else
2937  {
2938  // NC: simplified handling of face neighbor ranks
2939  return face_nbr_group[fn];
2940  }
2941 }
2942 
2943 void
2945 {
2946  int n, j;
2947  int el_nbr = i - GetNE();
2948  if (face_nbr_el_to_face)
2949  {
2950  face_nbr_el_to_face->GetRow(el_nbr, fcs);
2951  }
2952  else
2953  {
2954  MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2955  "face_nbr_el_to_face not generated.");
2956  }
2957  if (el_nbr < face_nbr_el_ori.Size())
2958  {
2959  const int * row = face_nbr_el_ori.GetRow(el_nbr);
2960  n = fcs.Size();
2961  cor.SetSize(n);
2962  for (j=0; j<n; j++)
2963  {
2964  cor[j] = row[j];
2965  }
2966  }
2967  else
2968  {
2969  MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2970  "face_nbr_el_to_face not generated.");
2971  }
2972 }
2973 
2975 {
2976  const Array<int> *s2l_face;
2977  if (Dim == 1)
2978  {
2979  s2l_face = &svert_lvert;
2980  }
2981  else if (Dim == 2)
2982  {
2983  s2l_face = &sedge_ledge;
2984  }
2985  else
2986  {
2987  s2l_face = &sface_lface;
2988  }
2989 
2990  Table *face_elem = new Table;
2991 
2992  face_elem->MakeI(faces_info.Size());
2993 
2994  for (int i = 0; i < faces_info.Size(); i++)
2995  {
2996  if (faces_info[i].Elem2No >= 0)
2997  {
2998  face_elem->AddColumnsInRow(i, 2);
2999  }
3000  else
3001  {
3002  face_elem->AddAColumnInRow(i);
3003  }
3004  }
3005  for (int i = 0; i < s2l_face->Size(); i++)
3006  {
3007  face_elem->AddAColumnInRow((*s2l_face)[i]);
3008  }
3009 
3010  face_elem->MakeJ();
3011 
3012  for (int i = 0; i < faces_info.Size(); i++)
3013  {
3014  face_elem->AddConnection(i, faces_info[i].Elem1No);
3015  if (faces_info[i].Elem2No >= 0)
3016  {
3017  face_elem->AddConnection(i, faces_info[i].Elem2No);
3018  }
3019  }
3020  for (int i = 0; i < s2l_face->Size(); i++)
3021  {
3022  int lface = (*s2l_face)[i];
3023  int nbr_elem_idx = -1 - faces_info[lface].Elem2No;
3024  face_elem->AddConnection(lface, NumOfElements + nbr_elem_idx);
3025  }
3026 
3027  face_elem->ShiftUpI();
3028 
3029  return face_elem;
3030 }
3031 
3033  FaceElementTransformations* FETr, Element::Type face_type,
3034  Geometry::Type face_geom)
3035 {
3036  // calculate composition of FETr->Loc1 and FETr->Elem1
3037  DenseMatrix &face_pm = FETr->GetPointMat();
3038  FETr->Reset();
3039  if (Nodes == NULL)
3040  {
3041  FETr->Elem1->Transform(FETr->Loc1.Transf.GetPointMat(), face_pm);
3042  FETr->SetFE(GetTransformationFEforElementType(face_type));
3043  }
3044  else
3045  {
3046  const FiniteElement* face_el =
3047  Nodes->FESpace()->GetTraceElement(FETr->Elem1No, face_geom);
3048  MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
3049  "Mesh requires nodal Finite Element.");
3050 
3051 #if 0 // TODO: handle the case of non-interpolatory Nodes
3052  DenseMatrix I;
3053  face_el->Project(Transformation.GetFE(), FETr->Loc1.Transf, I);
3054  MultABt(Transformation.GetPointMat(), I, pm_face);
3055 #else
3056  IntegrationRule eir(face_el->GetDof());
3057  FETr->Loc1.Transform(face_el->GetNodes(), eir);
3058  Nodes->GetVectorValues(*FETr->Elem1, eir, face_pm);
3059 #endif
3060  FETr->SetFE(face_el);
3061  }
3062 }
3063 
3065  int FaceNo,
3066  int mask)
3067 {
3068  if (FaceNo < GetNumFaces())
3069  {
3070  return Mesh::GetFaceElementTransformations(FaceNo, mask);
3071  }
3072  else
3073  {
3074  const bool fill2 = mask & 10; // Elem2 and/or Loc2
3075  return GetSharedFaceTransformationsByLocalIndex(FaceNo, fill2);
3076  }
3077 }
3078 
3080 GetSharedFaceTransformations(int sf, bool fill2)
3081 {
3082  int FaceNo = GetSharedFace(sf);
3083 
3084  return GetSharedFaceTransformationsByLocalIndex(FaceNo, fill2);
3085 }
3086 
3089 {
3090  FaceInfo &face_info = faces_info[FaceNo];
3091  MFEM_VERIFY(face_info.Elem2Inf >= 0, "The face must be shared.");
3092 
3093  bool is_slave = Nonconforming() && IsSlaveFace(face_info);
3094  bool is_ghost = Nonconforming() && FaceNo >= GetNumFaces();
3095 
3096  int mask = 0;
3098  FaceElemTr.Elem1 = NULL;
3099  FaceElemTr.Elem2 = NULL;
3100 
3101  NCFaceInfo* nc_info = NULL;
3102  if (is_slave) { nc_info = &nc_faces_info[face_info.NCFace]; }
3103 
3104  int local_face = is_ghost ? nc_info->MasterFace : FaceNo;
3105  Element::Type face_type = GetFaceElementType(local_face);
3106  Geometry::Type face_geom = GetFaceGeometry(local_face);
3107 
3108  // setup the transformation for the first element
3109  FaceElemTr.Elem1No = face_info.Elem1No;
3113 
3114  // setup the transformation for the second (neighbor) element
3115  int Elem2NbrNo;
3116  if (fill2)
3117  {
3118  Elem2NbrNo = -1 - face_info.Elem2No;
3119  // Store the "shifted index" for element 2 in FaceElemTr.Elem2No.
3120  // `Elem2NbrNo` is the index of the face neighbor (starting from 0),
3121  // and `FaceElemTr.Elem2No` will be offset by the number of (local)
3122  // elements in the mesh.
3123  FaceElemTr.Elem2No = NumOfElements + Elem2NbrNo;
3127  }
3128  else
3129  {
3130  FaceElemTr.Elem2No = -1;
3131  }
3132 
3133  // setup the face transformation if the face is not a ghost
3134  if (!is_ghost)
3135  {
3137  // NOTE: The above call overwrites FaceElemTr.Loc1
3139  }
3140  else
3141  {
3142  FaceElemTr.SetGeometryType(face_geom);
3143  }
3144 
3145  // setup Loc1 & Loc2
3146  int elem_type = GetElementType(face_info.Elem1No);
3147  GetLocalFaceTransformation(face_type, elem_type, FaceElemTr.Loc1.Transf,
3148  face_info.Elem1Inf);
3150 
3151  if (fill2)
3152  {
3153  elem_type = face_nbr_elements[Elem2NbrNo]->GetType();
3154  GetLocalFaceTransformation(face_type, elem_type, FaceElemTr.Loc2.Transf,
3155  face_info.Elem2Inf);
3157  }
3158 
3159  // adjust Loc1 or Loc2 of the master face if this is a slave face
3160  if (is_slave)
3161  {
3162  if (is_ghost || fill2)
3163  {
3164  // is_ghost -> modify side 1, otherwise -> modify side 2:
3165  ApplyLocalSlaveTransformation(FaceElemTr, face_info, is_ghost);
3166  }
3167  }
3168 
3169  // for ghost faces we need a special version of GetFaceTransformation
3170  if (is_ghost)
3171  {
3172  GetGhostFaceTransformation(&FaceElemTr, face_type, face_geom);
3174  }
3175 
3177 
3178  // This check can be useful for internal debugging, however it will fail on
3179  // periodic boundary faces, so we keep it disabled in general.
3180 #if 0
3181 #ifdef MFEM_DEBUG
3182  double dist = FaceElemTr.CheckConsistency();
3183  if (dist >= 1e-12)
3184  {
3185  mfem::out << "\nInternal error: face id = " << FaceNo
3186  << ", dist = " << dist << ", rank = " << MyRank << '\n';
3187  FaceElemTr.CheckConsistency(1); // print coordinates
3188  MFEM_ABORT("internal error");
3189  }
3190 #endif
3191 #endif
3192 
3193  return &FaceElemTr;
3194 }
3195 
3197 {
3198  if (Conforming())
3199  {
3200  switch (Dim)
3201  {
3202  case 1: return svert_lvert.Size();
3203  case 2: return sedge_ledge.Size();
3204  default: return sface_lface.Size();
3205  }
3206  }
3207  else
3208  {
3209  MFEM_ASSERT(Dim > 1, "");
3210  const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3211  return shared.conforming.Size() + shared.slaves.Size();
3212  }
3213 }
3214 
3215 int ParMesh::GetSharedFace(int sface) const
3216 {
3217  if (Conforming())
3218  {
3219  switch (Dim)
3220  {
3221  case 1: return svert_lvert[sface];
3222  case 2: return sedge_ledge[sface];
3223  default: return sface_lface[sface];
3224  }
3225  }
3226  else
3227  {
3228  MFEM_ASSERT(Dim > 1, "");
3229  const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3230  int csize = (int) shared.conforming.Size();
3231  return sface < csize
3232  ? shared.conforming[sface].index
3233  : shared.slaves[sface - csize].index;
3234  }
3235 }
3236 
3238 {
3239  const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
3240  return Mesh::GetNFbyType(type);
3241 }
3242 
3243 // shift cyclically 3 integers a, b, c, so that the smallest of
3244 // order[a], order[b], order[c] is first
3245 static inline
3246 void Rotate3Indirect(int &a, int &b, int &c,
3247  const Array<std::int64_t> &order)
3248 {
3249  if (order[a] < order[b])
3250  {
3251  if (order[a] > order[c])
3252  {
3253  ShiftRight(a, b, c);
3254  }
3255  }
3256  else
3257  {
3258  if (order[b] < order[c])
3259  {
3260  ShiftRight(c, b, a);
3261  }
3262  else
3263  {
3264  ShiftRight(a, b, c);
3265  }
3266  }
3267 }
3268 
3270 {
3271  if (Dim != 3 || !(meshgen & 1))
3272  {
3273  return;
3274  }
3275 
3276  ResetLazyData();
3277 
3278  DSTable *old_v_to_v = NULL;
3279  Table *old_elem_vert = NULL;
3280 
3281  if (Nodes)
3282  {
3283  PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3284  }
3285 
3286  // create a GroupCommunicator over shared vertices
3287  GroupCommunicator svert_comm(gtopo);
3288  GetSharedVertexCommunicator(svert_comm);
3289 
3290  // communicate the local index of each shared vertex from the group master to
3291  // other ranks in the group
3292  Array<int> svert_master_rank(svert_lvert.Size());
3293  Array<int> svert_master_index(svert_lvert);
3294  {
3295  for (int i = 0; i < group_svert.Size(); i++)
3296  {
3297  int rank = gtopo.GetGroupMasterRank(i+1);
3298  for (int j = 0; j < group_svert.RowSize(i); j++)
3299  {
3300  svert_master_rank[group_svert.GetRow(i)[j]] = rank;
3301  }
3302  }
3303  svert_comm.Bcast(svert_master_index);
3304  }
3305 
3306  // the pairs (master rank, master local index) define a globally consistent
3307  // vertex ordering
3308  Array<std::int64_t> glob_vert_order(vertices.Size());
3309  {
3310  Array<int> lvert_svert(vertices.Size());
3311  lvert_svert = -1;
3312  for (int i = 0; i < svert_lvert.Size(); i++)
3313  {
3314  lvert_svert[svert_lvert[i]] = i;
3315  }
3316 
3317  for (int i = 0; i < vertices.Size(); i++)
3318  {
3319  int s = lvert_svert[i];
3320  if (s >= 0)
3321  {
3322  glob_vert_order[i] =
3323  (std::int64_t(svert_master_rank[s]) << 32) + svert_master_index[s];
3324  }
3325  else
3326  {
3327  glob_vert_order[i] = (std::int64_t(MyRank) << 32) + i;
3328  }
3329  }
3330  }
3331 
3332  // rotate tetrahedra so that vertex zero is the lowest (global) index vertex,
3333  // vertex 1 is the second lowest (global) index and vertices 2 and 3 preserve
3334  // positive orientation of the element
3335  for (int i = 0; i < NumOfElements; i++)
3336  {
3338  {
3339  int *v = elements[i]->GetVertices();
3340 
3341  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3342 
3343  if (glob_vert_order[v[0]] < glob_vert_order[v[3]])
3344  {
3345  Rotate3Indirect(v[1], v[2], v[3], glob_vert_order);
3346  }
3347  else
3348  {
3349  ShiftRight(v[0], v[1], v[3]);
3350  }
3351  }
3352  }
3353 
3354  // rotate also boundary triangles
3355  for (int i = 0; i < NumOfBdrElements; i++)
3356  {
3358  {
3359  int *v = boundary[i]->GetVertices();
3360 
3361  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3362  }
3363  }
3364 
3365  const bool check_consistency = true;
3366  if (check_consistency)
3367  {
3368  // create a GroupCommunicator on the shared triangles
3369  GroupCommunicator stria_comm(gtopo);
3370  {
3371  // initialize stria_comm
3372  Table &gr_stria = stria_comm.GroupLDofTable();
3373  // gr_stria differs from group_stria - the latter does not store gr. 0
3374  gr_stria.SetDims(GetNGroups(), shared_trias.Size());
3375  gr_stria.GetI()[0] = 0;
3376  for (int gr = 1; gr <= GetNGroups(); gr++)
3377  {
3378  gr_stria.GetI()[gr] = group_stria.GetI()[gr-1];
3379  }
3380  for (int k = 0; k < shared_trias.Size(); k++)
3381  {
3382  gr_stria.GetJ()[k] = group_stria.GetJ()[k];
3383  }
3384  stria_comm.Finalize();
3385  }
3386  Array<int> stria_flag(shared_trias.Size());
3387  for (int i = 0; i < stria_flag.Size(); i++)
3388  {
3389  const int *v = shared_trias[i].v;
3390  if (glob_vert_order[v[0]] < glob_vert_order[v[1]])
3391  {
3392  stria_flag[i] = (glob_vert_order[v[0]] < glob_vert_order[v[2]]) ? 0 : 2;
3393  }
3394  else // v[1] < v[0]
3395  {
3396  stria_flag[i] = (glob_vert_order[v[1]] < glob_vert_order[v[2]]) ? 1 : 2;
3397  }
3398  }
3399 
3400  Array<int> stria_master_flag(stria_flag);
3401  stria_comm.Bcast(stria_master_flag);
3402  for (int i = 0; i < stria_flag.Size(); i++)
3403  {
3404  const int *v = shared_trias[i].v;
3405  MFEM_VERIFY(stria_flag[i] == stria_master_flag[i],
3406  "inconsistent vertex ordering found, shared triangle "
3407  << i << ": ("
3408  << v[0] << ", " << v[1] << ", " << v[2] << "), "
3409  << "local flag: " << stria_flag[i]
3410  << ", master flag: " << stria_master_flag[i]);
3411  }
3412  }
3413 
3414  // rotate shared triangle faces
3415  for (int i = 0; i < shared_trias.Size(); i++)
3416  {
3417  int *v = shared_trias[i].v;
3418 
3419  Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3420  }
3421 
3422  // finalize
3423  if (!Nodes)
3424  {
3426  GenerateFaces();
3427  if (el_to_edge)
3428  {
3430  }
3431  }
3432  else
3433  {
3434  DoNodeReorder(old_v_to_v, old_elem_vert);
3435  delete old_elem_vert;
3436  delete old_v_to_v;
3437  }
3438 
3439  // the local edge and face numbering is changed therefore we need to
3440  // update sedge_ledge and sface_lface.
3441  FinalizeParTopo();
3442 }
3443 
3444 void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
3445 {
3446  if (pncmesh)
3447  {
3448  MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
3449  }
3450 
3452 
3454 
3455  if (Dim == 3)
3456  {
3457  int uniform_refinement = 0;
3458  if (type < 0)
3459  {
3460  type = -type;
3461  uniform_refinement = 1;
3462  }
3463 
3464  // 1. Hash table of vertex to vertex connections corresponding to refined
3465  // edges.
3466  HashTable<Hashed2> v_to_v;
3467 
3468  // 2. Do the red refinement.
3469  switch (type)
3470  {
3471  case 1:
3472  for (int i = 0; i < marked_el.Size(); i++)
3473  {
3474  Bisection(marked_el[i], v_to_v);
3475  }
3476  break;
3477  case 2:
3478  for (int i = 0; i < marked_el.Size(); i++)
3479  {
3480  Bisection(marked_el[i], v_to_v);
3481 
3482  Bisection(NumOfElements - 1, v_to_v);
3483  Bisection(marked_el[i], v_to_v);
3484  }
3485  break;
3486  case 3:
3487  for (int i = 0; i < marked_el.Size(); i++)
3488  {
3489  Bisection(marked_el[i], v_to_v);
3490 
3491  int j = NumOfElements - 1;
3492  Bisection(j, v_to_v);
3493  Bisection(NumOfElements - 1, v_to_v);
3494  Bisection(j, v_to_v);
3495 
3496  Bisection(marked_el[i], v_to_v);
3497  Bisection(NumOfElements-1, v_to_v);
3498  Bisection(marked_el[i], v_to_v);
3499  }
3500  break;
3501  }
3502 
3503  // 3. Do the green refinement (to get conforming mesh).
3504  int need_refinement;
3505  int max_faces_in_group = 0;
3506  // face_splittings identify how the shared faces have been split
3507  Array<unsigned> *face_splittings = new Array<unsigned>[GetNGroups()-1];
3508  for (int i = 0; i < GetNGroups()-1; i++)
3509  {
3510  const int faces_in_group = GroupNTriangles(i+1);
3511  face_splittings[i].Reserve(faces_in_group);
3512  if (faces_in_group > max_faces_in_group)
3513  {
3514  max_faces_in_group = faces_in_group;
3515  }
3516  }
3517  int neighbor;
3518  Array<unsigned> iBuf(max_faces_in_group);
3519 
3520  MPI_Request *requests = new MPI_Request[GetNGroups()-1];
3521  MPI_Status status;
3522 
3523 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3524  int ref_loops_all = 0, ref_loops_par = 0;
3525 #endif
3526  do
3527  {
3528  need_refinement = 0;
3529  for (int i = 0; i < NumOfElements; i++)
3530  {
3531  if (elements[i]->NeedRefinement(v_to_v))
3532  {
3533  need_refinement = 1;
3534  Bisection(i, v_to_v);
3535  }
3536  }
3537 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3538  ref_loops_all++;
3539 #endif
3540 
3541  if (uniform_refinement)
3542  {
3543  continue;
3544  }
3545 
3546  // if the mesh is locally conforming start making it globally
3547  // conforming
3548  if (need_refinement == 0)
3549  {
3550 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3551  ref_loops_par++;
3552 #endif
3553  // MPI_Barrier(MyComm);
3554  const int tag = 293;
3555 
3556  // (a) send the type of interface splitting
3557  int req_count = 0;
3558  for (int i = 0; i < GetNGroups()-1; i++)
3559  {
3560  const int *group_faces = group_stria.GetRow(i);
3561  const int faces_in_group = group_stria.RowSize(i);
3562  // it is enough to communicate through the faces
3563  if (faces_in_group == 0) { continue; }
3564 
3565  face_splittings[i].SetSize(0);
3566  for (int j = 0; j < faces_in_group; j++)
3567  {
3568  GetFaceSplittings(shared_trias[group_faces[j]].v, v_to_v,
3569  face_splittings[i]);
3570  }
3571  const int *nbs = gtopo.GetGroup(i+1);
3572  neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3573  MPI_Isend(face_splittings[i], face_splittings[i].Size(),
3574  MPI_UNSIGNED, neighbor, tag, MyComm,
3575  &requests[req_count++]);
3576  }
3577 
3578  // (b) receive the type of interface splitting
3579  for (int i = 0; i < GetNGroups()-1; i++)
3580  {
3581  const int *group_faces = group_stria.GetRow(i);
3582  const int faces_in_group = group_stria.RowSize(i);
3583  if (faces_in_group == 0) { continue; }
3584 
3585  const int *nbs = gtopo.GetGroup(i+1);
3586  neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3587  MPI_Probe(neighbor, tag, MyComm, &status);
3588  int count;
3589  MPI_Get_count(&status, MPI_UNSIGNED, &count);
3590  iBuf.SetSize(count);
3591  MPI_Recv(iBuf, count, MPI_UNSIGNED, neighbor, tag, MyComm,
3592  MPI_STATUS_IGNORE);
3593 
3594  for (int j = 0, pos = 0; j < faces_in_group; j++)
3595  {
3596  const int *v = shared_trias[group_faces[j]].v;
3597  need_refinement |= DecodeFaceSplittings(v_to_v, v, iBuf, pos);
3598  }
3599  }
3600 
3601  int nr = need_refinement;
3602  MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3603 
3604  MPI_Waitall(req_count, requests, MPI_STATUSES_IGNORE);
3605  }
3606  }
3607  while (need_refinement == 1);
3608 
3609 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3610  {
3611  int i = ref_loops_all;
3612  MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3613  if (MyRank == 0)
3614  {
3615  mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3616  << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3617  << '\n' << endl;
3618  }
3619  }
3620 #endif
3621 
3622  delete [] requests;
3623  iBuf.DeleteAll();
3624  delete [] face_splittings;
3625 
3626  // 4. Update the boundary elements.
3627  do
3628  {
3629  need_refinement = 0;
3630  for (int i = 0; i < NumOfBdrElements; i++)
3631  {
3632  if (boundary[i]->NeedRefinement(v_to_v))
3633  {
3634  need_refinement = 1;
3635  BdrBisection(i, v_to_v);
3636  }
3637  }
3638  }
3639  while (need_refinement == 1);
3640 
3641  if (NumOfBdrElements != boundary.Size())
3642  {
3643  mfem_error("ParMesh::LocalRefinement :"
3644  " (NumOfBdrElements != boundary.Size())");
3645  }
3646 
3647  ResetLazyData();
3648 
3649  const int old_nv = NumOfVertices;
3650  NumOfVertices = vertices.Size();
3651 
3652  RefineGroups(old_nv, v_to_v);
3653 
3654  // 5. Update the groups after refinement.
3655  if (el_to_face != NULL)
3656  {
3658  GenerateFaces();
3659  }
3660 
3661  // 6. Update element-to-edge relations.
3662  if (el_to_edge != NULL)
3663  {
3665  }
3666  } // 'if (Dim == 3)'
3667 
3668 
3669  if (Dim == 2)
3670  {
3671  int uniform_refinement = 0;
3672  if (type < 0)
3673  {
3674  // type = -type; // not used
3675  uniform_refinement = 1;
3676  }
3677 
3678  // 1. Get table of vertex to vertex connections.
3679  DSTable v_to_v(NumOfVertices);
3680  GetVertexToVertexTable(v_to_v);
3681 
3682  // 2. Get edge to element connections in arrays edge1 and edge2
3683  int nedges = v_to_v.NumberOfEntries();
3684  int *edge1 = new int[nedges];
3685  int *edge2 = new int[nedges];
3686  int *middle = new int[nedges];
3687 
3688  for (int i = 0; i < nedges; i++)
3689  {
3690  edge1[i] = edge2[i] = middle[i] = -1;
3691  }
3692 
3693  for (int i = 0; i < NumOfElements; i++)
3694  {
3695  int *v = elements[i]->GetVertices();
3696  for (int j = 0; j < 3; j++)
3697  {
3698  int ind = v_to_v(v[j], v[(j+1)%3]);
3699  (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
3700  }
3701  }
3702 
3703  // 3. Do the red refinement.
3704  for (int i = 0; i < marked_el.Size(); i++)
3705  {
3706  RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
3707  }
3708 
3709  // 4. Do the green refinement (to get conforming mesh).
3710  int need_refinement;
3711  int edges_in_group, max_edges_in_group = 0;
3712  // edge_splittings identify how the shared edges have been split
3713  int **edge_splittings = new int*[GetNGroups()-1];
3714  for (int i = 0; i < GetNGroups()-1; i++)
3715  {
3716  edges_in_group = GroupNEdges(i+1);
3717  edge_splittings[i] = new int[edges_in_group];
3718  if (edges_in_group > max_edges_in_group)
3719  {
3720  max_edges_in_group = edges_in_group;
3721  }
3722  }
3723  int neighbor, *iBuf = new int[max_edges_in_group];
3724 
3725  Array<int> group_edges;
3726 
3727  MPI_Request request;
3728  MPI_Status status;
3729  Vertex V;
3730  V(2) = 0.0;
3731 
3732 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3733  int ref_loops_all = 0, ref_loops_par = 0;
3734 #endif
3735  do
3736  {
3737  need_refinement = 0;
3738  for (int i = 0; i < nedges; i++)
3739  {
3740  if (middle[i] != -1 && edge1[i] != -1)
3741  {
3742  need_refinement = 1;
3743  GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
3744  }
3745  }
3746 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3747  ref_loops_all++;
3748 #endif
3749 
3750  if (uniform_refinement)
3751  {
3752  continue;
3753  }
3754 
3755  // if the mesh is locally conforming start making it globally
3756  // conforming
3757  if (need_refinement == 0)
3758  {
3759 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3760  ref_loops_par++;
3761 #endif
3762  // MPI_Barrier(MyComm);
3763 
3764  // (a) send the type of interface splitting
3765  for (int i = 0; i < GetNGroups()-1; i++)
3766  {
3767  group_sedge.GetRow(i, group_edges);
3768  edges_in_group = group_edges.Size();
3769  // it is enough to communicate through the edges
3770  if (edges_in_group != 0)
3771  {
3772  for (int j = 0; j < edges_in_group; j++)
3773  {
3774  edge_splittings[i][j] =
3775  GetEdgeSplittings(shared_edges[group_edges[j]], v_to_v,
3776  middle);
3777  }
3778  const int *nbs = gtopo.GetGroup(i+1);
3779  if (nbs[0] == 0)
3780  {
3781  neighbor = gtopo.GetNeighborRank(nbs[1]);
3782  }
3783  else
3784  {
3785  neighbor = gtopo.GetNeighborRank(nbs[0]);
3786  }
3787  MPI_Isend(edge_splittings[i], edges_in_group, MPI_INT,
3788  neighbor, 0, MyComm, &request);
3789  }
3790  }
3791 
3792  // (b) receive the type of interface splitting
3793  for (int i = 0; i < GetNGroups()-1; i++)
3794  {
3795  group_sedge.GetRow(i, group_edges);
3796  edges_in_group = group_edges.Size();
3797  if (edges_in_group != 0)
3798  {
3799  const int *nbs = gtopo.GetGroup(i+1);
3800  if (nbs[0] == 0)
3801  {
3802  neighbor = gtopo.GetNeighborRank(nbs[1]);
3803  }
3804  else
3805  {
3806  neighbor = gtopo.GetNeighborRank(nbs[0]);
3807  }
3808  MPI_Recv(iBuf, edges_in_group, MPI_INT, neighbor,
3809  MPI_ANY_TAG, MyComm, &status);
3810 
3811  for (int j = 0; j < edges_in_group; j++)
3812  {
3813  if (iBuf[j] == 1 && edge_splittings[i][j] == 0)
3814  {
3815  int *v = shared_edges[group_edges[j]]->GetVertices();
3816  int ii = v_to_v(v[0], v[1]);
3817 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3818  if (middle[ii] != -1)
3819  {
3820  mfem_error("ParMesh::LocalRefinement (triangles) : "
3821  "Oops!");
3822  }
3823 #endif
3824  need_refinement = 1;
3825  middle[ii] = NumOfVertices++;
3826  for (int c = 0; c < 2; c++)
3827  {
3828  V(c) = 0.5 * (vertices[v[0]](c) + vertices[v[1]](c));
3829  }
3830  vertices.Append(V);
3831  }
3832  }
3833  }
3834  }
3835 
3836  int nr = need_refinement;
3837  MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3838  }
3839  }
3840  while (need_refinement == 1);
3841 
3842 #ifdef MFEM_DEBUG_PARMESH_LOCALREF
3843  {
3844  int i = ref_loops_all;
3845  MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3846  if (MyRank == 0)
3847  {
3848  mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3849  << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3850  << '\n' << endl;
3851  }
3852  }
3853 #endif
3854 
3855  for (int i = 0; i < GetNGroups()-1; i++)
3856  {
3857  delete [] edge_splittings[i];
3858  }
3859  delete [] edge_splittings;
3860 
3861  delete [] iBuf;
3862 
3863  // 5. Update the boundary elements.
3864  int v1[2], v2[2], bisect, temp;
3865  temp = NumOfBdrElements;
3866  for (int i = 0; i < temp; i++)
3867  {
3868  int *v = boundary[i]->GetVertices();
3869  bisect = v_to_v(v[0], v[1]);
3870  if (middle[bisect] != -1)
3871  {
3872  // the element was refined (needs updating)
3873  if (boundary[i]->GetType() == Element::SEGMENT)
3874  {
3875  v1[0] = v[0]; v1[1] = middle[bisect];
3876  v2[0] = middle[bisect]; v2[1] = v[1];
3877 
3878  boundary[i]->SetVertices(v1);
3879  boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
3880  }
3881  else
3882  {
3883  mfem_error("Only bisection of segment is implemented for bdr"
3884  " elem.");
3885  }
3886  }
3887  }
3888  NumOfBdrElements = boundary.Size();
3889 
3890  ResetLazyData();
3891 
3892  // 5a. Update the groups after refinement.
3893  RefineGroups(v_to_v, middle);
3894 
3895  // 6. Free the allocated memory.
3896  delete [] edge1;
3897  delete [] edge2;
3898  delete [] middle;
3899 
3900  if (el_to_edge != NULL)
3901  {
3903  GenerateFaces();
3904  }
3905  } // 'if (Dim == 2)'
3906 
3907  if (Dim == 1) // --------------------------------------------------------
3908  {
3909  int cne = NumOfElements, cnv = NumOfVertices;
3910  NumOfVertices += marked_el.Size();
3911  NumOfElements += marked_el.Size();
3912  vertices.SetSize(NumOfVertices);
3913  elements.SetSize(NumOfElements);
3915 
3916  for (int j = 0; j < marked_el.Size(); j++)
3917  {
3918  int i = marked_el[j];
3919  Segment *c_seg = (Segment *)elements[i];
3920  int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
3921  int new_v = cnv + j, new_e = cne + j;
3922  AverageVertices(vert, 2, new_v);
3923  elements[new_e] = new Segment(new_v, vert[1], attr);
3924  vert[1] = new_v;
3925 
3928  }
3929 
3930  static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
3932  UseExternalData(seg_children, 1, 2, 3);
3933 
3934  GenerateFaces();
3935  } // end of 'if (Dim == 1)'
3936 
3938  sequence++;
3939 
3940  UpdateNodes();
3941 
3942 #ifdef MFEM_DEBUG
3943  CheckElementOrientation(false);
3945 #endif
3946 }
3947 
3949  int nc_limit)
3950 {
3951  if (NURBSext)
3952  {
3953  MFEM_ABORT("NURBS meshes are not supported. Please project the "
3954  "NURBS to Nodes first with SetCurvature().");
3955  }
3956 
3957  if (!pncmesh)
3958  {
3959  MFEM_ABORT("Can't convert conforming ParMesh to nonconforming ParMesh "
3960  "(you need to initialize the ParMesh from a nonconforming "
3961  "serial Mesh)");
3962  }
3963 
3965 
3966  // NOTE: no check of !refinements.Size(), in parallel we would have to reduce
3967 
3968  // do the refinements
3970  pncmesh->Refine(refinements);
3971 
3972  if (nc_limit > 0)
3973  {
3974  pncmesh->LimitNCLevel(nc_limit);
3975  }
3976 
3977  // create a second mesh containing the finest elements from 'pncmesh'
3978  ParMesh* pmesh2 = new ParMesh(*pncmesh);
3979  pncmesh->OnMeshUpdated(pmesh2);
3980 
3981  attributes.Copy(pmesh2->attributes);
3983 
3984  // now swap the meshes, the second mesh will become the old coarse mesh
3985  // and this mesh will be the new fine mesh
3986  Mesh::Swap(*pmesh2, false);
3987 
3988  delete pmesh2; // NOTE: old face neighbors destroyed here
3989 
3991 
3993 
3995  sequence++;
3996 
3997  UpdateNodes();
3998 }
3999 
4001  double threshold, int nc_limit, int op)
4002 {
4003  MFEM_VERIFY(pncmesh, "Only supported for non-conforming meshes.");
4004  MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
4005  "Project the NURBS to Nodes first.");
4006 
4007  const Table &dt = pncmesh->GetDerefinementTable();
4008 
4009  pncmesh->SynchronizeDerefinementData(elem_error, dt);
4010 
4011  Array<int> level_ok;
4012  if (nc_limit > 0)
4013  {
4014  pncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
4015  }
4016 
4017  Array<int> derefs;
4018  for (int i = 0; i < dt.Size(); i++)
4019  {
4020  if (nc_limit > 0 && !level_ok[i]) { continue; }
4021 
4022  double error =
4023  AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
4024 
4025  if (error < threshold) { derefs.Append(i); }
4026  }
4027 
4028  long long glob_size = ReduceInt(derefs.Size());
4029  if (!glob_size) { return false; }
4030 
4031  // Destroy face-neighbor data only when actually de-refining.
4033 
4034  pncmesh->Derefine(derefs);
4035 
4036  ParMesh* mesh2 = new ParMesh(*pncmesh);
4037  pncmesh->OnMeshUpdated(mesh2);
4038 
4039  attributes.Copy(mesh2->attributes);
4041 
4042  Mesh::Swap(*mesh2, false);
4043  delete mesh2;
4044 
4046 
4048 
4050  sequence++;
4051 
4052  UpdateNodes();
4053 
4054  return true;
4055 }
4056 
4057 
4059 {
4060  RebalanceImpl(NULL); // default SFC-based partition
4061 }
4062 
4063 void ParMesh::Rebalance(const Array<int> &partition)
4064 {
4065  RebalanceImpl(&partition);
4066 }
4067 
4068 void ParMesh::RebalanceImpl(const Array<int> *partition)
4069 {
4070  if (Conforming())
4071  {
4072  MFEM_ABORT("Load balancing is currently not supported for conforming"
4073  " meshes.");
4074  }
4075 
4076  if (Nodes)
4077  {
4078  // check that Nodes use a parallel FE space, so we can call UpdateNodes()
4079  MFEM_VERIFY(dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace())
4080  != NULL, "internal error");
4081  }
4082 
4084 
4085  pncmesh->Rebalance(partition);
4086 
4087  ParMesh* pmesh2 = new ParMesh(*pncmesh);
4088  pncmesh->OnMeshUpdated(pmesh2);
4089 
4090  attributes.Copy(pmesh2->attributes);
4092 
4093  Mesh::Swap(*pmesh2, false);
4094  delete pmesh2;
4095 
4097 
4099 
4101  sequence++;
4102 
4103  UpdateNodes();
4104 }
4105 
4106 void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
4107 {
4108  // Refine groups after LocalRefinement in 2D (triangle meshes)
4109 
4110  MFEM_ASSERT(Dim == 2 && meshgen == 1, "internal error");
4111 
4112  Array<int> group_verts, group_edges;
4113 
4114  // To update the groups after a refinement, we observe that:
4115  // - every (new and old) vertex, edge and face belongs to exactly one group
4116  // - the refinement does not create new groups
4117  // - a new vertex appears only as the middle of a refined edge
4118  // - a face can be refined 2, 3 or 4 times producing new edges and faces
4119 
4120  int *I_group_svert, *J_group_svert;
4121  int *I_group_sedge, *J_group_sedge;
4122 
4123  I_group_svert = Memory<int>(GetNGroups()+1);
4124  I_group_sedge = Memory<int>(GetNGroups()+1);
4125 
4126  I_group_svert[0] = I_group_svert[1] = 0;
4127  I_group_sedge[0] = I_group_sedge[1] = 0;
4128 
4129  // overestimate the size of the J arrays
4130  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4132  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4133 
4134  for (int group = 0; group < GetNGroups()-1; group++)
4135  {
4136  // Get the group shared objects
4137  group_svert.GetRow(group, group_verts);
4138  group_sedge.GetRow(group, group_edges);
4139 
4140  // Check which edges have been refined
4141  for (int i = 0; i < group_sedge.RowSize(group); i++)
4142  {
4143  int *v = shared_edges[group_edges[i]]->GetVertices();
4144  const int ind = middle[v_to_v(v[0], v[1])];
4145  if (ind != -1)
4146  {
4147  // add a vertex
4148  group_verts.Append(svert_lvert.Append(ind)-1);
4149  // update the edges
4150  const int attr = shared_edges[group_edges[i]]->GetAttribute();
4151  shared_edges.Append(new Segment(v[1], ind, attr));
4152  group_edges.Append(sedge_ledge.Append(-1)-1);
4153  v[1] = ind;
4154  }
4155  }
4156 
4157  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4158  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4159 
4160  int *J;
4161  J = J_group_svert+I_group_svert[group];
4162  for (int i = 0; i < group_verts.Size(); i++)
4163  {
4164  J[i] = group_verts[i];
4165  }
4166  J = J_group_sedge+I_group_sedge[group];
4167  for (int i = 0; i < group_edges.Size(); i++)
4168  {
4169  J[i] = group_edges[i];
4170  }
4171  }
4172 
4173  FinalizeParTopo();
4174 
4175  group_svert.SetIJ(I_group_svert, J_group_svert);
4176  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4177 }
4178 
4179 void ParMesh::RefineGroups(int old_nv, const HashTable<Hashed2> &v_to_v)
4180 {
4181  // Refine groups after LocalRefinement in 3D (tetrahedral meshes)
4182 
4183  MFEM_ASSERT(Dim == 3 && meshgen == 1, "internal error");
4184 
4185  Array<int> group_verts, group_edges, group_trias;
4186 
4187  // To update the groups after a refinement, we observe that:
4188  // - every (new and old) vertex, edge and face belongs to exactly one group
4189  // - the refinement does not create new groups
4190  // - a new vertex appears only as the middle of a refined edge
4191  // - a face can be refined multiple times producing new edges and faces
4192 
4193  Array<Segment *> sedge_stack;
4194  Array<Vert3> sface_stack;
4195 
4196  Array<int> I_group_svert, J_group_svert;
4197  Array<int> I_group_sedge, J_group_sedge;
4198  Array<int> I_group_stria, J_group_stria;
4199 
4200  I_group_svert.SetSize(GetNGroups());
4201  I_group_sedge.SetSize(GetNGroups());
4202  I_group_stria.SetSize(GetNGroups());
4203 
4204  I_group_svert[0] = 0;
4205  I_group_sedge[0] = 0;
4206  I_group_stria[0] = 0;
4207 
4208  for (int group = 0; group < GetNGroups()-1; group++)
4209  {
4210  // Get the group shared objects
4211  group_svert.GetRow(group, group_verts);
4212  group_sedge.GetRow(group, group_edges);
4213  group_stria.GetRow(group, group_trias);
4214 
4215  // Check which edges have been refined
4216  for (int i = 0; i < group_sedge.RowSize(group); i++)
4217  {
4218  int *v = shared_edges[group_edges[i]]->GetVertices();
4219  int ind = v_to_v.FindId(v[0], v[1]);
4220  if (ind == -1) { continue; }
4221 
4222  // This shared edge is refined: walk the whole refinement tree
4223  const int attr = shared_edges[group_edges[i]]->GetAttribute();
4224  do
4225  {
4226  ind += old_nv;
4227  // Add new shared vertex
4228  group_verts.Append(svert_lvert.Append(ind)-1);
4229  // Put the right sub-edge on top of the stack
4230  sedge_stack.Append(new Segment(ind, v[1], attr));
4231  // The left sub-edge replaces the original edge
4232  v[1] = ind;
4233  ind = v_to_v.FindId(v[0], ind);
4234  }
4235  while (ind != -1);
4236  // Process all edges in the edge stack
4237  do
4238  {
4239  Segment *se = sedge_stack.Last();
4240  v = se->GetVertices();
4241  ind = v_to_v.FindId(v[0], v[1]);
4242  if (ind == -1)
4243  {
4244  // The edge 'se' is not refined
4245  sedge_stack.DeleteLast();
4246  // Add new shared edge
4247  shared_edges.Append(se);
4248  group_edges.Append(sedge_ledge.Append(-1)-1);
4249  }
4250  else
4251  {
4252  // The edge 'se' is refined
4253  ind += old_nv;
4254  // Add new shared vertex
4255  group_verts.Append(svert_lvert.Append(ind)-1);
4256  // Put the left sub-edge on top of the stack
4257  sedge_stack.Append(new Segment(v[0], ind, attr));
4258  // The right sub-edge replaces the original edge
4259  v[0] = ind;
4260  }
4261  }
4262  while (sedge_stack.Size() > 0);
4263  }
4264 
4265  // Check which triangles have been refined
4266  for (int i = 0; i < group_stria.RowSize(group); i++)
4267  {
4268  int *v = shared_trias[group_trias[i]].v;
4269  int ind = v_to_v.FindId(v[0], v[1]);
4270  if (ind == -1) { continue; }
4271 
4272  // This shared face is refined: walk the whole refinement tree
4273  const int edge_attr = 1;
4274  do
4275  {
4276  ind += old_nv;
4277  // Add the refinement edge to the edge stack
4278  sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4279  // Put the right sub-triangle on top of the face stack
4280  sface_stack.Append(Vert3(v[1], v[2], ind));
4281  // The left sub-triangle replaces the original one
4282  v[1] = v[0]; v[0] = v[2]; v[2] = ind;
4283  ind = v_to_v.FindId(v[0], v[1]);
4284  }
4285  while (ind != -1);
4286  // Process all faces (triangles) in the face stack
4287  do
4288  {
4289  Vert3 &st = sface_stack.Last();
4290  v = st.v;
4291  ind = v_to_v.FindId(v[0], v[1]);
4292  if (ind == -1)
4293  {
4294  // The triangle 'st' is not refined
4295  // Add new shared face
4296  shared_trias.Append(st);
4297  group_trias.Append(sface_lface.Append(-1)-1);
4298  sface_stack.DeleteLast();
4299  }
4300  else
4301  {
4302  // The triangle 'st' is refined
4303  ind += old_nv;
4304  // Add the refinement edge to the edge stack
4305  sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4306  // Put the left sub-triangle on top of the face stack
4307  sface_stack.Append(Vert3(v[2], v[0], ind));
4308  // Note that the above Append() may invalidate 'v'
4309  v = sface_stack[sface_stack.Size()-2].v;
4310  // The right sub-triangle replaces the original one
4311  v[0] = v[1]; v[1] = v[2]; v[2] = ind;
4312  }
4313  }
4314  while (sface_stack.Size() > 0);
4315  // Process all edges in the edge stack (same code as above)
4316  do
4317  {
4318  Segment *se = sedge_stack.Last();
4319  v = se->GetVertices();
4320  ind = v_to_v.FindId(v[0], v[1]);
4321  if (ind == -1)
4322  {
4323  // The edge 'se' is not refined
4324  sedge_stack.DeleteLast();
4325  // Add new shared edge
4326  shared_edges.Append(se);
4327  group_edges.Append(sedge_ledge.Append(-1)-1);
4328  }
4329  else
4330  {
4331  // The edge 'se' is refined
4332  ind += old_nv;
4333  // Add new shared vertex
4334  group_verts.Append(svert_lvert.Append(ind)-1);
4335  // Put the left sub-edge on top of the stack
4336  sedge_stack.Append(new Segment(v[0], ind, edge_attr));
4337  // The right sub-edge replaces the original edge
4338  v[0] = ind;
4339  }
4340  }
4341  while (sedge_stack.Size() > 0);
4342  }
4343 
4344  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4345  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4346  I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4347 
4348  J_group_svert.Append(group_verts);
4349  J_group_sedge.Append(group_edges);
4350  J_group_stria.Append(group_trias);
4351  }
4352 
4353  FinalizeParTopo();
4354 
4355  group_svert.SetIJ(I_group_svert, J_group_svert);
4356  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4357  group_stria.SetIJ(I_group_stria, J_group_stria);
4358  I_group_svert.LoseData(); J_group_svert.LoseData();
4359  I_group_sedge.LoseData(); J_group_sedge.LoseData();
4360  I_group_stria.LoseData(); J_group_stria.LoseData();
4361 }
4362 
4364 {
4365  Array<int> sverts, sedges;
4366 
4367  int *I_group_svert, *J_group_svert;
4368  int *I_group_sedge, *J_group_sedge;
4369 
4370  I_group_svert = Memory<int>(GetNGroups());
4371  I_group_sedge = Memory<int>(GetNGroups());
4372 
4373  I_group_svert[0] = 0;
4374  I_group_sedge[0] = 0;
4375 
4376  // compute the size of the J arrays
4377  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4379  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4380 
4381  for (int group = 0; group < GetNGroups()-1; group++)
4382  {
4383  // Get the group shared objects
4384  group_svert.GetRow(group, sverts);
4385  group_sedge.GetRow(group, sedges);
4386 
4387  // Process all the edges
4388  for (int i = 0; i < group_sedge.RowSize(group); i++)
4389  {
4390  int *v = shared_edges[sedges[i]]->GetVertices();
4391  const int ind = old_nv + sedge_ledge[sedges[i]];
4392  // add a vertex
4393  sverts.Append(svert_lvert.Append(ind)-1);
4394  // update the edges
4395  const int attr = shared_edges[sedges[i]]->GetAttribute();
4396  shared_edges.Append(new Segment(v[1], ind, attr));
4397  sedges.Append(sedge_ledge.Append(-1)-1);
4398  v[1] = ind;
4399  }
4400 
4401  I_group_svert[group+1] = I_group_svert[group] + sverts.Size();
4402  I_group_sedge[group+1] = I_group_sedge[group] + sedges.Size();
4403 
4404  sverts.CopyTo(J_group_svert + I_group_svert[group]);
4405  sedges.CopyTo(J_group_sedge + I_group_sedge[group]);
4406  }
4407 
4408  FinalizeParTopo();
4409 
4410  group_svert.SetIJ(I_group_svert, J_group_svert);
4411  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4412 }
4413 
4414 void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
4415  const DSTable &old_v_to_v,
4416  const STable3D &old_faces,
4417  Array<int> *f2qf)
4418 {
4419  // f2qf can be NULL if all faces are quads or there are no quad faces
4420 
4421  Array<int> group_verts, group_edges, group_trias, group_quads;
4422 
4423  int *I_group_svert, *J_group_svert;
4424  int *I_group_sedge, *J_group_sedge;
4425  int *I_group_stria, *J_group_stria;
4426  int *I_group_squad, *J_group_squad;
4427 
4428  I_group_svert = Memory<int>(GetNGroups());
4429  I_group_sedge = Memory<int>(GetNGroups());
4430  I_group_stria = Memory<int>(GetNGroups());
4431  I_group_squad = Memory<int>(GetNGroups());
4432 
4433  I_group_svert[0] = 0;
4434  I_group_sedge[0] = 0;
4435  I_group_stria[0] = 0;
4436  I_group_squad[0] = 0;
4437 
4438  // compute the size of the J arrays
4439  J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4442  J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections() +
4445  J_group_stria = Memory<int>(4*group_stria.Size_of_connections());
4446  J_group_squad = Memory<int>(4*group_squad.Size_of_connections());
4447 
4448  const int oface = old_nv + old_nedges;
4449 
4450  for (int group = 0; group < GetNGroups()-1; group++)
4451  {
4452  // Get the group shared objects
4453  group_svert.GetRow(group, group_verts);
4454  group_sedge.GetRow(group, group_edges);
4455  group_stria.GetRow(group, group_trias);
4456  group_squad.GetRow(group, group_quads);
4457 
4458  // Process the edges that have been refined
4459  for (int i = 0; i < group_sedge.RowSize(group); i++)
4460  {
4461  int *v = shared_edges[group_edges[i]]->GetVertices();
4462  const int ind = old_nv + old_v_to_v(v[0], v[1]);
4463  // add a vertex
4464  group_verts.Append(svert_lvert.Append(ind)-1);
4465  // update the edges
4466  const int attr = shared_edges[group_edges[i]]->GetAttribute();
4467  shared_edges.Append(new Segment(v[1], ind, attr));
4468  group_edges.Append(sedge_ledge.Append(-1)-1);
4469  v[1] = ind; // v[0] remains the same
4470  }
4471 
4472  // Process the triangles that have been refined
4473  for (int i = 0; i < group_stria.RowSize(group); i++)
4474  {
4475  int m[3];
4476  const int stria = group_trias[i];
4477  int *v = shared_trias[stria].v;
4478  // add the refinement edges
4479  m[0] = old_nv + old_v_to_v(v[0], v[1]);
4480  m[1] = old_nv + old_v_to_v(v[1], v[2]);
4481  m[2] = old_nv + old_v_to_v(v[2], v[0]);
4482  const int edge_attr = 1;
4483  shared_edges.Append(new Segment(m[0], m[1], edge_attr));
4484  group_edges.Append(sedge_ledge.Append(-1)-1);
4485  shared_edges.Append(new Segment(m[1], m[2], edge_attr));
4486  group_edges.Append(sedge_ledge.Append(-1)-1);
4487  shared_edges.Append(new Segment(m[0], m[2], edge_attr));
4488  group_edges.Append(sedge_ledge.Append(-1)-1);
4489  // update faces
4490  const int nst = shared_trias.Size();
4491  shared_trias.SetSize(nst+3);
4492  // The above SetSize() may invalidate 'v'
4493  v = shared_trias[stria].v;
4494  shared_trias[nst+0].Set(m[1],m[2],m[0]);
4495  shared_trias[nst+1].Set(m[0],v[1],m[1]);
4496  shared_trias[nst+2].Set(m[2],m[1],v[2]);
4497  v[1] = m[0]; v[2] = m[2]; // v[0] remains the same
4498  group_trias.Append(nst+0);
4499  group_trias.Append(nst+1);
4500  group_trias.Append(nst+2);
4501  // sface_lface is set later
4502  }
4503 
4504  // Process the quads that have been refined
4505  for (int i = 0; i < group_squad.RowSize(group); i++)
4506  {
4507  int m[5];
4508  const int squad = group_quads[i];
4509  int *v = shared_quads[squad].v;
4510  const int olf = old_faces(v[0], v[1], v[2], v[3]);
4511  // f2qf can be NULL if all faces are quads
4512  m[0] = oface + (f2qf ? (*f2qf)[olf] : olf);
4513  // add a vertex
4514  group_verts.Append(svert_lvert.Append(m[0])-1);
4515  // add the refinement edges
4516  m[1] = old_nv + old_v_to_v(v[0], v[1]);
4517  m[2] = old_nv + old_v_to_v(v[1], v[2]);
4518  m[3] = old_nv + old_v_to_v(v[2], v[3]);
4519  m[4] = old_nv + old_v_to_v(v[3], v[0]);
4520  const int edge_attr = 1;
4521  shared_edges.Append(new Segment(m[1], m[0], edge_attr));
4522  group_edges.Append(sedge_ledge.Append(-1)-1);
4523  shared_edges.Append(new Segment(m[2], m[0], edge_attr));
4524  group_edges.Append(sedge_ledge.Append(-1)-1);
4525  shared_edges.Append(new Segment(m[3], m[0], edge_attr));
4526  group_edges.Append(sedge_ledge.Append(-1)-1);
4527  shared_edges.Append(new Segment(m[4], m[0], edge_attr));
4528  group_edges.Append(sedge_ledge.Append(-1)-1);
4529  // update faces
4530  const int nsq = shared_quads.Size();
4531  shared_quads.SetSize(nsq+3);
4532  // The above SetSize() may invalidate 'v'
4533  v = shared_quads[squad].v;
4534  shared_quads[nsq+0].Set(m[1],v[1],m[2],m[0]);
4535  shared_quads[nsq+1].Set(m[0],m[2],v[2],m[3]);
4536  shared_quads[nsq+2].Set(m[4],m[0],m[3],v[3]);
4537  v[1] = m[1]; v[2] = m[0]; v[3] = m[4]; // v[0] remains the same
4538  group_quads.Append(nsq+0);
4539  group_quads.Append(nsq+1);
4540  group_quads.Append(nsq+2);
4541  // sface_lface is set later
4542  }
4543 
4544  I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4545  I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4546  I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4547  I_group_squad[group+1] = I_group_squad[group] + group_quads.Size();
4548 
4549  group_verts.CopyTo(J_group_svert + I_group_svert[group]);
4550  group_edges.CopyTo(J_group_sedge + I_group_sedge[group]);
4551  group_trias.CopyTo(J_group_stria + I_group_stria[group]);
4552  group_quads.CopyTo(J_group_squad + I_group_squad[group]);
4553  }
4554 
4555  FinalizeParTopo();
4556 
4557  group_svert.SetIJ(I_group_svert, J_group_svert);
4558  group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4559  group_stria.SetIJ(I_group_stria, J_group_stria);
4560  group_squad.SetIJ(I_group_squad, J_group_squad);
4561 }
4562 
4564 {
4566 
4567  const int old_nv = NumOfVertices;
4568 
4569  // call Mesh::UniformRefinement2D so that it won't update the nodes
4570  {
4571  const bool update_nodes = false;
4572  Mesh::UniformRefinement2D_base(update_nodes);
4573  }
4574 
4575  // update the groups
4576  UniformRefineGroups2D(old_nv);
4577 
4578  UpdateNodes();
4579 
4580 #ifdef MFEM_DEBUG
4581  // If there are no Nodes, the orientation is checked in the call to
4582  // UniformRefinement2D_base() above.
4583  if (Nodes) { CheckElementOrientation(false); }
4584 #endif
4585 }
4586 
4588 {
4590 
4591  const int old_nv = NumOfVertices;
4592  const int old_nedges = NumOfEdges;
4593 
4594  DSTable v_to_v(NumOfVertices);
4595  GetVertexToVertexTable(v_to_v);
4596  STable3D *faces_tbl = GetFacesTable();
4597 
4598  // call Mesh::UniformRefinement3D_base so that it won't update the nodes
4599  Array<int> f2qf;
4600  {
4601  const bool update_nodes = false;
4602  UniformRefinement3D_base(&f2qf, &v_to_v, update_nodes);
4603  // Note: for meshes that have triangular faces, v_to_v is modified by the
4604  // above call to return different edge indices - this is used when
4605  // updating the groups. This is needed by ReorientTetMesh().
4606  }
4607 
4608  // update the groups
4609  UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
4610  f2qf.Size() ? &f2qf : NULL);
4611  delete faces_tbl;
4612 
4613  UpdateNodes();
4614 }
4615 
4617 {
4618  if (MyRank == 0)
4619  {
4620  mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
4621  }
4622 }
4623 
4624 void ParMesh::PrintXG(std::ostream &os) const
4625 {
4626  MFEM_ASSERT(Dim == spaceDim, "2D manifolds not supported");
4627  if (Dim == 3 && meshgen == 1)
4628  {
4629  int i, j, nv;
4630  const int *ind;
4631 
4632  os << "NETGEN_Neutral_Format\n";
4633  // print the vertices
4634  os << NumOfVertices << '\n';
4635  for (i = 0; i < NumOfVertices; i++)
4636  {
4637  for (j = 0; j < Dim; j++)
4638  {
4639  os << " " << vertices[i](j);
4640  }
4641  os << '\n';
4642  }
4643 
4644  // print the elements
4645  os << NumOfElements << '\n';
4646  for (i = 0; i < NumOfElements; i++)
4647  {
4648  nv = elements[i]->GetNVertices();
4649  ind = elements[i]->GetVertices();
4650  os << elements[i]->GetAttribute();
4651  for (j = 0; j < nv; j++)
4652  {
4653  os << " " << ind[j]+1;
4654  }
4655  os << '\n';
4656  }
4657 
4658  // print the boundary + shared faces information
4659  os << NumOfBdrElements + sface_lface.Size() << '\n';
4660  // boundary
4661  for (i = 0; i < NumOfBdrElements; i++)
4662  {
4663  nv = boundary[i]->GetNVertices();
4664  ind = boundary[i]->GetVertices();
4665  os << boundary[i]->GetAttribute();
4666  for (j = 0; j < nv; j++)
4667  {
4668  os << " " << ind[j]+1;
4669  }
4670  os << '\n';
4671  }
4672  // shared faces
4673  const int sf_attr =
4674  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4675  for (i = 0; i < shared_trias.Size(); i++)
4676  {
4677  ind = shared_trias[i].v;
4678  os << sf_attr;
4679  for (j = 0; j < 3; j++)
4680  {
4681  os << ' ' << ind[j]+1;
4682  }
4683  os << '\n';
4684  }
4685  // There are no quad shared faces
4686  }
4687 
4688  if (Dim == 3 && meshgen == 2)
4689  {
4690  int i, j, nv;
4691  const int *ind;
4692 
4693  os << "TrueGrid\n"
4694  << "1 " << NumOfVertices << " " << NumOfElements
4695  << " 0 0 0 0 0 0 0\n"
4696  << "0 0 0 1 0 0 0 0 0 0 0\n"
4697  << "0 0 " << NumOfBdrElements+sface_lface.Size()
4698  << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
4699  << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
4700  << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
4701 
4702  // print the vertices
4703  for (i = 0; i < NumOfVertices; i++)
4704  {
4705  os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
4706  << " " << vertices[i](2) << " 0.0\n";
4707  }
4708 
4709  // print the elements
4710  for (i = 0; i < NumOfElements; i++)
4711  {
4712  nv = elements[i]->GetNVertices();
4713  ind = elements[i]->GetVertices();
4714  os << i+1 << " " << elements[i]->GetAttribute();
4715  for (j = 0; j < nv; j++)
4716  {
4717  os << " " << ind[j]+1;
4718  }
4719  os << '\n';
4720  }
4721 
4722  // print the boundary information
4723  for (i = 0; i < NumOfBdrElements; i++)
4724  {
4725  nv = boundary[i]->GetNVertices();
4726  ind = boundary[i]->GetVertices();
4727  os << boundary[i]->GetAttribute();
4728  for (j = 0; j < nv; j++)
4729  {
4730  os << " " << ind[j]+1;
4731  }
4732  os << " 1.0 1.0 1.0 1.0\n";
4733  }
4734 
4735  // print the shared faces information
4736  const int sf_attr =
4737  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4738  // There are no shared triangle faces
4739  for (i = 0; i < shared_quads.Size(); i++)
4740  {
4741  ind = shared_quads[i].v;
4742  os << sf_attr;
4743  for (j = 0; j < 4; j++)
4744  {
4745  os << ' ' << ind[j]+1;
4746  }
4747  os << " 1.0 1.0 1.0 1.0\n";
4748  }
4749  }
4750 
4751  if (Dim == 2)
4752  {
4753  int i, j, attr;
4754  Array<int> v;
4755 
4756  os << "areamesh2\n\n";
4757 
4758  // print the boundary + shared edges information
4759  os << NumOfBdrElements + shared_edges.Size() << '\n';
4760  // boundary
4761  for (i = 0; i < NumOfBdrElements; i++)
4762  {
4763  attr = boundary[i]->GetAttribute();
4764  boundary[i]->GetVertices(v);
4765  os << attr << " ";
4766  for (j = 0; j < v.Size(); j++)
4767  {
4768  os << v[j] + 1 << " ";
4769  }
4770  os << '\n';
4771  }
4772  // shared edges
4773  for (i = 0; i < shared_edges.Size(); i++)
4774  {
4775  attr = shared_edges[i]->GetAttribute();
4776  shared_edges[i]->GetVertices(v);
4777  os << attr << " ";
4778  for (j = 0; j < v.Size(); j++)
4779  {
4780  os << v[j] + 1 << " ";
4781  }
4782  os << '\n';
4783  }
4784 
4785  // print the elements
4786  os << NumOfElements << '\n';
4787  for (i = 0; i < NumOfElements; i++)
4788  {
4789  attr = elements[i]->GetAttribute();
4790  elements[i]->GetVertices(v);
4791 
4792  os << attr << " ";
4793  if ((j = GetElementType(i)) == Element::TRIANGLE)
4794  {
4795  os << 3 << " ";
4796  }
4797  else if (j == Element::QUADRILATERAL)
4798  {
4799  os << 4 << " ";
4800  }
4801  else if (j == Element::SEGMENT)
4802  {
4803  os << 2 << " ";
4804  }
4805  for (j = 0; j < v.Size(); j++)
4806  {
4807  os << v[j] + 1 << " ";
4808  }
4809  os << '\n';
4810  }
4811 
4812  // print the vertices
4813  os << NumOfVertices << '\n';
4814  for (i = 0; i < NumOfVertices; i++)
4815  {
4816  for (j = 0; j < Dim; j++)
4817  {
4818  os << vertices[i](j) << " ";
4819  }
4820  os << '\n';
4821  }
4822  }
4823  os.flush();
4824 }
4825 
4827 {
4828  // In 2D, this is a workaround for a CPU boundary rendering artifact. We need
4829  // to skip a shared master edge if one of its slaves has the same rank.
4830 
4831  const NCMesh::NCList &list = pncmesh->GetEdgeList();
4832  for (int i = master.slaves_begin; i < master.slaves_end; i++)
4833  {
4834  if (!pncmesh->IsGhost(1, list.slaves[i].index)) { return true; }
4835  }
4836  return false;
4837 }
4838 
4839 void ParMesh::Print(std::ostream &os) const
4840 {
4841  int shared_bdr_attr;
4842  Array<int> nc_shared_faces;
4843 
4844  if (NURBSext)
4845  {
4846  Printer(os); // does not print shared boundary
4847  return;
4848  }
4849 
4850  const Array<int>* s2l_face;
4851  if (!pncmesh)
4852  {
4853  s2l_face = ((Dim == 1) ? &svert_lvert :
4854  ((Dim == 2) ? &sedge_ledge : &sface_lface));
4855  }
4856  else
4857  {
4858  s2l_face = &nc_shared_faces;
4859  if (Dim >= 2)
4860  {
4861  // get a list of all shared non-ghost faces
4862  const NCMesh::NCList& sfaces =
4863  (Dim == 3) ? pncmesh->GetSharedFaces() : pncmesh->GetSharedEdges();
4864  const int nfaces = GetNumFaces();
4865  for (int i = 0; i < sfaces.conforming.Size(); i++)
4866  {
4867  int index = sfaces.conforming[i].index;
4868  if (index < nfaces) { nc_shared_faces.Append(index); }
4869  }
4870  for (int i = 0; i < sfaces.masters.Size(); i++)
4871  {
4872  if (Dim == 2 && WantSkipSharedMaster(sfaces.masters[i])) { continue; }
4873  int index = sfaces.masters[i].index;
4874  if (index < nfaces) { nc_shared_faces.Append(index); }
4875  }
4876  for (int i = 0; i < sfaces.slaves.Size(); i++)
4877  {
4878  int index = sfaces.slaves[i].index;
4879  if (index < nfaces) { nc_shared_faces.Append(index); }
4880  }
4881  }
4882  }
4883 
4884  os << "MFEM mesh v1.0\n";
4885 
4886  // optional
4887  os <<
4888  "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
4889  "# POINT = 0\n"
4890  "# SEGMENT = 1\n"
4891  "# TRIANGLE = 2\n"
4892  "# SQUARE = 3\n"
4893  "# TETRAHEDRON = 4\n"
4894  "# CUBE = 5\n"
4895  "# PRISM = 6\n"
4896  "#\n";
4897 
4898  os << "\ndimension\n" << Dim
4899  << "\n\nelements\n" << NumOfElements << '\n';
4900  for (int i = 0; i < NumOfElements; i++)
4901  {
4902  PrintElement(elements[i], os);
4903  }
4904 
4905  int num_bdr_elems = NumOfBdrElements;
4906  if (print_shared && Dim > 1)
4907  {
4908  num_bdr_elems += s2l_face->Size();
4909  }
4910  os << "\nboundary\n" << num_bdr_elems << '\n';
4911  for (int i = 0; i < NumOfBdrElements; i++)
4912  {
4913  PrintElement(boundary[i], os);
4914  }
4915 
4916  if (print_shared && Dim > 1)
4917  {
4918  if (bdr_attributes.Size())
4919  {
4920  shared_bdr_attr = bdr_attributes.Max() + MyRank + 1;
4921  }
4922  else
4923  {
4924  shared_bdr_attr = MyRank + 1;
4925  }
4926  for (int i = 0; i < s2l_face->Size(); i++)
4927  {
4928  // Modify the attributes of the faces (not used otherwise?)
4929  faces[(*s2l_face)[i]]->SetAttribute(shared_bdr_attr);
4930  PrintElement(faces[(*s2l_face)[i]], os);
4931  }
4932  }
4933  os << "\nvertices\n" << NumOfVertices << '\n';
4934  if (Nodes == NULL)
4935  {
4936  os << spaceDim << '\n';
4937  for (int i = 0; i < NumOfVertices; i++)
4938  {
4939  os << vertices[i](0);
4940  for (int j = 1; j < spaceDim; j++)
4941  {
4942  os << ' ' << vertices[i](j);
4943  }
4944  os << '\n';
4945  }
4946  os.flush();
4947  }
4948  else
4949  {
4950  os << "\nnodes\n";
4951  Nodes->Save(os);
4952  }
4953 }
4954 
4955 void ParMesh::Save(const char *fname, int precision) const
4956 {
4957  ostringstream fname_with_suffix;
4958  fname_with_suffix << fname << "." << setfill('0') << setw(6) << MyRank;
4959  ofstream ofs(fname_with_suffix.str().c_str());
4960  ofs.precision(precision);
4961  Print(ofs);
4962 }
4963 
4964 #ifdef MFEM_USE_ADIOS2
4966 {
4967  Mesh::Print(os);
4968 }
4969 #endif
4970 
4971 static void dump_element(const Element* elem, Array<int> &data)
4972 {
4973  data.Append(elem->GetGeometryType());
4974 
4975  int nv = elem->GetNVertices();
4976  const int *v = elem->GetVertices();
4977  for (int i = 0; i < nv; i++)
4978  {
4979  data.Append(v[i]);
4980  }
4981 }
4982 
4983 void ParMesh::PrintAsOne(std::ostream &os) const
4984 {
4985  int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc;
4986  const int *v;
4987  MPI_Status status;
4988  Array<double> vert;
4989  Array<int> ints;
4990 
4991  if (MyRank == 0)
4992  {
4993  os << "MFEM mesh v1.0\n";
4994 
4995  // optional
4996  os <<
4997  "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
4998  "# POINT = 0\n"
4999  "# SEGMENT = 1\n"
5000  "# TRIANGLE = 2\n"
5001  "# SQUARE = 3\n"
5002  "# TETRAHEDRON = 4\n"
5003  "# CUBE = 5\n"
5004  "# PRISM = 6\n"
5005  "#\n";
5006 
5007  os << "\ndimension\n" << Dim;
5008  }
5009 
5010  nv = NumOfElements;
5011  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5012  if (MyRank == 0)
5013  {
5014  os << "\n\nelements\n" << ne << '\n';
5015  for (i = 0; i < NumOfElements; i++)
5016  {
5017  // processor number + 1 as attribute and geometry type
5018  os << 1 << ' ' << elements[i]->GetGeometryType();
5019  // vertices
5020  nv = elements[i]->GetNVertices();
5021  v = elements[i]->GetVertices();
5022  for (j = 0; j < nv; j++)
5023  {
5024  os << ' ' << v[j];
5025  }
5026  os << '\n';
5027  }
5028  vc = NumOfVertices;
5029  for (p = 1; p < NRanks; p++)
5030  {
5031  MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
5032  ints.SetSize(ne);
5033  if (ne)
5034  {
5035  MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
5036  }
5037  for (i = 0; i < ne; )
5038  {
5039  // processor number + 1 as attribute and geometry type
5040  os << p+1 << ' ' << ints[i];
5041  // vertices
5042  k = Geometries.GetVertices(ints[i++])->GetNPoints();
5043  for (j = 0; j < k; j++)
5044  {
5045  os << ' ' << vc + ints[i++];
5046  }
5047  os << '\n';
5048  }
5049  vc += nv;
5050  }
5051  }
5052  else
5053  {
5054  // for each element send its geometry type and its vertices
5055  ne = 0;
5056  for (i = 0; i < NumOfElements; i++)
5057  {
5058  ne += 1 + elements[i]->GetNVertices();
5059  }
5060  nv = NumOfVertices;
5061  MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
5062 
5063  ints.Reserve(ne);
5064  ints.SetSize(0);
5065  for (i = 0; i < NumOfElements; i++)
5066  {
5067  dump_element(elements[i], ints);
5068  }
5069  MFEM_ASSERT(ints.Size() == ne, "");
5070  if (ne)
5071  {
5072  MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm);
5073  }
5074  }
5075 
5076  // boundary + shared boundary
5077  ne = NumOfBdrElements;
5078  if (!pncmesh)
5079  {
5080  ne += GetNSharedFaces();
5081  }
5082  else if (Dim > 1)
5083  {
5084  const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5085  ne += list.conforming.Size() + list.masters.Size() + list.slaves.Size();
5086  // In addition to the number returned by GetNSharedFaces(), include the
5087  // the master shared faces as well.
5088  }
5089  ints.Reserve(ne * (1 + 2*(Dim-1))); // just an upper bound
5090  ints.SetSize(0);
5091 
5092  // for each boundary and shared boundary element send its geometry type
5093  // and its vertices
5094  ne = 0;
5095  for (i = j = 0; i < NumOfBdrElements; i++)
5096  {
5097  dump_element(boundary[i], ints); ne++;
5098  }
5099  if (!pncmesh)
5100  {
5101  switch (Dim)
5102  {
5103  case 1:
5104  for (i = 0; i < svert_lvert.Size(); i++)
5105  {
5106  ints.Append(Geometry::POINT);
5107  ints.Append(svert_lvert[i]);
5108  ne++;
5109  }
5110  break;
5111 
5112  case 2:
5113  for (i = 0; i < shared_edges.Size(); i++)
5114  {
5115  dump_element(shared_edges[i], ints); ne++;
5116  }
5117  break;
5118 
5119  case 3:
5120  for (i = 0; i < shared_trias.Size(); i++)
5121  {
5122  ints.Append(Geometry::TRIANGLE);
5123  ints.Append(shared_trias[i].v, 3);
5124  ne++;
5125  }
5126  for (i = 0; i < shared_quads.Size(); i++)
5127  {
5128  ints.Append(Geometry::SQUARE);
5129  ints.Append(shared_quads[i].v, 4);
5130  ne++;
5131  }
5132  break;
5133 
5134  default:
5135  MFEM_ABORT("invalid dimension: " << Dim);
5136  }
5137  }
5138  else if (Dim > 1)
5139  {
5140  const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5141  const int nfaces = GetNumFaces();
5142  for (i = 0; i < list.conforming.Size(); i++)
5143  {
5144  int index = list.conforming[i].index;
5145  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5146  }
5147  for (i = 0; i < list.masters.Size(); i++)
5148  {
5149  int index = list.masters[i].index;
5150  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5151  }
5152  for (i = 0; i < list.slaves.Size(); i++)
5153  {
5154  int index = list.slaves[i].index;
5155  if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5156  }
5157  }
5158 
5159  MPI_Reduce(&ne, &k, 1, MPI_INT, MPI_SUM, 0, MyComm);
5160  if (MyRank == 0)
5161  {
5162  os << "\nboundary\n" << k << '\n';
5163  vc = 0;
5164  for (p = 0; p < NRanks; p++)
5165  {
5166  if (p)
5167  {
5168  MPI_Recv(nv_ne, 2, MPI_INT, p, 446, MyComm, &status);
5169  ints.SetSize(ne);
5170  if (ne)
5171  {
5172  MPI_Recv(ints.GetData(), ne, MPI_INT, p, 447, MyComm, &status);
5173  }
5174  }
5175  else
5176  {
5177  ne = ints.Size();
5178  nv = NumOfVertices;
5179  }
5180  for (i = 0; i < ne; )
5181  {
5182  // processor number + 1 as bdr. attr. and bdr. geometry type
5183  os << p+1 << ' ' << ints[i];
5184  k = Geometries.NumVerts[ints[i++]];
5185  // vertices
5186  for (j = 0; j < k; j++)
5187  {
5188  os << ' ' << vc + ints[i++];
5189  }
5190  os << '\n';
5191  }
5192  vc += nv;
5193  }
5194  }
5195  else
5196  {
5197  nv = NumOfVertices;
5198  ne = ints.Size();
5199  MPI_Send(nv_ne, 2, MPI_INT, 0, 446, MyComm);
5200  if (ne)
5201  {
5202  MPI_Send(ints.GetData(), ne, MPI_INT, 0, 447, MyComm);
5203  }
5204  }
5205 
5206  // vertices / nodes
5207  MPI_Reduce(&NumOfVertices, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5208  if (MyRank == 0)
5209  {
5210  os << "\nvertices\n" << nv << '\n';
5211  }
5212  if (Nodes == NULL)
5213  {
5214  if (MyRank == 0)
5215  {
5216  os << spaceDim << '\n';
5217  for (i = 0; i < NumOfVertices; i++)
5218  {
5219  os << vertices[i](0);
5220  for (j = 1; j < spaceDim; j++)
5221  {
5222  os << ' ' << vertices[i](j);
5223  }
5224  os << '\n';
5225  }
5226  for (p = 1; p < NRanks; p++)
5227  {
5228  MPI_Recv(&nv, 1, MPI_INT, p, 448, MyComm, &status);
5229  vert.SetSize(nv*spaceDim);
5230  if (nv)
5231  {
5232  MPI_Recv(&vert[0], nv*spaceDim, MPI_DOUBLE, p, 449, MyComm, &status);
5233  }
5234  for (i = 0; i < nv; i++)
5235  {
5236  os << vert[i*spaceDim];
5237  for (j = 1; j < spaceDim; j++)
5238  {
5239  os << ' ' << vert[i*spaceDim+j];
5240  }
5241  os << '\n';
5242  }
5243  }
5244  os.flush();
5245  }
5246  else
5247  {
5248  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 448, MyComm);
5250  for (i = 0; i < NumOfVertices; i++)
5251  {
5252  for (j = 0; j < spaceDim; j++)
5253  {
5254  vert[i*spaceDim+j] = vertices[i](j);
5255  }
5256  }
5257  if (NumOfVertices)
5258  {
5259  MPI_Send(&vert[0], NumOfVertices*spaceDim, MPI_DOUBLE, 0, 449, MyComm);
5260  }
5261  }
5262  }
5263  else
5264  {
5265  if (MyRank == 0)
5266  {
5267  os << "\nnodes\n";
5268  }
5269  ParGridFunction *pnodes = dynamic_cast<ParGridFunction *>(Nodes);
5270  if (pnodes)
5271  {
5272  pnodes->SaveAsOne(os);
5273  }
5274  else
5275  {
5276  ParFiniteElementSpace *pfes =
5277  dynamic_cast<ParFiniteElementSpace *>(Nodes->FESpace());
5278  if (pfes)
5279  {
5280  // create a wrapper ParGridFunction
5281  ParGridFunction ParNodes(pfes, Nodes);
5282  ParNodes.SaveAsOne(os);
5283  }
5284  else
5285  {
5286  mfem_error("ParMesh::PrintAsOne : Nodes have no parallel info!");
5287  }
5288  }
5289  }
5290 }
5291 
5292 void ParMesh::PrintAsSerial(std::ostream &os) const
5293 {
5294  int save_rank = 0;
5295  Mesh serialmesh = GetSerialMesh(save_rank);
5296  if (MyRank == save_rank)
5297  {
5298  serialmesh.Printer(os);
5299  }
5300  MPI_Barrier(MyComm);
5301 }
5302 
5303 Mesh ParMesh::GetSerialMesh(int save_rank) const
5304 {
5305  if (pncmesh || NURBSext)
5306  {
5307  MFEM_ABORT("Nonconforming meshes and NURBS meshes are not yet supported.");
5308  }
5309 
5310  // Define linear H1 space for vertex numbering
5311  H1_FECollection fec_linear(1, Dim);
5312  ParMesh *pm = const_cast<ParMesh *>(this);
5313  ParFiniteElementSpace pfespace_linear(pm, &fec_linear);
5314 
5315  long long ne_glob_l = GetGlobalNE(); // needs to be called by all ranks
5316  MFEM_VERIFY(int(ne_glob_l) == ne_glob_l,
5317  "overflow in the number of elements!");
5318  int ne_glob = (save_rank == MyRank) ? int(ne_glob_l) : 0;
5319 
5320  long long nvertices = pfespace_linear.GetTrueVSize();
5321  long long nvertices_glob_l = 0;
5322  MPI_Reduce(&nvertices, &nvertices_glob_l, 1, MPI_LONG_LONG, MPI_SUM,
5323  save_rank, MyComm);
5324  int nvertices_glob = int(nvertices_glob_l);
5325  MFEM_VERIFY(nvertices_glob == nvertices_glob_l,
5326  "overflow in the number of vertices!");
5327 
5328  long long nbe = NumOfBdrElements;
5329  long long nbe_glob_l = 0;
5330  MPI_Reduce(&nbe, &nbe_glob_l, 1, MPI_LONG_LONG, MPI_SUM, save_rank, MyComm);
5331  int nbe_glob = int(nbe_glob_l);
5332  MFEM_VERIFY(nbe_glob == nbe_glob_l,
5333  "overflow in the number of boundary elements!");
5334 
5335  // On ranks other than save_rank, the *_glob variables are 0, so the serial
5336  // mesh is empty.
5337  Mesh serialmesh(Dim, nvertices_glob, ne_glob, nbe_glob, spaceDim);
5338 
5339  int n_send_recv;
5340  MPI_Status status;
5341  Array<double> vert;
5342  Array<int> ints, dofs;
5343 
5344  // First set the connectivity of serial mesh using the True Dofs from
5345  // the linear H1 space.
5346  if (MyRank == save_rank)
5347  {
5348  for (int e = 0; e < NumOfElements; e++)
5349  {
5350  const int attr = elements[e]->GetAttribute();
5351  const int geom_type = elements[e]->GetGeometryType();
5352  pfespace_linear.GetElementDofs(e, dofs);
5353  for (int j = 0; j < dofs.Size(); j++)
5354  {
5355  dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5356  }
5357  Element *elem = serialmesh.NewElement(geom_type);
5358  elem->SetAttribute(attr);
5359  elem->SetVertices(dofs);
5360  serialmesh.AddElement(elem);
5361  }
5362 
5363  for (int p = 0; p < NRanks; p++)
5364  {
5365  if (p == save_rank) { continue; }
5366  MPI_Recv(&n_send_recv, 1, MPI_INT, p, 444, MyComm, &status);
5367  ints.SetSize(n_send_recv);
5368  if (n_send_recv)
5369  {
5370  MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 445, MyComm, &status);
5371  }
5372  for (int i = 0; i < n_send_recv; )
5373  {
5374  int attr = ints[i++];
5375  int geom_type = ints[i++];
5376  Element *elem = serialmesh.NewElement(geom_type);
5377  elem->SetAttribute(attr);
5378  elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5379  serialmesh.AddElement(elem);
5380  }
5381  }
5382  }
5383  else
5384  {
5385  n_send_recv = 0;
5386  for (int e = 0; e < NumOfElements; e++)
5387  {
5388  n_send_recv += 2 + elements[e]->GetNVertices();
5389  }
5390  MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 444, MyComm);
5391  ints.Reserve(n_send_recv);
5392  ints.SetSize(0);
5393  for (int e = 0; e < NumOfElements; e++)
5394  {
5395  const int attr = elements[e]->GetAttribute();
5396  const int geom_type = elements[e]->GetGeometryType();;
5397  ints.Append(attr);
5398  ints.Append(geom_type);
5399  pfespace_linear.GetElementDofs(e, dofs);
5400  for (int j = 0; j < dofs.Size(); j++)
5401  {
5402  ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5403  }
5404  }
5405  if (n_send_recv)
5406  {
5407  MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 445, MyComm);
5408  }
5409  }
5410 
5411  // Write out boundary elements
5412  if (MyRank == save_rank)
5413  {
5414  for (int e = 0; e < NumOfBdrElements; e++)
5415  {
5416  const int attr = boundary[e]->GetAttribute();
5417  const int geom_type = boundary[e]->GetGeometryType();
5418  pfespace_linear.GetBdrElementDofs(e, dofs);
5419  for (int j = 0; j < dofs.Size(); j++)
5420  {
5421  dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5422  }
5423  Element *elem = serialmesh.NewElement(geom_type);
5424  elem->SetAttribute(attr);
5425  elem->SetVertices(dofs);
5426  serialmesh.AddBdrElement(elem);
5427  }
5428 
5429  for (int p = 0; p < NRanks; p++)
5430  {
5431  if (p == save_rank) { continue; }
5432  MPI_Recv(&n_send_recv, 1, MPI_INT, p, 446, MyComm, &status);
5433  ints.SetSize(n_send_recv);
5434  if (n_send_recv)
5435  {
5436  MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 447, MyComm, &status);
5437  }
5438  for (int i = 0; i < n_send_recv; )
5439  {
5440  int attr = ints[i++];
5441  int geom_type = ints[i++];
5442  Element *elem = serialmesh.NewElement(geom_type);
5443  elem->SetAttribute(attr);
5444  elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5445  serialmesh.AddBdrElement(elem);
5446  }
5447  }
5448  } // MyRank == save_rank
5449  else
5450  {
5451  n_send_recv = 0;
5452  for (int e = 0; e < NumOfBdrElements; e++)
5453  {
5454  n_send_recv += 2 + GetBdrElement(e)->GetNVertices();
5455  }
5456  MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 446, MyComm);
5457  ints.Reserve(n_send_recv);
5458  ints.SetSize(0);
5459  for (int e = 0; e < NumOfBdrElements; e++)
5460  {
5461  const int attr = boundary[e]->GetAttribute();
5462  const int geom_type = boundary[e]->GetGeometryType();
5463  ints.Append(attr);
5464  ints.Append(geom_type);
5465  pfespace_linear.GetBdrElementDofs(e, dofs);
5466  for (int j = 0; j < dofs.Size(); j++)
5467  {
5468  ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5469  }
5470  }
5471  if (n_send_recv)
5472  {
5473  MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 447, MyComm);
5474  }
5475  } // MyRank != save_rank
5476 
5477  if (MyRank == save_rank)
5478  {
5479  for (int v = 0; v < nvertices_glob; v++)
5480  {
5481  serialmesh.AddVertex(0.0); // all other coordinates are 0 by default
5482  }
5483  serialmesh.FinalizeTopology();
5484  }
5485 
5486  // From each processor, we send element-wise vertex/dof locations and
5487  // overwrite the vertex/dof locations of the serial mesh.
5488  if (MyRank == save_rank && Nodes)
5489  {
5490  FiniteElementSpace *fespace_serial = NULL;
5491  // Duplicate the FE collection to make sure the serial mesh is completely
5492  // independent of the parallel mesh:
5493  auto fec_serial = FiniteElementCollection::New(
5494  GetNodalFESpace()->FEColl()->Name());
5495  fespace_serial = new FiniteElementSpace(&serialmesh,
5496  fec_serial,
5497  spaceDim,
5498  GetNodalFESpace()->GetOrdering());
5499  serialmesh.SetNodalFESpace(fespace_serial);
5500  serialmesh.GetNodes()->MakeOwner(fec_serial);
5501  // The serial mesh owns its Nodes and they, in turn, own fec_serial and
5502  // fespace_serial.
5503  }
5504 
5505  int elem_count = 0; // To keep track of element count in serial mesh
5506  if (MyRank == save_rank)
5507  {
5508  Vector nodeloc;
5509  Array<int> ints_serial;
5510  for (int e = 0; e < NumOfElements; e++)
5511  {
5512  if (Nodes)
5513  {
5514  Nodes->GetElementDofValues(e, nodeloc);
5515  serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5516  serialmesh.GetNodes()->SetSubVector(dofs, nodeloc);
5517  }
5518  else
5519  {
5520  GetElementVertices(e, ints);
5521  serialmesh.GetElementVertices(elem_count++, ints_serial);
5522  for (int i = 0; i < ints.Size(); i++)
5523  {
5524  const double *vdata = GetVertex(ints[i]);
5525  double *vdata_serial = serialmesh.GetVertex(ints_serial[i]);
5526  for (int d = 0; d < spaceDim; d++)
5527  {
5528  vdata_serial[d] = vdata[d];
5529  }
5530  }
5531  }
5532  }
5533 
5534  for (int p = 0; p < NRanks; p++)
5535  {
5536  if (p == save_rank) { continue; }
5537  MPI_Recv(&n_send_recv, 1, MPI_INT, p, 448, MyComm, &status);
5538  vert.SetSize(n_send_recv);
5539  if (n_send_recv)
5540  {
5541  MPI_Recv(&vert[0], n_send_recv, MPI_DOUBLE, p, 449, MyComm, &status);
5542  }
5543  for (int i = 0; i < n_send_recv; )
5544  {
5545  if (Nodes)
5546  {
5547  serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5548  serialmesh.GetNodes()->SetSubVector(dofs, &vert[i]);
5549  i += dofs.Size();
5550  }
5551  else
5552  {
5553  serialmesh.GetElementVertices(elem_count++, ints_serial);
5554  for (int j = 0; j < ints_serial.Size(); j++)
5555  {
5556  double *vdata_serial = serialmesh.GetVertex(ints_serial[j]);
5557  for (int d = 0; d < spaceDim; d++)
5558  {
5559  vdata_serial[d] = vert[i++];
5560  }
5561  }
5562  }
5563  }
5564  }
5565  } // MyRank == save_rank
5566  else
5567  {
5568  n_send_recv = 0;
5569  Vector nodeloc;
5570  for (int e = 0; e < NumOfElements; e++)
5571  {
5572  if (Nodes)
5573  {
5574  const FiniteElement *fe = Nodes->FESpace()->GetFE(e);
5575  n_send_recv += spaceDim*fe->GetDof();
5576  }
5577  else
5578  {
5579  n_send_recv += elements[e]->GetNVertices()*spaceDim;
5580  }
5581  }
5582  MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 448, MyComm);
5583  vert.Reserve(n_send_recv);
5584  vert.SetSize(0);
5585  for (int e = 0; e < NumOfElements; e++)
5586  {
5587  if (Nodes)
5588  {
5589  Nodes->GetElementDofValues(e, nodeloc);
5590  for (int j = 0; j < nodeloc.Size(); j++)
5591  {
5592  vert.Append(nodeloc(j));
5593  }
5594  }
5595  else
5596  {
5597  GetElementVertices(e, ints);
5598  for (int i = 0; i < ints.Size(); i++)
5599  {
5600  const double *vdata = GetVertex(ints[i]);
5601  for (int d = 0; d < spaceDim; d++)
5602  {
5603  vert.Append(vdata[d]);
5604  }
5605  }
5606  }
5607  }
5608  if (n_send_recv)
5609  {
5610  MPI_Send(&vert[0], n_send_recv, MPI_DOUBLE, save_rank, 449, MyComm);
5611  }
5612  }
5613 
5614  MPI_Barrier(MyComm);
5615  return serialmesh;
5616 }
5617 
5618 void ParMesh::SaveAsOne(const char *fname, int precision) const
5619 {
5620  ofstream ofs;
5621  if (MyRank == 0)
5622  {
5623  ofs.open(fname);
5624  ofs.precision(precision);
5625  }
5626  PrintAsOne(ofs);
5627 }
5628 
5629 void ParMesh::PrintAsOneXG(std::ostream &os)
5630 {
5631  MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported.");
5632  if (Dim == 3 && meshgen == 1)
5633  {
5634  int i, j, k, nv, ne, p;
5635  const int *ind, *v;
5636  MPI_Status status;
5637  Array<double> vert;
5638  Array<int> ints;
5639 
5640  if (MyRank == 0)
5641  {
5642  os << "NETGEN_Neutral_Format\n";
5643  // print the vertices
5644  ne = NumOfVertices;
5645  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5646  os << nv << '\n';
5647  for (i = 0; i < NumOfVertices; i++)
5648  {
5649  for (j = 0; j < Dim; j++)
5650  {
5651  os << " " << vertices[i](j);
5652  }
5653  os << '\n';
5654  }
5655  for (p = 1; p < NRanks; p++)
5656  {
5657  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5658  vert.SetSize(Dim*nv);
5659  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
5660  for (i = 0; i < nv; i++)
5661  {
5662  for (j = 0; j < Dim; j++)
5663  {
5664  os << " " << vert[Dim*i+j];
5665  }
5666  os << '\n';
5667  }
5668  }
5669 
5670  // print the elements
5671  nv = NumOfElements;
5672  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5673  os << ne << '\n';
5674  for (i = 0; i < NumOfElements; i++)
5675  {
5676  nv = elements[i]->GetNVertices();
5677  ind = elements[i]->GetVertices();
5678  os << 1;
5679  for (j = 0; j < nv; j++)
5680  {
5681  os << " " << ind[j]+1;
5682  }
5683  os << '\n';
5684  }
5685  k = NumOfVertices;
5686  for (p = 1; p < NRanks; p++)
5687  {
5688  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5689  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5690  ints.SetSize(4*ne);
5691  MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
5692  for (i = 0; i < ne; i++)
5693  {
5694  os << p+1;
5695  for (j = 0; j < 4; j++)
5696  {
5697  os << " " << k+ints[i*4+j]+1;
5698  }
5699  os << '\n';
5700  }
5701  k += nv;
5702  }
5703  // print the boundary + shared faces information
5705  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5706  os << ne << '\n';
5707  // boundary
5708  for (i = 0; i < NumOfBdrElements; i++)
5709  {
5710  nv = boundary[i]->GetNVertices();
5711  ind = boundary[i]->GetVertices();
5712  os << 1;
5713  for (j = 0; j < nv; j++)
5714  {
5715  os << " " << ind[j]+1;
5716  }
5717  os << '\n';
5718  }
5719  // shared faces
5720  const int sf_attr =
5721  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
5722  for (i = 0; i < shared_trias.Size(); i++)
5723  {
5724  ind = shared_trias[i].v;
5725  os << sf_attr;
5726  for (j = 0; j < 3; j++)
5727  {
5728  os << ' ' << ind[j]+1;
5729  }
5730  os << '\n';
5731  }
5732  // There are no quad shared faces
5733  k = NumOfVertices;
5734  for (p = 1; p < NRanks; p++)
5735  {
5736  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5737  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5738  ints.SetSize(3*ne);
5739  MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
5740  for (i = 0; i < ne; i++)
5741  {
5742  os << p+1;
5743  for (j = 0; j < 3; j++)
5744  {
5745  os << ' ' << k+ints[i*3+j]+1;
5746  }
5747  os << '\n';
5748  }
5749  k += nv;
5750  }
5751  }
5752  else
5753  {
5754  ne = NumOfVertices;
5755  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5756  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5757  vert.SetSize(Dim*NumOfVertices);
5758  for (i = 0; i < NumOfVertices; i++)
5759  for (j = 0; j < Dim; j++)
5760  {
5761  vert[Dim*i+j] = vertices[i](j);
5762  }
5763  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
5764  0, 445, MyComm);
5765  // elements
5766  ne = NumOfElements;
5767  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5768  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5769  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
5770  ints.SetSize(NumOfElements*4);
5771  for (i = 0; i < NumOfElements; i++)
5772  {
5773  v = elements[i]->GetVertices();
5774  for (j = 0; j < 4; j++)
5775  {
5776  ints[4*i+j] = v[j];
5777  }
5778  }
5779  MPI_Send(&ints[0], 4*NumOfElements, MPI_INT, 0, 447, MyComm);
5780  // boundary + shared faces
5782  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5783  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5785  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
5786  ints.SetSize(3*ne);
5787  for (i = 0; i < NumOfBdrElements; i++)
5788  {
5789  v = boundary[i]->GetVertices();
5790  for (j = 0; j < 3; j++)
5791  {
5792  ints[3*i+j] = v[j];
5793  }
5794  }
5795  for ( ; i < ne; i++)
5796  {
5797  v = shared_trias[i-NumOfBdrElements].v; // tet mesh
5798  for (j = 0; j < 3; j++)
5799  {
5800  ints[3*i+j] = v[j];
5801  }
5802  }
5803  MPI_Send(&ints[0], 3*ne, MPI_INT, 0, 447, MyComm);
5804  }
5805  }
5806 
5807  if (Dim == 3 && meshgen == 2)
5808  {
5809  int i, j, k, nv, ne, p;
5810  const int *ind, *v;
5811  MPI_Status status;
5812  Array<double> vert;
5813  Array<int> ints;
5814 
5815  int TG_nv, TG_ne, TG_nbe;
5816 
5817  if (MyRank == 0)
5818  {
5819  MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5820  MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5822  MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
5823 
5824  os << "TrueGrid\n"
5825  << "1 " << TG_nv << " " << TG_ne << " 0 0 0 0 0 0 0\n"
5826  << "0 0 0 1 0 0 0 0 0 0 0\n"
5827  << "0 0 " << TG_nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
5828  << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
5829  << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
5830 
5831  // print the vertices
5832  nv = TG_nv;
5833  for (i = 0; i < NumOfVertices; i++)
5834  {
5835  os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
5836  << " " << vertices[i](2) << " 0.0\n";
5837  }
5838  for (p = 1; p < NRanks; p++)
5839  {
5840  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5841  vert.SetSize(Dim*nv);
5842  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
5843  for (i = 0; i < nv; i++)
5844  {
5845  os << i+1 << " 0.0 " << vert[Dim*i] << " " << vert[Dim*i+1]
5846  << " " << vert[Dim*i+2] << " 0.0\n";
5847  }
5848  }
5849 
5850  // print the elements
5851  ne = TG_ne;
5852  for (i = 0; i < NumOfElements; i++)
5853  {
5854  nv = elements[i]->GetNVertices();
5855  ind = elements[i]->GetVertices();
5856  os << i+1 << " " << 1;
5857  for (j = 0; j < nv; j++)
5858  {
5859  os << " " << ind[j]+1;
5860  }
5861  os << '\n';
5862  }
5863  k = NumOfVertices;
5864  for (p = 1; p < NRanks; p++)
5865  {
5866  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5867  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5868  ints.SetSize(8*ne);
5869  MPI_Recv(&ints[0], 8*ne, MPI_INT, p, 447, MyComm, &status);
5870  for (i = 0; i < ne; i++)
5871  {
5872  os << i+1 << " " << p+1;
5873  for (j = 0; j < 8; j++)
5874  {
5875  os << " " << k+ints[i*8+j]+1;
5876  }
5877  os << '\n';
5878  }
5879  k += nv;
5880  }
5881 
5882  // print the boundary + shared faces information
5883  ne = TG_nbe;
5884  // boundary
5885  for (i = 0; i < NumOfBdrElements; i++)
5886  {
5887  nv = boundary[i]->GetNVertices();
5888  ind = boundary[i]->GetVertices();
5889  os << 1;
5890  for (j = 0; j < nv; j++)
5891  {
5892  os << " " << ind[j]+1;
5893  }
5894  os << " 1.0 1.0 1.0 1.0\n";
5895  }
5896  // shared faces
5897  const int sf_attr =
5898  MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
5899  // There are no shared triangle faces
5900  for (i = 0; i < shared_quads.Size(); i++)
5901  {
5902  ind = shared_quads[i].v;
5903  os << sf_attr;
5904  for (j = 0; j < 4; j++)
5905  {
5906  os << ' ' << ind[j]+1;
5907  }
5908  os << " 1.0 1.0 1.0 1.0\n";
5909  }
5910  k = NumOfVertices;
5911  for (p = 1; p < NRanks; p++)
5912  {
5913  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5914  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5915  ints.SetSize(4*ne);
5916  MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
5917  for (i = 0; i < ne; i++)
5918  {
5919  os << p+1;
5920  for (j = 0; j < 4; j++)
5921  {
5922  os << " " << k+ints[i*4+j]+1;
5923  }
5924  os << " 1.0 1.0 1.0 1.0\n";
5925  }
5926  k += nv;
5927  }
5928  }
5929  else
5930  {
5931  MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5932  MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5934  MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
5935 
5936  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5937  vert.SetSize(Dim*NumOfVertices);
5938  for (i = 0; i < NumOfVertices; i++)
5939  for (j = 0; j < Dim; j++)
5940  {
5941  vert[Dim*i+j] = vertices[i](j);
5942  }
5943  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE, 0, 445, MyComm);
5944  // elements
5945  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5946  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
5947  ints.SetSize(NumOfElements*8);
5948  for (i = 0; i < NumOfElements; i++)
5949  {
5950  v = elements[i]->GetVertices();
5951  for (j = 0; j < 8; j++)
5952  {
5953  ints[8*i+j] = v[j];
5954  }
5955  }
5956  MPI_Send(&ints[0], 8*NumOfElements, MPI_INT, 0, 447, MyComm);
5957  // boundary + shared faces
5958  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5960  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
5961  ints.SetSize(4*ne);
5962  for (i = 0; i < NumOfBdrElements; i++)
5963  {
5964  v = boundary[i]->GetVertices();
5965  for (j = 0; j < 4; j++)
5966  {
5967  ints[4*i+j] = v[j];
5968  }
5969  }
5970  for ( ; i < ne; i++)
5971  {
5972  v = shared_quads[i-NumOfBdrElements].v; // hex mesh
5973  for (j = 0; j < 4; j++)
5974  {
5975  ints[4*i+j] = v[j];
5976  }
5977  }
5978  MPI_Send(&ints[0], 4*ne, MPI_INT, 0, 447, MyComm);
5979  }
5980  }
5981 
5982  if (Dim == 2)
5983  {
5984  int i, j, k, attr, nv, ne, p;
5985  Array<int> v;
5986  MPI_Status status;
5987  Array<double> vert;
5988  Array<int> ints;
5989 
5990  if (MyRank == 0)
5991  {
5992  os << "areamesh2\n\n";
5993 
5994  // print the boundary + shared edges information
5995  nv = NumOfBdrElements + shared_edges.Size();
5996  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5997  os << ne << '\n';
5998  // boundary
5999  for (i = 0; i < NumOfBdrElements; i++)
6000  {
6001  attr = boundary[i]->GetAttribute();
6002  boundary[i]->GetVertices(v);
6003  os << attr << " ";
6004  for (j = 0; j < v.Size(); j++)
6005  {
6006  os << v[j] + 1 << " ";
6007  }
6008  os << '\n';
6009  }
6010  // shared edges
6011  for (i = 0; i < shared_edges.Size(); i++)
6012  {
6013  attr = shared_edges[i]->GetAttribute();
6014  shared_edges[i]->GetVertices(v);
6015  os << attr << " ";
6016  for (j = 0; j < v.Size(); j++)
6017  {
6018  os << v[j] + 1 << " ";
6019  }
6020  os << '\n';
6021  }
6022  k = NumOfVertices;
6023  for (p = 1; p < NRanks; p++)
6024  {
6025  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6026  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6027  ints.SetSize(2*ne);
6028  MPI_Recv(&ints[0], 2*ne, MPI_INT, p, 447, MyComm, &status);
6029  for (i = 0; i < ne; i++)
6030  {
6031  os << p+1;
6032  for (j = 0; j < 2; j++)
6033  {
6034  os << " " << k+ints[i*2+j]+1;
6035  }
6036  os << '\n';
6037  }
6038  k += nv;
6039  }
6040 
6041  // print the elements
6042  nv = NumOfElements;
6043  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6044  os << ne << '\n';
6045  for (i = 0; i < NumOfElements; i++)
6046  {
6047  // attr = elements[i]->GetAttribute(); // not used
6048  elements[i]->GetVertices(v);
6049  os << 1 << " " << 3 << " ";
6050  for (j = 0; j < v.Size(); j++)
6051  {
6052  os << v[j] + 1 << " ";
6053  }
6054  os << '\n';
6055  }
6056  k = NumOfVertices;
6057  for (p = 1; p < NRanks; p++)
6058  {
6059  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6060  MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6061  ints.SetSize(3*ne);
6062  MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
6063  for (i = 0; i < ne; i++)
6064  {
6065  os << p+1 << " " << 3;
6066  for (j = 0; j < 3; j++)
6067  {
6068  os << " " << k+ints[i*3+j]+1;
6069  }
6070  os << '\n';
6071  }
6072  k += nv;
6073  }
6074 
6075  // print the vertices
6076  ne = NumOfVertices;
6077  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6078  os << nv << '\n';
6079  for (i = 0; i < NumOfVertices; i++)
6080  {
6081  for (j = 0; j < Dim; j++)
6082  {
6083  os << vertices[i](j) << " ";
6084  }
6085  os << '\n';
6086  }
6087  for (p = 1; p < NRanks; p++)
6088  {
6089  MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6090  vert.SetSize(Dim*nv);
6091  MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
6092  for (i = 0; i < nv; i++)
6093  {
6094  for (j = 0; j < Dim; j++)
6095  {
6096  os << " " << vert[Dim*i+j];
6097  }
6098  os << '\n';
6099  }
6100  }
6101  }
6102  else
6103  {
6104  // boundary + shared faces
6105  nv = NumOfBdrElements + shared_edges.Size();
6106  MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6107  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6108  ne = NumOfBdrElements + shared_edges.Size();
6109  MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
6110  ints.SetSize(2*ne);
6111  for (i = 0; i < NumOfBdrElements; i++)
6112  {
6113  boundary[i]->GetVertices(v);
6114  for (j = 0; j < 2; j++)
6115  {
6116  ints[2*i+j] = v[j];
6117  }
6118  }
6119  for ( ; i < ne; i++)
6120  {
6121  shared_edges[i-NumOfBdrElements]->GetVertices(v);
6122  for (j = 0; j < 2; j++)
6123  {
6124  ints[2*i+j] = v[j];
6125  }
6126  }
6127  MPI_Send(&ints[0], 2*ne, MPI_INT, 0, 447, MyComm);
6128  // elements
6129  ne = NumOfElements;
6130  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6131  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6132  MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
6133  ints.SetSize(NumOfElements*3);
6134  for (i = 0; i < NumOfElements; i++)
6135  {
6136  elements[i]->GetVertices(v);
6137  for (j = 0; j < 3; j++)
6138  {
6139  ints[3*i+j] = v[j];
6140  }
6141  }
6142  MPI_Send(&ints[0], 3*NumOfElements, MPI_INT, 0, 447, MyComm);
6143  // vertices
6144  ne = NumOfVertices;
6145  MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6146  MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6147  vert.SetSize(Dim*NumOfVertices);
6148  for (i = 0; i < NumOfVertices; i++)
6149  for (j = 0; j < Dim; j++)
6150  {
6151  vert[Dim*i+j] = vertices[i](j);
6152  }
6153  MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
6154  0, 445, MyComm);
6155  }
6156  }
6157 }
6158 
6159 void ParMesh::GetBoundingBox(Vector &gp_min, Vector &gp_max, int ref)
6160 {
6161  int sdim;
6162  Vector p_min, p_max;
6163 
6164  this->Mesh::GetBoundingBox(p_min, p_max, ref);
6165 
6166  sdim = SpaceDimension();
6167 
6168  gp_min.SetSize(sdim);
6169  gp_max.SetSize(sdim);
6170 
6171  MPI_Allreduce(p_min.GetData(), gp_min.GetData(), sdim, MPI_DOUBLE,
6172  MPI_MIN, MyComm);
6173  MPI_Allreduce(p_max.GetData(), gp_max.GetData(), sdim, MPI_DOUBLE,
6174  MPI_MAX, MyComm);
6175 }
6176 
6177 void ParMesh::GetCharacteristics(double &gh_min, double &gh_max,
6178  double &gk_min, double &gk_max)
6179 {
6180  double h_min, h_max, kappa_min, kappa_max;
6181 
6182  this->Mesh::GetCharacteristics(h_min, h_max, kappa_min, kappa_max);
6183 
6184  MPI_Allreduce(&h_min, &gh_min, 1, MPI_DOUBLE, MPI_MIN, MyComm);
6185  MPI_Allreduce(&h_max, &gh_max, 1, MPI_DOUBLE, MPI_MAX, MyComm);
6186  MPI_Allreduce(&kappa_min, &gk_min, 1, MPI_DOUBLE, MPI_MIN, MyComm);
6187  MPI_Allreduce(&kappa_max, &gk_max, 1, MPI_DOUBLE, MPI_MAX, MyComm);
6188 }
6189 
6190 void ParMesh::PrintInfo(std::ostream &os)
6191 {
6192  int i;
6193  DenseMatrix J(Dim);
6194  double h_min, h_max, kappa_min, kappa_max, h, kappa;
6195 
6196  if (MyRank == 0)
6197  {
6198  os << "Parallel Mesh Stats:" << '\n';
6199  }
6200 
6201  for (i = 0; i < NumOfElements; i++)
6202  {
6203  GetElementJacobian(i, J);
6204  h = pow(fabs(J.Weight()), 1.0/double(Dim));
6205  kappa = (Dim == spaceDim) ?
6206  J.CalcSingularvalue(0) / J.CalcSingularvalue(Dim-1) : -1.0;
6207  if (i == 0)
6208  {
6209  h_min = h_max = h;
6210  kappa_min = kappa_max = kappa;
6211  }
6212  else
6213  {
6214  if (h < h_min) { h_min = h; }
6215  if (h > h_max) { h_max = h; }
6216  if (kappa < kappa_min) { kappa_min = kappa; }
6217  if (kappa > kappa_max) { kappa_max = kappa; }
6218  }
6219  }
6220 
6221  double gh_min, gh_max, gk_min, gk_max;
6222  MPI_Reduce(&h_min, &gh_min, 1, MPI_DOUBLE, MPI_MIN, 0, MyComm);
6223  MPI_Reduce(&h_max, &gh_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
6224  MPI_Reduce(&kappa_min, &gk_min, 1, MPI_DOUBLE, MPI_MIN, 0, MyComm);
6225  MPI_Reduce(&kappa_max, &gk_max, 1, MPI_DOUBLE, MPI_MAX, 0, MyComm);
6226 
6227  // TODO: collect and print stats by geometry
6228 
6229  long long ldata[5]; // vert, edge, face, elem, neighbors;
6230  long long mindata[5], maxdata[5], sumdata[5];
6231 
6232  // count locally owned vertices, edges, and faces
6233  ldata[0] = GetNV();
6234  ldata[1] = GetNEdges();
6235  ldata[2] = GetNFaces();
6236  ldata[3] = GetNE();
6237  ldata[4] = gtopo.GetNumNeighbors()-1;
6238  for (int gr = 1; gr < GetNGroups(); gr++)
6239  {
6240  if (!gtopo.IAmMaster(gr)) // we are not the master
6241  {
6242  ldata[0] -= group_svert.RowSize(gr-1);
6243  ldata[1] -= group_sedge.RowSize(gr-1);
6244  ldata[2] -= group_stria.RowSize(gr-1);
6245  ldata[2] -= group_squad.RowSize(gr-1);
6246  }
6247  }
6248 
6249  MPI_Reduce(ldata, mindata, 5, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
6250  MPI_Reduce(ldata, sumdata, 5, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
6251  MPI_Reduce(ldata, maxdata, 5, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
6252 
6253  if (MyRank == 0)
6254  {
6255  os << '\n'
6256  << " "
6257  << setw(12) << "minimum"
6258  << setw(12) << "average"
6259  << setw(12) << "maximum"
6260  << setw(12) << "total" << '\n';
6261  os << " vertices "
6262  << setw(12) << mindata[0]
6263  << setw(12) << sumdata[0]/NRanks
6264  << setw(12) << maxdata[0]
6265  << setw(12) << sumdata[0] << '\n';
6266  os << " edges "
6267  << setw(12) << mindata[1]
6268  << setw(12) << sumdata[1]/NRanks
6269  << setw(12) << maxdata[1]
6270  << setw(12) << sumdata[1] << '\n';
6271  if (Dim == 3)
6272  {
6273  os << " faces "
6274  << setw(12) << mindata[2]
6275  << setw(12) << sumdata[2]/NRanks
6276  << setw(12) << maxdata[2]
6277  << setw(12) << sumdata[2] << '\n';
6278  }
6279  os << " elements "
6280  << setw(12) << mindata[3]
6281  << setw(12) << sumdata[3]/NRanks
6282  << setw(12) << maxdata[3]
6283  << setw(12) << sumdata[3] << '\n';
6284  os << " neighbors "
6285  << setw(12) << mindata[4]
6286  << setw(12) << sumdata[4]/NRanks
6287  << setw(12) << maxdata[4] << '\n';
6288  os << '\n'
6289  << " "
6290  << setw(12) << "minimum"
6291  << setw(12) << "maximum" << '\n';
6292  os << " h "
6293  << setw(12) << gh_min
6294  << setw(12) << gh_max << '\n';
6295  os << " kappa "
6296  << setw(12) << gk_min
6297  << setw(12) << gk_max << '\n';
6298  os << std::flush;
6299  }
6300 }
6301 
6302 long long ParMesh::ReduceInt(int value) const
6303 {
6304  long long local = value, global;
6305  MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MyComm);
6306  return global;
6307 }
6308 
6309 void ParMesh::ParPrint(ostream &os) const
6310 {
6311  if (NURBSext)
6312  {
6313  // TODO: NURBS meshes.
6314  Print(os); // use the serial MFEM v1.0 format for now
6315  return;
6316  }
6317 
6318  if (Nonconforming())
6319  {
6320  // the NC mesh format works both in serial and in parallel
6321  Printer(os);
6322  return;
6323  }
6324 
6325  // Write out serial mesh. Tell serial mesh to deliniate the end of it's
6326  // output with 'mfem_serial_mesh_end' instead of 'mfem_mesh_end', as we will
6327  // be adding additional parallel mesh information.
6328  Printer(os, "mfem_serial_mesh_end");
6329 
6330  // write out group topology info.
6331  gtopo.Save(os);
6332 
6333  os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6334  if (Dim >= 2)
6335  {
6336  os << "total_shared_edges " << shared_edges.Size() << '\n';
6337  }
6338  if (Dim >= 3)
6339  {
6340  os << "total_shared_faces " << sface_lface.Size() << '\n';
6341  }
6342  for (int gr = 1; gr < GetNGroups(); gr++)
6343  {
6344  {
6345  const int nv = group_svert.RowSize(gr-1);
6346  const int *sv = group_svert.GetRow(gr-1);
6347  os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
6348  for (int i = 0; i < nv; i++)
6349  {
6350  os << svert_lvert[sv[i]] << '\n';
6351  }
6352  }
6353  if (Dim >= 2)
6354  {
6355  const int ne = group_sedge.RowSize(gr-1);
6356  const int *se = group_sedge.GetRow(gr-1);
6357  os << "\nshared_edges " << ne << '\n';
6358  for (int i = 0; i < ne; i++)
6359  {
6360  const int *v = shared_edges[se[i]]->GetVertices();
6361  os << v[0] << ' ' << v[1] << '\n';
6362  }
6363  }
6364  if (Dim >= 3)
6365  {
6366  const int nt = group_stria.RowSize(gr-1);
6367  const int *st = group_stria.GetRow(gr-1);
6368  const int nq = group_squad.RowSize(gr-1);
6369  const int *sq = group_squad.GetRow(gr-1);
6370  os << "\nshared_faces " << nt+nq << '\n';
6371  for (int i = 0; i < nt; i++)
6372  {
6373  os << Geometry::TRIANGLE;
6374  const int *v = shared_trias[st[i]].v;
6375  for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6376  os << '\n';
6377  }
6378  for (int i = 0; i < nq; i++)
6379  {
6380  os << Geometry::SQUARE;
6381  const int *v = shared_quads[sq[i]].v;
6382  for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6383  os << '\n';
6384  }
6385  }
6386  }
6387 
6388  // Write out section end tag for mesh.
6389  os << "\nmfem_mesh_end" << endl;
6390 }
6391 
6392 void ParMesh::PrintVTU(std::string pathname,
6393  VTKFormat format,
6394  bool high_order_output,
6395  int compression_level,
6396  bool bdr)
6397 {
6398  int pad_digits_rank = 6;
6399  DataCollection::create_directory(pathname, this, MyRank);
6400 
6401  std::string::size_type pos = pathname.find_last_of('/');
6402  std::string fname
6403  = (pos == std::string::npos) ? pathname : pathname.substr(pos+1);
6404 
6405  if (MyRank == 0)
6406  {
6407  std::string pvtu_name = pathname + "/" + fname + ".pvtu";
6408  std::ofstream os(pvtu_name);
6409 
6410  std::string data_type = (format == VTKFormat::BINARY32) ? "Float32" : "Float64";
6411  std::string data_format = (format == VTKFormat::ASCII) ? "ascii" : "binary";
6412 
6413  os << "<?xml version=\"1.0\"?>\n";
6414  os << "<VTKFile type=\"PUnstructuredGrid\"";
6415  os << " version =\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
6416  os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
6417 
6418  os << "<PPoints>\n";
6419  os << "\t<PDataArray type=\"" << data_type << "\" ";
6420  os << " Name=\"Points\" NumberOfComponents=\"3\""
6421  << " format=\"" << data_format << "\"/>\n";
6422  os << "</PPoints>\n";
6423 
6424  os << "<PCells>\n";
6425  os << "\t<PDataArray type=\"Int32\" ";
6426  os << " Name=\"connectivity\" NumberOfComponents=\"1\""
6427  << " format=\"" << data_format << "\"/>\n";
6428  os << "\t<PDataArray type=\"Int32\" ";
6429  os << " Name=\"offsets\" NumberOfComponents=\"1\""
6430  << " format=\"" << data_format << "\"/>\n";
6431  os << "\t<PDataArray type=\"UInt8\" ";
6432  os << " Name=\"types\" NumberOfComponents=\"1\""
6433  << " format=\"" << data_format << "\"/>\n";
6434  os << "</PCells>\n";
6435 
6436  os << "<PCellData>\n";
6437  os << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
6438  << "\" NumberOfComponents=\"1\""
6439  << " format=\"" << data_format << "\"/>\n";
6440  os << "</PCellData>\n";
6441 
6442  for (int ii=0; ii<NRanks; ii++)
6443  {
6444  std::string piece = fname + ".proc"
6445  + to_padded_string(ii, pad_digits_rank) + ".vtu";
6446  os << "<Piece Source=\"" << piece << "\"/>\n";
6447  }
6448 
6449  os << "</PUnstructuredGrid>\n";
6450  os << "</VTKFile>\n";
6451  os.close();
6452  }
6453 
6454  std::string vtu_fname = pathname + "/" + fname + ".proc"
6455  + to_padded_string(MyRank, pad_digits_rank);
6456  Mesh::PrintVTU(vtu_fname, format, high_order_output, compression_level, bdr);
6457 }
6458 
6459 int ParMesh::FindPoints(DenseMatrix& point_mat, Array<int>& elem_id,
6460  Array<IntegrationPoint>& ip, bool warn,
6461  InverseElementTransformation *inv_trans)
6462 {
6463  const int npts = point_mat.Width();
6464  if (npts == 0) { return 0; }
6465 
6466  const bool no_warn = false;
6467  Mesh::FindPoints(point_mat, elem_id, ip, no_warn, inv_trans);
6468 
6469  // If multiple processors find the same point, we need to choose only one of
6470  // the processors to mark that point as found.
6471  // Here, we choose the processor with the minimal rank.
6472 
6473  Array<int> my_point_rank(npts), glob_point_rank(npts);
6474  for (int k = 0; k < npts; k++)
6475  {
6476  my_point_rank[k] = (elem_id[k] == -1) ? NRanks : MyRank;
6477  }
6478 
6479  MPI_Allreduce(my_point_rank.GetData(), glob_point_rank.GetData(), npts,
6480  MPI_INT, MPI_MIN, MyComm);
6481 
6482  int pts_found = 0;
6483  for (int k = 0; k < npts; k++)
6484  {
6485  if (glob_point_rank[k] == NRanks) { elem_id[k] = -1; }
6486  else
6487  {
6488  pts_found++;
6489  if (glob_point_rank[k] != MyRank) { elem_id[k] = -2; }
6490  }
6491  }
6492  if (warn && pts_found != npts && MyRank == 0)
6493  {
6494  MFEM_WARNING((npts-pts_found) << " points were not found");
6495  }
6496  return pts_found;
6497 }
6498 
6499 static void PrintVertex(const Vertex &v, int space_dim, ostream &os)
6500 {
6501  os << v(0);
6502  for (int d = 1; d < space_dim; d++)
6503  {
6504  os << ' ' << v(d);
6505  }
6506 }
6507 
6508 void ParMesh::PrintSharedEntities(const char *fname_prefix) const
6509 {
6510  stringstream out_name;
6511  out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
6512  << ".shared_entities";
6513  ofstream os(out_name.str().c_str());
6514  os.precision(16);
6515 
6516  gtopo.Save(out);
6517 
6518  os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6519  if (Dim >= 2)
6520  {
6521  os << "total_shared_edges " << shared_edges.Size() << '\n';
6522  }
6523  if (Dim >= 3)
6524  {
6525  os << "total_shared_faces " << sface_lface.Size() << '\n';
6526  }
6527  for (int gr = 1; gr < GetNGroups(); gr++)
6528  {
6529  {
6530  const int nv = group_svert.RowSize(gr-1);
6531  const int *sv = group_svert.GetRow(gr-1);
6532  os << "\n# group " << gr << "\n\nshared_vertices " << nv << '\n';
6533  for (int i = 0; i < nv; i++)
6534  {
6535  const int lvi = svert_lvert[sv[i]];
6536  // os << lvi << '\n';
6537  PrintVertex(vertices[lvi], spaceDim, os);
6538  os << '\n';
6539  }
6540  }
6541  if (Dim >= 2)
6542  {
6543  const int ne = group_sedge.RowSize(gr-1);
6544  const int *se = group_sedge.GetRow(gr-1);
6545  os << "\nshared_edges " << ne << '\n';
6546  for (int i = 0; i < ne; i++)
6547  {
6548  const int *v = shared_edges[se[i]]->GetVertices();
6549  // os << v[0] << ' ' << v[1] << '\n';
6550  PrintVertex(vertices[v[0]], spaceDim, os);
6551  os << " | ";
6552  PrintVertex(vertices[v[1]], spaceDim, os);
6553  os << '\n';
6554  }
6555  }
6556  if (Dim >= 3)
6557  {
6558  const int nt = group_stria.RowSize(gr-1);
6559  const int *st = group_stria.GetRow(gr-1);
6560  const int nq = group_squad.RowSize(gr-1);
6561  const int *sq = group_squad.GetRow(gr-1);
6562  os << "\nshared_faces " << nt+nq << '\n';
6563  for (int i = 0; i < nt; i++)
6564  {
6565  const int *v = shared_trias[st[i]].v;
6566 #if 0
6567  os << Geometry::TRIANGLE;
6568  for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6569  os << '\n';
6570 #endif
6571  for (int j = 0; j < 3; j++)
6572  {
6573  PrintVertex(vertices[v[j]], spaceDim, os);
6574  (j < 2) ? os << " | " : os << '\n';
6575  }
6576  }
6577  for (int i = 0; i < nq; i++)
6578  {
6579  const int *v = shared_quads[sq[i]].v;
6580 #if 0
6581  os << Geometry::SQUARE;
6582  for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6583  os << '\n';
6584 #endif
6585  for (int j = 0; j < 4; j++)
6586  {
6587  PrintVertex(vertices[v[j]], spaceDim, os);
6588  (j < 3) ? os << " | " : os << '\n';
6589  }
6590  }
6591  }
6592  }
6593 }
6594 
6596 {
6597  H1_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6598  ParMesh *pm = const_cast<ParMesh *>(this);
6599  ParFiniteElementSpace fespace(pm, &fec);
6600 
6601  gi.SetSize(GetNV());
6602 
6603  Array<int> dofs;
6604  for (int i=0; i<GetNV(); ++i)
6605  {
6606  fespace.GetVertexDofs(i, dofs);
6607  gi[i] = fespace.GetGlobalTDofNumber(dofs[0]);
6608  }
6609 }
6610 
6612 {
6613  if (Dim == 1)
6614  {
6616  return;
6617  }
6618 
6619  ND_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6620  ParMesh *pm = const_cast<ParMesh *>(this);
6621  ParFiniteElementSpace fespace(pm, &fec);
6622 
6623  gi.SetSize(GetNEdges());
6624 
6625  Array<int> dofs;
6626  for (int i=0; i<GetNEdges(); ++i)
6627  {
6628  fespace.GetEdgeDofs(i, dofs);
6629  const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6630  gi[i] = fespace.GetGlobalTDofNumber(ldof);
6631  }
6632 }
6633 
6635 {
6636  if (Dim == 2)
6637  {
6639  return;
6640  }
6641  else if (Dim == 1)
6642  {
6644  return;
6645  }
6646 
6647  RT_FECollection fec(0, Dim); // Order 0, mesh dimension (not spatial dimension).
6648  ParMesh *pm = const_cast<ParMesh *>(this);
6649  ParFiniteElementSpace fespace(pm, &fec);
6650 
6651  gi.SetSize(GetNFaces());
6652 
6653  Array<int> dofs;
6654  for (int i=0; i<GetNFaces(); ++i)
6655  {
6656  fespace.GetFaceDofs(i, dofs);
6657  const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6658  gi[i] = fespace.GetGlobalTDofNumber(ldof);
6659  }
6660 }
6661 
6663 {
6665 
6666  // Cast from long long to HYPRE_BigInt
6667  const HYPRE_BigInt offset = glob_elem_offset;
6668 
6669  gi.SetSize(GetNE());
6670  for (int i=0; i<GetNE(); ++i)
6671  {
6672  gi[i] = offset + i;
6673  }
6674 }
6675 
6677 {
6678  Mesh::Swap(other, true);
6679 
6680  mfem::Swap(MyComm, other.MyComm);
6681  mfem::Swap(NRanks, other.NRanks);
6682  mfem::Swap(MyRank, other.MyRank);
6683 
6686 
6687  gtopo.Swap(other.gtopo);
6688 
6689  group_svert.Swap(other.group_svert);
6690  group_sedge.Swap(other.group_sedge);
6691  group_stria.Swap(other.group_stria);
6692  group_squad.Swap(other.group_squad);
6693 
6700 
6701  // Swap face-neighbor data
6710 
6711  // Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
6712  mfem::Swap(pncmesh, other.pncmesh);
6713 
6714  print_shared = other.print_shared;
6715 }
6716 
6718 {
6719  delete pncmesh;
6720  ncmesh = pncmesh = NULL;
6721 
6723 
6724  for (int i = 0; i < shared_edges.Size(); i++)
6725  {
6727  }
6728  shared_edges.DeleteAll();
6729 
6730  delete face_nbr_el_to_face;
6731  face_nbr_el_to_face = NULL;
6732 }
6733 
6735 {
6736  ParMesh::Destroy();
6737 
6738  // The Mesh destructor is called automatically
6739 }
6740 
6741 }
6742 
6743 #endif
Abstract class for all finite elements.
Definition: fe_base.hpp:232
void UniformRefinement3D() override
Refine a mixed 3D mesh uniformly.
Definition: pmesh.cpp:4587
void PrintAsOneXG(std::ostream &out=mfem::out)
Old mesh format (Netgen/Truegrid) version of &#39;PrintAsOne&#39;.
Definition: pmesh.cpp:5629
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition: mesh.cpp:3979
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:2781
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
Definition: pfespace.cpp:631
void SetSubVector(const Array< int > &dofs, const double value)
Set the entries listed in dofs to the given value.
Definition: vector.cpp:574
void GetConformingSharedStructures(class ParMesh &pmesh)
Definition: pncmesh.cpp:787
int GetGroupMasterRank(int g) const
Return the rank of the group master for group &#39;g&#39;.
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition: intrules.hpp:247
int Push(int i, int j)
Definition: table.cpp:219
void Recreate(const int n, const int *p)
Create an integer set from C-array &#39;p&#39; of &#39;n&#39; integers. Overwrites any existing set data...
Definition: sets.cpp:68
int * GetJ()
Definition: table.hpp:114
void GetGhostFaceTransformation(FaceElementTransformations *FETr, Element::Type face_type, Geometry::Type face_geom)
Definition: pmesh.cpp:3032
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:90
const FiniteElementSpace * GetNodalFESpace() const
Definition: mesh.cpp:5351
Element on side 1 is configured.
Definition: eltrans.hpp:513
void GetSharedVertexCommunicator(int ordering, GroupCommunicator &svert_comm) const
Get the shared vertices GroupCommunicator.
Definition: pmesh.cpp:1661
virtual ~ParMesh()
Definition: pmesh.cpp:6734
void LoadSharedEntities(std::istream &input)
Definition: pmesh.cpp:978
long glob_offset_sequence
Definition: pmesh.hpp:87
void UniformRefineGroups2D(int old_nv)
Definition: pmesh.cpp:4363
void FreeElement(Element *E)
Definition: mesh.cpp:11937
FaceElementTransformations * GetSharedFaceTransformationsByLocalIndex(int FaceNo, bool fill2=true)
Definition: pmesh.cpp:3088
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition: mesh.cpp:5433
int NRanks
Definition: pmesh.hpp:38
void SetSize(int dim, int connections_per_row)
Set the size and the number of connections for the table.
Definition: table.cpp:124
ParMesh & operator=(ParMesh &&mesh)
Move assignment operator.
Definition: pmesh.cpp:101
void GetSharedQuadCommunicator(int ordering, GroupCommunicator &squad_comm) const
Get the shared face quadrilaterals GroupCommunicator.
Definition: pmesh.cpp:1685
void MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type)
Internal function used in ParMesh::MakeRefined (and related constructor)
Definition: pmesh.cpp:1132
virtual Element * Duplicate(Mesh *m) const =0
int FindSharedVertices(const int *partition, Table *vertex_element, ListOfIntegerSets &groups)
Definition: pmesh.cpp:585
void PrintSharedEntities(const char *fname_prefix) const
Debugging method.
Definition: pmesh.cpp:6508
void AddColumnsInRow(int r, int ncol)
Definition: table.hpp:78
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 UniformRefinement3D_base(Array< int > *f2qf=NULL, DSTable *v_to_v_p=NULL, bool update_nodes=true)
Definition: mesh.cpp:8206
void PrintInfo(std::ostream &out=mfem::out) override
Print various parallel mesh stats.
Definition: pmesh.cpp:6190
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition: table.cpp:81
const FiniteElement * GetFaceNbrFE(int i) const
Definition: pfespace.cpp:1517
virtual void GetVertices(Array< int > &v) const =0
Returns element&#39;s vertices.
Geometry::Type GetElementBaseGeometry(int i) const
Definition: mesh.hpp:1108
int GetBdrElementEdgeIndex(int i) const
Definition: mesh.cpp:6235
static ParMesh MakeRefined(ParMesh &orig_mesh, int ref_factor, int ref_type)
Create a uniformly refined (by any factor) version of orig_mesh.
Definition: pmesh.cpp:1367
bool print_shared
Definition: pmesh.hpp:92
Array< Slave > slaves
Definition: ncmesh.hpp:231
void GetSharedTriCommunicator(int ordering, GroupCommunicator &stria_comm) const
Get the shared face triangles GroupCommunicator.
Definition: pmesh.cpp:1709
Array< Element * > boundary
Definition: mesh.hpp:91
friend class ParNCMesh
Definition: pmesh.hpp:662
int Dimension() const
Definition: mesh.hpp:1047
int * GeneratePartitioning(int nparts, int part_method=1)
Definition: mesh.cpp:7074
int BuildLocalVertices(const Mesh &global_mesh, const int *partitioning, Array< int > &vert_global_local)
Fills out partitioned Mesh::vertices.
Definition: pmesh.cpp:302
void UniformRefinement2D() override
Refine a mixed 2D mesh uniformly.
Definition: pmesh.cpp:4563
void SaveAsOne(const char *fname, int precision=16) const
Definition: pgridfunc.cpp:899
CoarseFineTransformations CoarseFineTr
Definition: mesh.hpp:240
int own_nodes
Definition: mesh.hpp:246
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition: mesh.cpp:5389
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:512
IsoparametricTransformation Transformation
Definition: mesh.hpp:234
void GetCharacteristics(double &h_min, double &h_max, double &kappa_min, double &kappa_max, Vector *Vh=NULL, Vector *Vk=NULL)
Definition: mesh.cpp:193
Array< Element * > face_nbr_elements
Definition: pmesh.hpp:382
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Definition: mesh.cpp:327
void NewNodes(GridFunction &nodes, bool make_owner=false)
Replace the internal node GridFunction with the given GridFunction.
Definition: mesh.cpp:7976
void GetFaceNeighbors(class ParMesh &pmesh)
Definition: pncmesh.cpp:886
void Swap(Table &other)
Definition: table.cpp:394
int NumOfEdges
Definition: mesh.hpp:70
Lists all edges/faces in the nonconforming mesh.
Definition: ncmesh.hpp:227
Element::Type GetBdrElementType(int i) const
Returns the type of boundary element i.
Definition: mesh.cpp:6296
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition: mesh.cpp:129
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition: mesh.cpp:1413
void ShiftRight(int &a, int &b, int &c)
Definition: mesh.hpp:2044
Array< int > sface_lface
Definition: pmesh.hpp:78
void SetDims(int rows, int nnz)
Definition: table.cpp:140
static const int FaceVert[NumFaces][MaxFaceVert]
Definition: geom.hpp:226
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition: operator.hpp:72
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:720
void GetVertexToVertexTable(DSTable &) const
Definition: mesh.cpp:6372
int GetAttribute() const
Return element&#39;s attribute.
Definition: element.hpp:55
A specialized ElementTransformation class representing a face and its two neighboring elements...
Definition: eltrans.hpp:480
T * GetData()
Returns the data.
Definition: array.hpp:115
bool Nonconforming() const
Definition: mesh.hpp:1729
static const int FaceVert[NumFaces][MaxFaceVert]
Definition: geom.hpp:274
int Size() const
Returns the size of the vector.
Definition: vector.hpp:199
int Push(int r, int c, int f)
Check to see if this entry is in the table and add it to the table if it is not there. Returns the number assigned to the table entry.
Definition: stable3d.cpp:64
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition: array.cpp:68
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
GridFunction * Nodes
Definition: mesh.hpp:245
int NumOfElements
Definition: mesh.hpp:69
Point transformation for side 2 is configured.
Definition: eltrans.hpp:516
bool IsSlaveFace(const FaceInfo &fi) const
Definition: mesh.cpp:1051
IntegrationPointTransformation Loc2
Definition: eltrans.hpp:524
void ApplyLocalSlaveTransformation(FaceElementTransformations &FT, const FaceInfo &fi, bool is_ghost)
Definition: mesh.cpp:1056
void GetCharacteristics(double &h_min, double &h_max, double &kappa_min, double &kappa_max)
Definition: pmesh.cpp:6177
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:8011
void SynchronizeDerefinementData(Array< Type > &elem_data, const Table &deref_table)
Definition: pncmesh.cpp:1545
void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0) override
This function is not public anymore. Use GeneralRefinement instead.
Definition: pmesh.cpp:3948
double CalcSingularvalue(const int i) const
Return the i-th singular value (decreasing order) of NxN matrix, N=1,2,3.
Definition: densemat.cpp:1268
void EnsureParNodes()
If the mesh is curved, make sure &#39;Nodes&#39; is ParGridFunction.
Definition: pmesh.cpp:2095
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:3175
virtual void SetVertices(const int *ind)
Set the indices the element according to the input.
Definition: element.cpp:17
int GetNeighborRank(int i) const
Return the MPI rank of neighbor &#39;i&#39;.
int GetNEdges() const
Return the number of edges.
Definition: mesh.hpp:942
void UniformRefineGroups3D(int old_nv, int old_nedges, const DSTable &old_v_to_v, const STable3D &old_faces, Array< int > *f2qf)
Definition: pmesh.cpp:4414
const Element * GetElement(int i) const
Definition: mesh.hpp:1081
int FindPoints(DenseMatrix &point_mat, Array< int > &elem_ids, Array< IntegrationPoint > &ips, bool warn=true, InverseElementTransformation *inv_trans=NULL) override
Find the ids of the elements that contain the given points, and their corresponding reference coordin...
Definition: pmesh.cpp:6459
virtual void Derefine(const Array< int > &derefs)
Definition: pncmesh.cpp:1373
double kappa
Definition: ex24.cpp:54
Abstract parallel finite element space.
Definition: pfespace.hpp:28
bool have_face_nbr_data
Definition: pmesh.hpp:378
Array< Vert3 > shared_trias
Definition: pmesh.hpp:65
Array< int > face_nbr_vertices_offset
Definition: pmesh.hpp:381
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec and fes.
Definition: gridfunc.hpp:122
Data type for vertex.
Definition: vertex.hpp:22
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition: mesh.cpp:3926
Array< int > RefEdges
Definition: geom.hpp:315
STL namespace.
Array< int > face_nbr_group
Definition: pmesh.hpp:379
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition: mesh.cpp:5629
bool NonconformingDerefinement(Array< double > &elem_error, double threshold, int nc_limit=0, int op=1) override
NC version of GeneralDerefinement.
Definition: pmesh.cpp:4000
double Weight() const
Definition: densemat.cpp:545
Data arrays will be written in ASCII format.
Element on side 2 is configured.
Definition: eltrans.hpp:514
The inverse transformation of a given ElementTransformation.
Definition: eltrans.hpp:185
void RebalanceImpl(const Array< int > *partition)
Definition: pmesh.cpp:4068
void GroupEdge(int group, int i, int &edge, int &o) const
Definition: pmesh.cpp:1607
void MarkTetMeshForRefinement(DSTable &v_to_v) override
Definition: pmesh.cpp:1733
ElementTransformation * Elem2
Definition: eltrans.hpp:522
void BuildFaceGroup(int ngroups, const Mesh &mesh, const Array< int > &face_group, int &nstria, int &nsquad)
Definition: pmesh.cpp:622
void Swap(GroupTopology &other)
Swap the internal data with another GroupTopology object.
void GetVertexDofs(int i, Array< int > &dofs) const
Definition: fespace.cpp:3035
virtual FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31)
Definition: mesh.cpp:961
int RowSize(int i) const
Definition: table.hpp:108
Array< Element * > faces
Definition: mesh.hpp:92
const IntegrationRule * GetVertices(int GeomType)
Return an IntegrationRule consisting of all vertices of the given Geometry::Type, GeomType...
Definition: geom.cpp:265
const NCList & GetSharedEdges()
Definition: pncmesh.hpp:124
int GetNBE() const
Returns number of boundary elements.
Definition: mesh.hpp:939
Geometry Geometries
Definition: fe.cpp:49
STable3D * GetSharedFacesTable()
Definition: pmesh.cpp:2661
Array< int > sedge_ledge
Definition: pmesh.hpp:76
void RedRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition: mesh.hpp:359
Operation last_operation
Definition: mesh.hpp:289
void Save(std::ostream &out) const
Save the data in a stream.
void skip_comment_lines(std::istream &is, const char comment_char)
Check if the stream starts with comment_char. If so skip it.
Definition: text.hpp:31
bool HasBoundaryElements() const override
Checks if any rank in the mesh has boundary elements.
Definition: pmesh.cpp:1598
void DeleteAll()
Delete the whole array.
Definition: array.hpp:851
Table group_stria
Definition: pmesh.hpp:71
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th element in t...
Definition: fespace.cpp:2783
void AddConnections(int r, const int *c, int nc)
Definition: table.cpp:104
void InitRefinementTransforms()
Definition: mesh.cpp:9968
const NCList & GetSharedList(int entity)
Helper to get shared vertices/edges/faces (&#39;entity&#39; == 0/1/2 resp.).
Definition: pncmesh.hpp:128
void UniformRefinement2D_base(bool update_nodes=true)
Definition: mesh.cpp:8047
Array< NCFaceInfo > nc_faces_info
Definition: mesh.hpp:218
void OnMeshUpdated(Mesh *mesh)
Definition: ncmesh.cpp:2532
A parallel extension of the NCMesh class.
Definition: pncmesh.hpp:64
Element * NewElement(int geom)
Definition: mesh.cpp:3854
Table * el_to_face
Definition: mesh.hpp:221
FaceElementTransformations FaceElemTr
Definition: mesh.hpp:237
ParNCMesh * pncmesh
Definition: pmesh.hpp:388
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Definition: mesh.cpp:1394
void SetVerticesFromNodes(const GridFunction *nodes)
Helper to set vertex coordinates given a high-order curvature function.
Definition: mesh.cpp:5375
void CopyTo(U *dest)
STL-like copyTo dest from begin to end.
Definition: array.hpp:282
int FindSharedEdges(const Mesh &mesh, const int *partition, Table *&edge_element, ListOfIntegerSets &groups)
Definition: pmesh.cpp:531
virtual void Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
Given a coefficient and a transformation, compute its projection (approximation) in the local finite ...
Definition: fe_base.cpp:125
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition: mesh.hpp:933
void MakeRefined_(Mesh &orig_mesh, const Array< int > ref_factors, int ref_type)
Internal function used in Mesh::MakeRefined.
Definition: mesh.cpp:4346
void ExchangeFaceNbrData()
Definition: pmesh.cpp:2117
void BuildSharedVertMapping(int nvert, const Table *vert_element, const Array< int > &vert_global_local)
Definition: pmesh.cpp:834
int GetAttribute(int i) const
Return the attribute of element i.
Definition: mesh.hpp:1544
DofTransformation * GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
Definition: pfespace.cpp:1464
void ReduceMeshGen()
Definition: pmesh.cpp:881
void NURBSUniformRefinement() override
Refine NURBS mesh.
Definition: pmesh.cpp:4616
void GetElementJacobian(int i, DenseMatrix &J)
Definition: mesh.cpp:60
void Rebalance()
Definition: pmesh.cpp:4058
void MarkEdge(DenseMatrix &pmat)
Definition: triangle.cpp:53
int AddVertex(double x, double y=0.0, double z=0.0)
Definition: mesh.cpp:1618
void GetGlobalEdgeIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition: pmesh.cpp:6611
void GetBdrElementFace(int i, int *f, int *o) const
Return the index and the orientation of the face of bdr element i. (3D)
Definition: mesh.cpp:6212
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:383
FaceType
Definition: mesh.hpp:45
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
void ParPrint(std::ostream &out) const
Save the mesh in a parallel mesh format.
Definition: pmesh.cpp:6309
virtual void GetVertices(Array< int > &v) const
Returns the indices of the element&#39;s vertices.
Definition: segment.cpp:40
long long GetGlobalNE() const
Return the total (global) number of elements.
Definition: mesh.hpp:968
Array< int > face_nbr_elements_offset
Definition: pmesh.hpp:380
void GetEdgeOrdering(DSTable &v_to_v, Array< int > &order)
Definition: mesh.cpp:2445
MPI_Comm MyComm
Definition: pmesh.hpp:37
void GetElementFaces(int i, Array< int > &faces, Array< int > &ori) const
Return the indices and the orientations of all faces of element i.
Definition: mesh.cpp:6161
GeometryRefiner GlobGeometryRefiner
Definition: geom.cpp:1773
void Finalize(bool refine=false, bool fix_orientation=false) override
Finalize the construction of a general Mesh.
Definition: pmesh.cpp:1516
double f(const Vector &xvec)
Definition: lor_mms.hpp:32
double GetElementSize(ElementTransformation *T, int type=0)
Definition: mesh.cpp:76
Geometry::Type GetGeometryType() const
Definition: element.hpp:52
Symmetric 3D Table stored as an array of rows each of which has a stack of column, floor, number nodes. The number of the node is assigned by counting the nodes from zero as they are pushed into the table. Diagonals of any kind are not allowed so the row, column and floor must all be different for each node. Only one node is stored for all 6 symmetric entries that are indexable by unique triplets of row, column, and floor.
Definition: stable3d.hpp:34
void SetGeometryType(Geometry::Type g)
Method to set the geometry type of the face.
Definition: eltrans.hpp:536
void GetBoundingBox(Vector &p_min, Vector &p_max, int ref=2)
Definition: pmesh.cpp:6159
void GetVertices(Vector &vert_coord) const
Definition: mesh.cpp:7878
const Table & GetDerefinementTable()
Definition: ncmesh.cpp:1937
DenseTensor point_matrices[Geometry::NumGeom]
Definition: ncmesh.hpp:77
Array< Element * > shared_edges
Definition: pmesh.hpp:61
virtual void SetAttributes()
Definition: mesh.cpp:1564
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:756
void Destroy()
Definition: pmesh.cpp:6717
const FiniteElementCollection * FEColl() const
Definition: fespace.hpp:601
IntegrationPointTransformation Loc1
Definition: eltrans.hpp:524
void Clear()
Definition: table.cpp:380
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:154
std::string to_padded_string(int i, int digits)
Convert an integer to a 0-padded string with the given number of digits.
Definition: text.hpp:54
void BuildSharedEdgeElems(int nedges, Mesh &mesh, const Array< int > &vert_global_local, const Table *edge_element)
Definition: pmesh.cpp:797
double b
Definition: lissajous.cpp:42
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
void GetFaceTransformation(int i, IsoparametricTransformation *FTr)
Definition: mesh.cpp:497
int GroupNVertices(int group) const
Definition: pmesh.hpp:395
FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31) override
Definition: pmesh.cpp:3064
static const int NumVerts[NumGeom]
Definition: geom.hpp:49
int GroupVertex(int group, int i) const
Definition: pmesh.hpp:400
void AddConnection(int r, int c)
Definition: table.hpp:80
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition: mesh.cpp:6839
FaceElementTransformations * GetSharedFaceTransformations(int sf, bool fill2=true)
Definition: pmesh.cpp:3080
void LoseData()
NULL-ifies the data.
Definition: array.hpp:135
void Reset()
Force the reevaluation of the Jacobian in the next call.
Definition: eltrans.hpp:89
bool Conforming() const
Definition: mesh.hpp:1728
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition: mesh.cpp:6029
Table send_face_nbr_vertices
Definition: pmesh.hpp:386
Type
Constants for the classes derived from Element.
Definition: element.hpp:41
int GetNGroups() const
Definition: pmesh.hpp:392
This structure stores the low level information necessary to interpret the configuration of elements ...
Definition: mesh.hpp:153
const double * GetVertex(int i) const
Return pointer to vertex i&#39;s coordinates.
Definition: mesh.hpp:1053
VTKFormat
Data array format for VTK and VTU files.
Definition: vtk.hpp:96
virtual const char * Name() const
Definition: fe_coll.hpp:73
void GetGlobalVertexIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition: pmesh.cpp:6595
int Insert(IntegerSet &s)
Check to see if set &#39;s&#39; is in the list. If not append it to the end of the list. Returns the index of...
Definition: sets.cpp:91
virtual int DofForGeometry(Geometry::Type GeomType) const
Definition: fe_coll.hpp:268
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition: array.hpp:159
void PrintVTU(std::string pathname, VTKFormat format=VTKFormat::ASCII, bool high_order_output=false, int compression_level=0, bool bdr=false) override
Definition: pmesh.cpp:6392
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition: mesh.hpp:1148
int GetNFaceNeighbors() const
Definition: pmesh.hpp:462
int AddElement(Element *elem)
The parameter elem should be allocated using the NewElement() method.
Definition: mesh.cpp:1821
static int create_directory(const std::string &dir_name, const Mesh *mesh, int myid)
Array< Embedding > embeddings
Fine element positions in their parents.
Definition: ncmesh.hpp:73
const int * GetGroup(int g) const
Return a pointer to a list of neighbors for a given group. Neighbor 0 is the local processor...
int GetElementToEdgeTable(Table &, Array< int > &)
Definition: mesh.cpp:6397
virtual MFEM_DEPRECATED int GetNFaces(int &nFaceVertices) const =0
void MarkCoarseLevel()
Definition: ncmesh.cpp:4516
void ResetLazyData()
Definition: mesh.cpp:1553
IntegrationRule RefPts
Definition: geom.hpp:314
void GetSharedEdgeCommunicator(int ordering, GroupCommunicator &sedge_comm) const
Get the shared edges GroupCommunicator.
Definition: pmesh.cpp:1637
int GetNFbyType(FaceType type) const override
Returns the number of local faces according to the requested type, does not count master non-conformi...
Definition: pmesh.cpp:3237
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
Definition: geom.cpp:1099
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:896
IsoparametricTransformation Transformation2
Definition: mesh.hpp:234
Table * face_nbr_el_to_face
Definition: pmesh.hpp:80
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
Definition: pfespace.cpp:607
virtual void SetNodalFESpace(FiniteElementSpace *nfes)
Definition: mesh.cpp:5298
FiniteElementCollection * OwnFEC()
Definition: gridfunc.hpp:124
long long ReduceInt(int value) const override
Utility function: sum integers from all processors (Allreduce).
Definition: pmesh.cpp:6302
FiniteElementSpace * FESpace()
Definition: gridfunc.hpp:683
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition: table.cpp:187
int NumOfBdrElements
Definition: mesh.hpp:69
Table * el_to_edge
Definition: mesh.hpp:220
int GetSharedFace(int sface) const
Return the local face index for the given shared face.
Definition: pmesh.cpp:3215
int Size()
Return the number of integer sets in the list.
Definition: sets.hpp:70
STable3D * GetFaceNbrElementToFaceTable(int ret_ftbl=0)
Definition: pmesh.cpp:2724
void GetGlobalElementIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are supported.
Definition: pmesh.cpp:6662
MPI_Comm GetComm() const
Definition: pmesh.hpp:351
Array< Vert4 > shared_quads
Definition: pmesh.hpp:66
void Rebalance(const Array< int > *custom_partition=NULL)
Definition: pncmesh.cpp:1683
int GroupNTriangles(int group) const
Definition: pmesh.hpp:397
int slaves_end
slave faces
Definition: ncmesh.hpp:205
Data type tetrahedron element.
Definition: tetrahedron.hpp:22
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:208
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition: fe_coll.hpp:373
void PrintXG(std::ostream &out=mfem::out) const override
Definition: pmesh.cpp:4624
Point transformation for side 1 is configured.
Definition: eltrans.hpp:515
int GetVDim() const
Returns vector dimension.
Definition: fespace.hpp:581
virtual DofTransformation * GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: pfespace.cpp:471
void PrintVTU(std::ostream &os, int ref=1, VTKFormat format=VTKFormat::ASCII, bool high_order_output=false, int compression_level=0, bool bdr_elements=false)
Definition: mesh.cpp:10565
Vector & FaceNbrData()
Definition: pgridfunc.hpp:208
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: fespace.cpp:281
int CheckBdrElementOrientation(bool fix_it=true)
Check the orientation of the boundary elements.
Definition: mesh.cpp:5810
Table face_nbr_el_ori
Definition: pmesh.hpp:81
int GetMyRank() const
Definition: pmesh.hpp:353
virtual void GetElementDofValues(int el, Vector &dof_vals) const
Definition: gridfunc.cpp:1841
virtual int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const
Definition: pfespace.cpp:519
void Swap(Array< T > &, Array< T > &)
Definition: array.hpp:635
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition: mesh.hpp:275
const Table & ElementToEdgeTable() const
Definition: mesh.cpp:6481
void FindSharedFaces(const Mesh &mesh, const int *partition, Array< int > &face_group, ListOfIntegerSets &groups)
Definition: pmesh.cpp:504
void SetConfigurationMask(int m)
Set the mask indicating which portions of the object have been setup.
Definition: eltrans.hpp:507
void LocalRefinement(const Array< int > &marked_el, int type=3) override
This function is not public anymore. Use GeneralRefinement instead.
Definition: pmesh.cpp:3444
int GetNSharedFaces() const
Return the number of shared faces (3D), edges (2D), vertices (1D)
Definition: pmesh.cpp:3196
void RefineGroups(const DSTable &v_to_v, int *middle)
Update the groups after triangle refinement.
Definition: pmesh.cpp:4106
void Finalize()
Definition: table.cpp:242
Array< Vertex > face_nbr_vertices
Definition: pmesh.hpp:383
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:96
static void PrintElement(const Element *, std::ostream &)
Definition: mesh.cpp:3920
double CheckConsistency(int print_level=0, std::ostream &out=mfem::out)
Check for self-consistency: compares the result of mapping the reference face vertices to physical co...
Definition: eltrans.cpp:646
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition: fe_coll.hpp:26
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
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
Definition: pncmesh.hpp:184
void Swap(ParMesh &other)
Definition: pmesh.cpp:6676
ParFiniteElementSpace * ParFESpace() const
Definition: pgridfunc.hpp:113
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition: mesh.cpp:2902
void AddAColumnInRow(int r)
Definition: table.hpp:77
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition: mesh.hpp:1610
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:684
void SaveAsOne(const char *fname, int precision=16) const
Definition: pmesh.cpp:5618
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition: mesh.cpp:2493
HYPRE_Int HYPRE_BigInt
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition: table.cpp:413
long long glob_elem_offset
Definition: pmesh.hpp:86
void Transform(const IntegrationPoint &, IntegrationPoint &)
Definition: eltrans.cpp:546
int GetEdgeDofs(int edge, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
Definition: fespace.cpp:2987
Array< Vertex > vertices
Definition: mesh.hpp:90
int Push4(int r, int c, int f, int t)
Check to see if this entry is in the table and add it to the table if it is not there. The entry is addressed by the three smallest values of (r,c,f,t). Returns the number assigned to the table entry.
Definition: stable3d.cpp:140
Table * GetFaceToAllElementTable() const
Definition: pmesh.cpp:2974
int Index(int r, int c, int f) const
Definition: stable3d.cpp:117
void DeleteLast()
Delete the last entry of the array.
Definition: array.hpp:196
void PrintAsOne(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:4983
bool WantSkipSharedMaster(const NCMesh::Master &master) const
Definition: pmesh.cpp:4826
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition: fe_base.hpp:322
ParMesh()
Default constructor. Create an empty ParMesh.
Definition: pmesh.hpp:290
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
Array< Element * > elements
Definition: mesh.hpp:85
void SetAttributes() override
Definition: pmesh.cpp:1580
void ShiftUpI()
Definition: table.cpp:115
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
Definition: pmesh.cpp:1933
int SpaceDimension() const
Definition: mesh.hpp:1048
int meshgen
Definition: mesh.hpp:77
static const int FaceVert[NumFaces][MaxFaceVert]
Definition: geom.hpp:252
void BuildVertexGroup(int ngroups, const Table &vert_element)
Definition: pmesh.cpp:694
static FiniteElementCollection * New(const char *name)
Factory method: return a newly allocated FiniteElementCollection according to the given name...
Definition: fe_coll.cpp:116
void GetFaceNbrElementTransformation(int i, IsoparametricTransformation *ElTr)
Definition: pmesh.cpp:1976
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:936
virtual int GetTrueVSize() const
Return the number of local vector true dofs.
Definition: pfespace.hpp:289
double a
Definition: lissajous.cpp:41
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:277
int GetLocalElementNum(long long global_element_num) const
Definition: pmesh.cpp:1530
Array< int > be_to_edge
Definition: mesh.hpp:223
Face transformation is configured.
Definition: eltrans.hpp:517
void GetFaceNbrElementFaces(int i, Array< int > &fcs, Array< int > &cor) const
Definition: pmesh.cpp:2944
void DistributeAttributes(Array< int > &attr)
Ensure that bdr_attributes and attributes agree across processors.
Definition: pmesh.cpp:1544
int GetNRanks() const
Definition: pmesh.hpp:352
virtual void Refine(const Array< Refinement > &refinements)
Definition: pncmesh.cpp:1246
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition: mesh.cpp:6291
void Swap(Mesh &other, bool non_geometry)
Definition: mesh.cpp:9346
void BuildEdgeGroup(int ngroups, const Table &edge_element)
Definition: pmesh.cpp:668
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:1862
A standard isoparametric element transformation.
Definition: eltrans.hpp:361
int GetGroupSize(int g) const
Get the number of processors in a group.
void GetElementTransformation(int i, IsoparametricTransformation *ElTr)
Definition: mesh.cpp:348
Table group_sedge
Definition: pmesh.hpp:70
virtual int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type, does not count master nonconforming face...
Definition: mesh.cpp:5405
int Size() const
Returns the number of TYPE I elements.
Definition: table.hpp:92
void ExchangeFaceNbrNodes()
Definition: pmesh.cpp:2601
Ordering::Type GetOrdering() const
Return the ordering method.
Definition: fespace.hpp:599
const Element * GetFace(int i) const
Definition: mesh.hpp:1089
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition: mesh.cpp:2995
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:699
static const int Orient[NumOrient][NumVert]
Definition: geom.hpp:209
Array< Master > masters
Definition: ncmesh.hpp:230
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:350
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:207
const char * VTKByteOrder()
Determine the byte order and return either "BigEndian" or "LittleEndian".
Definition: vtk.cpp:602
void MakeJ()
Definition: table.cpp:91
Table group_svert
Shared objects in each group.
Definition: pmesh.hpp:69
void PrintAsSerial(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:5292
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition: pmesh.cpp:2056
bool DecodeFaceSplittings(HashTable< Hashed2 > &v_to_v, const int *v, const Array< unsigned > &codes, int &pos)
Definition: pmesh.cpp:1897
Array< MeshId > conforming
Definition: ncmesh.hpp:229
Element::Type GetFaceElementType(int Face) const
Definition: mesh.cpp:1432
void SetFE(const FiniteElement *FE)
Set the element that will be used to compute the transformations.
Definition: eltrans.hpp:381
double GetFaceNbrElementSize(int i, int type=0)
Definition: pmesh.cpp:2030
long sequence
Definition: mesh.hpp:83
IsoparametricTransformation Transf
Definition: eltrans.hpp:464
int index(int i, int j, int nx, int ny)
Definition: life.cpp:235
int NumberOfEntries() const
Definition: table.hpp:266
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition: array.hpp:861
ElementTransformation * Elem1
Definition: eltrans.hpp:522
void MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
Definition: mesh.cpp:4582
long long GetGlobalElementNum(int local_element_num) const
Map a local element number to a global element number.
Definition: pmesh.cpp:1538
Array< FaceInfo > faces_info
Definition: mesh.hpp:217
virtual int GetNVertices() const =0
void SetAttribute(const int attr)
Set element&#39;s attribute.
Definition: element.hpp:58
STable3D * GetFacesTable()
Definition: mesh.cpp:6776
int Size_of_connections() const
Definition: table.hpp:98
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
Definition: pfespace.cpp:584
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition: mesh.hpp:278
void GetVectorValues(int i, const IntegrationRule &ir, DenseMatrix &vals, DenseMatrix &tr) const
Definition: gridfunc.cpp:717
int AddBdrElement(Element *elem)
Definition: mesh.cpp:1828
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
T & Last()
Return the last element in the array.
Definition: array.hpp:789
void GetGlobalFaceIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition: pmesh.cpp:6634
int Dim
Definition: mesh.hpp:66
virtual void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level)
Definition: pncmesh.cpp:1625
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:1847
const int * GetDofMap(Geometry::Type GeomType) const
Get the Cartesian to local H1 dof map.
Definition: fe_coll.cpp:2007
double AggregateError(const Array< double > &elem_error, const int *fine, int nfine, int op)
Derefinement helper.
Definition: mesh.cpp:9205
void GetMarkedFace(const int face, int *fv)
int InitialPartition(int index) const
Helper to get the partitioning when the serial mesh gets split initially.
Definition: pncmesh.hpp:309
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:11962
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition: fe_coll.hpp:447
int GetFaceNbrRank(int fn) const
Definition: pmesh.cpp:2926
Vector data type.
Definition: vector.hpp:60
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition: mesh.hpp:945
virtual void Print(std::ostream &os=mfem::out) const
Definition: mesh.hpp:1749
virtual void Transform(const IntegrationPoint &, Vector &)=0
Transform integration point from reference coordinates to physical coordinates and store them in the ...
int GroupNEdges(int group) const
Definition: pmesh.hpp:396
const DenseMatrix & GetPointMat() const
Return the stored point matrix.
Definition: eltrans.hpp:404
static ParMesh MakeSimplicial(ParMesh &orig_mesh)
Definition: pmesh.cpp:1374
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition: mesh.cpp:5581
void SetNodalFESpace(FiniteElementSpace *nfes) override
Definition: pmesh.cpp:2076
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:608
Arbitrary order H1-conforming (continuous) finite elements.
Definition: fe_coll.hpp:252
Array< int > svert_lvert
Shared to local index mapping.
Definition: pmesh.hpp:75
void GenerateFaces()
Definition: mesh.cpp:6614
const NCList & GetSharedFaces()
Definition: pncmesh.hpp:125
int GroupNQuadrilaterals(int group) const
Definition: pmesh.hpp:398
void GetNodes(Vector &node_coord) const
Definition: mesh.cpp:7949
int MyRank
Definition: pmesh.hpp:38
int spaceDim
Definition: mesh.hpp:67
RefCoord s[3]
void Printer(std::ostream &out=mfem::out, std::string section_delimiter="") const
Definition: mesh.cpp:10217
void Print(std::ostream &out=mfem::out) const override
Definition: pmesh.cpp:4839
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition: ncmesh.hpp:260
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition: mesh.cpp:2559
const Element * GetBdrElement(int i) const
Definition: mesh.hpp:1085
Class for parallel grid function.
Definition: pgridfunc.hpp:32
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition: mesh.cpp:5345
void GroupQuadrilateral(int group, int i, int &face, int &o) const
Definition: pmesh.cpp:1626
const FiniteElement * GetFE() const
Get the current element used to compute the transformations.
Definition: eltrans.hpp:389
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition: densemat.hpp:105
Table send_face_nbr_elements
Definition: pmesh.hpp:385
List of integer sets.
Definition: sets.hpp:62
int NumOfVertices
Definition: mesh.hpp:69
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Definition: gridfunc.cpp:3673
void FinalizeParTopo()
Definition: pmesh.cpp:887
void DeleteFaceNbrData()
Definition: pmesh.cpp:2035
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition: mesh.cpp:9302
GroupTopology gtopo
Definition: pmesh.hpp:375
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition: mesh.cpp:9633
Mesh GetSerialMesh(int save_rank) const
Definition: pmesh.cpp:5303
void Load(std::istream &input, int generate_edges=0, int refine=1, bool fix_orientation=true) override
Parallel version of Mesh::Load().
Definition: pmesh.cpp:942
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition: fe_base.hpp:388
void GroupTriangle(int group, int i, int &face, int &o) const
Definition: pmesh.cpp:1615
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
Definition: pfespace.cpp:1083
Class for parallel meshes.
Definition: pmesh.hpp:32
Abstract data type element.
Definition: element.hpp:28
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:9841
void ComputeGlobalElementOffset() const
Definition: pmesh.cpp:868
Table group_squad
Definition: pmesh.hpp:72
void GenerateNCFaceInfo()
Definition: mesh.cpp:6715
Array< int > RefGeoms
Definition: geom.hpp:315
void Save(const char *fname, int precision=16) const override
Definition: pmesh.cpp:4955
virtual Type GetType() const =0
Returns element&#39;s type.
double GetLength(int i, int j) const
Return the length of the segment from node i to node j.
Definition: mesh.cpp:6335
virtual void LimitNCLevel(int max_nc_level)
Parallel version of NCMesh::LimitNCLevel.
Definition: pncmesh.cpp:1324
virtual DofTransformation * GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i&#39;th boundary element.
Definition: pfespace.cpp:495
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition: mesh.hpp:273
class Mesh * mesh
The Mesh object containing the element.
Definition: eltrans.hpp:84
Table * GetVertexToElementTable()
The returned Table must be destroyed by the caller.
Definition: mesh.cpp:6092
static const int Orient[NumOrient][NumVert]
Definition: geom.hpp:184
void Load(std::istream &in)
Load the data from a stream.
void UpdateNodes()
Update the nodes of a curved mesh after the topological part of a Mesh::Operation, such as refinement, has been performed.
Definition: mesh.cpp:8032
Defines the position of a fine element within a coarse element.
Definition: ncmesh.hpp:49
bool IAmMaster(int g) const
Return true if I am master for group &#39;g&#39;.
MFEM_DEPRECATED void ReorientTetMesh() override
See the remarks for the serial version in mesh.hpp.
Definition: pmesh.cpp:3269
void GreenRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition: mesh.hpp:365
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition: fe_coll.hpp:320
static const int FaceVert[NumFaces][MaxFaceVert]
Definition: geom.hpp:296
int NumOfFaces
Definition: mesh.hpp:70