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