MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
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 auto parent_elements = 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 if (orig_mesh.GetNodes() != nullptr)
1521 {
1522 mesh.MakeHigherOrderSimplicial_(orig_mesh, parent_elements);
1523 }
1524
1525 return mesh;
1526}
1527
1528void ParMesh::Finalize(bool refine, bool fix_orientation)
1529{
1530 const int meshgen_save = meshgen; // Mesh::Finalize() may call SetMeshGen()
1531 // 'mesh_geoms' is local, so there's no need to save and restore it.
1532
1533 Mesh::Finalize(refine, fix_orientation);
1534
1535 meshgen = meshgen_save;
1536 // Note: if Mesh::Finalize() calls MarkTetMeshForRefinement() then the
1537 // shared_trias have been rotated as necessary.
1538
1539 // Setup secondary parallel mesh data: sedge_ledge, sface_lface
1541}
1542
1543int ParMesh::GetLocalElementNum(long long global_element_num) const
1544{
1546 long long local = global_element_num - glob_elem_offset;
1547 if (local < 0 || local >= NumOfElements) { return -1; }
1548 return local;
1549}
1550
1551long long ParMesh::GetGlobalElementNum(int local_element_num) const
1552{
1554 return glob_elem_offset + local_element_num;
1555}
1556
1558{
1559 // Determine the largest attribute number across all processors
1560 int max_attr = attr.Size() ? attr.Max() : 1 /*allow empty ranks*/;
1561 int glb_max_attr = -1;
1562 MPI_Allreduce(&max_attr, &glb_max_attr, 1, MPI_INT, MPI_MAX, MyComm);
1563
1564 // Create marker arrays to indicate which attributes are present
1565 // assuming attribute numbers are in the range [1,glb_max_attr].
1566 bool *attr_marker = new bool[glb_max_attr];
1567 bool *glb_attr_marker = new bool[glb_max_attr];
1568 for (int i = 0; i < glb_max_attr; i++)
1569 {
1570 attr_marker[i] = false;
1571 }
1572 for (int i = 0; i < attr.Size(); i++)
1573 {
1574 attr_marker[attr[i] - 1] = true;
1575 }
1576 MPI_Allreduce(attr_marker, glb_attr_marker, glb_max_attr,
1577 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
1578 delete [] attr_marker;
1579
1580 // Translate from the marker array to a unique, sorted list of attributes
1581 attr.SetSize(0);
1582 attr.Reserve(glb_max_attr);
1583 for (int i = 0; i < glb_max_attr; i++)
1584 {
1585 if (glb_attr_marker[i])
1586 {
1587 attr.Append(i + 1);
1588 }
1589 }
1590 delete [] glb_attr_marker;
1591}
1592
1593void ParMesh::SetAttributes(bool elem_attrs_changed, bool bdr_attrs_changed)
1594{
1595 // Determine the attributes occurring in local interior and boundary elements
1596 Mesh::SetAttributes(elem_attrs_changed, bdr_attrs_changed);
1597
1598 if (bdr_attrs_changed)
1599 {
1601 if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1602 {
1603 MFEM_WARNING("Non-positive boundary element attributes found!");
1604 }
1605 }
1606
1607 if (elem_attrs_changed)
1608 {
1610 if (attributes.Size() > 0 && attributes[0] <= 0)
1611 {
1612 MFEM_WARNING("Non-positive element attributes found!");
1613 }
1614 }
1615}
1616
1618{
1619 // maximum number of boundary elements over all ranks
1620 int maxNumOfBdrElements;
1621 MPI_Allreduce(const_cast<int*>(&NumOfBdrElements), &maxNumOfBdrElements, 1,
1622 MPI_INT, MPI_MAX, MyComm);
1623 return (maxNumOfBdrElements > 0);
1624}
1625
1626void ParMesh::GroupEdge(int group, int i, int &edge, int &o) const
1627{
1628 int sedge = group_sedge.GetRow(group-1)[i];
1629 edge = sedge_ledge[sedge];
1630 int *v = shared_edges[sedge]->GetVertices();
1631 o = (v[0] < v[1]) ? (+1) : (-1);
1632}
1633
1634void ParMesh::GroupTriangle(int group, int i, int &face, int &o) const
1635{
1636 int stria = group_stria.GetRow(group-1)[i];
1637 face = sface_lface[stria];
1638 // face gives the base orientation
1639 MFEM_ASSERT(faces[face]->GetType() == Element::TRIANGLE,
1640 "Expecting a triangular face.");
1641
1642 o = GetTriOrientation(faces[face]->GetVertices(), shared_trias[stria].v);
1643}
1644
1645void ParMesh::GroupQuadrilateral(int group, int i, int &face, int &o) const
1646{
1647 int squad = group_squad.GetRow(group-1)[i];
1648 face = sface_lface[shared_trias.Size()+squad];
1649 // face gives the base orientation
1650 MFEM_ASSERT(faces[face]->GetType() == Element::QUADRILATERAL,
1651 "Expecting a quadrilateral face.");
1652
1653 o = GetQuadOrientation(faces[face]->GetVertices(), shared_quads[squad].v);
1654}
1655
1657 GroupCommunicator& sedge_comm) const
1658{
1659 Table &gr_sedge = sedge_comm.GroupLDofTable();
1660 gr_sedge.SetDims(GetNGroups(), shared_edges.Size());
1661 gr_sedge.GetI()[0] = 0;
1662 for (int gr = 1; gr <= GetNGroups(); gr++)
1663 {
1664 gr_sedge.GetI()[gr] = group_sedge.GetI()[gr-1];
1665 }
1666 for (int k = 0; k < shared_edges.Size(); k++)
1667 {
1668 if (ordering == 1)
1669 {
1670 gr_sedge.GetJ()[k] =k;
1671 }
1672 else
1673 {
1674 gr_sedge.GetJ()[k] = group_sedge.GetJ()[k];
1675 }
1676 }
1677 sedge_comm.Finalize();
1678}
1679
1681 GroupCommunicator& svert_comm) const
1682{
1683 Table &gr_svert = svert_comm.GroupLDofTable();
1684 gr_svert.SetDims(GetNGroups(), svert_lvert.Size());
1685 gr_svert.GetI()[0] = 0;
1686 for (int gr = 1; gr <= GetNGroups(); gr++)
1687 {
1688 gr_svert.GetI()[gr] = group_svert.GetI()[gr-1];
1689 }
1690 for (int k = 0; k < svert_lvert.Size(); k++)
1691 {
1692 if (ordering == 1)
1693 {
1694 gr_svert.GetJ()[k] = k;
1695 }
1696 else
1697 {
1698 gr_svert.GetJ()[k] = group_svert.GetJ()[k];
1699 }
1700 }
1701 svert_comm.Finalize();
1702}
1703
1705 GroupCommunicator& squad_comm) const
1706{
1707 Table &gr_squad = squad_comm.GroupLDofTable();
1708 gr_squad.SetDims(GetNGroups(), shared_quads.Size());
1709 gr_squad.GetI()[0] = 0;
1710 for (int gr = 1; gr <= GetNGroups(); gr++)
1711 {
1712 gr_squad.GetI()[gr] = group_squad.GetI()[gr-1];
1713 }
1714 for (int k = 0; k < shared_quads.Size(); k++)
1715 {
1716 if (ordering == 1)
1717 {
1718 gr_squad.GetJ()[k] = k;
1719 }
1720 else
1721 {
1722 gr_squad.GetJ()[k] = group_squad.GetJ()[k];
1723 }
1724 }
1725 squad_comm.Finalize();
1726}
1727
1729 GroupCommunicator& stria_comm) const
1730{
1731 Table &gr_stria = stria_comm.GroupLDofTable();
1732 gr_stria.SetDims(GetNGroups(), shared_trias.Size());
1733 gr_stria.GetI()[0] = 0;
1734 for (int gr = 1; gr <= GetNGroups(); gr++)
1735 {
1736 gr_stria.GetI()[gr] = group_stria.GetI()[gr-1];
1737 }
1738 for (int k = 0; k < shared_trias.Size(); k++)
1739 {
1740 if (ordering == 1)
1741 {
1742 gr_stria.GetJ()[k] = k;
1743 }
1744 else
1745 {
1746 gr_stria.GetJ()[k] = group_stria.GetJ()[k];
1747 }
1748 }
1749 stria_comm.Finalize();
1750}
1751
1753{
1754 Array<int> order;
1755 GetEdgeOrdering(v_to_v, order); // local edge ordering
1756
1757 // create a GroupCommunicator on the shared edges
1758 GroupCommunicator sedge_comm(gtopo);
1759 GetSharedEdgeCommunicator(0, sedge_comm);
1760
1761 Array<int> sedge_ord(shared_edges.Size());
1762 Array<Pair<int,int> > sedge_ord_map(shared_edges.Size());
1763 for (int k = 0; k < shared_edges.Size(); k++)
1764 {
1765 // sedge_ledge may be undefined -- use shared_edges and v_to_v instead
1766 const int sedge = group_sedge.GetJ()[k];
1767 const int *v = shared_edges[sedge]->GetVertices();
1768 sedge_ord[k] = order[v_to_v(v[0], v[1])];
1769 }
1770
1771 sedge_comm.Bcast<int>(sedge_ord, 1);
1772
1773 for (int k = 0, gr = 1; gr < GetNGroups(); gr++)
1774 {
1775 const int n = group_sedge.RowSize(gr-1);
1776 if (n == 0) { continue; }
1777 sedge_ord_map.SetSize(n);
1778 for (int j = 0; j < n; j++)
1779 {
1780 sedge_ord_map[j].one = sedge_ord[k+j];
1781 sedge_ord_map[j].two = j;
1782 }
1783 SortPairs<int, int>(sedge_ord_map, n);
1784 for (int j = 0; j < n; j++)
1785 {
1786 const int sedge_from = group_sedge.GetJ()[k+j];
1787 const int *v = shared_edges[sedge_from]->GetVertices();
1788 sedge_ord[k+j] = order[v_to_v(v[0], v[1])];
1789 }
1790 std::sort(&sedge_ord[k], &sedge_ord[k] + n);
1791 for (int j = 0; j < n; j++)
1792 {
1793 const int sedge_to = group_sedge.GetJ()[k+sedge_ord_map[j].two];
1794 const int *v = shared_edges[sedge_to]->GetVertices();
1795 order[v_to_v(v[0], v[1])] = sedge_ord[k+j];
1796 }
1797 k += n;
1798 }
1799
1800#ifdef MFEM_DEBUG
1801 {
1802 Array<Pair<int, real_t> > ilen_len(order.Size());
1803
1804 for (int i = 0; i < NumOfVertices; i++)
1805 {
1806 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
1807 {
1808 int j = it.Index();
1809 ilen_len[j].one = order[j];
1810 ilen_len[j].two = GetLength(i, it.Column());
1811 }
1812 }
1813
1814 SortPairs<int, real_t>(ilen_len, order.Size());
1815
1816 real_t d_max = 0.;
1817 for (int i = 1; i < order.Size(); i++)
1818 {
1819 d_max = std::max(d_max, ilen_len[i-1].two-ilen_len[i].two);
1820 }
1821
1822#if 0
1823 // Debug message from every MPI rank.
1824 mfem::out << "proc. " << MyRank << '/' << NRanks << ": d_max = " << d_max
1825 << endl;
1826#else
1827 // Debug message just from rank 0.
1828 real_t glob_d_max;
1829 MPI_Reduce(&d_max, &glob_d_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX, 0,
1830 MyComm);
1831 if (MyRank == 0)
1832 {
1833 mfem::out << "glob_d_max = " << glob_d_max << endl;
1834 }
1835#endif
1836 }
1837#endif
1838
1839 // use 'order' to mark the tets, the boundary triangles, and the shared
1840 // triangle faces
1841 for (int i = 0; i < NumOfElements; i++)
1842 {
1843 if (elements[i]->GetType() == Element::TETRAHEDRON)
1844 {
1845 elements[i]->MarkEdge(v_to_v, order);
1846 }
1847 }
1848
1849 for (int i = 0; i < NumOfBdrElements; i++)
1850 {
1851 if (boundary[i]->GetType() == Element::TRIANGLE)
1852 {
1853 boundary[i]->MarkEdge(v_to_v, order);
1854 }
1855 }
1856
1857 for (int i = 0; i < shared_trias.Size(); i++)
1858 {
1859 Triangle::MarkEdge(shared_trias[i].v, v_to_v, order);
1860 }
1861}
1862
1863// For a line segment with vertices v[0] and v[1], return a number with
1864// the following meaning:
1865// 0 - the edge was not refined
1866// 1 - the edge e was refined once by splitting v[0],v[1]
1868 int *middle)
1869{
1870 int m, *v = edge->GetVertices();
1871
1872 if ((m = v_to_v(v[0], v[1])) != -1 && middle[m] != -1)
1873 {
1874 return 1;
1875 }
1876 else
1877 {
1878 return 0;
1879 }
1880}
1881
1882void ParMesh::GetFaceSplittings(const int *fv, const HashTable<Hashed2> &v_to_v,
1883 Array<unsigned> &codes)
1884{
1885 typedef Triple<int,int,int> face_t;
1886 Array<face_t> face_stack;
1887
1888 unsigned code = 0;
1889 face_stack.Append(face_t(fv[0], fv[1], fv[2]));
1890 for (unsigned bit = 0; face_stack.Size() > 0; bit++)
1891 {
1892 if (bit == 8*sizeof(unsigned))
1893 {
1894 codes.Append(code);
1895 code = bit = 0;
1896 }
1897
1898 const face_t &f = face_stack.Last();
1899 int mid = v_to_v.FindId(f.one, f.two);
1900 if (mid == -1)
1901 {
1902 // leave a 0 at bit 'bit'
1903 face_stack.DeleteLast();
1904 }
1905 else
1906 {
1907 code += (1 << bit); // set bit 'bit' to 1
1908 mid += NumOfVertices;
1909 face_stack.Append(face_t(f.three, f.one, mid));
1910 face_t &r = face_stack[face_stack.Size()-2];
1911 r = face_t(r.two, r.three, mid);
1912 }
1913 }
1914 codes.Append(code);
1915}
1916
1918 const Array<unsigned> &codes, int &pos)
1919{
1920 typedef Triple<int,int,int> face_t;
1921 Array<face_t> face_stack;
1922
1923 bool need_refinement = 0;
1924 face_stack.Append(face_t(v[0], v[1], v[2]));
1925 for (unsigned bit = 0, code = codes[pos++]; face_stack.Size() > 0; bit++)
1926 {
1927 if (bit == 8*sizeof(unsigned))
1928 {
1929 code = codes[pos++];
1930 bit = 0;
1931 }
1932
1933 if ((code & (1 << bit)) == 0) { face_stack.DeleteLast(); continue; }
1934
1935 const face_t &f = face_stack.Last();
1936 int mid = v_to_v.FindId(f.one, f.two);
1937 if (mid == -1)
1938 {
1939 mid = v_to_v.GetId(f.one, f.two);
1940 int ind[2] = { f.one, f.two };
1941 vertices.Append(Vertex());
1942 AverageVertices(ind, 2, vertices.Size()-1);
1943 need_refinement = 1;
1944 }
1945 mid += NumOfVertices;
1946 face_stack.Append(face_t(f.three, f.one, mid));
1947 face_t &r = face_stack[face_stack.Size()-2];
1948 r = face_t(r.two, r.three, mid);
1949 }
1950 return need_refinement;
1951}
1952
1954 Array<HYPRE_BigInt> *offsets[]) const
1955{
1956 if (HYPRE_AssumedPartitionCheck())
1957 {
1958 Array<HYPRE_BigInt> temp(N);
1959 MPI_Scan(loc_sizes, temp.GetData(), N, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
1960 for (int i = 0; i < N; i++)
1961 {
1962 offsets[i]->SetSize(3);
1963 (*offsets[i])[0] = temp[i] - loc_sizes[i];
1964 (*offsets[i])[1] = temp[i];
1965 }
1966 MPI_Bcast(temp.GetData(), N, HYPRE_MPI_BIG_INT, NRanks-1, MyComm);
1967 for (int i = 0; i < N; i++)
1968 {
1969 (*offsets[i])[2] = temp[i];
1970 // check for overflow
1971 MFEM_VERIFY((*offsets[i])[0] >= 0 && (*offsets[i])[1] >= 0,
1972 "overflow in offsets");
1973 }
1974 }
1975 else
1976 {
1978 MPI_Allgather(loc_sizes, N, HYPRE_MPI_BIG_INT, temp.GetData(), N,
1979 HYPRE_MPI_BIG_INT, MyComm);
1980 for (int i = 0; i < N; i++)
1981 {
1982 Array<HYPRE_BigInt> &offs = *offsets[i];
1983 offs.SetSize(NRanks+1);
1984 offs[0] = 0;
1985 for (int j = 0; j < NRanks; j++)
1986 {
1987 offs[j+1] = offs[j] + temp[i+N*j];
1988 }
1989 // Check for overflow
1990 MFEM_VERIFY(offs[MyRank] >= 0 && offs[MyRank+1] >= 0,
1991 "overflow in offsets");
1992 }
1993 }
1994}
1995
1997{
1998 if (!have_face_nbr_data)
1999 {
2000 return;
2001 }
2002
2003 have_face_nbr_data = false;
2007 for (int i = 0; i < face_nbr_elements.Size(); i++)
2008 {
2010 }
2011 face_nbr_elements.DeleteAll();
2012 face_nbr_vertices.DeleteAll();
2015}
2016
2017void ParMesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
2018{
2020 space_dim = (space_dim == -1) ? spaceDim : space_dim;
2022 if (discont)
2023 {
2024 nfec = new L2_FECollection(order, Dim, BasisType::GaussLobatto);
2025 }
2026 else
2027 {
2028 nfec = new H1_FECollection(order, Dim);
2029 }
2030 ParFiniteElementSpace* nfes = new ParFiniteElementSpace(this, nfec, space_dim,
2031 ordering);
2032 auto pnodes = new ParGridFunction(nfes);
2033 GetNodes(*pnodes);
2034 NewNodes(*pnodes, true);
2035 Nodes->MakeOwner(nfec);
2036}
2037
2039{
2041 ParFiniteElementSpace *npfes = dynamic_cast<ParFiniteElementSpace*>(nfes);
2042 if (npfes)
2043 {
2044 SetNodalFESpace(npfes);
2045 }
2046 else
2047 {
2049 }
2050}
2051
2058
2060{
2061 if (Nodes && dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace()) == NULL)
2062 {
2064 ParFiniteElementSpace *pfes =
2065 new ParFiniteElementSpace(*Nodes->FESpace(), *this);
2066 ParGridFunction *new_nodes = new ParGridFunction(pfes);
2067 *new_nodes = *Nodes;
2068 if (Nodes->OwnFEC())
2069 {
2070 new_nodes->MakeOwner(Nodes->OwnFEC());
2071 Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
2072 delete Nodes->FESpace();
2073 }
2074 delete Nodes;
2075 Nodes = new_nodes;
2076 }
2077}
2078
2080{
2082 {
2083 return;
2084 }
2085
2086 if (Nonconforming())
2087 {
2088 // With ParNCMesh we can set up face neighbors mostly without communication.
2089 pncmesh->GetFaceNeighbors(*this);
2090 have_face_nbr_data = true;
2091
2093 return;
2094 }
2095
2096 Table *gr_sface;
2097 int *s2l_face;
2098 bool del_tables = false;
2099 if (Dim == 1)
2100 {
2101 gr_sface = &group_svert;
2102 s2l_face = svert_lvert;
2103 }
2104 else if (Dim == 2)
2105 {
2106 gr_sface = &group_sedge;
2107 s2l_face = sedge_ledge;
2108 }
2109 else
2110 {
2111 s2l_face = sface_lface;
2112 if (shared_trias.Size() == sface_lface.Size())
2113 {
2114 // All shared faces are Triangular
2115 gr_sface = &group_stria;
2116 }
2117 else if (shared_quads.Size() == sface_lface.Size())
2118 {
2119 // All shared faced are Quadrilateral
2120 gr_sface = &group_squad;
2121 }
2122 else
2123 {
2124 // Shared faces contain a mixture of triangles and quads
2125 gr_sface = new Table;
2126 del_tables = true;
2127
2128 // Merge the Tables group_stria and group_squad
2129 gr_sface->MakeI(group_stria.Size());
2130 for (int gr=0; gr<group_stria.Size(); gr++)
2131 {
2132 gr_sface->AddColumnsInRow(gr,
2133 group_stria.RowSize(gr) +
2134 group_squad.RowSize(gr));
2135 }
2136 gr_sface->MakeJ();
2137 const int nst = shared_trias.Size();
2138 for (int gr=0; gr<group_stria.Size(); gr++)
2139 {
2140 gr_sface->AddConnections(gr,
2141 group_stria.GetRow(gr),
2142 group_stria.RowSize(gr));
2143 for (int c=0; c<group_squad.RowSize(gr); c++)
2144 {
2145 gr_sface->AddConnection(gr,
2146 nst + group_squad.GetRow(gr)[c]);
2147 }
2148 }
2149 gr_sface->ShiftUpI();
2150 }
2151 }
2152
2153 ExchangeFaceNbrData(gr_sface, s2l_face);
2154
2155 if (Dim == 3)
2156 {
2158 }
2159
2160 if (del_tables) { delete gr_sface; }
2161
2162 if ( have_face_nbr_data ) { return; }
2163
2164 have_face_nbr_data = true;
2165
2167}
2168
2169void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
2170{
2171 int num_face_nbrs = 0;
2172 for (int g = 1; g < GetNGroups(); g++)
2173 {
2174 if (gr_sface->RowSize(g-1) > 0)
2175 {
2176 num_face_nbrs++;
2177 }
2178 }
2179
2180 face_nbr_group.SetSize(num_face_nbrs);
2181
2182 if (num_face_nbrs == 0)
2183 {
2184 have_face_nbr_data = true;
2185 return;
2186 }
2187
2188 {
2189 // sort face-neighbors by processor rank
2190 Array<Pair<int, int> > rank_group(num_face_nbrs);
2191
2192 for (int g = 1, counter = 0; g < GetNGroups(); g++)
2193 {
2194 if (gr_sface->RowSize(g-1) > 0)
2195 {
2196 MFEM_ASSERT(gtopo.GetGroupSize(g) == 2, "group size is not 2!");
2197
2198 const int *nbs = gtopo.GetGroup(g);
2199 int lproc = (nbs[0]) ? nbs[0] : nbs[1];
2200 rank_group[counter].one = gtopo.GetNeighborRank(lproc);
2201 rank_group[counter].two = g;
2202 counter++;
2203 }
2204 }
2205
2206 SortPairs<int, int>(rank_group, rank_group.Size());
2207
2208 for (int fn = 0; fn < num_face_nbrs; fn++)
2209 {
2210 face_nbr_group[fn] = rank_group[fn].two;
2211 }
2212 }
2213
2214 MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2215 MPI_Request *send_requests = requests;
2216 MPI_Request *recv_requests = requests + num_face_nbrs;
2217 MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2218
2219 int *nbr_data = new int[6*num_face_nbrs];
2220 int *nbr_send_data = nbr_data;
2221 int *nbr_recv_data = nbr_data + 3*num_face_nbrs;
2222
2223 Array<int> el_marker(GetNE());
2224 Array<int> vertex_marker(GetNV());
2225 el_marker = -1;
2226 vertex_marker = -1;
2227
2228 Array<int> fcs, cor;
2229
2230 Table send_face_nbr_elemdata, send_face_nbr_facedata;
2231
2232 send_face_nbr_elements.MakeI(num_face_nbrs);
2233 send_face_nbr_vertices.MakeI(num_face_nbrs);
2234 send_face_nbr_elemdata.MakeI(num_face_nbrs);
2235 send_face_nbr_facedata.MakeI(num_face_nbrs);
2236 for (int fn = 0; fn < num_face_nbrs; fn++)
2237 {
2238 int nbr_group = face_nbr_group[fn];
2239 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2240 int *sface = gr_sface->GetRow(nbr_group-1);
2241 for (int i = 0; i < num_sfaces; i++)
2242 {
2243 int lface = s2l_face[sface[i]];
2244 int el = faces_info[lface].Elem1No;
2245 if (el_marker[el] != fn)
2246 {
2247 el_marker[el] = fn;
2249
2250 const int nv = elements[el]->GetNVertices();
2251 const int *v = elements[el]->GetVertices();
2252 for (int j = 0; j < nv; j++)
2253 if (vertex_marker[v[j]] != fn)
2254 {
2255 vertex_marker[v[j]] = fn;
2257 }
2258
2259 const int nf = elements[el]->GetNFaces();
2260
2261 send_face_nbr_elemdata.AddColumnsInRow(fn, nv + nf + 2);
2262 }
2263 }
2264 send_face_nbr_facedata.AddColumnsInRow(fn, 2*num_sfaces);
2265
2266 nbr_send_data[3*fn ] = send_face_nbr_elements.GetI()[fn];
2267 nbr_send_data[3*fn+1] = send_face_nbr_vertices.GetI()[fn];
2268 nbr_send_data[3*fn+2] = send_face_nbr_elemdata.GetI()[fn];
2269
2270 int nbr_rank = GetFaceNbrRank(fn);
2271 int tag = 0;
2272
2273 MPI_Isend(&nbr_send_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2274 &send_requests[fn]);
2275 MPI_Irecv(&nbr_recv_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2276 &recv_requests[fn]);
2277 }
2280 send_face_nbr_elemdata.MakeJ();
2281 send_face_nbr_facedata.MakeJ();
2282 el_marker = -1;
2283 vertex_marker = -1;
2284 const int nst = shared_trias.Size();
2285 for (int fn = 0; fn < num_face_nbrs; fn++)
2286 {
2287 int nbr_group = face_nbr_group[fn];
2288 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2289 int *sface = gr_sface->GetRow(nbr_group-1);
2290 for (int i = 0; i < num_sfaces; i++)
2291 {
2292 const int sf = sface[i];
2293 int lface = s2l_face[sf];
2294 int el = faces_info[lface].Elem1No;
2295 if (el_marker[el] != fn)
2296 {
2297 el_marker[el] = fn;
2299
2300 const int nv = elements[el]->GetNVertices();
2301 const int *v = elements[el]->GetVertices();
2302 for (int j = 0; j < nv; j++)
2303 if (vertex_marker[v[j]] != fn)
2304 {
2305 vertex_marker[v[j]] = fn;
2307 }
2308
2309 send_face_nbr_elemdata.AddConnection(fn, GetAttribute(el));
2310 send_face_nbr_elemdata.AddConnection(
2311 fn, GetElementBaseGeometry(el));
2312 send_face_nbr_elemdata.AddConnections(fn, v, nv);
2313
2314 if (Dim == 3)
2315 {
2316 const int nf = elements[el]->GetNFaces();
2317 GetElementFaces(el, fcs, cor);
2318 send_face_nbr_elemdata.AddConnections(fn, cor, nf);
2319 }
2320 }
2321 send_face_nbr_facedata.AddConnection(fn, el);
2322 int info = faces_info[lface].Elem1Inf;
2323 // change the orientation in info to be relative to the shared face
2324 // in 1D and 2D keep the orientation equal to 0
2325 if (Dim == 3)
2326 {
2327 const int *lf_v = faces[lface]->GetVertices();
2328 if (sf < nst) // triangle shared face
2329 {
2330 info += GetTriOrientation(shared_trias[sf].v, lf_v);
2331 }
2332 else // quad shared face
2333 {
2334 info += GetQuadOrientation(shared_quads[sf-nst].v, lf_v);
2335 }
2336 }
2337 send_face_nbr_facedata.AddConnection(fn, info);
2338 }
2339 }
2342 send_face_nbr_elemdata.ShiftUpI();
2343 send_face_nbr_facedata.ShiftUpI();
2344
2345 // convert the vertex indices in send_face_nbr_elemdata
2346 // convert the element indices in send_face_nbr_facedata
2347 for (int fn = 0; fn < num_face_nbrs; fn++)
2348 {
2349 int num_elems = send_face_nbr_elements.RowSize(fn);
2350 int *elems = send_face_nbr_elements.GetRow(fn);
2351 int num_verts = send_face_nbr_vertices.RowSize(fn);
2352 int *verts = send_face_nbr_vertices.GetRow(fn);
2353 int *elemdata = send_face_nbr_elemdata.GetRow(fn);
2354 int num_sfaces = send_face_nbr_facedata.RowSize(fn)/2;
2355 int *facedata = send_face_nbr_facedata.GetRow(fn);
2356
2357 for (int i = 0; i < num_verts; i++)
2358 {
2359 vertex_marker[verts[i]] = i;
2360 }
2361
2362 for (int el = 0; el < num_elems; el++)
2363 {
2364 const int nv = elements[elems[el]]->GetNVertices();
2365 const int nf = (Dim == 3) ? elements[elems[el]]->GetNFaces() : 0;
2366 elemdata += 2; // skip the attribute and the geometry type
2367 for (int j = 0; j < nv; j++)
2368 {
2369 elemdata[j] = vertex_marker[elemdata[j]];
2370 }
2371 elemdata += nv + nf;
2372
2373 el_marker[elems[el]] = el;
2374 }
2375
2376 for (int i = 0; i < num_sfaces; i++)
2377 {
2378 facedata[2*i] = el_marker[facedata[2*i]];
2379 }
2380 }
2381
2382 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2383
2384 Array<int> recv_face_nbr_facedata;
2385 Table recv_face_nbr_elemdata;
2386
2387 // fill-in face_nbr_elements_offset, face_nbr_vertices_offset
2388 face_nbr_elements_offset.SetSize(num_face_nbrs + 1);
2389 face_nbr_vertices_offset.SetSize(num_face_nbrs + 1);
2390 recv_face_nbr_elemdata.MakeI(num_face_nbrs);
2393 for (int fn = 0; fn < num_face_nbrs; fn++)
2394 {
2396 face_nbr_elements_offset[fn] + nbr_recv_data[3*fn];
2398 face_nbr_vertices_offset[fn] + nbr_recv_data[3*fn+1];
2399 recv_face_nbr_elemdata.AddColumnsInRow(fn, nbr_recv_data[3*fn+2]);
2400 }
2401 recv_face_nbr_elemdata.MakeJ();
2402
2403 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2404
2405 // send and receive the element data
2406 for (int fn = 0; fn < num_face_nbrs; fn++)
2407 {
2408 int nbr_rank = GetFaceNbrRank(fn);
2409 int tag = 0;
2410
2411 MPI_Isend(send_face_nbr_elemdata.GetRow(fn),
2412 send_face_nbr_elemdata.RowSize(fn),
2413 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2414
2415 MPI_Irecv(recv_face_nbr_elemdata.GetRow(fn),
2416 recv_face_nbr_elemdata.RowSize(fn),
2417 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2418 }
2419
2420 // convert the element data into face_nbr_elements
2421 face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
2422 face_nbr_el_ori.reset(new Table(face_nbr_elements_offset[num_face_nbrs], 6));
2423 while (true)
2424 {
2425 int fn;
2426 MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2427
2428 if (fn == MPI_UNDEFINED)
2429 {
2430 break;
2431 }
2432
2433 int vert_off = face_nbr_vertices_offset[fn];
2434 int elem_off = face_nbr_elements_offset[fn];
2435 int num_elems = face_nbr_elements_offset[fn+1] - elem_off;
2436 int *recv_elemdata = recv_face_nbr_elemdata.GetRow(fn);
2437
2438 for (int i = 0; i < num_elems; i++)
2439 {
2440 Element *el = NewElement(recv_elemdata[1]);
2441 el->SetAttribute(recv_elemdata[0]);
2442 recv_elemdata += 2;
2443 int nv = el->GetNVertices();
2444 for (int j = 0; j < nv; j++)
2445 {
2446 recv_elemdata[j] += vert_off;
2447 }
2448 el->SetVertices(recv_elemdata);
2449 recv_elemdata += nv;
2450 if (Dim == 3)
2451 {
2452 int nf = el->GetNFaces();
2453 int * fn_ori = face_nbr_el_ori->GetRow(elem_off);
2454 for (int j = 0; j < nf; j++)
2455 {
2456 fn_ori[j] = recv_elemdata[j];
2457 }
2458 recv_elemdata += nf;
2459 }
2460 face_nbr_elements[elem_off++] = el;
2461 }
2462 }
2463 face_nbr_el_ori->Finalize();
2464
2465 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2466
2467 // send and receive the face data
2468 recv_face_nbr_facedata.SetSize(
2469 send_face_nbr_facedata.Size_of_connections());
2470 for (int fn = 0; fn < num_face_nbrs; fn++)
2471 {
2472 int nbr_rank = GetFaceNbrRank(fn);
2473 int tag = 0;
2474
2475 MPI_Isend(send_face_nbr_facedata.GetRow(fn),
2476 send_face_nbr_facedata.RowSize(fn),
2477 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2478
2479 // the size of the send and receive face data is the same
2480 MPI_Irecv(&recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]],
2481 send_face_nbr_facedata.RowSize(fn),
2482 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2483 }
2484
2485 // transfer the received face data into faces_info
2486 while (true)
2487 {
2488 int fn;
2489 MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2490
2491 if (fn == MPI_UNDEFINED)
2492 {
2493 break;
2494 }
2495
2496 int elem_off = face_nbr_elements_offset[fn];
2497 int nbr_group = face_nbr_group[fn];
2498 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2499 int *sface = gr_sface->GetRow(nbr_group-1);
2500 int *facedata =
2501 &recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]];
2502
2503 for (int i = 0; i < num_sfaces; i++)
2504 {
2505 const int sf = sface[i];
2506 int lface = s2l_face[sf];
2507 FaceInfo &face_info = faces_info[lface];
2508 face_info.Elem2No = -1 - (facedata[2*i] + elem_off);
2509 int info = facedata[2*i+1];
2510 // change the orientation in info to be relative to the local face
2511 if (Dim < 3)
2512 {
2513 info++; // orientation 0 --> orientation 1
2514 }
2515 else
2516 {
2517 int nbr_ori = info%64, nbr_v[4];
2518 const int *lf_v = faces[lface]->GetVertices();
2519
2520 if (sf < nst) // triangle shared face
2521 {
2522 // apply the nbr_ori to sf_v to get nbr_v
2523 const int *perm = tri_t::Orient[nbr_ori];
2524 const int *sf_v = shared_trias[sf].v;
2525 for (int j = 0; j < 3; j++)
2526 {
2527 nbr_v[perm[j]] = sf_v[j];
2528 }
2529 // get the orientation of nbr_v w.r.t. the local face
2530 nbr_ori = GetTriOrientation(lf_v, nbr_v);
2531 }
2532 else // quad shared face
2533 {
2534 // apply the nbr_ori to sf_v to get nbr_v
2535 const int *perm = quad_t::Orient[nbr_ori];
2536 const int *sf_v = shared_quads[sf-nst].v;
2537 for (int j = 0; j < 4; j++)
2538 {
2539 nbr_v[perm[j]] = sf_v[j];
2540 }
2541 // get the orientation of nbr_v w.r.t. the local face
2542 nbr_ori = GetQuadOrientation(lf_v, nbr_v);
2543 }
2544
2545 info = 64*(info/64) + nbr_ori;
2546 }
2547 face_info.Elem2Inf = info;
2548 }
2549 }
2550
2551 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2552
2553 // allocate the face_nbr_vertices
2554 face_nbr_vertices.SetSize(face_nbr_vertices_offset[num_face_nbrs]);
2555
2556 delete [] nbr_data;
2557
2558 delete [] statuses;
2559 delete [] requests;
2560}
2561
2563{
2564 if (!have_face_nbr_data)
2565 {
2566 ExchangeFaceNbrData(); // calls this method at the end
2567 }
2568 else if (Nodes == NULL)
2569 {
2570 if (Nonconforming())
2571 {
2572 // with ParNCMesh we already have the vertices
2573 return;
2574 }
2575
2576 int num_face_nbrs = GetNFaceNeighbors();
2577
2578 if (!num_face_nbrs) { return; }
2579
2580 MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2581 MPI_Request *send_requests = requests;
2582 MPI_Request *recv_requests = requests + num_face_nbrs;
2583 MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2584
2585 // allocate buffer and copy the vertices to be sent
2587 for (int i = 0; i < send_vertices.Size(); i++)
2588 {
2589 send_vertices[i] = vertices[send_face_nbr_vertices.GetJ()[i]];
2590 }
2591
2592 // send and receive the vertices
2593 for (int fn = 0; fn < num_face_nbrs; fn++)
2594 {
2595 int nbr_rank = GetFaceNbrRank(fn);
2596 int tag = 0;
2597
2598 MPI_Isend(send_vertices[send_face_nbr_vertices.GetI()[fn]](),
2600 MPITypeMap<real_t>::mpi_type, nbr_rank, tag, MyComm, &send_requests[fn]);
2601
2603 3*(face_nbr_vertices_offset[fn+1] -
2605 MPITypeMap<real_t>::mpi_type, nbr_rank, tag, MyComm, &recv_requests[fn]);
2606 }
2607
2608 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2609 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2610
2611 delete [] statuses;
2612 delete [] requests;
2613 }
2614 else
2615 {
2616 ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
2617 MFEM_VERIFY(pNodes != NULL, "Nodes are not ParGridFunction!");
2618 pNodes->ExchangeFaceNbrData();
2619 }
2620}
2621
2623{
2624 STable3D *sfaces_tbl = new STable3D(face_nbr_vertices.Size());
2625 for (int i = 0; i < face_nbr_elements.Size(); i++)
2626 {
2627 const int *v = face_nbr_elements[i]->GetVertices();
2628 switch (face_nbr_elements[i]->GetType())
2629 {
2631 {
2632 for (int j = 0; j < 4; j++)
2633 {
2634 const int *fv = tet_t::FaceVert[j];
2635 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2636 }
2637 break;
2638 }
2639 case Element::WEDGE:
2640 {
2641 for (int j = 0; j < 2; j++)
2642 {
2643 const int *fv = pri_t::FaceVert[j];
2644 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2645 }
2646 for (int j = 2; j < 5; j++)
2647 {
2648 const int *fv = pri_t::FaceVert[j];
2649 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2650 }
2651 break;
2652 }
2653 case Element::PYRAMID:
2654 {
2655 for (int j = 0; j < 1; j++)
2656 {
2657 const int *fv = pyr_t::FaceVert[j];
2658 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2659 }
2660 for (int j = 1; j < 5; j++)
2661 {
2662 const int *fv = pyr_t::FaceVert[j];
2663 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2664 }
2665 break;
2666 }
2668 {
2669 // find the face by the vertices with the smallest 3 numbers
2670 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
2671 for (int j = 0; j < 6; j++)
2672 {
2673 const int *fv = hex_t::FaceVert[j];
2674 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2675 }
2676 break;
2677 }
2678 default:
2679 MFEM_ABORT("Unexpected type of Element.");
2680 }
2681 }
2682 return sfaces_tbl;
2683}
2684
2685template <int N>
2686void
2688 const std::unique_ptr<STable3D> &faces,
2689 const std::unique_ptr<STable3D> &shared_faces,
2690 int elem, int start, int end, const int fverts[][N])
2691{
2692 for (int i = start; i < end; ++i)
2693 {
2694 // Reference face vertices.
2695 const auto fv = fverts[i];
2696 // Element specific face vertices.
2697 const Vert3 elem_fv(elem_vertices[fv[0]], elem_vertices[fv[1]],
2698 elem_vertices[fv[2]]);
2699
2700 // Check amongst the faces of elements local to this rank for this set of vertices
2701 const int lf = faces->Index(elem_fv.v[0], elem_fv.v[1], elem_fv.v[2]);
2702
2703 // If the face wasn't found amonst processor local elements, search the
2704 // ghosts for this set of vertices.
2705 const int sf = lf < 0 ? shared_faces->Index(elem_fv.v[0], elem_fv.v[1],
2706 elem_fv.v[2]) : -1;
2707 // If find local face -> use that
2708 // else if find shared face -> shift and use that
2709 // else no face found -> set to -1
2710 const int face_to_add = lf < 0 ? (sf >= 0 ? sf + NumOfFaces : -1) : lf;
2711
2712 MFEM_ASSERT(sf >= 0 ||
2713 lf >= 0, "Face must be from a local or a face neighbor element");
2714
2715 // Add this discovered face to the list of faces of this face neighbor element
2716 face_nbr_el_to_face->Push(elem, face_to_add);
2717 }
2718}
2719
2721{
2722 const auto faces = std::unique_ptr<STable3D>(GetFacesTable());
2723 const auto shared_faces = std::unique_ptr<STable3D>(GetSharedFacesTable());
2724
2725 face_nbr_el_to_face.reset(new Table(face_nbr_elements.Size(), 6));
2726
2727 Array<int> v;
2728
2729 // Helper for adding quadrilateral faces.
2730 auto add_quad_faces = [&faces, &shared_faces, &v, this]
2731 (int elem, int start, int end, const int fverts[][4])
2732 {
2733 for (int i = start; i < end; ++i)
2734 {
2735 const int * const fv = fverts[i];
2736 int k = 0;
2737 int max = v[fv[0]];
2738
2739 if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
2740 if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
2741 if (max < v[fv[3]]) { k = 3; }
2742
2743 int v0 = -1, v1 = -1, v2 = -1;
2744 switch (k)
2745 {
2746 case 0:
2747 v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2748 break;
2749 case 1:
2750 v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2751 break;
2752 case 2:
2753 v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
2754 break;
2755 case 3:
2756 v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
2757 break;
2758 }
2759 int lf = faces->Index(v0, v1, v2);
2760 if (lf < 0)
2761 {
2762 lf = shared_faces->Index(v0, v1, v2);
2763 if (lf >= 0)
2764 {
2765 lf += NumOfFaces;
2766 }
2767 }
2768 face_nbr_el_to_face->Push(elem, lf);
2769 }
2770 };
2771
2772 for (int i = 0; i < face_nbr_elements.Size(); i++)
2773 {
2774 face_nbr_elements[i]->GetVertices(v);
2775 switch (face_nbr_elements[i]->GetType())
2776 {
2778 {
2779 AddTriFaces(v, faces, shared_faces, i, 0, 4, tet_t::FaceVert);
2780 break;
2781 }
2782 case Element::WEDGE:
2783 {
2784 AddTriFaces(v, faces, shared_faces, i, 0, 2, pri_t::FaceVert);
2785 add_quad_faces(i, 2, 5, pri_t::FaceVert);
2786 break;
2787 }
2788 case Element::PYRAMID:
2789 {
2790 add_quad_faces(i, 0, 1, pyr_t::FaceVert);
2791 AddTriFaces(v, faces, shared_faces, i, 1, 5, pyr_t::FaceVert);
2792 break;
2793 }
2795 {
2796 add_quad_faces(i, 0, 6, hex_t::FaceVert);
2797 break;
2798 }
2799 default:
2800 MFEM_ABORT("Unexpected type of Element.");
2801 }
2802 }
2803 face_nbr_el_to_face->Finalize();
2804}
2805
2807{
2808 if (Conforming())
2809 {
2810 int nbr_group = face_nbr_group[fn];
2811 const int *nbs = gtopo.GetGroup(nbr_group);
2812 int nbr_lproc = (nbs[0]) ? nbs[0] : nbs[1];
2813 int nbr_rank = gtopo.GetNeighborRank(nbr_lproc);
2814 return nbr_rank;
2815 }
2816 else
2817 {
2818 // NC: simplified handling of face neighbor ranks
2819 return face_nbr_group[fn];
2820 }
2821}
2822
2823void
2825 Array<int> &orientations) const
2826{
2827 int el_nbr = i - GetNE();
2828 if (face_nbr_el_to_face != nullptr && el_nbr < face_nbr_el_to_face->Size())
2829 {
2830 face_nbr_el_to_face->GetRow(el_nbr, faces);
2831 }
2832 else
2833 {
2834 MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2835 "face_nbr_el_to_face not generated correctly.");
2836 }
2837
2838 if (face_nbr_el_ori != nullptr && el_nbr < face_nbr_el_ori->Size())
2839 {
2840 face_nbr_el_ori->GetRow(el_nbr, orientations);
2841 }
2842 else
2843 {
2844 MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2845 "face_nbr_el_ori not generated correctly.");
2846 }
2847}
2848
2850{
2851 const Array<int> *s2l_face;
2852 if (Dim == 1)
2853 {
2854 s2l_face = &svert_lvert;
2855 }
2856 else if (Dim == 2)
2857 {
2858 s2l_face = &sedge_ledge;
2859 }
2860 else
2861 {
2862 s2l_face = &sface_lface;
2863 }
2864
2865 Table *face_elem = new Table;
2866
2867 face_elem->MakeI(faces_info.Size());
2868
2869 for (int i = 0; i < faces_info.Size(); i++)
2870 {
2871 if (faces_info[i].Elem2No >= 0)
2872 {
2873 face_elem->AddColumnsInRow(i, 2);
2874 }
2875 else
2876 {
2877 face_elem->AddAColumnInRow(i);
2878 }
2879 }
2880 for (int i = 0; i < s2l_face->Size(); i++)
2881 {
2882 face_elem->AddAColumnInRow((*s2l_face)[i]);
2883 }
2884
2885 face_elem->MakeJ();
2886
2887 for (int i = 0; i < faces_info.Size(); i++)
2888 {
2889 face_elem->AddConnection(i, faces_info[i].Elem1No);
2890 if (faces_info[i].Elem2No >= 0)
2891 {
2892 face_elem->AddConnection(i, faces_info[i].Elem2No);
2893 }
2894 }
2895 for (int i = 0; i < s2l_face->Size(); i++)
2896 {
2897 int lface = (*s2l_face)[i];
2898 int nbr_elem_idx = -1 - faces_info[lface].Elem2No;
2899 face_elem->AddConnection(lface, NumOfElements + nbr_elem_idx);
2900 }
2901
2902 face_elem->ShiftUpI();
2903
2904 return face_elem;
2905}
2906
2914
2919 int mask) const
2920{
2921 if (FaceNo < GetNumFaces())
2922 {
2923 Mesh::GetFaceElementTransformations(FaceNo, FElTr, ElTr1, ElTr2, mask);
2924 }
2925 else
2926 {
2927 const bool fill2 = mask & 10; // Elem2 and/or Loc2
2928 GetSharedFaceTransformationsByLocalIndex(FaceNo, FElTr, ElTr1, ElTr2,
2929 fill2);
2930 }
2931}
2932
2940
2945 bool fill2) const
2946{
2947 int FaceNo = GetSharedFace(sf);
2948 GetSharedFaceTransformationsByLocalIndex(FaceNo, FElTr, ElTr1, ElTr2, fill2);
2949}
2950
2958
2960 int FaceNo, FaceElementTransformations &FElTr,
2962 bool fill2) const
2963{
2964 const FaceInfo &face_info = faces_info[FaceNo];
2965 MFEM_VERIFY(face_info.Elem2Inf >= 0, "The face must be shared.");
2966
2967 bool is_slave = Nonconforming() && IsSlaveFace(face_info);
2968 bool is_ghost = Nonconforming() && FaceNo >= GetNumFaces();
2969
2970 int mask = 0;
2971 FElTr.SetConfigurationMask(0);
2972 FElTr.Elem1 = NULL;
2973 FElTr.Elem2 = NULL;
2974
2975 int local_face =
2976 is_ghost ? nc_faces_info[face_info.NCFace].MasterFace : FaceNo;
2977 Element::Type face_type = GetFaceElementType(local_face);
2978 Geometry::Type face_geom = GetFaceGeometry(local_face);
2979
2980 // setup the transformation for the first element
2981 FElTr.Elem1No = face_info.Elem1No;
2982 GetElementTransformation(FElTr.Elem1No, &ElTr1);
2983 FElTr.Elem1 = &ElTr1;
2985
2986 // setup the transformation for the second (neighbor) element
2987 int Elem2NbrNo;
2988 if (fill2)
2989 {
2990 Elem2NbrNo = -1 - face_info.Elem2No;
2991 // Store the "shifted index" for element 2 in FElTr.Elem2No.
2992 // `Elem2NbrNo` is the index of the face neighbor (starting from 0),
2993 // and `FElTr.Elem2No` will be offset by the number of (local)
2994 // elements in the mesh.
2995 FElTr.Elem2No = NumOfElements + Elem2NbrNo;
2996 GetFaceNbrElementTransformation(Elem2NbrNo, ElTr2);
2997 FElTr.Elem2 = &ElTr2;
2999 }
3000 else
3001 {
3002 FElTr.Elem2No = -1;
3003 }
3004
3005 // setup the face transformation if the face is not a ghost
3006 if (!is_ghost)
3007 {
3008 GetFaceTransformation(FaceNo, &FElTr);
3009 // NOTE: The above call overwrites FElTr.Loc1
3011 }
3012 else
3013 {
3014 FElTr.SetGeometryType(face_geom);
3015 }
3016
3017 // setup Loc1 & Loc2
3018 int elem_type = GetElementType(face_info.Elem1No);
3019 GetLocalFaceTransformation(face_type, elem_type, FElTr.Loc1.Transf,
3020 face_info.Elem1Inf);
3022
3023 if (fill2)
3024 {
3025 elem_type = face_nbr_elements[Elem2NbrNo]->GetType();
3026 GetLocalFaceTransformation(face_type, elem_type, FElTr.Loc2.Transf,
3027 face_info.Elem2Inf);
3029 }
3030
3031 // adjust Loc1 or Loc2 of the master face if this is a slave face
3032 if (is_slave)
3033 {
3034 if (is_ghost || fill2)
3035 {
3036 // is_ghost -> modify side 1, otherwise -> modify side 2:
3037 ApplyLocalSlaveTransformation(FElTr, face_info, is_ghost);
3038 }
3039 }
3040
3041 // for ghost faces we need a special version of GetFaceTransformation
3042 if (is_ghost)
3043 {
3044 GetGhostFaceTransformation(FElTr, face_type, face_geom);
3046 }
3047
3048 FElTr.SetConfigurationMask(mask);
3049
3050 // This check can be useful for internal debugging, however it will fail on
3051 // periodic boundary faces, so we keep it disabled in general.
3052#if 0
3053#ifdef MFEM_DEBUG
3054 real_t dist = FElTr.CheckConsistency();
3055 if (dist >= 1e-12)
3056 {
3057 mfem::out << "\nInternal error: face id = " << FaceNo
3058 << ", dist = " << dist << ", rank = " << MyRank << '\n';
3059 FElTr.CheckConsistency(1); // print coordinates
3060 MFEM_ABORT("internal error");
3061 }
3062#endif
3063#endif
3064}
3065
3067 FaceElementTransformations &FElTr, Element::Type face_type,
3068 Geometry::Type face_geom) const
3069{
3070 // calculate composition of FElTr.Loc1 and FElTr.Elem1
3071 DenseMatrix &face_pm = FElTr.GetPointMat();
3072 FElTr.Reset();
3073 if (Nodes == NULL)
3074 {
3075 FElTr.Elem1->Transform(FElTr.Loc1.Transf.GetPointMat(), face_pm);
3076 FElTr.SetFE(GetTransformationFEforElementType(face_type));
3077 }
3078 else
3079 {
3080 const FiniteElement* face_el =
3081 Nodes->FESpace()->GetTraceElement(FElTr.Elem1No, face_geom);
3082 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
3083 "Mesh requires nodal Finite Element.");
3084
3085#if 0 // TODO: handle the case of non-interpolatory Nodes
3086 DenseMatrix I;
3087 face_el->Project(Transformation.GetFE(), FElTr.Loc1.Transf, I);
3088 MultABt(Transformation.GetPointMat(), I, pm_face);
3089#else
3090 IntegrationRule eir(face_el->GetDof());
3091 FElTr.Loc1.Transform(face_el->GetNodes(), eir);
3092 Nodes->GetVectorValues(*FElTr.Elem1, eir, face_pm);
3093#endif
3094 FElTr.SetFE(face_el);
3095 }
3096}
3097
3103
3105 int FaceNo, IsoparametricTransformation &ElTr) const
3106{
3107 DenseMatrix &pointmat = ElTr.GetPointMat();
3108 Element *elem = face_nbr_elements[FaceNo];
3109
3110 ElTr.Attribute = elem->GetAttribute();
3111 ElTr.ElementNo = NumOfElements + FaceNo;
3113 ElTr.mesh = this;
3114 ElTr.Reset();
3115
3116 if (Nodes == NULL)
3117 {
3118 const int nv = elem->GetNVertices();
3119 const int *v = elem->GetVertices();
3120
3121 pointmat.SetSize(spaceDim, nv);
3122 for (int k = 0; k < spaceDim; k++)
3123 {
3124 for (int j = 0; j < nv; j++)
3125 {
3126 pointmat(k, j) = face_nbr_vertices[v[j]](k);
3127 }
3128 }
3129
3131 }
3132 else
3133 {
3134 Array<int> vdofs;
3135 ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
3136 if (pNodes)
3137 {
3138 pNodes->ParFESpace()->GetFaceNbrElementVDofs(FaceNo, vdofs);
3139 int n = vdofs.Size()/spaceDim;
3140 pointmat.SetSize(spaceDim, n);
3141 pNodes->FaceNbrData().HostRead();
3142 for (int k = 0; k < spaceDim; k++)
3143 {
3144 for (int j = 0; j < n; j++)
3145 {
3146 pointmat(k,j) = AsConst(pNodes->FaceNbrData())(vdofs[n*k+j]);
3147 }
3148 }
3149
3150 ElTr.SetFE(pNodes->ParFESpace()->GetFaceNbrFE(FaceNo));
3151 }
3152 else
3153 {
3154 MFEM_ABORT("Nodes are not ParGridFunction!");
3155 }
3156 }
3157}
3158
3163
3165{
3166 if (Conforming())
3167 {
3168 switch (Dim)
3169 {
3170 case 1: return svert_lvert.Size();
3171 case 2: return sedge_ledge.Size();
3172 default: return sface_lface.Size();
3173 }
3174 }
3175 else
3176 {
3177 MFEM_ASSERT(Dim > 1, "");
3178 const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3179 return shared.conforming.Size() + shared.slaves.Size();
3180 }
3181}
3182
3183int ParMesh::GetSharedFace(int sface) const
3184{
3185 if (Conforming())
3186 {
3187 switch (Dim)
3188 {
3189 case 1: return svert_lvert[sface];
3190 case 2: return sedge_ledge[sface];
3191 default: return sface_lface[sface];
3192 }
3193 }
3194 else
3195 {
3196 MFEM_ASSERT(Dim > 1, "");
3197 const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3198 int csize = shared.conforming.Size();
3199 return sface < csize
3200 ? shared.conforming[sface].index
3201 : shared.slaves[sface - csize].index;
3202 }
3203}
3204
3206{
3207 const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
3208 return Mesh::GetNFbyType(type);
3209}
3210
3211// shift cyclically 3 integers a, b, c, so that the smallest of
3212// order[a], order[b], order[c] is first
3213static inline
3214void Rotate3Indirect(int &a, int &b, int &c,
3215 const Array<std::int64_t> &order)
3216{
3217 if (order[a] < order[b])
3218 {
3219 if (order[a] > order[c])
3220 {
3221 ShiftRight(a, b, c);
3222 }
3223 }
3224 else
3225 {
3226 if (order[b] < order[c])
3227 {
3228 ShiftRight(c, b, a);
3229 }
3230 else
3231 {
3232 ShiftRight(a, b, c);
3233 }
3234 }
3235}
3236
3238{
3239 if (Dim != 3 || !(meshgen & 1))
3240 {
3241 return;
3242 }
3243
3244 ResetLazyData();
3245
3246 DSTable *old_v_to_v = NULL;
3247 Table *old_elem_vert = NULL;
3248
3249 if (Nodes)
3250 {
3251 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3252 }
3253
3254 // create a GroupCommunicator over shared vertices
3255 GroupCommunicator svert_comm(gtopo);
3256 GetSharedVertexCommunicator(0, svert_comm);
3257
3258 // communicate the local index of each shared vertex from the group master to
3259 // other ranks in the group
3260 Array<int> svert_master_rank(svert_lvert.Size());
3261 Array<int> svert_master_index(svert_lvert);
3262 for (int i = 0; i < group_svert.Size(); i++)
3263 {
3264 int rank = gtopo.GetGroupMasterRank(i+1);
3265 for (int j = 0; j < group_svert.RowSize(i); j++)
3266 {
3267 svert_master_rank[group_svert.GetRow(i)[j]] = rank;
3268 }
3269 }
3270 svert_comm.Bcast(svert_master_index);
3271
3272 // the pairs (master rank, master local index) define a globally consistent
3273 // vertex ordering
3274 Array<std::int64_t> glob_vert_order(vertices.Size());
3275 {
3276 Array<int> lvert_svert(vertices.Size());
3277 lvert_svert = -1;
3278 for (int i = 0; i < svert_lvert.Size(); i++)
3279 {
3280 lvert_svert[svert_lvert[i]] = i;
3281 }
3282
3283 for (int i = 0; i < vertices.Size(); i++)
3284 {
3285 int s = lvert_svert[i];
3286 if (s >= 0)
3287 {
3288 glob_vert_order[i] =
3289 (std::int64_t(svert_master_rank[s]) << 32) + svert_master_index[s];
3290 }
3291 else
3292 {
3293 glob_vert_order[i] = (std::int64_t(MyRank) << 32) + i;
3294 }
3295 }
3296 }
3297
3298 // rotate tetrahedra so that vertex zero is the lowest (global) index vertex,
3299 // vertex 1 is the second lowest (global) index and vertices 2 and 3 preserve
3300 // positive orientation of the element
3301 for (int i = 0; i < NumOfElements; i++)
3302 {
3304 {
3305 int *v = elements[i]->GetVertices();
3306
3307 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3308
3309 if (glob_vert_order[v[0]] < glob_vert_order[v[3]])
3310 {
3311 Rotate3Indirect(v[1], v[2], v[3], glob_vert_order);
3312 }
3313 else
3314 {
3315 ShiftRight(v[0], v[1], v[3]);
3316 }
3317 }
3318 }
3319
3320 // rotate also boundary triangles
3321 for (int i = 0; i < NumOfBdrElements; i++)
3322 {
3324 {
3325 int *v = boundary[i]->GetVertices();
3326
3327 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3328 }
3329 }
3330
3331 const bool check_consistency = true;
3332 if (check_consistency)
3333 {
3334 // create a GroupCommunicator on the shared triangles
3335 GroupCommunicator stria_comm(gtopo);
3336 GetSharedTriCommunicator(0, stria_comm);
3337
3338 Array<int> stria_flag(shared_trias.Size());
3339 for (int i = 0; i < stria_flag.Size(); i++)
3340 {
3341 const int *v = shared_trias[i].v;
3342 if (glob_vert_order[v[0]] < glob_vert_order[v[1]])
3343 {
3344 stria_flag[i] = (glob_vert_order[v[0]] < glob_vert_order[v[2]]) ? 0 : 2;
3345 }
3346 else // v[1] < v[0]
3347 {
3348 stria_flag[i] = (glob_vert_order[v[1]] < glob_vert_order[v[2]]) ? 1 : 2;
3349 }
3350 }
3351
3352 Array<int> stria_master_flag(stria_flag);
3353 stria_comm.Bcast(stria_master_flag);
3354 for (int i = 0; i < stria_flag.Size(); i++)
3355 {
3356 const int *v = shared_trias[i].v;
3357 MFEM_VERIFY(stria_flag[i] == stria_master_flag[i],
3358 "inconsistent vertex ordering found, shared triangle "
3359 << i << ": ("
3360 << v[0] << ", " << v[1] << ", " << v[2] << "), "
3361 << "local flag: " << stria_flag[i]
3362 << ", master flag: " << stria_master_flag[i]);
3363 }
3364 }
3365
3366 // rotate shared triangle faces
3367 for (int i = 0; i < shared_trias.Size(); i++)
3368 {
3369 int *v = shared_trias[i].v;
3370
3371 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3372 }
3373
3374 // finalize
3375 if (!Nodes)
3376 {
3378 GenerateFaces();
3379 if (el_to_edge)
3380 {
3382 }
3383 }
3384 else
3385 {
3386 DoNodeReorder(old_v_to_v, old_elem_vert);
3387 delete old_elem_vert;
3388 delete old_v_to_v;
3389 }
3390
3391 // the local edge and face numbering is changed therefore we need to
3392 // update sedge_ledge and sface_lface.
3394}
3395
3396void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
3397{
3398 if (pncmesh)
3399 {
3400 MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
3401 }
3402
3404
3406
3407 if (Dim == 3)
3408 {
3409 int uniform_refinement = 0;
3410 if (type < 0)
3411 {
3412 type = -type;
3413 uniform_refinement = 1;
3414 }
3415
3416 // 1. Hash table of vertex to vertex connections corresponding to refined
3417 // edges.
3418 HashTable<Hashed2> v_to_v;
3419
3420 // 2. Do the red refinement.
3421 switch (type)
3422 {
3423 case 1:
3424 for (int i = 0; i < marked_el.Size(); i++)
3425 {
3426 Bisection(marked_el[i], v_to_v);
3427 }
3428 break;
3429 case 2:
3430 for (int i = 0; i < marked_el.Size(); i++)
3431 {
3432 Bisection(marked_el[i], v_to_v);
3433
3434 Bisection(NumOfElements - 1, v_to_v);
3435 Bisection(marked_el[i], v_to_v);
3436 }
3437 break;
3438 case 3:
3439 for (int i = 0; i < marked_el.Size(); i++)
3440 {
3441 Bisection(marked_el[i], v_to_v);
3442
3443 int j = NumOfElements - 1;
3444 Bisection(j, v_to_v);
3445 Bisection(NumOfElements - 1, v_to_v);
3446 Bisection(j, v_to_v);
3447
3448 Bisection(marked_el[i], v_to_v);
3449 Bisection(NumOfElements-1, v_to_v);
3450 Bisection(marked_el[i], v_to_v);
3451 }
3452 break;
3453 }
3454
3455 // 3. Do the green refinement (to get conforming mesh).
3456 int need_refinement;
3457 int max_faces_in_group = 0;
3458 // face_splittings identify how the shared faces have been split
3459 Array<unsigned> *face_splittings = new Array<unsigned>[GetNGroups()-1];
3460 for (int i = 0; i < GetNGroups()-1; i++)
3461 {
3462 const int faces_in_group = GroupNTriangles(i+1);
3463 face_splittings[i].Reserve(faces_in_group);
3464 if (faces_in_group > max_faces_in_group)
3465 {
3466 max_faces_in_group = faces_in_group;
3467 }
3468 }
3469 int neighbor;
3470 Array<unsigned> iBuf(max_faces_in_group);
3471
3472 MPI_Request *requests = new MPI_Request[GetNGroups()-1];
3473 MPI_Status status;
3474
3475#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3476 int ref_loops_all = 0, ref_loops_par = 0;
3477#endif
3478 do
3479 {
3480 need_refinement = 0;
3481 for (int i = 0; i < NumOfElements; i++)
3482 {
3483 if (elements[i]->NeedRefinement(v_to_v))
3484 {
3485 need_refinement = 1;
3486 Bisection(i, v_to_v);
3487 }
3488 }
3489#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3490 ref_loops_all++;
3491#endif
3492
3493 if (uniform_refinement)
3494 {
3495 continue;
3496 }
3497
3498 // if the mesh is locally conforming start making it globally
3499 // conforming
3500 if (need_refinement == 0)
3501 {
3502#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3503 ref_loops_par++;
3504#endif
3505 // MPI_Barrier(MyComm);
3506 const int tag = 293;
3507
3508 // (a) send the type of interface splitting
3509 int req_count = 0;
3510 for (int i = 0; i < GetNGroups()-1; i++)
3511 {
3512 const int *group_faces = group_stria.GetRow(i);
3513 const int faces_in_group = group_stria.RowSize(i);
3514 // it is enough to communicate through the faces
3515 if (faces_in_group == 0) { continue; }
3516
3517 face_splittings[i].SetSize(0);
3518 for (int j = 0; j < faces_in_group; j++)
3519 {
3520 GetFaceSplittings(shared_trias[group_faces[j]].v, v_to_v,
3521 face_splittings[i]);
3522 }
3523 const int *nbs = gtopo.GetGroup(i+1);
3524 neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3525 MPI_Isend(face_splittings[i], face_splittings[i].Size(),
3526 MPI_UNSIGNED, neighbor, tag, MyComm,
3527 &requests[req_count++]);
3528 }
3529
3530 // (b) receive the type of interface splitting
3531 for (int i = 0; i < GetNGroups()-1; i++)
3532 {
3533 const int *group_faces = group_stria.GetRow(i);
3534 const int faces_in_group = group_stria.RowSize(i);
3535 if (faces_in_group == 0) { continue; }
3536
3537 const int *nbs = gtopo.GetGroup(i+1);
3538 neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3539 MPI_Probe(neighbor, tag, MyComm, &status);
3540 int count;
3541 MPI_Get_count(&status, MPI_UNSIGNED, &count);
3542 iBuf.SetSize(count);
3543 MPI_Recv(iBuf, count, MPI_UNSIGNED, neighbor, tag, MyComm,
3544 MPI_STATUS_IGNORE);
3545
3546 for (int j = 0, pos = 0; j < faces_in_group; j++)
3547 {
3548 const int *v = shared_trias[group_faces[j]].v;
3549 need_refinement |= DecodeFaceSplittings(v_to_v, v, iBuf, pos);
3550 }
3551 }
3552
3553 int nr = need_refinement;
3554 MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3555
3556 MPI_Waitall(req_count, requests, MPI_STATUSES_IGNORE);
3557 }
3558 }
3559 while (need_refinement == 1);
3560
3561#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3562 {
3563 int i = ref_loops_all;
3564 MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3565 if (MyRank == 0)
3566 {
3567 mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3568 << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3569 << '\n' << endl;
3570 }
3571 }
3572#endif
3573
3574 delete [] requests;
3575 iBuf.DeleteAll();
3576 delete [] face_splittings;
3577
3578 // 4. Update the boundary elements.
3579 do
3580 {
3581 need_refinement = 0;
3582 for (int i = 0; i < NumOfBdrElements; i++)
3583 {
3584 if (boundary[i]->NeedRefinement(v_to_v))
3585 {
3586 need_refinement = 1;
3587 BdrBisection(i, v_to_v);
3588 }
3589 }
3590 }
3591 while (need_refinement == 1);
3592
3593 if (NumOfBdrElements != boundary.Size())
3594 {
3595 mfem_error("ParMesh::LocalRefinement :"
3596 " (NumOfBdrElements != boundary.Size())");
3597 }
3598
3599 ResetLazyData();
3600
3601 const int old_nv = NumOfVertices;
3602 NumOfVertices = vertices.Size();
3603
3604 RefineGroups(old_nv, v_to_v);
3605
3606 // 5. Update the groups after refinement.
3607 if (el_to_face != NULL)
3608 {
3610 GenerateFaces();
3611 }
3612
3613 // 6. Update element-to-edge relations.
3614 if (el_to_edge != NULL)
3615 {
3617 }
3618 } // 'if (Dim == 3)'
3619
3620
3621 if (Dim == 2)
3622 {
3623 int uniform_refinement = 0;
3624 if (type < 0)
3625 {
3626 // type = -type; // not used
3627 uniform_refinement = 1;
3628 }
3629
3630 // 1. Get table of vertex to vertex connections.
3631 DSTable v_to_v(NumOfVertices);
3632 GetVertexToVertexTable(v_to_v);
3633
3634 // 2. Get edge to element connections in arrays edge1 and edge2
3635 int nedges = v_to_v.NumberOfEntries();
3636 int *edge1 = new int[nedges];
3637 int *edge2 = new int[nedges];
3638 int *middle = new int[nedges];
3639
3640 for (int i = 0; i < nedges; i++)
3641 {
3642 edge1[i] = edge2[i] = middle[i] = -1;
3643 }
3644
3645 for (int i = 0; i < NumOfElements; i++)
3646 {
3647 int *v = elements[i]->GetVertices();
3648 for (int j = 0; j < 3; j++)
3649 {
3650 int ind = v_to_v(v[j], v[(j+1)%3]);
3651 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
3652 }
3653 }
3654
3655 // 3. Do the red refinement.
3656 for (int i = 0; i < marked_el.Size(); i++)
3657 {
3658 RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
3659 }
3660
3661 // 4. Do the green refinement (to get conforming mesh).
3662 int need_refinement;
3663 int edges_in_group, max_edges_in_group = 0;
3664 // edge_splittings identify how the shared edges have been split
3665 int **edge_splittings = new int*[GetNGroups()-1];
3666 for (int i = 0; i < GetNGroups()-1; i++)
3667 {
3668 edges_in_group = GroupNEdges(i+1);
3669 edge_splittings[i] = new int[edges_in_group];
3670 if (edges_in_group > max_edges_in_group)
3671 {
3672 max_edges_in_group = edges_in_group;
3673 }
3674 }
3675 int neighbor, *iBuf = new int[max_edges_in_group];
3676
3677 Array<int> group_edges;
3678
3679 MPI_Request request;
3680 MPI_Status status;
3681 Vertex V;
3682 V(2) = 0.0;
3683
3684#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3685 int ref_loops_all = 0, ref_loops_par = 0;
3686#endif
3687 do
3688 {
3689 need_refinement = 0;
3690 for (int i = 0; i < nedges; i++)
3691 {
3692 if (middle[i] != -1 && edge1[i] != -1)
3693 {
3694 need_refinement = 1;
3695 GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
3696 }
3697 }
3698#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3699 ref_loops_all++;
3700#endif
3701
3702 if (uniform_refinement)
3703 {
3704 continue;
3705 }
3706
3707 // if the mesh is locally conforming start making it globally
3708 // conforming
3709 if (need_refinement == 0)
3710 {
3711#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3712 ref_loops_par++;
3713#endif
3714 // MPI_Barrier(MyComm);
3715
3716 // (a) send the type of interface splitting
3717 for (int i = 0; i < GetNGroups()-1; i++)
3718 {
3719 group_sedge.GetRow(i, group_edges);
3720 edges_in_group = group_edges.Size();
3721 // it is enough to communicate through the edges
3722 if (edges_in_group != 0)
3723 {
3724 for (int j = 0; j < edges_in_group; j++)
3725 {
3726 edge_splittings[i][j] =
3727 GetEdgeSplittings(shared_edges[group_edges[j]], v_to_v,
3728 middle);
3729 }
3730 const int *nbs = gtopo.GetGroup(i+1);
3731 if (nbs[0] == 0)
3732 {
3733 neighbor = gtopo.GetNeighborRank(nbs[1]);
3734 }
3735 else
3736 {
3737 neighbor = gtopo.GetNeighborRank(nbs[0]);
3738 }
3739 MPI_Isend(edge_splittings[i], edges_in_group, MPI_INT,
3740 neighbor, 0, MyComm, &request);
3741 }
3742 }
3743
3744 // (b) receive the type of interface splitting
3745 for (int i = 0; i < GetNGroups()-1; i++)
3746 {
3747 group_sedge.GetRow(i, group_edges);
3748 edges_in_group = group_edges.Size();
3749 if (edges_in_group != 0)
3750 {
3751 const int *nbs = gtopo.GetGroup(i+1);
3752 if (nbs[0] == 0)
3753 {
3754 neighbor = gtopo.GetNeighborRank(nbs[1]);
3755 }
3756 else
3757 {
3758 neighbor = gtopo.GetNeighborRank(nbs[0]);
3759 }
3760 MPI_Recv(iBuf, edges_in_group, MPI_INT, neighbor,
3761 MPI_ANY_TAG, MyComm, &status);
3762
3763 for (int j = 0; j < edges_in_group; j++)
3764 {
3765 if (iBuf[j] == 1 && edge_splittings[i][j] == 0)
3766 {
3767 int *v = shared_edges[group_edges[j]]->GetVertices();
3768 int ii = v_to_v(v[0], v[1]);
3769#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3770 if (middle[ii] != -1)
3771 {
3772 mfem_error("ParMesh::LocalRefinement (triangles) : "
3773 "Oops!");
3774 }
3775#endif
3776 need_refinement = 1;
3777 middle[ii] = NumOfVertices++;
3778 for (int c = 0; c < spaceDim; c++)
3779 {
3780 V(c) = 0.5 * (vertices[v[0]](c) + vertices[v[1]](c));
3781 }
3782 vertices.Append(V);
3783 }
3784 }
3785 }
3786 }
3787
3788 int nr = need_refinement;
3789 MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3790 }
3791 }
3792 while (need_refinement == 1);
3793
3794#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3795 {
3796 int i = ref_loops_all;
3797 MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3798 if (MyRank == 0)
3799 {
3800 mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3801 << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3802 << '\n' << endl;
3803 }
3804 }
3805#endif
3806
3807 for (int i = 0; i < GetNGroups()-1; i++)
3808 {
3809 delete [] edge_splittings[i];
3810 }
3811 delete [] edge_splittings;
3812
3813 delete [] iBuf;
3814
3815 // 5. Update the boundary elements.
3816 int v1[2], v2[2], bisect, temp;
3817 temp = NumOfBdrElements;
3818 for (int i = 0; i < temp; i++)
3819 {
3820 int *v = boundary[i]->GetVertices();
3821 bisect = v_to_v(v[0], v[1]);
3822 if (middle[bisect] != -1)
3823 {
3824 // the element was refined (needs updating)
3825 if (boundary[i]->GetType() == Element::SEGMENT)
3826 {
3827 v1[0] = v[0]; v1[1] = middle[bisect];
3828 v2[0] = middle[bisect]; v2[1] = v[1];
3829
3830 boundary[i]->SetVertices(v1);
3831 boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
3832 }
3833 else
3834 {
3835 mfem_error("Only bisection of segment is implemented for bdr"
3836 " elem.");
3837 }
3838 }
3839 }
3840 NumOfBdrElements = boundary.Size();
3841
3842 ResetLazyData();
3843
3844 // 5a. Update the groups after refinement.
3845 RefineGroups(v_to_v, middle);
3846
3847 // 6. Free the allocated memory.
3848 delete [] edge1;
3849 delete [] edge2;
3850 delete [] middle;
3851
3852 if (el_to_edge != NULL)
3853 {
3855 GenerateFaces();
3856 }
3857 } // 'if (Dim == 2)'
3858
3859 if (Dim == 1) // --------------------------------------------------------
3860 {
3861 int cne = NumOfElements, cnv = NumOfVertices;
3862 NumOfVertices += marked_el.Size();
3863 NumOfElements += marked_el.Size();
3864 vertices.SetSize(NumOfVertices);
3865 elements.SetSize(NumOfElements);
3867
3868 for (int j = 0; j < marked_el.Size(); j++)
3869 {
3870 int i = marked_el[j];
3871 Segment *c_seg = (Segment *)elements[i];
3872 int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
3873 int new_v = cnv + j, new_e = cne + j;
3874 AverageVertices(vert, 2, new_v);
3875 elements[new_e] = new Segment(new_v, vert[1], attr);
3876 vert[1] = new_v;
3877
3880 }
3881
3882 static real_t seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
3884 UseExternalData(seg_children, 1, 2, 3);
3885
3886 GenerateFaces();
3887 } // end of 'if (Dim == 1)'
3888
3890 sequence++;
3891
3892 UpdateNodes();
3893
3894#ifdef MFEM_DEBUG
3897#endif
3898}
3899
3901 std::set<int> &conflicts) const
3902{
3903 MFEM_VERIFY(pncmesh, "AnisotropicConflict should be called only for NCMesh");
3904 return pncmesh->AnisotropicConflict(refinements, conflicts);
3905}
3906
3908 int nc_limit)
3909{
3910 if (NURBSext)
3911 {
3912 MFEM_ABORT("NURBS meshes are not supported. Please project the "
3913 "NURBS to Nodes first with SetCurvature().");
3914 }
3915
3916 if (!pncmesh)
3917 {
3918 MFEM_ABORT("Can't convert conforming ParMesh to nonconforming ParMesh "
3919 "(you need to initialize the ParMesh from a nonconforming "
3920 "serial Mesh)");
3921 }
3922
3923 ResetLazyData();
3924
3926
3927 // NOTE: no check of !refinements.Size(), in parallel we would have to reduce
3928
3929 // do the refinements
3931 pncmesh->Refine(refinements);
3932
3933 if (nc_limit > 0)
3934 {
3935 pncmesh->LimitNCLevel(nc_limit);
3936 }
3937
3938 // create a second mesh containing the finest elements from 'pncmesh'
3939 ParMesh* pmesh2 = new ParMesh(*pncmesh);
3940 pncmesh->OnMeshUpdated(pmesh2);
3941
3942 attributes.Copy(pmesh2->attributes);
3944
3945 // Copy attribute and bdr_attribute names
3948
3949 // now swap the meshes, the second mesh will become the old coarse mesh
3950 // and this mesh will be the new fine mesh
3951 Mesh::Swap(*pmesh2, false);
3952
3953 delete pmesh2; // NOTE: old face neighbors destroyed here
3954
3956
3958
3960 sequence++;
3961
3962 UpdateNodes();
3963}
3964
3966 real_t threshold, int nc_limit, int op)
3967{
3968 MFEM_VERIFY(pncmesh, "Only supported for non-conforming meshes.");
3969 MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
3970 "Project the NURBS to Nodes first.");
3971
3972 const Table &dt = pncmesh->GetDerefinementTable();
3973
3974 pncmesh->SynchronizeDerefinementData(elem_error, dt);
3975
3976 Array<int> level_ok;
3977 if (nc_limit > 0)
3978 {
3979 pncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
3980 }
3981
3982 Array<int> derefs;
3983 for (int i = 0; i < dt.Size(); i++)
3984 {
3985 if (nc_limit > 0 && !level_ok[i]) { continue; }
3986
3987 real_t error =
3988 AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
3989
3990 if (error < threshold) { derefs.Append(i); }
3991 }
3992
3993 long long glob_size = ReduceInt(derefs.Size());
3994 if (!glob_size) { return false; }
3995
3996 // Destroy face-neighbor data only when actually de-refining.
3998
3999 pncmesh->Derefine(derefs);
4000
4001 ParMesh* mesh2 = new ParMesh(*pncmesh);
4002 pncmesh->OnMeshUpdated(mesh2);
4003
4004 attributes.Copy(mesh2->attributes);
4006
4007 // Copy attribute and bdr_attribute names
4010
4011 Mesh::Swap(*mesh2, false);
4012 delete mesh2;
4013
4015
4017
4019 sequence++;
4020
4021 UpdateNodes();
4022
4023 return true;
4024}
4025
4026
4028{
4029 RebalanceImpl(NULL); // default SFC-based partition
4030}
4031
4032void ParMesh::Rebalance(const Array<int> &partition)
4033{
4034 RebalanceImpl(&partition);
4035}
4036
4038{
4039 if (Conforming())
4040 {
4041 MFEM_ABORT("Load balancing is currently not supported for conforming"
4042 " meshes.");
4043 }
4044
4045 if (Nodes)
4046 {
4047 // check that Nodes use a parallel FE space, so we can call UpdateNodes()
4048 MFEM_VERIFY(dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace())
4049 != NULL, "internal error");
4050 }
4051
4053
4054 pncmesh->Rebalance(partition);
4055
4056 ParMesh* pmesh2 = new ParMesh(*pncmesh);
4057 pncmesh->OnMeshUpdated(pmesh2);
4058
4059 attributes.Copy(pmesh2->attributes);
4061
4062 // Copy attribute and bdr_attribute names
4065
4066 Mesh::Swap(*pmesh2, false);
4067 delete pmesh2;
4068
4070
4072
4074 sequence++;
4075
4076 UpdateNodes();
4077}
4078
4079void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
4080{
4081 // Refine groups after LocalRefinement in 2D (triangle meshes)
4082
4083 MFEM_ASSERT(Dim == 2 && meshgen == 1, "internal error");
4084
4085 Array<int> group_verts, group_edges;
4086
4087 // To update the groups after a refinement, we observe that:
4088 // - every (new and old) vertex, edge and face belongs to exactly one group
4089 // - the refinement does not create new groups
4090 // - a new vertex appears only as the middle of a refined edge
4091 // - a face can be refined 2, 3 or 4 times producing new edges and faces
4092
4093 int *I_group_svert, *J_group_svert;
4094 int *I_group_sedge, *J_group_sedge;
4095
4096 I_group_svert = Memory<int>(GetNGroups()+1);
4097 I_group_sedge = Memory<int>(GetNGroups()+1);
4098
4099 I_group_svert[0] = I_group_svert[1] = 0;
4100 I_group_sedge[0] = I_group_sedge[1] = 0;
4101
4102 // overestimate the size of the J arrays
4103 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4105 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4106
4107 for (int group = 0; group < GetNGroups()-1; group++)
4108 {
4109 // Get the group shared objects
4110 group_svert.GetRow(group, group_verts);
4111 group_sedge.GetRow(group, group_edges);
4112
4113 // Check which edges have been refined
4114 for (int i = 0; i < group_sedge.RowSize(group); i++)
4115 {
4116 int *v = shared_edges[group_edges[i]]->GetVertices();
4117 const int ind = middle[v_to_v(v[0], v[1])];
4118 if (ind != -1)
4119 {
4120 // add a vertex
4121 group_verts.Append(svert_lvert.Append(ind)-1);
4122 // update the edges
4123 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4124 shared_edges.Append(new Segment(v[1], ind, attr));
4125 group_edges.Append(sedge_ledge.Append(-1)-1);
4126 v[1] = ind;
4127 }
4128 }
4129
4130 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4131 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4132
4133 int *J;
4134 J = J_group_svert+I_group_svert[group];
4135 for (int i = 0; i < group_verts.Size(); i++)
4136 {
4137 J[i] = group_verts[i];
4138 }
4139 J = J_group_sedge+I_group_sedge[group];
4140 for (int i = 0; i < group_edges.Size(); i++)
4141 {
4142 J[i] = group_edges[i];
4143 }
4144 }
4145
4147
4148 group_svert.SetIJ(I_group_svert, J_group_svert);
4149 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4150}
4151
4152void ParMesh::RefineGroups(int old_nv, const HashTable<Hashed2> &v_to_v)
4153{
4154 // Refine groups after LocalRefinement in 3D (tetrahedral meshes)
4155
4156 MFEM_ASSERT(Dim == 3 && meshgen == 1, "internal error");
4157
4158 Array<int> group_verts, group_edges, group_trias;
4159
4160 // To update the groups after a refinement, we observe that:
4161 // - every (new and old) vertex, edge and face belongs to exactly one group
4162 // - the refinement does not create new groups
4163 // - a new vertex appears only as the middle of a refined edge
4164 // - a face can be refined multiple times producing new edges and faces
4165
4166 Array<Segment *> sedge_stack;
4167 Array<Vert3> sface_stack;
4168
4169 Array<int> I_group_svert, J_group_svert;
4170 Array<int> I_group_sedge, J_group_sedge;
4171 Array<int> I_group_stria, J_group_stria;
4172
4173 I_group_svert.SetSize(GetNGroups());
4174 I_group_sedge.SetSize(GetNGroups());
4175 I_group_stria.SetSize(GetNGroups());
4176
4177 I_group_svert[0] = 0;
4178 I_group_sedge[0] = 0;
4179 I_group_stria[0] = 0;
4180
4181 for (int group = 0; group < GetNGroups()-1; group++)
4182 {
4183 // Get the group shared objects
4184 group_svert.GetRow(group, group_verts);
4185 group_sedge.GetRow(group, group_edges);
4186 group_stria.GetRow(group, group_trias);
4187
4188 // Check which edges have been refined
4189 for (int i = 0; i < group_sedge.RowSize(group); i++)
4190 {
4191 int *v = shared_edges[group_edges[i]]->GetVertices();
4192 int ind = v_to_v.FindId(v[0], v[1]);
4193 if (ind == -1) { continue; }
4194
4195 // This shared edge is refined: walk the whole refinement tree
4196 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4197 do
4198 {
4199 ind += old_nv;
4200 // Add new shared vertex
4201 group_verts.Append(svert_lvert.Append(ind)-1);
4202 // Put the right sub-edge on top of the stack
4203 sedge_stack.Append(new Segment(ind, v[1], attr));
4204 // The left sub-edge replaces the original edge
4205 v[1] = ind;
4206 ind = v_to_v.FindId(v[0], ind);
4207 }
4208 while (ind != -1);
4209 // Process all edges in the edge stack
4210 do
4211 {
4212 Segment *se = sedge_stack.Last();
4213 v = se->GetVertices();
4214 ind = v_to_v.FindId(v[0], v[1]);
4215 if (ind == -1)
4216 {
4217 // The edge 'se' is not refined
4218 sedge_stack.DeleteLast();
4219 // Add new shared edge
4220 shared_edges.Append(se);
4221 group_edges.Append(sedge_ledge.Append(-1)-1);
4222 }
4223 else
4224 {
4225 // The edge 'se' is refined
4226 ind += old_nv;
4227 // Add new shared vertex
4228 group_verts.Append(svert_lvert.Append(ind)-1);
4229 // Put the left sub-edge on top of the stack
4230 sedge_stack.Append(new Segment(v[0], ind, attr));
4231 // The right sub-edge replaces the original edge
4232 v[0] = ind;
4233 }
4234 }
4235 while (sedge_stack.Size() > 0);
4236 }
4237
4238 // Check which triangles have been refined
4239 for (int i = 0; i < group_stria.RowSize(group); i++)
4240 {
4241 int *v = shared_trias[group_trias[i]].v;
4242 int ind = v_to_v.FindId(v[0], v[1]);
4243 if (ind == -1) { continue; }
4244
4245 // This shared face is refined: walk the whole refinement tree
4246 const int edge_attr = 1;
4247 do
4248 {
4249 ind += old_nv;
4250 // Add the refinement edge to the edge stack
4251 sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4252 // Put the right sub-triangle on top of the face stack
4253 sface_stack.Append(Vert3(v[1], v[2], ind));
4254 // The left sub-triangle replaces the original one
4255 v[1] = v[0]; v[0] = v[2]; v[2] = ind;
4256 ind = v_to_v.FindId(v[0], v[1]);
4257 }
4258 while (ind != -1);
4259 // Process all faces (triangles) in the face stack
4260 do
4261 {
4262 Vert3 &st = sface_stack.Last();
4263 v = st.v;
4264 ind = v_to_v.FindId(v[0], v[1]);
4265 if (ind == -1)
4266 {
4267 // The triangle 'st' is not refined
4268 // Add new shared face
4269 shared_trias.Append(st);
4270 group_trias.Append(sface_lface.Append(-1)-1);
4271 sface_stack.DeleteLast();
4272 }
4273 else
4274 {
4275 // The triangle 'st' is refined
4276 ind += old_nv;
4277 // Add the refinement edge to the edge stack
4278 sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4279 // Put the left sub-triangle on top of the face stack
4280 sface_stack.Append(Vert3(v[2], v[0], ind));
4281 // Note that the above Append() may invalidate 'v'
4282 v = sface_stack[sface_stack.Size()-2].v;
4283 // The right sub-triangle replaces the original one
4284 v[0] = v[1]; v[1] = v[2]; v[2] = ind;
4285 }
4286 }
4287 while (sface_stack.Size() > 0);
4288 // Process all edges in the edge stack (same code as above)
4289 do
4290 {
4291 Segment *se = sedge_stack.Last();
4292 v = se->GetVertices();
4293 ind = v_to_v.FindId(v[0], v[1]);
4294 if (ind == -1)
4295 {
4296 // The edge 'se' is not refined
4297 sedge_stack.DeleteLast();
4298 // Add new shared edge
4299 shared_edges.Append(se);
4300 group_edges.Append(sedge_ledge.Append(-1)-1);
4301 }
4302 else
4303 {
4304 // The edge 'se' is refined
4305 ind += old_nv;
4306 // Add new shared vertex
4307 group_verts.Append(svert_lvert.Append(ind)-1);
4308 // Put the left sub-edge on top of the stack
4309 sedge_stack.Append(new Segment(v[0], ind, edge_attr));
4310 // The right sub-edge replaces the original edge
4311 v[0] = ind;
4312 }
4313 }
4314 while (sedge_stack.Size() > 0);
4315 }
4316
4317 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4318 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4319 I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4320
4321 J_group_svert.Append(group_verts);
4322 J_group_sedge.Append(group_edges);
4323 J_group_stria.Append(group_trias);
4324 }
4325
4327
4328 group_svert.SetIJ(I_group_svert, J_group_svert);
4329 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4330 group_stria.SetIJ(I_group_stria, J_group_stria);
4331 I_group_svert.LoseData(); J_group_svert.LoseData();
4332 I_group_sedge.LoseData(); J_group_sedge.LoseData();
4333 I_group_stria.LoseData(); J_group_stria.LoseData();
4334}
4335
4337{
4338 Array<int> sverts, sedges;
4339
4340 int *I_group_svert, *J_group_svert;
4341 int *I_group_sedge, *J_group_sedge;
4342
4343 I_group_svert = Memory<int>(GetNGroups());
4344 I_group_sedge = Memory<int>(GetNGroups());
4345
4346 I_group_svert[0] = 0;
4347 I_group_sedge[0] = 0;
4348
4349 // compute the size of the J arrays
4350 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4352 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4353
4354 for (int group = 0; group < GetNGroups()-1; group++)
4355 {
4356 // Get the group shared objects
4357 group_svert.GetRow(group, sverts);
4358 group_sedge.GetRow(group, sedges);
4359
4360 // Process all the edges
4361 for (int i = 0; i < group_sedge.RowSize(group); i++)
4362 {
4363 int *v = shared_edges[sedges[i]]->GetVertices();
4364 const int ind = old_nv + sedge_ledge[sedges[i]];
4365 // add a vertex
4366 sverts.Append(svert_lvert.Append(ind)-1);
4367 // update the edges
4368 const int attr = shared_edges[sedges[i]]->GetAttribute();
4369 shared_edges.Append(new Segment(v[1], ind, attr));
4370 sedges.Append(sedge_ledge.Append(-1)-1);
4371 v[1] = ind;
4372 }
4373
4374 I_group_svert[group+1] = I_group_svert[group] + sverts.Size();
4375 I_group_sedge[group+1] = I_group_sedge[group] + sedges.Size();
4376
4377 sverts.CopyTo(J_group_svert + I_group_svert[group]);
4378 sedges.CopyTo(J_group_sedge + I_group_sedge[group]);
4379 }
4380
4382
4383 group_svert.SetIJ(I_group_svert, J_group_svert);
4384 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4385}
4386
4387void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
4388 const DSTable &old_v_to_v,
4389 const STable3D &old_faces,
4390 Array<int> *f2qf)
4391{
4392 // f2qf can be NULL if all faces are quads or there are no quad faces
4393
4394 Array<int> group_verts, group_edges, group_trias, group_quads;
4395
4396 int *I_group_svert, *J_group_svert;
4397 int *I_group_sedge, *J_group_sedge;
4398 int *I_group_stria, *J_group_stria;
4399 int *I_group_squad, *J_group_squad;
4400
4401 I_group_svert = Memory<int>(GetNGroups());
4402 I_group_sedge = Memory<int>(GetNGroups());
4403 I_group_stria = Memory<int>(GetNGroups());
4404 I_group_squad = Memory<int>(GetNGroups());
4405
4406 I_group_svert[0] = 0;
4407 I_group_sedge[0] = 0;
4408 I_group_stria[0] = 0;
4409 I_group_squad[0] = 0;
4410
4411 // compute the size of the J arrays
4412 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4415 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections() +
4418 J_group_stria = Memory<int>(4*group_stria.Size_of_connections());
4419 J_group_squad = Memory<int>(4*group_squad.Size_of_connections());
4420
4421 const int oface = old_nv + old_nedges;
4422
4423 for (int group = 0; group < GetNGroups()-1; group++)
4424 {
4425 // Get the group shared objects
4426 group_svert.GetRow(group, group_verts);
4427 group_sedge.GetRow(group, group_edges);
4428 group_stria.GetRow(group, group_trias);
4429 group_squad.GetRow(group, group_quads);
4430
4431 // Process the edges that have been refined
4432 for (int i = 0; i < group_sedge.RowSize(group); i++)
4433 {
4434 int *v = shared_edges[group_edges[i]]->GetVertices();
4435 const int ind = old_nv + old_v_to_v(v[0], v[1]);
4436 // add a vertex
4437 group_verts.Append(svert_lvert.Append(ind)-1);
4438 // update the edges
4439 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4440 shared_edges.Append(new Segment(v[1], ind, attr));
4441 group_edges.Append(sedge_ledge.Append(-1)-1);
4442 v[1] = ind; // v[0] remains the same
4443 }
4444
4445 // Process the triangles that have been refined
4446 for (int i = 0; i < group_stria.RowSize(group); i++)
4447 {
4448 int m[3];
4449 const int stria = group_trias[i];
4450 int *v = shared_trias[stria].v;
4451 // add the refinement edges
4452 m[0] = old_nv + old_v_to_v(v[0], v[1]);
4453 m[1] = old_nv + old_v_to_v(v[1], v[2]);
4454 m[2] = old_nv + old_v_to_v(v[2], v[0]);
4455 const int edge_attr = 1;
4456 shared_edges.Append(new Segment(m[0], m[1], edge_attr));
4457 group_edges.Append(sedge_ledge.Append(-1)-1);
4458 shared_edges.Append(new Segment(m[1], m[2], edge_attr));
4459 group_edges.Append(sedge_ledge.Append(-1)-1);
4460 shared_edges.Append(new Segment(m[0], m[2], edge_attr));
4461 group_edges.Append(sedge_ledge.Append(-1)-1);
4462 // update faces
4463 const int nst = shared_trias.Size();
4464 shared_trias.SetSize(nst+3);
4465 // The above SetSize() may invalidate 'v'
4466 v = shared_trias[stria].v;
4467 shared_trias[nst+0].Set(m[1],m[2],m[0]);
4468 shared_trias[nst+1].Set(m[0],v[1],m[1]);
4469 shared_trias[nst+2].Set(m[2],m[1],v[2]);
4470 v[1] = m[0]; v[2] = m[2]; // v[0] remains the same
4471 group_trias.Append(nst+0);
4472 group_trias.Append(nst+1);
4473 group_trias.Append(nst+2);
4474 // sface_lface is set later
4475 }
4476
4477 // Process the quads that have been refined
4478 for (int i = 0; i < group_squad.RowSize(group); i++)
4479 {
4480 int m[5];
4481 const int squad = group_quads[i];
4482 int *v = shared_quads[squad].v;
4483 const int olf = old_faces(v[0], v[1], v[2], v[3]);
4484 // f2qf can be NULL if all faces are quads
4485 m[0] = oface + (f2qf ? (*f2qf)[olf] : olf);
4486 // add a vertex
4487 group_verts.Append(svert_lvert.Append(m[0])-1);
4488 // add the refinement edges
4489 m[1] = old_nv + old_v_to_v(v[0], v[1]);
4490 m[2] = old_nv + old_v_to_v(v[1], v[2]);
4491 m[3] = old_nv + old_v_to_v(v[2], v[3]);
4492 m[4] = old_nv + old_v_to_v(v[3], v[0]);
4493 const int edge_attr = 1;
4494 shared_edges.Append(new Segment(m[1], m[0], edge_attr));
4495 group_edges.Append(sedge_ledge.Append(-1)-1);
4496 shared_edges.Append(new Segment(m[2], m[0], edge_attr));
4497 group_edges.Append(sedge_ledge.Append(-1)-1);
4498 shared_edges.Append(new Segment(m[3], m[0], edge_attr));
4499 group_edges.Append(sedge_ledge.Append(-1)-1);
4500 shared_edges.Append(new Segment(m[4], m[0], edge_attr));
4501 group_edges.Append(sedge_ledge.Append(-1)-1);
4502 // update faces
4503 const int nsq = shared_quads.Size();
4504 shared_quads.SetSize(nsq+3);
4505 // The above SetSize() may invalidate 'v'
4506 v = shared_quads[squad].v;
4507 shared_quads[nsq+0].Set(m[1],v[1],m[2],m[0]);
4508 shared_quads[nsq+1].Set(m[0],m[2],v[2],m[3]);
4509 shared_quads[nsq+2].Set(m[4],m[0],m[3],v[3]);
4510 v[1] = m[1]; v[2] = m[0]; v[3] = m[4]; // v[0] remains the same
4511 group_quads.Append(nsq+0);
4512 group_quads.Append(nsq+1);
4513 group_quads.Append(nsq+2);
4514 // sface_lface is set later
4515 }
4516
4517 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4518 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4519 I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4520 I_group_squad[group+1] = I_group_squad[group] + group_quads.Size();
4521
4522 group_verts.CopyTo(J_group_svert + I_group_svert[group]);
4523 group_edges.CopyTo(J_group_sedge + I_group_sedge[group]);
4524 group_trias.CopyTo(J_group_stria + I_group_stria[group]);
4525 group_quads.CopyTo(J_group_squad + I_group_squad[group]);
4526 }
4527
4529
4530 group_svert.SetIJ(I_group_svert, J_group_svert);
4531 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4532 group_stria.SetIJ(I_group_stria, J_group_stria);
4533 group_squad.SetIJ(I_group_squad, J_group_squad);
4534}
4535
4537{
4539
4540 const int old_nv = NumOfVertices;
4541
4542 // call Mesh::UniformRefinement2D so that it won't update the nodes
4543 {
4544 const bool update_nodes = false;
4545 Mesh::UniformRefinement2D_base(update_nodes);
4546 }
4547
4548 // update the groups
4549 UniformRefineGroups2D(old_nv);
4550
4551 UpdateNodes();
4552
4553#ifdef MFEM_DEBUG
4554 // If there are no Nodes, the orientation is checked in the call to
4555 // UniformRefinement2D_base() above.
4556 if (Nodes) { CheckElementOrientation(false); }
4557#endif
4558}
4559
4561{
4563
4564 const int old_nv = NumOfVertices;
4565 const int old_nedges = NumOfEdges;
4566
4567 DSTable v_to_v(NumOfVertices);
4568 GetVertexToVertexTable(v_to_v);
4569 auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
4570
4571 // call Mesh::UniformRefinement3D_base so that it won't update the nodes
4572 Array<int> f2qf;
4573 {
4574 const bool update_nodes = false;
4575 UniformRefinement3D_base(&f2qf, &v_to_v, update_nodes);
4576 // Note: for meshes that have triangular faces, v_to_v is modified by the
4577 // above call to return different edge indices - this is used when
4578 // updating the groups. This is needed by ReorientTetMesh().
4579 }
4580
4581 // update the groups
4582 UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
4583 f2qf.Size() ? &f2qf : NULL);
4584
4585 UpdateNodes();
4586}
4587
4589{
4590 if (MyRank == 0)
4591 {
4592 mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
4593 }
4594}
4595
4597{
4598 if (MyRank == 0)
4599 {
4600 mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
4601 }
4602}
4603
4604void ParMesh::RefineNURBSWithKVFactors(int rf, const std::string &kvf)
4605{
4606 if (MyRank == 0)
4607 {
4608 mfem::out << "\nRefineNURBSWithKVFactors : Not supported yet!\n";
4609 }
4610}
4611
4612void ParMesh::PrintXG(std::ostream &os) const
4613{
4614 MFEM_ASSERT(Dim == spaceDim, "2D manifolds not supported");
4615 if (Dim == 3 && meshgen == 1)
4616 {
4617 int i, j, nv;
4618 const int *ind;
4619
4620 os << "NETGEN_Neutral_Format\n";
4621 // print the vertices
4622 os << NumOfVertices << '\n';
4623 for (i = 0; i < NumOfVertices; i++)
4624 {
4625 for (j = 0; j < Dim; j++)
4626 {
4627 os << " " << vertices[i](j);
4628 }
4629 os << '\n';
4630 }
4631
4632 // print the elements
4633 os << NumOfElements << '\n';
4634 for (i = 0; i < NumOfElements; i++)
4635 {
4636 nv = elements[i]->GetNVertices();
4637 ind = elements[i]->GetVertices();
4638 os << elements[i]->GetAttribute();
4639 for (j = 0; j < nv; j++)
4640 {
4641 os << " " << ind[j]+1;
4642 }
4643 os << '\n';
4644 }
4645
4646 // print the boundary + shared faces information
4647 os << NumOfBdrElements + sface_lface.Size() << '\n';
4648 // boundary
4649 for (i = 0; i < NumOfBdrElements; i++)
4650 {
4651 nv = boundary[i]->GetNVertices();
4652 ind = boundary[i]->GetVertices();
4653 os << boundary[i]->GetAttribute();
4654 for (j = 0; j < nv; j++)
4655 {
4656 os << " " << ind[j]+1;
4657 }
4658 os << '\n';
4659 }
4660 // shared faces
4661 const int sf_attr =
4662 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4663 for (i = 0; i < shared_trias.Size(); i++)
4664 {
4665 ind = shared_trias[i].v;
4666 os << sf_attr;
4667 for (j = 0; j < 3; j++)
4668 {
4669 os << ' ' << ind[j]+1;
4670 }
4671 os << '\n';
4672 }
4673 // There are no quad shared faces
4674 }
4675
4676 if (Dim == 3 && meshgen == 2)
4677 {
4678 int i, j, nv;
4679 const int *ind;
4680
4681 os << "TrueGrid\n"
4682 << "1 " << NumOfVertices << " " << NumOfElements
4683 << " 0 0 0 0 0 0 0\n"
4684 << "0 0 0 1 0 0 0 0 0 0 0\n"
4685 << "0 0 " << NumOfBdrElements+sface_lface.Size()
4686 << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
4687 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
4688 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
4689
4690 // print the vertices
4691 for (i = 0; i < NumOfVertices; i++)
4692 {
4693 os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
4694 << " " << vertices[i](2) << " 0.0\n";
4695 }
4696
4697 // print the elements
4698 for (i = 0; i < NumOfElements; i++)
4699 {
4700 nv = elements[i]->GetNVertices();
4701 ind = elements[i]->GetVertices();
4702 os << i+1 << " " << elements[i]->GetAttribute();
4703 for (j = 0; j < nv; j++)
4704 {
4705 os << " " << ind[j]+1;
4706 }
4707 os << '\n';
4708 }
4709
4710 // print the boundary information
4711 for (i = 0; i < NumOfBdrElements; i++)
4712 {
4713 nv = boundary[i]->GetNVertices();
4714 ind = boundary[i]->GetVertices();
4715 os << boundary[i]->GetAttribute();
4716 for (j = 0; j < nv; j++)
4717 {
4718 os << " " << ind[j]+1;
4719 }
4720 os << " 1.0 1.0 1.0 1.0\n";
4721 }
4722
4723 // print the shared faces information
4724 const int sf_attr =
4725 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4726 // There are no shared triangle faces
4727 for (i = 0; i < shared_quads.Size(); i++)
4728 {
4729 ind = shared_quads[i].v;
4730 os << sf_attr;
4731 for (j = 0; j < 4; j++)
4732 {
4733 os << ' ' << ind[j]+1;
4734 }
4735 os << " 1.0 1.0 1.0 1.0\n";
4736 }
4737 }
4738
4739 if (Dim == 2)
4740 {
4741 int i, j, attr;
4742 Array<int> v;
4743
4744 os << "areamesh2\n\n";
4745
4746 // print the boundary + shared edges information
4747 os << NumOfBdrElements + shared_edges.Size() << '\n';
4748 // boundary
4749 for (i = 0; i < NumOfBdrElements; i++)
4750 {
4751 attr = boundary[i]->GetAttribute();
4752 boundary[i]->GetVertices(v);
4753 os << attr << " ";
4754 for (j = 0; j < v.Size(); j++)
4755 {
4756 os << v[j] + 1 << " ";
4757 }
4758 os << '\n';
4759 }
4760 // shared edges
4761 for (i = 0; i < shared_edges.Size(); i++)
4762 {
4763 attr = shared_edges[i]->GetAttribute();
4764 shared_edges[i]->GetVertices(v);
4765 os << attr << " ";
4766 for (j = 0; j < v.Size(); j++)
4767 {
4768 os << v[j] + 1 << " ";
4769 }
4770 os << '\n';
4771 }
4772
4773 // print the elements
4774 os << NumOfElements << '\n';
4775 for (i = 0; i < NumOfElements; i++)
4776 {
4777 attr = elements[i]->GetAttribute();
4778 elements[i]->GetVertices(v);
4779
4780 os << attr << " ";
4781 if ((j = GetElementType(i)) == Element::TRIANGLE)
4782 {
4783 os << 3 << " ";
4784 }
4785 else if (j == Element::QUADRILATERAL)
4786 {
4787 os << 4 << " ";
4788 }
4789 else if (j == Element::SEGMENT)
4790 {
4791 os << 2 << " ";
4792 }
4793 for (j = 0; j < v.Size(); j++)
4794 {
4795 os << v[j] + 1 << " ";
4796 }
4797 os << '\n';
4798 }
4799
4800 // print the vertices
4801 os << NumOfVertices << '\n';
4802 for (i = 0; i < NumOfVertices; i++)
4803 {
4804 for (j = 0; j < Dim; j++)
4805 {
4806 os << vertices[i](j) << " ";
4807 }
4808 os << '\n';
4809 }
4810 }
4811 os.flush();
4812}
4813
4815{
4816 // In 2D, this is a workaround for a CPU boundary rendering artifact. We need
4817 // to skip a shared master edge if one of its slaves has the same rank.
4818
4819 const NCMesh::NCList &list = pncmesh->GetEdgeList();
4820 for (int i = master.slaves_begin; i < master.slaves_end; i++)
4821 {
4822 if (!pncmesh->IsGhost(1, list.slaves[i].index)) { return true; }
4823 }
4824 return false;
4825}
4826
4827void ParMesh::Print(std::ostream &os, const std::string &comments) const
4828{
4829 int shared_bdr_attr;
4830 Array<int> nc_shared_faces;
4831
4832 if (NURBSext)
4833 {
4834 Printer(os, "", comments); // does not print shared boundary
4835 return;
4836 }
4837
4838 const Array<int>* s2l_face;
4839 if (!pncmesh)
4840 {
4841 s2l_face = ((Dim == 1) ? &svert_lvert :
4842 ((Dim == 2) ? &sedge_ledge : &sface_lface));
4843 }
4844 else
4845 {
4846 s2l_face = &nc_shared_faces;
4847 if (Dim >= 2)
4848 {
4849 // get a list of all shared non-ghost faces
4850 const NCMesh::NCList& sfaces =
4852 const int nfaces = GetNumFaces();
4853 for (int i = 0; i < sfaces.conforming.Size(); i++)
4854 {
4855 int index = sfaces.conforming[i].index;
4856 if (index < nfaces) { nc_shared_faces.Append(index); }
4857 }
4858 for (int i = 0; i < sfaces.masters.Size(); i++)
4859 {
4860 if (Dim == 2 && WantSkipSharedMaster(sfaces.masters[i])) { continue; }
4861 int index = sfaces.masters[i].index;
4862 if (index < nfaces) { nc_shared_faces.Append(index); }
4863 }
4864 for (int i = 0; i < sfaces.slaves.Size(); i++)
4865 {
4866 int index = sfaces.slaves[i].index;
4867 if (index < nfaces) { nc_shared_faces.Append(index); }
4868 }
4869 }
4870 }
4871
4872 const bool set_names = attribute_sets.SetsExist() ||
4874
4875 os << (!set_names ? "MFEM mesh v1.0\n" : "MFEM mesh v1.3\n");
4876
4877 if (!comments.empty()) { os << '\n' << comments << '\n'; }
4878
4879 // optional
4880 os <<
4881 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
4882 "# POINT = 0\n"
4883 "# SEGMENT = 1\n"
4884 "# TRIANGLE = 2\n"
4885 "# SQUARE = 3\n"
4886 "# TETRAHEDRON = 4\n"
4887 "# CUBE = 5\n"
4888 "# PRISM = 6\n"
4889 "#\n";
4890
4891 os << "\ndimension\n" << Dim
4892 << "\n\nelements\n" << NumOfElements << '\n';
4893 for (int i = 0; i < NumOfElements; i++)
4894 {
4895 PrintElement(elements[i], os);
4896 }
4897
4898 if (set_names)
4899 {
4900 os << "\nattribute_sets\n";
4902 }
4903
4904 int num_bdr_elems = NumOfBdrElements;
4905 if (print_shared && Dim > 1)
4906 {
4907 num_bdr_elems += s2l_face->Size();
4908 }
4909 os << "\nboundary\n" << num_bdr_elems << '\n';
4910 for (int i = 0; i < NumOfBdrElements; i++)
4911 {
4912 PrintElement(boundary[i], os);
4913 }
4914
4915 if (print_shared && Dim > 1)
4916 {
4917 if (bdr_attributes.Size())
4918 {
4919 shared_bdr_attr = bdr_attributes.Max() + MyRank + 1;
4920 }
4921 else
4922 {
4923 shared_bdr_attr = MyRank + 1;
4924 }
4925 for (int i = 0; i < s2l_face->Size(); i++)
4926 {
4927 // Modify the attributes of the faces (not used otherwise?)
4928 faces[(*s2l_face)[i]]->SetAttribute(shared_bdr_attr);
4929 PrintElement(faces[(*s2l_face)[i]], os);
4930 }
4931 }
4932
4933 if (set_names)
4934 {
4935 os << "\nbdr_attribute_sets\n";
4937 }
4938
4939 os << "\nvertices\n" << NumOfVertices << '\n';
4940 if (Nodes == NULL)
4941 {
4942 os << spaceDim << '\n';
4943 for (int i = 0; i < NumOfVertices; i++)
4944 {
4945 os << vertices[i](0);
4946 for (int j = 1; j < spaceDim; j++)
4947 {
4948 os << ' ' << vertices[i](j);
4949 }
4950 os << '\n';
4951 }
4952 os.flush();
4953 }
4954 else
4955 {
4956 os << "\nnodes\n";
4957 Nodes->Save(os);
4958 }
4959
4960 if (set_names)
4961 {
4962 os << "\nmfem_mesh_end" << endl;
4963 }
4964}
4965
4966void ParMesh::Save(const std::string &fname, int precision) const
4967{
4968 ostringstream fname_with_suffix;
4969 fname_with_suffix << fname << "." << setfill('0') << setw(6) << MyRank;
4970 ofstream ofs(fname_with_suffix.str().c_str());
4971 ofs.precision(precision);
4972 Print(ofs);
4973}
4974
4975#ifdef MFEM_USE_ADIOS2
4977{
4978 Mesh::Print(os);
4979}
4980#endif
4981
4982static void dump_element(const Element* elem, Array<int> &data)
4983{
4984 data.Append(elem->GetGeometryType());
4985
4986 int nv = elem->GetNVertices();
4987 const int *v = elem->GetVertices();
4988 for (int i = 0; i < nv; i++)
4989 {
4990 data.Append(v[i]);
4991 }
4992}
4993
4994void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
4995{
4996 int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc;
4997 const int *v;
4998 MPI_Status status;
4999 Array<real_t> vert;
5000 Array<int> ints;
5001
5002 if (MyRank == 0)
5003 {
5004 os << "MFEM mesh v1.0\n";
5005
5006 if (!comments.empty()) { os << '\n' << comments << '\n'; }
5007
5008 // optional
5009 os <<
5010 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
5011 "# POINT = 0\n"
5012 "# SEGMENT = 1\n"
5013 "# TRIANGLE = 2\n"
5014 "# SQUARE = 3\n"
5015 "# TETRAHEDRON = 4\n"
5016 "# CUBE = 5\n"
5017 "# PRISM = 6\n"
5018 "#\n";
5019
5020 os << "\ndimension\n" << Dim;
5021 }
5022
5023 nv = NumOfElements;
5024 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5025 if (MyRank == 0)
5026 {
5027 os << "\n\nelements\n" << ne << '\n';
5028 for (i = 0; i < NumOfElements; i++)
5029 {
5030 // processor number + 1 as attribute and geometry type
5031 os << 1 << ' ' << elements[i]->GetGeometryType();
5032 // vertices
5033 nv = elements[i]->GetNVertices();
5034 v = elements[i]->GetVertices();
5035 for (j = 0; j < nv; j++)
5036 {
5037 os << ' ' << v[j];
5038 }
5039 os << '\n';
5040 }
5041 vc = NumOfVertices;
5042 for (p = 1; p < NRanks; p++)
5043 {
5044 MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
5045 ints.SetSize(ne);
5046 if (ne)
5047 {
5048 MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
5049 }
5050 for (i = 0; i < ne; )
5051 {
5052 // processor number + 1 as attribute and geometry type
5053 os << p+1 << ' ' << ints[i];
5054 // vertices
5055 k = Geometries.GetVertices(ints[i++])->GetNPoints();
5056 for (j = 0; j < k; j++)
5057 {
5058 os << ' ' << vc + ints[i++];
5059 }
5060 os << '\n';
5061 }
5062 vc += nv;
5063 }
5064 }
5065 else
5066 {
5067 // for each element send its geometry type and its vertices
5068 ne = 0;
5069 for (i = 0; i < NumOfElements; i++)
5070 {
5071 ne += 1 + elements[i]->GetNVertices();
5072 }
5073 nv = NumOfVertices;
5074 MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
5075
5076 ints.Reserve(ne);
5077 ints.SetSize(0);
5078 for (i = 0; i < NumOfElements; i++)
5079 {
5080 dump_element(elements[i], ints);
5081 }
5082 MFEM_ASSERT(ints.Size() == ne, "");
5083 if (ne)
5084 {
5085 MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm);
5086 }
5087 }
5088
5089 // boundary + shared boundary
5090 ne = NumOfBdrElements;
5091 if (!pncmesh)
5092 {
5093 ne += GetNSharedFaces();
5094 }
5095 else if (Dim > 1)
5096 {
5097 const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5098 ne += list.conforming.Size() + list.masters.Size() + list.slaves.Size();
5099 // In addition to the number returned by GetNSharedFaces(), include the
5100 // the master shared faces as well.
5101 }
5102 ints.Reserve(ne * (1 + 2*(Dim-1))); // just an upper bound
5103 ints.SetSize(0);
5104
5105 // for each boundary and shared boundary element send its geometry type
5106 // and its vertices
5107 ne = 0;
5108 for (i = j = 0; i < NumOfBdrElements; i++)
5109 {
5110 dump_element(boundary[i], ints); ne++;
5111 }
5112 if (!pncmesh)
5113 {
5114 switch (Dim)
5115 {
5116 case 1:
5117 for (i = 0; i < svert_lvert.Size(); i++)
5118 {
5119 ints.Append(Geometry::POINT);
5120 ints.Append(svert_lvert[i]);
5121 ne++;
5122 }
5123 break;
5124
5125 case 2:
5126 for (i = 0; i < shared_edges.Size(); i++)
5127 {
5128 dump_element(shared_edges[i], ints); ne++;
5129 }
5130 break;
5131
5132 case 3:
5133 for (i = 0; i < shared_trias.Size(); i++)
5134 {
5136 ints.Append(shared_trias[i].v, 3);
5137 ne++;
5138 }
5139 for (i = 0; i < shared_quads.Size(); i++)
5140 {
5142 ints.Append(shared_quads[i].v, 4);
5143 ne++;
5144 }
5145 break;
5146
5147 default:
5148 MFEM_ABORT("invalid dimension: " << Dim);
5149 }
5150 }
5151 else if (Dim > 1)
5152 {
5153 const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5154 const int nfaces = GetNumFaces();
5155 for (i = 0; i < list.conforming.Size(); i++)
5156 {
5157 int index = list.conforming[i].index;
5158 if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5159 }
5160 for (i = 0; i < list.masters.Size(); i++)
5161 {
5162 int index = list.masters[i].index;
5163 if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5164 }
5165 for (i = 0; i < list.slaves.Size(); i++)
5166 {
5167 int index = list.slaves[i].index;
5168 if (index < nfaces) { dump_element(faces[index], ints); ne++; }
5169 }
5170 }
5171
5172 MPI_Reduce(&ne, &k, 1, MPI_INT, MPI_SUM, 0, MyComm);
5173 if (MyRank == 0)
5174 {
5175 os << "\nboundary\n" << k << '\n';
5176 vc = 0;
5177 for (p = 0; p < NRanks; p++)
5178 {
5179 if (p)
5180 {
5181 MPI_Recv(nv_ne, 2, MPI_INT, p, 446, MyComm, &status);
5182 ints.SetSize(ne);
5183 if (ne)
5184 {
5185 MPI_Recv(ints.GetData(), ne, MPI_INT, p, 447, MyComm, &status);
5186 }
5187 }
5188 else
5189 {
5190 ne = ints.Size();
5191 nv = NumOfVertices;
5192 }
5193 for (i = 0; i < ne; )
5194 {
5195 // processor number + 1 as bdr. attr. and bdr. geometry type
5196 os << p+1 << ' ' << ints[i];
5197 k = Geometries.NumVerts[ints[i++]];
5198 // vertices
5199 for (j = 0; j < k; j++)
5200 {
5201 os << ' ' << vc + ints[i++];
5202 }
5203 os << '\n';
5204 }
5205 vc += nv;
5206 }
5207 }
5208 else
5209 {
5210 nv = NumOfVertices;
5211 ne = ints.Size();
5212 MPI_Send(nv_ne, 2, MPI_INT, 0, 446, MyComm);
5213 if (ne)
5214 {
5215 MPI_Send(ints.GetData(), ne, MPI_INT, 0, 447, MyComm);
5216 }
5217 }
5218
5219 // vertices / nodes
5220 MPI_Reduce(const_cast<int*>(&NumOfVertices), &nv, 1, MPI_INT, MPI_SUM, 0,
5221 MyComm);
5222 if (MyRank == 0)
5223 {
5224 os << "\nvertices\n" << nv << '\n';
5225 }
5226 if (Nodes == NULL)
5227 {
5228 if (MyRank == 0)
5229 {
5230 os << spaceDim << '\n';
5231 for (i = 0; i < NumOfVertices; i++)
5232 {
5233 os << vertices[i](0);
5234 for (j = 1; j < spaceDim; j++)
5235 {
5236 os << ' ' << vertices[i](j);
5237 }
5238 os << '\n';
5239 }
5240 for (p = 1; p < NRanks; p++)
5241 {
5242 MPI_Recv(&nv, 1, MPI_INT, p, 448, MyComm, &status);
5243 vert.SetSize(nv*spaceDim);
5244 if (nv)
5245 {
5246 MPI_Recv(&vert[0], nv*spaceDim, MPITypeMap<real_t>::mpi_type, p, 449, MyComm,
5247 &status);
5248 }
5249 for (i = 0; i < nv; i++)
5250 {
5251 os << vert[i*spaceDim];
5252 for (j = 1; j < spaceDim; j++)
5253 {
5254 os << ' ' << vert[i*spaceDim+j];
5255 }
5256 os << '\n';
5257 }
5258 }
5259 os.flush();
5260 }
5261 else
5262 {
5263 MPI_Send(const_cast<int*>(&NumOfVertices), 1, MPI_INT, 0, 448, MyComm);
5265 for (i = 0; i < NumOfVertices; i++)
5266 {
5267 for (j = 0; j < spaceDim; j++)
5268 {
5269 vert[i*spaceDim+j] = vertices[i](j);
5270 }
5271 }
5272 if (NumOfVertices)
5273 {
5274 MPI_Send(&vert[0], NumOfVertices*spaceDim, MPITypeMap<real_t>::mpi_type, 0, 449,
5275 MyComm);
5276 }
5277 }
5278 }
5279 else
5280 {
5281 if (MyRank == 0)
5282 {
5283 os << "\nnodes\n";
5284 }
5285 ParGridFunction *pnodes = dynamic_cast<ParGridFunction *>(Nodes);
5286 if (pnodes)
5287 {
5288 pnodes->SaveAsOne(os);
5289 }
5290 else
5291 {
5292 ParFiniteElementSpace *pfes =
5293 dynamic_cast<ParFiniteElementSpace *>(Nodes->FESpace());
5294 if (pfes)
5295 {
5296 // create a wrapper ParGridFunction
5297 ParGridFunction ParNodes(pfes, Nodes);
5298 ParNodes.SaveAsOne(os);
5299 }
5300 else
5301 {
5302 mfem_error("ParMesh::PrintAsOne : Nodes have no parallel info!");
5303 }
5304 }
5305 }
5306}
5307
5308void ParMesh::PrintAsSerial(std::ostream &os, const std::string &comments) const
5309{
5310 int save_rank = 0;
5311 Mesh serialmesh = GetSerialMesh(save_rank);
5312 if (MyRank == save_rank)
5313 {
5314 serialmesh.Printer(os, "", comments);
5315 }
5316 MPI_Barrier(MyComm);
5317}
5318
5319Mesh ParMesh::GetSerialMesh(int save_rank) const
5320{
5321 if (pncmesh || NURBSext)
5322 {
5323 MFEM_ABORT("Nonconforming meshes and NURBS meshes are not yet supported.");
5324 }
5325
5326 // Define linear H1 space for vertex numbering
5327 H1_FECollection fec_linear(1, Dim);
5328 ParMesh *pm = const_cast<ParMesh *>(this);
5329 ParFiniteElementSpace pfespace_linear(pm, &fec_linear);
5330
5331 long long ne_glob_l = GetGlobalNE(); // needs to be called by all ranks
5332 MFEM_VERIFY(int(ne_glob_l) == ne_glob_l,
5333 "overflow in the number of elements!");
5334 int ne_glob = (save_rank == MyRank) ? int(ne_glob_l) : 0;
5335
5336 long long nvertices = pfespace_linear.GetTrueVSize();
5337 long long nvertices_glob_l = 0;
5338 MPI_Reduce(&nvertices, &nvertices_glob_l, 1, MPI_LONG_LONG, MPI_SUM,
5339 save_rank, MyComm);
5340 int nvertices_glob = int(nvertices_glob_l);
5341 MFEM_VERIFY(nvertices_glob == nvertices_glob_l,
5342 "overflow in the number of vertices!");
5343
5344 long long nbe = NumOfBdrElements;
5345 long long nbe_glob_l = 0;
5346 MPI_Reduce(&nbe, &nbe_glob_l, 1, MPI_LONG_LONG, MPI_SUM, save_rank, MyComm);
5347 int nbe_glob = int(nbe_glob_l);
5348 MFEM_VERIFY(nbe_glob == nbe_glob_l,
5349 "overflow in the number of boundary elements!");
5350
5351 // On ranks other than save_rank, the *_glob variables are 0, so the serial
5352 // mesh is empty.
5353 Mesh serialmesh(Dim, nvertices_glob, ne_glob, nbe_glob, spaceDim);
5354
5355 int n_send_recv;
5356 MPI_Status status;
5357 Array<real_t> vert;
5358 Array<int> ints, dofs;
5359
5360 // First set the connectivity of serial mesh using the True Dofs from
5361 // the linear H1 space.
5362 if (MyRank == save_rank)
5363 {
5364 for (int e = 0; e < NumOfElements; e++)
5365 {
5366 const int attr = elements[e]->GetAttribute();
5367 const int geom_type = elements[e]->GetGeometryType();
5368 pfespace_linear.GetElementDofs(e, dofs);
5369 for (int j = 0; j < dofs.Size(); j++)
5370 {
5371 dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5372 }
5373 Element *elem = serialmesh.NewElement(geom_type);
5374 elem->SetAttribute(attr);
5375 elem->SetVertices(dofs);
5376 serialmesh.AddElement(elem);
5377 }
5378
5379 for (int p = 0; p < NRanks; p++)
5380 {
5381 if (p == save_rank) { continue; }
5382 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 444, MyComm, &status);
5383 ints.SetSize(n_send_recv);
5384 if (n_send_recv)
5385 {
5386 MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 445, MyComm, &status);
5387 }
5388 for (int i = 0; i < n_send_recv; )
5389 {
5390 int attr = ints[i++];
5391 int geom_type = ints[i++];
5392 Element *elem = serialmesh.NewElement(geom_type);
5393 elem->SetAttribute(attr);
5394 elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5395 serialmesh.AddElement(elem);
5396 }
5397 }
5398 }
5399 else
5400 {
5401 n_send_recv = 0;
5402 for (int e = 0; e < NumOfElements; e++)
5403 {
5404 n_send_recv += 2 + elements[e]->GetNVertices();
5405 }
5406 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 444, MyComm);
5407 ints.Reserve(n_send_recv);
5408 ints.SetSize(0);
5409 for (int e = 0; e < NumOfElements; e++)
5410 {
5411 const int attr = elements[e]->GetAttribute();
5412 const int geom_type = elements[e]->GetGeometryType();
5413 ints.Append(attr);
5414 ints.Append(geom_type);
5415 pfespace_linear.GetElementDofs(e, dofs);
5416 for (int j = 0; j < dofs.Size(); j++)
5417 {
5418 ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5419 }
5420 }
5421 if (n_send_recv)
5422 {
5423 MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 445, MyComm);
5424 }
5425 }
5426
5427 // Write out boundary elements
5428 if (MyRank == save_rank)
5429 {
5430 for (int e = 0; e < NumOfBdrElements; e++)
5431 {
5432 const int attr = boundary[e]->GetAttribute();
5433 const int geom_type = boundary[e]->GetGeometryType();
5434 pfespace_linear.GetBdrElementDofs(e, dofs);
5435 for (int j = 0; j < dofs.Size(); j++)
5436 {
5437 dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5438 }
5439 Element *elem = serialmesh.NewElement(geom_type);
5440 elem->SetAttribute(attr);
5441 elem->SetVertices(dofs);
5442 serialmesh.AddBdrElement(elem);
5443 }
5444
5445 for (int p = 0; p < NRanks; p++)
5446 {
5447 if (p == save_rank) { continue; }
5448 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 446, MyComm, &status);
5449 ints.SetSize(n_send_recv);
5450 if (n_send_recv)
5451 {
5452 MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 447, MyComm, &status);
5453 }
5454 for (int i = 0; i < n_send_recv; )
5455 {
5456 int attr = ints[i++];
5457 int geom_type = ints[i++];
5458 Element *elem = serialmesh.NewElement(geom_type);
5459 elem->SetAttribute(attr);
5460 elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5461 serialmesh.AddBdrElement(elem);
5462 }
5463 }
5464 } // MyRank == save_rank
5465 else
5466 {
5467 n_send_recv = 0;
5468 for (int e = 0; e < NumOfBdrElements; e++)
5469 {
5470 n_send_recv += 2 + GetBdrElement(e)->GetNVertices();
5471 }
5472 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 446, MyComm);
5473 ints.Reserve(n_send_recv);
5474 ints.SetSize(0);
5475 for (int e = 0; e < NumOfBdrElements; e++)
5476 {
5477 const int attr = boundary[e]->GetAttribute();
5478 const int geom_type = boundary[e]->GetGeometryType();
5479 ints.Append(attr);
5480 ints.Append(geom_type);
5481 pfespace_linear.GetBdrElementDofs(e, dofs);
5482 for (int j = 0; j < dofs.Size(); j++)
5483 {
5484 ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5485 }
5486 }
5487 if (n_send_recv)
5488 {
5489 MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 447, MyComm);
5490 }
5491 } // MyRank != save_rank
5492
5493 if (MyRank == save_rank)
5494 {
5495 for (int v = 0; v < nvertices_glob; v++)
5496 {
5497 serialmesh.AddVertex(0.0); // all other coordinates are 0 by default
5498 }
5499 serialmesh.FinalizeTopology();
5500 }
5501
5502 // From each processor, we send element-wise vertex/dof locations and
5503 // overwrite the vertex/dof locations of the serial mesh.
5504 if (MyRank == save_rank && Nodes)
5505 {
5506 FiniteElementSpace *fespace_serial = NULL;
5507 // Duplicate the FE collection to make sure the serial mesh is completely
5508 // independent of the parallel mesh:
5509 auto fec_serial = FiniteElementCollection::New(
5510 GetNodalFESpace()->FEColl()->Name());
5511 fespace_serial = new FiniteElementSpace(&serialmesh,
5512 fec_serial,
5513 spaceDim,
5514 GetNodalFESpace()->GetOrdering());
5515 serialmesh.SetNodalFESpace(fespace_serial);
5516 serialmesh.GetNodes()->MakeOwner(fec_serial);
5517 // The serial mesh owns its Nodes and they, in turn, own fec_serial and
5518 // fespace_serial.
5519 }
5520
5521 int elem_count = 0; // To keep track of element count in serial mesh
5522 if (MyRank == save_rank)
5523 {
5524 Vector nodeloc;
5525 Array<int> ints_serial;
5526 for (int e = 0; e < NumOfElements; e++)
5527 {
5528 if (Nodes)
5529 {
5530 Nodes->GetElementDofValues(e, nodeloc);
5531 serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5532 serialmesh.GetNodes()->SetSubVector(dofs, nodeloc);
5533 }
5534 else
5535 {
5536 GetElementVertices(e, ints);
5537 serialmesh.GetElementVertices(elem_count++, ints_serial);
5538 for (int i = 0; i < ints.Size(); i++)
5539 {
5540 const real_t *vdata = GetVertex(ints[i]);
5541 real_t *vdata_serial = serialmesh.GetVertex(ints_serial[i]);
5542 for (int d = 0; d < spaceDim; d++)
5543 {
5544 vdata_serial[d] = vdata[d];
5545 }
5546 }
5547 }
5548 }
5549
5550 for (int p = 0; p < NRanks; p++)
5551 {
5552 if (p == save_rank) { continue; }
5553 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 448, MyComm, &status);
5554 vert.SetSize(n_send_recv);
5555 if (n_send_recv)
5556 {
5557 MPI_Recv(&vert[0], n_send_recv, MPITypeMap<real_t>::mpi_type, p, 449, MyComm,
5558 &status);
5559 }
5560 for (int i = 0; i < n_send_recv; )
5561 {
5562 if (Nodes)
5563 {
5564 serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5565 serialmesh.GetNodes()->SetSubVector(dofs, &vert[i]);
5566 i += dofs.Size();
5567 }
5568 else
5569 {
5570 serialmesh.GetElementVertices(elem_count++, ints_serial);
5571 for (int j = 0; j < ints_serial.Size(); j++)
5572 {
5573 real_t *vdata_serial = serialmesh.GetVertex(ints_serial[j]);
5574 for (int d = 0; d < spaceDim; d++)
5575 {
5576 vdata_serial[d] = vert[i++];
5577 }
5578 }
5579 }
5580 }
5581 }
5582 } // MyRank == save_rank
5583 else
5584 {
5585 n_send_recv = 0;
5586 Vector nodeloc;
5587 for (int e = 0; e < NumOfElements; e++)
5588 {
5589 if (Nodes)
5590 {
5591 const FiniteElement *fe = Nodes->FESpace()->GetFE(e);
5592 n_send_recv += spaceDim*fe->GetDof();
5593 }
5594 else
5595 {
5596 n_send_recv += elements[e]->GetNVertices()*spaceDim;
5597 }
5598 }
5599 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 448, MyComm);
5600 vert.Reserve(n_send_recv);
5601 vert.SetSize(0);
5602 for (int e = 0; e < NumOfElements; e++)
5603 {
5604 if (Nodes)
5605 {
5606 Nodes->GetElementDofValues(e, nodeloc);
5607 for (int j = 0; j < nodeloc.Size(); j++)
5608 {
5609 vert.Append(nodeloc(j));
5610 }
5611 }
5612 else
5613 {
5614 GetElementVertices(e, ints);
5615 for (int i = 0; i < ints.Size(); i++)
5616 {
5617 const real_t *vdata = GetVertex(ints[i]);
5618 for (int d = 0; d < spaceDim; d++)
5619 {
5620 vert.Append(vdata[d]);
5621 }
5622 }
5623 }
5624 }
5625 if (n_send_recv)
5626 {
5627 MPI_Send(&vert[0], n_send_recv, MPITypeMap<real_t>::mpi_type, save_rank, 449,
5628 MyComm);
5629 }
5630 }
5631
5632 MPI_Barrier(MyComm);
5633 return serialmesh;
5634}
5635
5636void ParMesh::SaveAsOne(const std::string &fname, int precision) const
5637{
5638 ofstream ofs;
5639 if (MyRank == 0)
5640 {
5641 ofs.open(fname);
5642 ofs.precision(precision);
5643 }
5644 PrintAsOne(ofs);
5645}
5646
5647void ParMesh::PrintAsOneXG(std::ostream &os)
5648{
5649 MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported.");
5650 if (Dim == 3 && meshgen == 1)
5651 {
5652 int i, j, k, nv, ne, p;
5653 const int *ind, *v;
5654 MPI_Status status;
5655 Array<real_t> vert;
5656 Array<int> ints;
5657
5658 if (MyRank == 0)
5659 {
5660 os << "NETGEN_Neutral_Format\n";
5661 // print the vertices
5662 ne = NumOfVertices;
5663 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5664 os << nv << '\n';
5665 for (i = 0; i < NumOfVertices; i++)
5666 {
5667 for (j = 0; j < Dim; j++)
5668 {
5669 os << " " << vertices[i](j);
5670 }
5671 os << '\n';
5672 }
5673 for (p = 1; p < NRanks; p++)
5674 {
5675 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5676 vert.SetSize(Dim*nv);
5677 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
5678 &status);
5679 for (i = 0; i < nv; i++)
5680 {
5681 for (j = 0; j < Dim; j++)
5682 {
5683 os << " " << vert[Dim*i+j];
5684 }
5685 os << '\n';
5686 }
5687 }
5688
5689 // print the elements
5690 nv = NumOfElements;
5691 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5692 os << ne << '\n';
5693 for (i = 0; i < NumOfElements; i++)
5694 {
5695 nv = elements[i]->GetNVertices();
5696 ind = elements[i]->GetVertices();
5697 os << 1;
5698 for (j = 0; j < nv; j++)
5699 {
5700 os << " " << ind[j]+1;
5701 }
5702 os << '\n';
5703 }
5704 k = NumOfVertices;
5705 for (p = 1; p < NRanks; p++)
5706 {
5707 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5708 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5709 ints.SetSize(4*ne);
5710 MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
5711 for (i = 0; i < ne; i++)
5712 {
5713 os << p+1;
5714 for (j = 0; j < 4; j++)
5715 {
5716 os << " " << k+ints[i*4+j]+1;
5717 }
5718 os << '\n';
5719 }
5720 k += nv;
5721 }
5722 // print the boundary + shared faces information
5724 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5725 os << ne << '\n';
5726 // boundary
5727 for (i = 0; i < NumOfBdrElements; i++)
5728 {
5729 nv = boundary[i]->GetNVertices();
5730 ind = boundary[i]->GetVertices();
5731 os << 1;
5732 for (j = 0; j < nv; j++)
5733 {
5734 os << " " << ind[j]+1;
5735 }
5736 os << '\n';
5737 }
5738 // shared faces
5739 const int sf_attr =
5740 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
5741 for (i = 0; i < shared_trias.Size(); i++)
5742 {
5743 ind = shared_trias[i].v;
5744 os << sf_attr;
5745 for (j = 0; j < 3; j++)
5746 {
5747 os << ' ' << ind[j]+1;
5748 }
5749 os << '\n';
5750 }
5751 // There are no quad shared faces
5752 k = NumOfVertices;
5753 for (p = 1; p < NRanks; p++)
5754 {
5755 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5756 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5757 ints.SetSize(3*ne);
5758 MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
5759 for (i = 0; i < ne; i++)
5760 {
5761 os << p+1;
5762 for (j = 0; j < 3; j++)
5763 {
5764 os << ' ' << k+ints[i*3+j]+1;
5765 }
5766 os << '\n';
5767 }
5768 k += nv;
5769 }
5770 }
5771 else
5772 {
5773 ne = NumOfVertices;
5774 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5775 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5777 for (i = 0; i < NumOfVertices; i++)
5778 for (j = 0; j < Dim; j++)
5779 {
5780 vert[Dim*i+j] = vertices[i](j);
5781 }
5783 0, 445, MyComm);
5784 // elements
5785 ne = NumOfElements;
5786 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5787 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5788 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
5789 ints.SetSize(NumOfElements*4);
5790 for (i = 0; i < NumOfElements; i++)
5791 {
5792 v = elements[i]->GetVertices();
5793 for (j = 0; j < 4; j++)
5794 {
5795 ints[4*i+j] = v[j];
5796 }
5797 }
5798 MPI_Send(&ints[0], 4*NumOfElements, MPI_INT, 0, 447, MyComm);
5799 // boundary + shared faces
5801 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5802 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5804 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
5805 ints.SetSize(3*ne);
5806 for (i = 0; i < NumOfBdrElements; i++)
5807 {
5808 v = boundary[i]->GetVertices();
5809 for (j = 0; j < 3; j++)
5810 {
5811 ints[3*i+j] = v[j];
5812 }
5813 }
5814 for ( ; i < ne; i++)
5815 {
5816 v = shared_trias[i-NumOfBdrElements].v; // tet mesh
5817 for (j = 0; j < 3; j++)
5818 {
5819 ints[3*i+j] = v[j];
5820 }
5821 }
5822 MPI_Send(&ints[0], 3*ne, MPI_INT, 0, 447, MyComm);
5823 }
5824 }
5825
5826 if (Dim == 3 && meshgen == 2)
5827 {
5828 int i, j, k, nv, ne, p;
5829 const int *ind, *v;
5830 MPI_Status status;
5831 Array<real_t> vert;
5832 Array<int> ints;
5833
5834 int TG_nv, TG_ne, TG_nbe;
5835
5836 if (MyRank == 0)
5837 {
5838 MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5839 MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5841 MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
5842
5843 os << "TrueGrid\n"
5844 << "1 " << TG_nv << " " << TG_ne << " 0 0 0 0 0 0 0\n"
5845 << "0 0 0 1 0 0 0 0 0 0 0\n"
5846 << "0 0 " << TG_nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
5847 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
5848 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
5849
5850 // print the vertices
5851 nv = TG_nv;
5852 for (i = 0; i < NumOfVertices; i++)
5853 {
5854 os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
5855 << " " << vertices[i](2) << " 0.0\n";
5856 }
5857 for (p = 1; p < NRanks; p++)
5858 {
5859 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5860 vert.SetSize(Dim*nv);
5861 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
5862 &status);
5863 for (i = 0; i < nv; i++)
5864 {
5865 os << i+1 << " 0.0 " << vert[Dim*i] << " " << vert[Dim*i+1]
5866 << " " << vert[Dim*i+2] << " 0.0\n";
5867 }
5868 }
5869
5870 // print the elements
5871 ne = TG_ne;
5872 for (i = 0; i < NumOfElements; i++)
5873 {
5874 nv = elements[i]->GetNVertices();
5875 ind = elements[i]->GetVertices();
5876 os << i+1 << " " << 1;
5877 for (j = 0; j < nv; j++)
5878 {
5879 os << " " << ind[j]+1;
5880 }
5881 os << '\n';
5882 }
5883 k = NumOfVertices;
5884 for (p = 1; p < NRanks; p++)
5885 {
5886 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5887 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5888 ints.SetSize(8*ne);
5889 MPI_Recv(&ints[0], 8*ne, MPI_INT, p, 447, MyComm, &status);
5890 for (i = 0; i < ne; i++)
5891 {
5892 os << i+1 << " " << p+1;
5893 for (j = 0; j < 8; j++)
5894 {
5895 os << " " << k+ints[i*8+j]+1;
5896 }
5897 os << '\n';
5898 }
5899 k += nv;
5900 }
5901
5902 // print the boundary + shared faces information
5903 ne = TG_nbe;
5904 // boundary
5905 for (i = 0; i < NumOfBdrElements; i++)
5906 {
5907 nv = boundary[i]->GetNVertices();
5908 ind = boundary[i]->GetVertices();
5909 os << 1;
5910 for (j = 0; j < nv; j++)
5911 {
5912 os << " " << ind[j]+1;
5913 }
5914 os << " 1.0 1.0 1.0 1.0\n";
5915 }
5916 // shared faces
5917 const int sf_attr =
5918 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
5919 // There are no shared triangle faces
5920 for (i = 0; i < shared_quads.Size(); i++)
5921 {
5922 ind = shared_quads[i].v;
5923 os << sf_attr;
5924 for (j = 0; j < 4; j++)
5925 {
5926 os << ' ' << ind[j]+1;
5927 }
5928 os << " 1.0 1.0 1.0 1.0\n";
5929 }
5930 k = NumOfVertices;
5931 for (p = 1; p < NRanks; p++)
5932 {
5933 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5934 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5935 ints.SetSize(4*ne);
5936 MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
5937 for (i = 0; i < ne; i++)
5938 {
5939 os << p+1;
5940 for (j = 0; j < 4; j++)
5941 {
5942 os << " " << k+ints[i*4+j]+1;
5943 }
5944 os << " 1.0 1.0 1.0 1.0\n";
5945 }
5946 k += nv;
5947 }
5948 }
5949 else
5950 {
5951 MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5952 MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5954 MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
5955
5956 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5958 for (i = 0; i < NumOfVertices; i++)
5959 for (j = 0; j < Dim; j++)
5960 {
5961 vert[Dim*i+j] = vertices[i](j);
5962 }
5963 MPI_Send(&vert[0], Dim*NumOfVertices, MPITypeMap<real_t>::mpi_type, 0, 445,
5964 MyComm);
5965 // elements
5966 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5967 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
5968 ints.SetSize(NumOfElements*8);
5969 for (i = 0; i < NumOfElements; i++)
5970 {
5971 v = elements[i]->GetVertices();
5972 for (j = 0; j < 8; j++)
5973 {
5974 ints[8*i+j] = v[j];
5975 }
5976 }
5977 MPI_Send(&ints[0], 8*NumOfElements, MPI_INT, 0, 447, MyComm);
5978 // boundary + shared faces
5979 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
5981 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
5982 ints.SetSize(4*ne);
5983 for (i = 0; i < NumOfBdrElements; i++)
5984 {
5985 v = boundary[i]->GetVertices();
5986 for (j = 0; j < 4; j++)
5987 {
5988 ints[4*i+j] = v[j];
5989 }
5990 }
5991 for ( ; i < ne; i++)
5992 {
5993 v = shared_quads[i-NumOfBdrElements].v; // hex mesh
5994 for (j = 0; j < 4; j++)
5995 {
5996 ints[4*i+j] = v[j];
5997 }
5998 }
5999 MPI_Send(&ints[0], 4*ne, MPI_INT, 0, 447, MyComm);
6000 }
6001 }
6002
6003 if (Dim == 2)
6004 {
6005 int i, j, k, attr, nv, ne, p;
6006 Array<int> v;
6007 MPI_Status status;
6008 Array<real_t> vert;
6009 Array<int> ints;
6010
6011 if (MyRank == 0)
6012 {
6013 os << "areamesh2\n\n";
6014
6015 // print the boundary + shared edges information
6016 nv = NumOfBdrElements + shared_edges.Size();
6017 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6018 os << ne << '\n';
6019 // boundary
6020 for (i = 0; i < NumOfBdrElements; i++)
6021 {
6022 attr = boundary[i]->GetAttribute();
6023 boundary[i]->GetVertices(v);
6024 os << attr << " ";
6025 for (j = 0; j < v.Size(); j++)
6026 {
6027 os << v[j] + 1 << " ";
6028 }
6029 os << '\n';
6030 }
6031 // shared edges
6032 for (i = 0; i < shared_edges.Size(); i++)
6033 {
6034 attr = shared_edges[i]->GetAttribute();
6035 shared_edges[i]->GetVertices(v);
6036 os << attr << " ";
6037 for (j = 0; j < v.Size(); j++)
6038 {
6039 os << v[j] + 1 << " ";
6040 }
6041 os << '\n';
6042 }
6043 k = NumOfVertices;
6044 for (p = 1; p < NRanks; p++)
6045 {
6046 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6047 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6048 ints.SetSize(2*ne);
6049 MPI_Recv(&ints[0], 2*ne, MPI_INT, p, 447, MyComm, &status);
6050 for (i = 0; i < ne; i++)
6051 {
6052 os << p+1;
6053 for (j = 0; j < 2; j++)
6054 {
6055 os << " " << k+ints[i*2+j]+1;
6056 }
6057 os << '\n';
6058 }
6059 k += nv;
6060 }
6061
6062 // print the elements
6063 nv = NumOfElements;
6064 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6065 os << ne << '\n';
6066 for (i = 0; i < NumOfElements; i++)
6067 {
6068 // attr = elements[i]->GetAttribute(); // not used
6069 elements[i]->GetVertices(v);
6070 os << 1 << " " << 3 << " ";
6071 for (j = 0; j < v.Size(); j++)
6072 {
6073 os << v[j] + 1 << " ";
6074 }
6075 os << '\n';
6076 }
6077 k = NumOfVertices;
6078 for (p = 1; p < NRanks; p++)
6079 {
6080 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6081 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6082 ints.SetSize(3*ne);
6083 MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
6084 for (i = 0; i < ne; i++)
6085 {
6086 os << p+1 << " " << 3;
6087 for (j = 0; j < 3; j++)
6088 {
6089 os << " " << k+ints[i*3+j]+1;
6090 }
6091 os << '\n';
6092 }
6093 k += nv;
6094 }
6095
6096 // print the vertices
6097 ne = NumOfVertices;
6098 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6099 os << nv << '\n';
6100 for (i = 0; i < NumOfVertices; i++)
6101 {
6102 for (j = 0; j < Dim; j++)
6103 {
6104 os << vertices[i](j) << " ";
6105 }
6106 os << '\n';
6107 }
6108 for (p = 1; p < NRanks; p++)
6109 {
6110 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6111 vert.SetSize(Dim*nv);
6112 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
6113 &status);
6114 for (i = 0; i < nv; i++)
6115 {
6116 for (j = 0; j < Dim; j++)
6117 {
6118 os << " " << vert[Dim*i+j];
6119 }
6120 os << '\n';
6121 }
6122 }
6123 }
6124 else
6125 {
6126 // boundary + shared faces
6127 nv = NumOfBdrElements + shared_edges.Size();
6128 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6129 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6130 ne = NumOfBdrElements + shared_edges.Size();
6131 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
6132 ints.SetSize(2*ne);
6133 for (i = 0; i < NumOfBdrElements; i++)
6134 {
6135 boundary[i]->GetVertices(v);
6136 for (j = 0; j < 2; j++)
6137 {
6138 ints[2*i+j] = v[j];
6139 }
6140 }
6141 for ( ; i < ne; i++)
6142 {
6143 shared_edges[i-NumOfBdrElements]->GetVertices(v);
6144 for (j = 0; j < 2; j++)
6145 {
6146 ints[2*i+j] = v[j];
6147 }
6148 }
6149 MPI_Send(&ints[0], 2*ne, MPI_INT, 0, 447, MyComm);
6150 // elements
6151 ne = NumOfElements;
6152 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6153 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6154 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
6155 ints.SetSize(NumOfElements*3);
6156 for (i = 0; i < NumOfElements; i++)
6157 {
6158 elements[i]->GetVertices(v);
6159 for (j = 0; j < 3; j++)
6160 {
6161 ints[3*i+j] = v[j];
6162 }
6163 }
6164 MPI_Send(&ints[0], 3*NumOfElements, MPI_INT, 0, 447, MyComm);
6165 // vertices
6166 ne = NumOfVertices;
6167 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6168 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6170 for (i = 0; i < NumOfVertices; i++)
6171 for (j = 0; j < Dim; j++)
6172 {
6173 vert[Dim*i+j] = vertices[i](j);
6174 }
6176 0, 445, MyComm);
6177 }
6178 }
6179}
6180
6181void ParMesh::GetBoundingBox(Vector &gp_min, Vector &gp_max, int ref)
6182{
6183 int sdim;
6184 Vector p_min, p_max;
6185
6186 this->Mesh::GetBoundingBox(p_min, p_max, ref);
6187
6188 sdim = SpaceDimension();
6189
6190 gp_min.SetSize(sdim);
6191 gp_max.SetSize(sdim);
6192
6193 MPI_Allreduce(p_min.GetData(), gp_min.GetData(), sdim,
6195 MPI_MIN, MyComm);
6196 MPI_Allreduce(p_max.GetData(), gp_max.GetData(), sdim,
6198 MPI_MAX, MyComm);
6199}
6200
6202 real_t &gk_min, real_t &gk_max)
6203{
6204 real_t h_min, h_max, kappa_min, kappa_max;
6205
6206 this->Mesh::GetCharacteristics(h_min, h_max, kappa_min, kappa_max);
6207
6208 MPI_Allreduce(&h_min, &gh_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN,
6209 MyComm);
6210 MPI_Allreduce(&h_max, &gh_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
6211 MyComm);
6212 MPI_Allreduce(&kappa_min, &gk_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN,
6213 MyComm);
6214 MPI_Allreduce(&kappa_max, &gk_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
6215 MyComm);
6216}
6217
6218void ParMesh::PrintInfo(std::ostream &os)
6219{
6220 int i;
6221 DenseMatrix J(Dim);
6222 real_t h_min, h_max, kappa_min, kappa_max, h, kappa;
6223
6224 if (MyRank == 0)
6225 {
6226 os << "Parallel Mesh Stats:" << '\n';
6227 }
6228
6229 for (i = 0; i < NumOfElements; i++)
6230 {
6231 GetElementJacobian(i, J);
6232 h = pow(fabs(J.Weight()), 1.0/real_t(Dim));
6233 kappa = (Dim == spaceDim) ?
6234 J.CalcSingularvalue(0) / J.CalcSingularvalue(Dim-1) : -1.0;
6235 if (i == 0)
6236 {
6237 h_min = h_max = h;
6238 kappa_min = kappa_max = kappa;
6239 }
6240 else
6241 {
6242 if (h < h_min) { h_min = h; }
6243 if (h > h_max) { h_max = h; }
6244 if (kappa < kappa_min) { kappa_min = kappa; }
6245 if (kappa > kappa_max) { kappa_max = kappa; }
6246 }
6247 }
6248
6249 real_t gh_min, gh_max, gk_min, gk_max;
6250 MPI_Reduce(&h_min, &gh_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN, 0,
6251 MyComm);
6252 MPI_Reduce(&h_max, &gh_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX, 0,
6253 MyComm);
6254 MPI_Reduce(&kappa_min, &gk_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN, 0,
6255 MyComm);
6256 MPI_Reduce(&kappa_max, &gk_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX, 0,
6257 MyComm);
6258
6259 // TODO: collect and print stats by geometry
6260
6261 long long ldata[5]; // vert, edge, face, elem, neighbors;
6262 long long mindata[5], maxdata[5], sumdata[5];
6263
6264 // count locally owned vertices, edges, and faces
6265 ldata[0] = GetNV();
6266 ldata[1] = GetNEdges();
6267 ldata[2] = GetNFaces();
6268 ldata[3] = GetNE();
6269 ldata[4] = gtopo.GetNumNeighbors()-1;
6270 for (int gr = 1; gr < GetNGroups(); gr++)
6271 {
6272 if (!gtopo.IAmMaster(gr)) // we are not the master
6273 {
6274 ldata[0] -= group_svert.RowSize(gr-1);
6275 ldata[1] -= group_sedge.RowSize(gr-1);
6276 ldata[2] -= group_stria.RowSize(gr-1);
6277 ldata[2] -= group_squad.RowSize(gr-1);
6278 }
6279 }
6280
6281 MPI_Reduce(ldata, mindata, 5, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
6282 MPI_Reduce(ldata, sumdata, 5, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
6283 MPI_Reduce(ldata, maxdata, 5, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
6284
6285 if (MyRank == 0)
6286 {
6287 os << '\n'
6288 << " "
6289 << setw(12) << "minimum"
6290 << setw(12) << "average"
6291 << setw(12) << "maximum"
6292 << setw(12) << "total" << '\n';
6293 os << " vertices "
6294 << setw(12) << mindata[0]
6295 << setw(12) << sumdata[0]/NRanks
6296 << setw(12) << maxdata[0]
6297 << setw(12) << sumdata[0] << '\n';
6298 os << " edges "
6299 << setw(12) << mindata[1]
6300 << setw(12) << sumdata[1]/NRanks
6301 << setw(12) << maxdata[1]
6302 << setw(12) << sumdata[1] << '\n';
6303 if (Dim == 3)
6304 {
6305 os << " faces "
6306 << setw(12) << mindata[2]
6307 << setw(12) << sumdata[2]/NRanks
6308 << setw(12) << maxdata[2]
6309 << setw(12) << sumdata[2] << '\n';
6310 }
6311 os << " elements "
6312 << setw(12) << mindata[3]
6313 << setw(12) << sumdata[3]/NRanks
6314 << setw(12) << maxdata[3]
6315 << setw(12) << sumdata[3] << '\n';
6316 os << " neighbors "
6317 << setw(12) << mindata[4]
6318 << setw(12) << sumdata[4]/NRanks
6319 << setw(12) << maxdata[4] << '\n';
6320 os << '\n'
6321 << " "
6322 << setw(12) << "minimum"
6323 << setw(12) << "maximum" << '\n';
6324 os << " h "
6325 << setw(12) << gh_min
6326 << setw(12) << gh_max << '\n';
6327 os << " kappa "
6328 << setw(12) << gk_min
6329 << setw(12) << gk_max << '\n';
6330 os << std::flush;
6331 }
6332}
6333
6334long long ParMesh::ReduceInt(int value) const
6335{
6336 long long local = value, global;
6337 MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MyComm);
6338 return global;
6339}
6340
6341void ParMesh::ParPrint(ostream &os, const std::string &comments) const
6342{
6343 if (NURBSext)
6344 {
6345 // TODO: NURBS meshes.
6346 Print(os, comments); // use the serial MFEM v1.0 format for now
6347 return;
6348 }
6349
6350 if (Nonconforming())
6351 {
6352 // the NC mesh format works both in serial and in parallel
6353 Printer(os, "", comments);
6354 return;
6355 }
6356
6357 // Write out serial mesh. Tell serial mesh to delineate the end of its
6358 // output with 'mfem_serial_mesh_end' instead of 'mfem_mesh_end', as we will
6359 // be adding additional parallel mesh information.
6360 Printer(os, "mfem_serial_mesh_end", comments);
6361
6362 // write out group topology info.
6363 gtopo.Save(os);
6364
6365 os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6366 if (Dim >= 2)
6367 {
6368 os << "total_shared_edges " << shared_edges.Size() << '\n';
6369 }
6370 if (Dim >= 3)
6371 {
6372 os << "total_shared_faces " << sface_lface.Size() << '\n';
6373 }
6374 os << "\n# group 0 has no shared entities\n";
6375 for (int gr = 1; gr < GetNGroups(); gr++)
6376 {
6377 {
6378 const int nv = group_svert.RowSize(gr-1);
6379 const int *sv = group_svert.GetRow(gr-1);
6380 os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
6381 for (int i = 0; i < nv; i++)
6382 {
6383 os << svert_lvert[sv[i]] << '\n';
6384 }
6385 }
6386 if (Dim >= 2)
6387 {
6388 const int ne = group_sedge.RowSize(gr-1);
6389 const int *se = group_sedge.GetRow(gr-1);
6390 os << "\nshared_edges " << ne << '\n';
6391 for (int i = 0; i < ne; i++)
6392 {
6393 const int *v = shared_edges[se[i]]->GetVertices();
6394 os << v[0] << ' ' << v[1] << '\n';
6395 }
6396 }
6397 if (Dim >= 3)
6398 {
6399 const int nt = group_stria.RowSize(gr-1);
6400 const int *st = group_stria.GetRow(gr-1);
6401 const int nq = group_squad.RowSize(gr-1);
6402 const int *sq = group_squad.GetRow(gr-1);
6403 os << "\nshared_faces " << nt+nq << '\n';
6404 for (int i = 0; i < nt; i++)
6405 {
6406 os << Geometry::TRIANGLE;
6407 const int *v = shared_trias[st[i]].v;
6408 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6409 os << '\n';
6410 }
6411 for (int i = 0; i < nq; i++)
6412 {
6413 os << Geometry::SQUARE;
6414 const int *v = shared_quads[sq[i]].v;
6415 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6416 os << '\n';
6417 }
6418 }
6419 }
6420
6421 // Write out section end tag for mesh.
6422 os << "\nmfem_mesh_end" << endl;
6423}
6424
6425void ParMesh::PrintVTU(std::string pathname,
6426 VTKFormat format,
6427 bool high_order_output,
6428 int compression_level,
6429 bool bdr_elements)
6430{
6431 int pad_digits_rank = 6;
6433
6434 std::string::size_type pos = pathname.find_last_of('/');
6435 std::string fname
6436 = (pos == std::string::npos) ? pathname : pathname.substr(pos+1);
6437
6438 if (MyRank == 0)
6439 {
6440 std::string pvtu_name = pathname + "/" + fname + ".pvtu";
6441 std::ofstream os(pvtu_name);
6442
6443 std::string data_type = (format == VTKFormat::BINARY32) ? "Float32" : "Float64";
6444 std::string data_format = (format == VTKFormat::ASCII) ? "ascii" : "binary";
6445
6446 os << "<?xml version=\"1.0\"?>\n";
6447 os << "<VTKFile type=\"PUnstructuredGrid\"";
6448 os << " version =\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
6449 os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
6450
6451 os << "<PPoints>\n";
6452 os << "\t<PDataArray type=\"" << data_type << "\" ";
6453 os << " Name=\"Points\" NumberOfComponents=\"3\""
6454 << " format=\"" << data_format << "\"/>\n";
6455 os << "</PPoints>\n";
6456
6457 os << "<PCells>\n";
6458 os << "\t<PDataArray type=\"Int32\" ";
6459 os << " Name=\"connectivity\" NumberOfComponents=\"1\""
6460 << " format=\"" << data_format << "\"/>\n";
6461 os << "\t<PDataArray type=\"Int32\" ";
6462 os << " Name=\"offsets\" NumberOfComponents=\"1\""
6463 << " format=\"" << data_format << "\"/>\n";
6464 os << "\t<PDataArray type=\"UInt8\" ";
6465 os << " Name=\"types\" NumberOfComponents=\"1\""
6466 << " format=\"" << data_format << "\"/>\n";
6467 os << "</PCells>\n";
6468
6469 os << "<PCellData>\n";
6470 os << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
6471 << "\" NumberOfComponents=\"1\""
6472 << " format=\"" << data_format << "\"/>\n";
6473 os << "</PCellData>\n";
6474
6475 for (int ii=0; ii<NRanks; ii++)
6476 {
6477 std::string piece = fname + ".proc"
6478 + to_padded_string(ii, pad_digits_rank) + ".vtu";
6479 os << "<Piece Source=\"" << piece << "\"/>\n";
6480 }
6481
6482 os << "</PUnstructuredGrid>\n";
6483 os << "</VTKFile>\n";
6484 os.close();
6485 }
6486
6487 std::string vtu_fname = pathname + "/" + fname + ".proc"
6488 + to_padded_string(MyRank, pad_digits_rank);
6489 Mesh::PrintVTU(vtu_fname, format, high_order_output, compression_level,
6490 bdr_elements);
6491}
6492
6494 Array<IntegrationPoint>& ip, bool warn,
6496{
6497 const int npts = point_mat.Width();
6498 if (npts == 0) { return 0; }
6499
6500 const bool no_warn = false;
6501 Mesh::FindPoints(point_mat, elem_id, ip, no_warn, inv_trans);
6502
6503 // If multiple processors find the same point, we need to choose only one of
6504 // the processors to mark that point as found.
6505 // Here, we choose the processor with the minimal rank.
6506
6507 Array<int> my_point_rank(npts), glob_point_rank(npts);
6508 for (int k = 0; k < npts; k++)
6509 {
6510 my_point_rank[k] = (elem_id[k] == -1) ? NRanks : MyRank;
6511 }
6512
6513 MPI_Allreduce(my_point_rank.GetData(), glob_point_rank.GetData(), npts,
6514 MPI_INT, MPI_MIN, MyComm);
6515
6516 int pts_found = 0;
6517 for (int k = 0; k < npts; k++)
6518 {
6519 if (glob_point_rank[k] == NRanks) { elem_id[k] = -1; }
6520 else
6521 {
6522 pts_found++;
6523 if (glob_point_rank[k] != MyRank) { elem_id[k] = -2; }
6524 }
6525 }
6526 if (warn && pts_found != npts && MyRank == 0)
6527 {
6528 MFEM_WARNING((npts-pts_found) << " points were not found");
6529 }
6530 return pts_found;
6531}
6532
6533static void PrintVertex(const Vertex &v, int space_dim, ostream &os)
6534{
6535 os << v(0);
6536 for (int d = 1; d < space_dim; d++)
6537 {
6538 os << ' ' << v(d);
6539 }
6540}
6541
6542void ParMesh::PrintSharedEntities(const std::string &fname_prefix) const
6543{
6544 stringstream out_name;
6545 out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
6546 << ".shared_entities";
6547 ofstream os(out_name.str().c_str());
6548 os.precision(16);
6549
6550 gtopo.Save(out);
6551
6552 os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6553 if (Dim >= 2)
6554 {
6555 os << "total_shared_edges " << shared_edges.Size() << '\n';
6556 }
6557 if (Dim >= 3)
6558 {
6559 os << "total_shared_faces " << sface_lface.Size() << '\n';
6560 }
6561 for (int gr = 1; gr < GetNGroups(); gr++)
6562 {
6563 {
6564 const int nv = group_svert.RowSize(gr-1);
6565 const int *sv = group_svert.GetRow(gr-1);
6566 os << "\n# group " << gr << "\n\nshared_vertices " << nv << '\n';
6567 for (int i = 0; i < nv; i++)
6568 {
6569 const int lvi = svert_lvert[sv[i]];
6570 // os << lvi << '\n';
6571 PrintVertex(vertices[lvi], spaceDim, os);
6572 os << '\n';
6573 }
6574 }
6575 if (Dim >= 2)
6576 {
6577 const int ne = group_sedge.RowSize(gr-1);
6578 const int *se = group_sedge.GetRow(gr-1);
6579 os << "\nshared_edges " << ne << '\n';
6580 for (int i = 0; i < ne; i++)
6581 {
6582 const int *v = shared_edges[se[i]]->GetVertices();
6583 // os << v[0] << ' ' << v[1] << '\n';
6584 PrintVertex(vertices[v[0]], spaceDim, os);
6585 os << " | ";
6586 PrintVertex(vertices[v[1]], spaceDim, os);
6587 os << '\n';
6588 }
6589 }
6590 if (Dim >= 3)
6591 {
6592 const int nt = group_stria.RowSize(gr-1);
6593 const int *st = group_stria.GetRow(gr-1);
6594 const int nq = group_squad.RowSize(gr-1);
6595 const int *sq = group_squad.GetRow(gr-1);
6596 os << "\nshared_faces " << nt+nq << '\n';
6597 for (int i = 0; i < nt; i++)
6598 {
6599 const int *v = shared_trias[st[i]].v;
6600#if 0
6601 os << Geometry::TRIANGLE;
6602 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6603 os << '\n';
6604#endif
6605 for (int j = 0; j < 3; j++)
6606 {
6607 PrintVertex(vertices[v[j]], spaceDim, os);
6608 (j < 2) ? os << " | " : os << '\n';
6609 }
6610 }
6611 for (int i = 0; i < nq; i++)
6612 {
6613 const int *v = shared_quads[sq[i]].v;
6614#if 0
6615 os << Geometry::SQUARE;
6616 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6617 os << '\n';
6618#endif
6619 for (int j = 0; j < 4; j++)
6620 {
6621 PrintVertex(vertices[v[j]], spaceDim, os);
6622 (j < 3) ? os << " | " : os << '\n';
6623 }
6624 }
6625 }
6626 }
6627}
6628
6630{
6631 H1_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6632 ParMesh *pm = const_cast<ParMesh *>(this);
6633 ParFiniteElementSpace fespace(pm, &fec);
6634
6635 gi.SetSize(GetNV());
6636
6637 Array<int> dofs;
6638 for (int i=0; i<GetNV(); ++i)
6639 {
6640 fespace.GetVertexDofs(i, dofs);
6641 gi[i] = fespace.GetGlobalTDofNumber(dofs[0]);
6642 }
6643}
6644
6646{
6647 if (Dim == 1)
6648 {
6650 return;
6651 }
6652
6653 ND_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6654 ParMesh *pm = const_cast<ParMesh *>(this);
6655 ParFiniteElementSpace fespace(pm, &fec);
6656
6657 gi.SetSize(GetNEdges());
6658
6659 Array<int> dofs;
6660 for (int i=0; i<GetNEdges(); ++i)
6661 {
6662 fespace.GetEdgeDofs(i, dofs);
6663 const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6664 gi[i] = fespace.GetGlobalTDofNumber(ldof);
6665 }
6666}
6667
6669{
6670 if (Dim == 2)
6671 {
6673 return;
6674 }
6675 else if (Dim == 1)
6676 {
6678 return;
6679 }
6680
6681 RT_FECollection fec(0, Dim); // Order 0, mesh dimension (not spatial dimension).
6682 ParMesh *pm = const_cast<ParMesh *>(this);
6683 ParFiniteElementSpace fespace(pm, &fec);
6684
6685 gi.SetSize(GetNFaces());
6686
6687 Array<int> dofs;
6688 for (int i=0; i<GetNFaces(); ++i)
6689 {
6690 fespace.GetFaceDofs(i, dofs);
6691 const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6692 gi[i] = fespace.GetGlobalTDofNumber(ldof);
6693 }
6694}
6695
6697{
6699
6700 // Cast from long long to HYPRE_BigInt
6701 const HYPRE_BigInt offset = glob_elem_offset;
6702
6703 gi.SetSize(GetNE());
6704 for (int i=0; i<GetNE(); ++i)
6705 {
6706 gi[i] = offset + i;
6707 }
6708}
6709
6711{
6712 const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
6713
6714 Mesh::GetExteriorFaceMarker(face_marker);
6715}
6716
6717void ParMesh::UnmarkInternalBoundaries(Array<int> &bdr_marker, bool excl) const
6718{
6719 const int max_bdr_attr = bdr_attributes.Max();
6720
6721 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
6722 "bdr_marker must be at least bdr_attriburtes.Max() in length");
6723
6724 Array<int> ext_face_marker;
6725 GetExteriorFaceMarker(ext_face_marker);
6726
6727 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
6728 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
6729
6730 // Identify attributes which contain local interior faces and those which
6731 // contain local exterior faces.
6732 for (int be = 0; be < boundary.Size(); be++)
6733 {
6734 const int bea = boundary[be]->GetAttribute();
6735
6736 if (bdr_marker[bea-1] != 0)
6737 {
6738 const int f = be_to_face[be];
6739
6740 if (ext_face_marker[f] > 0)
6741 {
6742 exterior_bdr[bea-1] = true;
6743 }
6744 else
6745 {
6746 interior_bdr[bea-1] = true;
6747 }
6748 }
6749 }
6750
6751 Array<bool> glb_interior_bdr(bdr_attributes.Max()); glb_interior_bdr = false;
6752 Array<bool> glb_exterior_bdr(bdr_attributes.Max()); glb_exterior_bdr = false;
6753
6754 MPI_Allreduce(&interior_bdr[0], &glb_interior_bdr[0], bdr_attributes.Max(),
6755 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6756 MPI_Allreduce(&exterior_bdr[0], &glb_exterior_bdr[0], bdr_attributes.Max(),
6757 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6758
6759 // Unmark attributes which are currently marked, contain interior faces,
6760 // and satisfy the appropriate exclusivity requirement.
6761 for (int b = 0; b < max_bdr_attr; b++)
6762 {
6763 if (bdr_marker[b] != 0 && glb_interior_bdr[b])
6764 {
6765 if (!excl || !glb_exterior_bdr[b])
6766 {
6767 bdr_marker[b] = 0;
6768 }
6769 }
6770 }
6771}
6772
6773void ParMesh::MarkExternalBoundaries(Array<int> &bdr_marker, bool excl) const
6774{
6775 const int max_bdr_attr = bdr_attributes.Max();
6776
6777 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
6778 "bdr_marker must be at least bdr_attriburtes.Max() in length");
6779
6780 Array<int> ext_face_marker;
6781 GetExteriorFaceMarker(ext_face_marker);
6782
6783 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
6784 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
6785
6786 // Identify boundary attributes containing local exterior faces and those
6787 // containing local interior faces.
6788 for (int be = 0; be < boundary.Size(); be++)
6789 {
6790 const int bea = boundary[be]->GetAttribute();
6791
6792 const int f = be_to_face[be];
6793
6794 if (ext_face_marker[f] > 0)
6795 {
6796 exterior_bdr[bea-1] = true;
6797 }
6798 else
6799 {
6800 interior_bdr[bea-1] = true;
6801 }
6802 }
6803
6804 Array<bool> glb_interior_bdr(bdr_attributes.Max()); glb_interior_bdr = false;
6805 Array<bool> glb_exterior_bdr(bdr_attributes.Max()); glb_exterior_bdr = false;
6806
6807 MPI_Allreduce(&interior_bdr[0], &glb_interior_bdr[0], bdr_attributes.Max(),
6808 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6809 MPI_Allreduce(&exterior_bdr[0], &glb_exterior_bdr[0], bdr_attributes.Max(),
6810 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6811
6812 // Mark the attributes which are currently unmarked, containing exterior
6813 // faces, and satisfying the necessary exclusivity requirements.
6814 for (int b = 0; b < max_bdr_attr; b++)
6815 {
6816 if (bdr_marker[b] == 0 && glb_exterior_bdr[b])
6817 {
6818 if (!excl || !glb_interior_bdr[b])
6819 {
6820 bdr_marker[b] = 1;
6821 }
6822 }
6823 }
6824}
6825
6827{
6828 Mesh::Swap(other, true);
6829
6830 mfem::Swap(MyComm, other.MyComm);
6831 mfem::Swap(NRanks, other.NRanks);
6832 mfem::Swap(MyRank, other.MyRank);
6833
6836
6837 gtopo.Swap(other.gtopo);
6838
6843
6850
6851 // Swap face-neighbor data
6860 std::swap(face_nbr_el_ori, other.face_nbr_el_ori);
6861 std::swap(face_nbr_el_to_face, other.face_nbr_el_to_face);
6862
6863 // Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
6864 mfem::Swap(pncmesh, other.pncmesh);
6865
6866 print_shared = other.print_shared;
6867}
6868
6870{
6871 delete pncmesh;
6872 ncmesh = pncmesh = NULL;
6873
6875
6876 for (int i = 0; i < shared_edges.Size(); i++)
6877 {
6879 }
6880 shared_edges.DeleteAll();
6881
6882 face_nbr_el_to_face = nullptr;
6883}
6884
6886{
6888
6889 // The Mesh destructor is called automatically
6890}
6891
6892}
6893
6894#endif
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:184
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
void LoseData()
NULL-ifies the data.
Definition array.hpp:160
int Size() const
Return the logical size of the array.
Definition array.hpp:166
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1053
void DeleteAll()
Delete the whole array.
Definition array.hpp:1033
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:912
T * GetData()
Returns the data.
Definition array.hpp:140
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1042
void CopyTo(U *dest)
STL-like copyTo dest from begin to end.
Definition array.hpp:352
void DeleteLast()
Delete the last entry of the array.
Definition array.hpp:224
T & Last()
Return the last element in the array.
Definition array.hpp:945
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:266
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:116
real_t Weight() const
Definition densemat.cpp:553
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:208
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3750
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:304
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:3824
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:854
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:3702
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:3942
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:856
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:819
Abstract class for all finite elements.
Definition fe_base.hpp:247
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:403
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:136
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:337
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:125
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:123
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:707
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:279
int DofForGeometry(Geometry::Type GeomType) const override
Definition fe_coll.hpp:295
const int * GetDofMap(Geometry::Type GeomType) const
Get the Cartesian to local H1 dof map.
Definition fe_coll.cpp:2088
int GetId(int p1, int p2)
Get the "id" of the item whose parents are p1, p2, this "id" corresponding to the index of the item i...
Definition hash.hpp:684
int FindId(int p1, int p2) const
Find the "id" of an item whose parents are p1, p2. Return -1 if it does not exist.
Definition hash.hpp:775
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:350
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:65
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition mesh.cpp:6890
Array< Vertex > vertices
Definition mesh.hpp:107
void GetEdgeOrdering(const DSTable &v_to_v, Array< int > &order)
Definition mesh.cpp:2996
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:1038
void SetVerticesFromNodes(const GridFunction *nodes)
Helper to set vertex coordinates given a high-order curvature function.
Definition mesh.cpp:6832
int GetElementToEdgeTable(Table &)
Definition mesh.cpp:8068
int meshgen
Definition mesh.hpp:90
Element * NewElement(int geom)
Definition mesh.cpp:4818
IsoparametricTransformation Transformation2
Definition mesh.hpp:256
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1383
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:7886
static void PrintElement(const Element *el, std::ostream &os)
Definition mesh.cpp:4884
Array< FaceInfo > faces_info
Definition mesh.hpp:239
CoarseFineTransformations CoarseFineTr
Definition mesh.hpp:262
void GetElementJacobian(int i, DenseMatrix &J, const IntegrationPoint *ip=NULL)
Definition mesh.cpp:65
int AddBdrElement(Element *elem)
Definition mesh.cpp:2370
virtual FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31)
Definition mesh.cpp:1104
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:304
void RedRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:421
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:312
virtual void GetExteriorFaceMarker(Array< int > &face_marker) const
Populate a marker array identifying exterior faces.
Definition mesh.cpp:1656
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Return FiniteElement for reference element of the specified type.
Definition mesh.cpp:339
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7130
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:7962
int NumOfBdrElements
Definition mesh.hpp:81
void BdrBisection(int i, const HashTable< Hashed2 > &)
Bisect a boundary triangle: boundary element with index i is bisected.
Definition mesh.cpp:11600
Element::Type GetBdrElementType(int i) const
Returns the type of boundary element i.
Definition mesh.cpp:7967
const Table & ElementToEdgeTable() const
Definition mesh.cpp:8152
bool Conforming() const
Definition mesh.cpp:15455
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6846
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1576
void MakeHigherOrderSimplicial_(const Mesh &orig_mesh, const Array< int > &parent_elements)
Helper function for constructing higher order nodes from a mesh transformed into simplices....
Definition mesh.cpp:5912
int GetAttribute(int i) const
Return the attribute of element i.
Definition mesh.hpp:1484
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1609
void UniformRefinement3D_base(Array< int > *f2qf=NULL, DSTable *v_to_v_p=NULL, bool update_nodes=true)
Definition mesh.cpp:9892
Array< NCFaceInfo > nc_faces_info
Definition mesh.hpp:240
@ REBALANCE
Definition mesh.hpp:299
Array< int > MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
Internal helper user in MakeSimplicial (and ParMesh::MakeSimplicial). Optional return is used in asse...
Definition mesh.cpp:5575
void MakeRefined_(Mesh &orig_mesh, const Array< int > &ref_factors, int ref_type)
Internal function used in Mesh::MakeRefined.
Definition mesh.cpp:5335
real_t GetLength(int i, int j) const
Return the length of the segment from node i to node j.
Definition mesh.cpp:8006
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:6794
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition mesh.cpp:4943
void GenerateNCFaceInfo()
Definition mesh.cpp:8392
void ApplyLocalSlaveTransformation(FaceElementTransformations &FT, const FaceInfo &fi, bool is_ghost) const
Definition mesh.cpp:1254
Array< Element * > faces
Definition mesh.hpp:109
int Dim
Definition mesh.hpp:78
real_t AggregateError(const Array< real_t > &elem_error, const int *fine, int nfine, int op)
Derefinement helper.
Definition mesh.cpp:10891
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition mesh.cpp:3110
bool Nonconforming() const
Definition mesh.hpp:2499
void GenerateFaces()
Definition mesh.cpp:8285
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:310
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1689
void GetVertices(Vector &vert_coord) const
Definition mesh.cpp:9553
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition mesh.cpp:10991
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:6862
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1434
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7041
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1386
ElementTransformation * GetFaceTransformation(int FaceNo)
Returns a pointer to the transformation defining the given face element.
Definition mesh.cpp:610
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3502
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Print the mesh to the given stream using the default MFEM mesh format.
Definition mesh.hpp:2567
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition mesh.cpp:3044
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2000
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
const Element * GetFace(int i) const
Return pointer to the i'th face element object.
Definition mesh.hpp:1461
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:141
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
Table * el_to_face
Definition mesh.hpp:243
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1449
void GreenRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:427
void UpdateNodes()
Update the nodes of a curved mesh after the topological part of a Mesh::Operation,...
Definition mesh.cpp:9718
long sequence
Definition mesh.hpp:97
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:110
int AddElement(Element *elem)
Definition mesh.cpp:2363
Table * el_to_edge
Definition mesh.hpp:242
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:360
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition mesh.cpp:8513
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Definition mesh.cpp:1557
void Printer(std::ostream &os=mfem::out, std::string section_delimiter="", const std::string &comments="") const
If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1 format. If section_delimiter is empty,...
Definition mesh.cpp:11976
IsoparametricTransformation Transformation
Definition mesh.hpp:256
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:7835
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1309
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:205
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition mesh.cpp:6788
virtual void SetAttributes(bool elem_attrs_changed=true, bool bdr_face_attrs_changed=true)
Determine the sets of unique attribute values in domain if elem_attrs_changed and boundary elements i...
Definition mesh.cpp:1937
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9627
int NumOfVertices
Definition mesh.hpp:81
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:307
Array< int > be_to_face
Definition mesh.hpp:245
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1374
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7672
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:12392
GridFunction * Nodes
Definition mesh.hpp:267
Element::Type GetFaceElementType(int Face) const
Definition mesh.cpp:1612
int CheckBdrElementOrientation(bool fix_it=true)
Check the orientation of the boundary elements.
Definition mesh.cpp:7342
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:9697
int NumOfElements
Definition mesh.hpp:81
void Swap(Mesh &other, bool non_geometry)
Definition mesh.cpp:11036
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition mesh.cpp:11391
void ResetLazyData()
Definition mesh.cpp:1918
void UniformRefinement2D_base(bool update_nodes=true)
Definition mesh.cpp:9733
int NumOfFaces
Definition mesh.hpp:82
int spaceDim
Definition mesh.hpp:79
virtual void SetNodalFESpace(FiniteElementSpace *nfes)
Definition mesh.cpp:6741
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1380
long long GetGlobalNE() const
Return the total (global) number of elements.
Definition mesh.hpp:1406
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition mesh.cpp:3608
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:13800
int own_nodes
Definition mesh.hpp:268
bool IsSlaveFace(const FaceInfo &fi) const
Definition mesh.cpp:1249
void FreeElement(Element *E)
Definition mesh.cpp:13775
Array< Element * > boundary
Definition mesh.hpp:108
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:313
void NewNodes(GridFunction &nodes, bool make_owner=false)
Replace the internal node GridFunction with the given GridFunction.
Definition mesh.cpp:9654
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition mesh.hpp:2359
STable3D * GetFacesTable()
Definition mesh.cpp:8450
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition mesh.cpp:4890
FaceElementTransformations FaceElemTr
Definition mesh.hpp:259
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:8043
int NumOfEdges
Definition mesh.hpp:82
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1556
Operation last_operation
Definition mesh.hpp:329
Table * GetVertexToElementTable()
Definition mesh.cpp:7735
void InitRefinementTransforms()
Definition mesh.cpp:11727
int * GeneratePartitioning(int nparts, int part_method=1)
Definition mesh.cpp:8749
Array< Element * > elements
Definition mesh.hpp:102
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition mesh.hpp:302
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1416
void OnMeshUpdated(Mesh *mesh)
Definition ncmesh.cpp:2887
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:376
void MarkCoarseLevel()
Definition ncmesh.cpp:5156
const Table & GetDerefinementTable()
Definition ncmesh.cpp:2265
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:500
Class for standard nodal finite elements.
Definition fe_base.hpp:724
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:31
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
Definition pfespace.cpp:708
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
Definition pfespace.cpp:685
int GetTrueVSize() const override
Return the number of local vector true dofs.
Definition pfespace.hpp:353
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:616
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
Definition pfespace.cpp:732
void GetElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetElementDofs(), but with a user-provided DofTransformation object.
Definition pfespace.cpp:562
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-provided DofTransformation object.
Definition pfespace.cpp:589
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:5319
void GetCharacteristics(real_t &h_min, real_t &h_max, real_t &kappa_min, real_t &kappa_max)
Definition pmesh.cpp:6201
void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0) override
This function is not public anymore. Use GeneralRefinement instead.
Definition pmesh.cpp:3907
int GroupNQuadrilaterals(int group) const
Definition pmesh.hpp:480
ElementTransformation * GetFaceNbrElementTransformation(int FaceNo)
Returns a pointer to the transformation defining the i-th face neighbor.
Definition pmesh.cpp:3098
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:1882
Table send_face_nbr_elements
Definition pmesh.hpp:469
Array< int > face_nbr_vertices_offset
Definition pmesh.hpp:465
void BuildVertexGroup(int ngroups, const Table &vert_element)
Definition pmesh.cpp:705
void NURBSUniformRefinement(int rf=2, real_t tol=1.0e-12) override
Refine NURBS mesh, with an optional refinement factor.
Definition pmesh.cpp:4588
void SetAttributes(bool elem_attrs_changed=true, bool bdr_attrs_changed=true) override
Determine the sets of unique attribute values in domain if elem_attrs_changed and boundary elements i...
Definition pmesh.cpp:1593
MPI_Comm GetComm() const
Definition pmesh.hpp:405
void PrintXG(std::ostream &out=mfem::out) const override
Definition pmesh.cpp:4612
Array< Element * > shared_edges
Definition pmesh.hpp:69
int GetMyRank() const
Definition pmesh.hpp:407
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:1867
void RefineGroups(const DSTable &v_to_v, int *middle)
Update the groups after triangle refinement.
Definition pmesh.cpp:4079
void ParPrint(std::ostream &out, const std::string &comments="") const
Definition pmesh.cpp:6341
friend class ParNCMesh
Definition pmesh.hpp:35
void GetSharedTriCommunicator(int ordering, GroupCommunicator &stria_comm) const
Get the shared face triangles GroupCommunicator.
Definition pmesh.cpp:1728
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:3965
int GetNSharedFaces() const
Return the number of shared faces (3D), edges (2D), vertices (1D)
Definition pmesh.cpp:3164
Array< int > sface_lface
Definition pmesh.hpp:86
void SaveAsOne(const std::string &fname, int precision=16) const
Definition pmesh.cpp:5636
void ExchangeFaceNbrData()
Definition pmesh.cpp:2079
Table group_sedge
Definition pmesh.hpp:78
int GetNRanks() const
Definition pmesh.hpp:406
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:3237
int GroupVertex(int group, int i) const
Accessors for entities within a shared group structure.
Definition pmesh.hpp:493
void UniformRefinement2D() override
Refine a mixed 2D mesh uniformly.
Definition pmesh.cpp:4536
bool WantSkipSharedMaster(const NCMesh::Master &master) const
Definition pmesh.cpp:4814
FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31) override
Definition pmesh.cpp:2907
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:1551
bool AnisotropicConflict(const Array< Refinement > &refinements, std::set< int > &conflicts) const
Return true if the input array of refinements to be performed would result in conflicting anisotropic...
Definition pmesh.cpp:3900
Table * GetFaceToAllElementTable() const
Definition pmesh.cpp:2849
virtual ~ParMesh()
Definition pmesh.cpp:6885
void ReduceMeshGen()
Definition pmesh.cpp:891
void GetGlobalElementIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are supported.
Definition pmesh.cpp:6696
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:2687
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:1656
void PrintInfo(std::ostream &out=mfem::out) override
Print various parallel mesh stats.
Definition pmesh.cpp:6218
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:3205
void GetGlobalVertexIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition pmesh.cpp:6629
bool HasBoundaryElements() const override
Checks if any rank in the mesh has boundary elements.
Definition pmesh.cpp:1617
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:3396
void GetFaceNbrElementFaces(int i, Array< int > &faces, Array< int > &orientation) const
Definition pmesh.cpp:2824
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:2017
long glob_offset_sequence
Definition pmesh.hpp:96
int GetLocalElementNum(long long global_element_num) const
Definition pmesh.cpp:1543
void MarkExternalBoundaries(Array< int > &bdr_marker, bool excl=true) const override
Mark boundary attributes of external boundaries.
Definition pmesh.cpp:6773
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:6493
void GetSharedQuadCommunicator(int ordering, GroupCommunicator &squad_comm) const
Get the shared face quadrilaterals GroupCommunicator.
Definition pmesh.cpp:1704
void BuildFaceNbrElementToFaceTable()
Definition pmesh.cpp:2720
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:6668
Array< Vertex > face_nbr_vertices
Definition pmesh.hpp:467
void UniformRefineGroups2D(int old_nv)
Definition pmesh.cpp:4336
void GetGlobalEdgeIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition pmesh.cpp:6645
void ExchangeFaceNbrNodes()
Definition pmesh.cpp:2562
void SetNodalFESpace(FiniteElementSpace *nfes) override
Definition pmesh.cpp:2038
void UniformRefinement3D() override
Refine a mixed 3D mesh uniformly.
Definition pmesh.cpp:4560
void PrintVTU(std::string pathname, VTKFormat format=VTKFormat::ASCII, bool high_order_output=false, int compression_level=0, bool bdr_elements=false) override
Definition pmesh.cpp:6425
void Rebalance()
Definition pmesh.cpp:4027
bool have_face_nbr_data
Definition pmesh.hpp:462
long long ReduceInt(int value) const override
Utility function: sum integers from all processors (Allreduce).
Definition pmesh.cpp:6334
Table send_face_nbr_vertices
Definition pmesh.hpp:470
ParMesh()
Default constructor. Create an empty ParMesh.
Definition pmesh.hpp:337
void GetExteriorFaceMarker(Array< int > &face_marker) const override
Populate a marker array identifying exterior faces.
Definition pmesh.cpp:6710
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:1917
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:1528
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:466
GroupTopology gtopo
Definition pmesh.hpp:459
void Destroy()
Definition pmesh.cpp:6869
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:2952
void PrintSharedEntities(const std::string &fname_prefix) const
Debugging method.
Definition pmesh.cpp:6542
int GroupNTriangles(int group) const
Definition pmesh.hpp:479
void GetSharedVertexCommunicator(int ordering, GroupCommunicator &svert_comm) const
Get the shared vertices GroupCommunicator.
Definition pmesh.cpp:1680
int GetSharedFace(int sface) const
Return the local face index for the given shared face.
Definition pmesh.cpp:3183
void EnsureParNodes()
If the mesh is curved, make sure 'Nodes' is ParGridFunction.
Definition pmesh.cpp:2059
int GroupNEdges(int group) const
Definition pmesh.hpp:478
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:1953
void MarkTetMeshForRefinement(const DSTable &v_to_v) override
Definition pmesh.cpp:1752
Array< int > face_nbr_group
Definition pmesh.hpp:463
Array< int > face_nbr_elements_offset
Definition pmesh.hpp:464
ParMesh & operator=(ParMesh &&mesh)
Move assignment operator.
Definition pmesh.cpp:100
void DeleteFaceNbrData()
Definition pmesh.cpp:1996
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:576
void UniformRefineGroups3D(int old_nv, int old_nedges, const DSTable &old_v_to_v, const STable3D &old_faces, Array< int > *f2qf)
Definition pmesh.cpp:4387
void GetGhostFaceTransformation(FaceElementTransformations &FElTr, Element::Type face_type, Geometry::Type face_geom) const
Definition pmesh.cpp:3066
void RebalanceImpl(const Array< int > *partition)
Definition pmesh.cpp:4037
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:5647
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:1645
void Save(const std::string &fname, int precision=16) const override
Definition pmesh.cpp:4966
void DistributeAttributes(Array< int > &attr)
Ensure that bdr_attributes and attributes agree across processors.
Definition pmesh.cpp:1557
STable3D * GetSharedFacesTable()
Definition pmesh.cpp:2622
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:474
real_t GetFaceNbrElementSize(int i, int type=0)
Definition pmesh.cpp:3159
ParNCMesh * pncmesh
Definition pmesh.hpp:472
void UnmarkInternalBoundaries(Array< int > &bdr_marker, bool excl=true) const override
Unmark boundary attributes of internal boundaries.
Definition pmesh.cpp:6717
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:2806
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:1634
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:2933
void PrintAsSerial(std::ostream &out=mfem::out, const std::string &comments="") const
Definition pmesh.cpp:5308
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:6181
void PrintAsOne(std::ostream &out=mfem::out, const std::string &comments="") const
Definition pmesh.cpp:4994
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4827
void RefineNURBSWithKVFactors(int rf, const std::string &kvf) override
Definition pmesh.cpp:4604
void Swap(ParMesh &other)
Definition pmesh.cpp:6826
void GroupEdge(int group, int i, int &edge, int &o) const
Definition pmesh.cpp:1626
int GroupNVertices(int group) const
Definition pmesh.hpp:477
A parallel extension of the NCMesh class.
Definition pncmesh.hpp:63
bool AnisotropicConflict(const Array< Refinement > &refinements, std::set< int > &conflicts)
Definition pncmesh.cpp:1507
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
Definition pncmesh.hpp:194
void GetFaceNeighbors(class ParMesh &pmesh)
Definition pncmesh.cpp:993
void LimitNCLevel(int max_nc_level) override
Parallel version of NCMesh::LimitNCLevel.
Definition pncmesh.cpp:2087
void Refine(const Array< Refinement > &refinements) override
Definition pncmesh.cpp:2009
void GetConformingSharedStructures(class ParMesh &pmesh)
Definition pncmesh.cpp:892
void Derefine(const Array< int > &derefs) override
Definition pncmesh.cpp:2136
void Rebalance(const Array< int > *custom_partition=NULL)
Definition pncmesh.cpp:2448
void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level) override
Definition pncmesh.cpp:2390
const NCList & GetSharedList(int entity)
Helper to get shared vertices/edges/faces ('entity' == 0/1/2 resp.).
Definition pncmesh.hpp:138
const NCList & GetSharedEdges()
Definition pncmesh.hpp:130
const NCList & GetSharedFaces()
Definition pncmesh.hpp:131
int InitialPartition(int index) const
Helper to get the partitioning when the serial mesh gets split initially.
Definition pncmesh.hpp:347
void SynchronizeDerefinementData(Array< Type > &elem_data, const Table &deref_table)
Definition pncmesh.cpp:2308
Parallel version of NURBSExtension.
Definition nurbs.hpp:1036
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:407
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
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
int * GetJ()
Definition table.hpp:128
void AddConnections(int r, const int *c, int nc)
Definition table.cpp:152
void Swap(Table &other)
Definition table.cpp:432
int RowSize(int i) const
Definition table.hpp:122
void ShiftUpI()
Definition table.cpp:163
void Clear()
Definition table.cpp:420
void SetSize(int dim, int connections_per_row)
Set the size and the number of connections for the table.
Definition table.cpp:172
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
void AddConnection(int r, int c)
Definition table.hpp:89
void MakeI(int nrows)
Definition table.cpp:130
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:253
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:103
int Size_of_connections() const
Returns the number of connections in the table.
Definition table.hpp:110
void AddColumnsInRow(int r, int ncol)
Definition table.hpp:87
void MakeJ()
Definition table.cpp:140
int * GetI()
Definition table.hpp:127
void AddAColumnInRow(int r)
Definition table.hpp:86
void SetDims(int rows, int nnz)
Set the rows and the number of all connections for the table.
Definition table.cpp:188
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
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:524
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:702
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
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 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:443
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:3205
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
Definition array.hpp:738
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
const T & AsConst(const T &a)
Utility function similar to std::as_const in c++17.
Definition array.hpp:424
float real_t
Definition config.hpp:46
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:49
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:176
int slaves_end
slave faces
Definition ncmesh.hpp:277
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:301
Array< MeshId > conforming
All MeshIds corresponding to conformal faces.
Definition ncmesh.hpp:302
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:304
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:303
std::array< int, NCMesh::MaxFaceNodes > nodes