MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
pmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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
2017std::unique_ptr<ParGridFunction> ParMesh::GetJacobianDeterminantGF() const
2018{
2019 int mesh_poly_deg =
2020 Nodes != NULL ? Nodes->FESpace()->GetMaxElementOrder() : 1;
2021 // determinant order is d*p-1 for tensor product elements and
2022 // d*(p-1) for simplices. We use the former here for simplicity.
2023 int det_order = Dim*mesh_poly_deg-1;
2024 L2_FECollection *fec_det = new L2_FECollection(det_order, Dim,
2026 ParFiniteElementSpace *fespace_det =
2027 new ParFiniteElementSpace(const_cast<ParMesh *>(this), fec_det);
2028 auto detgf = std::make_unique<ParGridFunction>(fespace_det);
2029 detgf->MakeOwner(fec_det);
2031 return detgf;
2032}
2033
2034void ParMesh::SetCurvature(int order, bool discont, int space_dim, int ordering,
2035 int pyrtype)
2036{
2038 space_dim = (space_dim == -1) ? spaceDim : space_dim;
2040 if (discont)
2041 {
2042 nfec = new L2_FECollection(order, Dim, BasisType::GaussLobatto,
2043 FiniteElement::VALUE, pyrtype);
2044 }
2045 else
2046 {
2047 nfec = new H1_FECollection(order, Dim, BasisType::GaussLobatto, pyrtype);
2048 }
2049 ParFiniteElementSpace* nfes = new ParFiniteElementSpace(this, nfec, space_dim,
2050 ordering);
2051 auto pnodes = new ParGridFunction(nfes);
2052 GetNodes(*pnodes);
2053 NewNodes(*pnodes, true);
2054 Nodes->MakeOwner(nfec);
2055}
2056
2058{
2060 ParFiniteElementSpace *npfes = dynamic_cast<ParFiniteElementSpace*>(nfes);
2061 if (npfes)
2062 {
2063 SetNodalFESpace(npfes);
2064 }
2065 else
2066 {
2068 }
2069}
2070
2077
2079{
2080 if (Nodes && dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace()) == NULL)
2081 {
2083 ParFiniteElementSpace *pfes =
2084 new ParFiniteElementSpace(*Nodes->FESpace(), *this);
2085 ParGridFunction *new_nodes = new ParGridFunction(pfes);
2086 *new_nodes = *Nodes;
2087 if (Nodes->OwnFEC())
2088 {
2089 new_nodes->MakeOwner(Nodes->OwnFEC());
2090 Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
2091 delete Nodes->FESpace();
2092 }
2093 delete Nodes;
2094 Nodes = new_nodes;
2095 }
2096}
2097
2099{
2101 {
2102 return;
2103 }
2104
2105 if (Nonconforming())
2106 {
2107 // With ParNCMesh we can set up face neighbors mostly without communication.
2108 pncmesh->GetFaceNeighbors(*this);
2109 have_face_nbr_data = true;
2110
2112 return;
2113 }
2114
2115 Table *gr_sface;
2116 int *s2l_face;
2117 bool del_tables = false;
2118 if (Dim == 1)
2119 {
2120 gr_sface = &group_svert;
2121 s2l_face = svert_lvert;
2122 }
2123 else if (Dim == 2)
2124 {
2125 gr_sface = &group_sedge;
2126 s2l_face = sedge_ledge;
2127 }
2128 else
2129 {
2130 s2l_face = sface_lface;
2131 if (shared_trias.Size() == sface_lface.Size())
2132 {
2133 // All shared faces are Triangular
2134 gr_sface = &group_stria;
2135 }
2136 else if (shared_quads.Size() == sface_lface.Size())
2137 {
2138 // All shared faced are Quadrilateral
2139 gr_sface = &group_squad;
2140 }
2141 else
2142 {
2143 // Shared faces contain a mixture of triangles and quads
2144 gr_sface = new Table;
2145 del_tables = true;
2146
2147 // Merge the Tables group_stria and group_squad
2148 gr_sface->MakeI(group_stria.Size());
2149 for (int gr=0; gr<group_stria.Size(); gr++)
2150 {
2151 gr_sface->AddColumnsInRow(gr,
2152 group_stria.RowSize(gr) +
2153 group_squad.RowSize(gr));
2154 }
2155 gr_sface->MakeJ();
2156 const int nst = shared_trias.Size();
2157 for (int gr=0; gr<group_stria.Size(); gr++)
2158 {
2159 gr_sface->AddConnections(gr,
2160 group_stria.GetRow(gr),
2161 group_stria.RowSize(gr));
2162 for (int c=0; c<group_squad.RowSize(gr); c++)
2163 {
2164 gr_sface->AddConnection(gr,
2165 nst + group_squad.GetRow(gr)[c]);
2166 }
2167 }
2168 gr_sface->ShiftUpI();
2169 }
2170 }
2171
2172 ExchangeFaceNbrData(gr_sface, s2l_face);
2173
2174 if (Dim == 3)
2175 {
2177 }
2178
2179 if (del_tables) { delete gr_sface; }
2180
2181 if ( have_face_nbr_data ) { return; }
2182
2183 have_face_nbr_data = true;
2184
2186}
2187
2188void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
2189{
2190 int num_face_nbrs = 0;
2191 for (int g = 1; g < GetNGroups(); g++)
2192 {
2193 if (gr_sface->RowSize(g-1) > 0)
2194 {
2195 num_face_nbrs++;
2196 }
2197 }
2198
2199 face_nbr_group.SetSize(num_face_nbrs);
2200
2201 if (num_face_nbrs == 0)
2202 {
2203 have_face_nbr_data = true;
2204 return;
2205 }
2206
2207 {
2208 // sort face-neighbors by processor rank
2209 Array<Pair<int, int> > rank_group(num_face_nbrs);
2210
2211 for (int g = 1, counter = 0; g < GetNGroups(); g++)
2212 {
2213 if (gr_sface->RowSize(g-1) > 0)
2214 {
2215 MFEM_ASSERT(gtopo.GetGroupSize(g) == 2, "group size is not 2!");
2216
2217 const int *nbs = gtopo.GetGroup(g);
2218 int lproc = (nbs[0]) ? nbs[0] : nbs[1];
2219 rank_group[counter].one = gtopo.GetNeighborRank(lproc);
2220 rank_group[counter].two = g;
2221 counter++;
2222 }
2223 }
2224
2225 SortPairs<int, int>(rank_group, rank_group.Size());
2226
2227 for (int fn = 0; fn < num_face_nbrs; fn++)
2228 {
2229 face_nbr_group[fn] = rank_group[fn].two;
2230 }
2231 }
2232
2233 MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2234 MPI_Request *send_requests = requests;
2235 MPI_Request *recv_requests = requests + num_face_nbrs;
2236 MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2237
2238 int *nbr_data = new int[6*num_face_nbrs];
2239 int *nbr_send_data = nbr_data;
2240 int *nbr_recv_data = nbr_data + 3*num_face_nbrs;
2241
2242 Array<int> el_marker(GetNE());
2243 Array<int> vertex_marker(GetNV());
2244 el_marker = -1;
2245 vertex_marker = -1;
2246
2247 Array<int> fcs, cor;
2248
2249 Table send_face_nbr_elemdata, send_face_nbr_facedata;
2250
2251 send_face_nbr_elements.MakeI(num_face_nbrs);
2252 send_face_nbr_vertices.MakeI(num_face_nbrs);
2253 send_face_nbr_elemdata.MakeI(num_face_nbrs);
2254 send_face_nbr_facedata.MakeI(num_face_nbrs);
2255 for (int fn = 0; fn < num_face_nbrs; fn++)
2256 {
2257 int nbr_group = face_nbr_group[fn];
2258 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2259 int *sface = gr_sface->GetRow(nbr_group-1);
2260 for (int i = 0; i < num_sfaces; i++)
2261 {
2262 int lface = s2l_face[sface[i]];
2263 int el = faces_info[lface].Elem1No;
2264 if (el_marker[el] != fn)
2265 {
2266 el_marker[el] = fn;
2268
2269 const int nv = elements[el]->GetNVertices();
2270 const int *v = elements[el]->GetVertices();
2271 for (int j = 0; j < nv; j++)
2272 if (vertex_marker[v[j]] != fn)
2273 {
2274 vertex_marker[v[j]] = fn;
2276 }
2277
2278 const int nf = elements[el]->GetNFaces();
2279
2280 send_face_nbr_elemdata.AddColumnsInRow(fn, nv + nf + 2);
2281 }
2282 }
2283 send_face_nbr_facedata.AddColumnsInRow(fn, 2*num_sfaces);
2284
2285 nbr_send_data[3*fn ] = send_face_nbr_elements.GetI()[fn];
2286 nbr_send_data[3*fn+1] = send_face_nbr_vertices.GetI()[fn];
2287 nbr_send_data[3*fn+2] = send_face_nbr_elemdata.GetI()[fn];
2288
2289 int nbr_rank = GetFaceNbrRank(fn);
2290 int tag = 0;
2291
2292 MPI_Isend(&nbr_send_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2293 &send_requests[fn]);
2294 MPI_Irecv(&nbr_recv_data[3*fn], 3, MPI_INT, nbr_rank, tag, MyComm,
2295 &recv_requests[fn]);
2296 }
2299 send_face_nbr_elemdata.MakeJ();
2300 send_face_nbr_facedata.MakeJ();
2301 el_marker = -1;
2302 vertex_marker = -1;
2303 const int nst = shared_trias.Size();
2304 for (int fn = 0; fn < num_face_nbrs; fn++)
2305 {
2306 int nbr_group = face_nbr_group[fn];
2307 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2308 int *sface = gr_sface->GetRow(nbr_group-1);
2309 for (int i = 0; i < num_sfaces; i++)
2310 {
2311 const int sf = sface[i];
2312 int lface = s2l_face[sf];
2313 int el = faces_info[lface].Elem1No;
2314 if (el_marker[el] != fn)
2315 {
2316 el_marker[el] = fn;
2318
2319 const int nv = elements[el]->GetNVertices();
2320 const int *v = elements[el]->GetVertices();
2321 for (int j = 0; j < nv; j++)
2322 if (vertex_marker[v[j]] != fn)
2323 {
2324 vertex_marker[v[j]] = fn;
2326 }
2327
2328 send_face_nbr_elemdata.AddConnection(fn, GetAttribute(el));
2329 send_face_nbr_elemdata.AddConnection(
2330 fn, GetElementBaseGeometry(el));
2331 send_face_nbr_elemdata.AddConnections(fn, v, nv);
2332
2333 if (Dim == 3)
2334 {
2335 const int nf = elements[el]->GetNFaces();
2336 GetElementFaces(el, fcs, cor);
2337 send_face_nbr_elemdata.AddConnections(fn, cor, nf);
2338 }
2339 }
2340 send_face_nbr_facedata.AddConnection(fn, el);
2341 int info = faces_info[lface].Elem1Inf;
2342 // change the orientation in info to be relative to the shared face
2343 // in 1D and 2D keep the orientation equal to 0
2344 if (Dim == 3)
2345 {
2346 const int *lf_v = faces[lface]->GetVertices();
2347 if (sf < nst) // triangle shared face
2348 {
2349 info += GetTriOrientation(shared_trias[sf].v, lf_v);
2350 }
2351 else // quad shared face
2352 {
2353 info += GetQuadOrientation(shared_quads[sf-nst].v, lf_v);
2354 }
2355 }
2356 send_face_nbr_facedata.AddConnection(fn, info);
2357 }
2358 }
2361 send_face_nbr_elemdata.ShiftUpI();
2362 send_face_nbr_facedata.ShiftUpI();
2363
2364 // convert the vertex indices in send_face_nbr_elemdata
2365 // convert the element indices in send_face_nbr_facedata
2366 for (int fn = 0; fn < num_face_nbrs; fn++)
2367 {
2368 int num_elems = send_face_nbr_elements.RowSize(fn);
2369 int *elems = send_face_nbr_elements.GetRow(fn);
2370 int num_verts = send_face_nbr_vertices.RowSize(fn);
2371 int *verts = send_face_nbr_vertices.GetRow(fn);
2372 int *elemdata = send_face_nbr_elemdata.GetRow(fn);
2373 int num_sfaces = send_face_nbr_facedata.RowSize(fn)/2;
2374 int *facedata = send_face_nbr_facedata.GetRow(fn);
2375
2376 for (int i = 0; i < num_verts; i++)
2377 {
2378 vertex_marker[verts[i]] = i;
2379 }
2380
2381 for (int el = 0; el < num_elems; el++)
2382 {
2383 const int nv = elements[elems[el]]->GetNVertices();
2384 const int nf = (Dim == 3) ? elements[elems[el]]->GetNFaces() : 0;
2385 elemdata += 2; // skip the attribute and the geometry type
2386 for (int j = 0; j < nv; j++)
2387 {
2388 elemdata[j] = vertex_marker[elemdata[j]];
2389 }
2390 elemdata += nv + nf;
2391
2392 el_marker[elems[el]] = el;
2393 }
2394
2395 for (int i = 0; i < num_sfaces; i++)
2396 {
2397 facedata[2*i] = el_marker[facedata[2*i]];
2398 }
2399 }
2400
2401 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2402
2403 Array<int> recv_face_nbr_facedata;
2404 Table recv_face_nbr_elemdata;
2405
2406 // fill-in face_nbr_elements_offset, face_nbr_vertices_offset
2407 face_nbr_elements_offset.SetSize(num_face_nbrs + 1);
2408 face_nbr_vertices_offset.SetSize(num_face_nbrs + 1);
2409 recv_face_nbr_elemdata.MakeI(num_face_nbrs);
2412 for (int fn = 0; fn < num_face_nbrs; fn++)
2413 {
2415 face_nbr_elements_offset[fn] + nbr_recv_data[3*fn];
2417 face_nbr_vertices_offset[fn] + nbr_recv_data[3*fn+1];
2418 recv_face_nbr_elemdata.AddColumnsInRow(fn, nbr_recv_data[3*fn+2]);
2419 }
2420 recv_face_nbr_elemdata.MakeJ();
2421
2422 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2423
2424 // send and receive the element data
2425 for (int fn = 0; fn < num_face_nbrs; fn++)
2426 {
2427 int nbr_rank = GetFaceNbrRank(fn);
2428 int tag = 0;
2429
2430 MPI_Isend(send_face_nbr_elemdata.GetRow(fn),
2431 send_face_nbr_elemdata.RowSize(fn),
2432 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2433
2434 MPI_Irecv(recv_face_nbr_elemdata.GetRow(fn),
2435 recv_face_nbr_elemdata.RowSize(fn),
2436 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2437 }
2438
2439 // convert the element data into face_nbr_elements
2440 face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
2441 face_nbr_el_ori.reset(new Table(face_nbr_elements_offset[num_face_nbrs], 6));
2442 while (true)
2443 {
2444 int fn;
2445 MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2446
2447 if (fn == MPI_UNDEFINED)
2448 {
2449 break;
2450 }
2451
2452 int vert_off = face_nbr_vertices_offset[fn];
2453 int elem_off = face_nbr_elements_offset[fn];
2454 int num_elems = face_nbr_elements_offset[fn+1] - elem_off;
2455 int *recv_elemdata = recv_face_nbr_elemdata.GetRow(fn);
2456
2457 for (int i = 0; i < num_elems; i++)
2458 {
2459 Element *el = NewElement(recv_elemdata[1]);
2460 el->SetAttribute(recv_elemdata[0]);
2461 recv_elemdata += 2;
2462 int nv = el->GetNVertices();
2463 for (int j = 0; j < nv; j++)
2464 {
2465 recv_elemdata[j] += vert_off;
2466 }
2467 el->SetVertices(recv_elemdata);
2468 recv_elemdata += nv;
2469 if (Dim == 3)
2470 {
2471 int nf = el->GetNFaces();
2472 int * fn_ori = face_nbr_el_ori->GetRow(elem_off);
2473 for (int j = 0; j < nf; j++)
2474 {
2475 fn_ori[j] = recv_elemdata[j];
2476 }
2477 recv_elemdata += nf;
2478 }
2479 face_nbr_elements[elem_off++] = el;
2480 }
2481 }
2482 face_nbr_el_ori->Finalize();
2483
2484 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2485
2486 // send and receive the face data
2487 recv_face_nbr_facedata.SetSize(
2488 send_face_nbr_facedata.Size_of_connections());
2489 for (int fn = 0; fn < num_face_nbrs; fn++)
2490 {
2491 int nbr_rank = GetFaceNbrRank(fn);
2492 int tag = 0;
2493
2494 MPI_Isend(send_face_nbr_facedata.GetRow(fn),
2495 send_face_nbr_facedata.RowSize(fn),
2496 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2497
2498 // the size of the send and receive face data is the same
2499 MPI_Irecv(&recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]],
2500 send_face_nbr_facedata.RowSize(fn),
2501 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2502 }
2503
2504 // transfer the received face data into faces_info
2505 while (true)
2506 {
2507 int fn;
2508 MPI_Waitany(num_face_nbrs, recv_requests, &fn, statuses);
2509
2510 if (fn == MPI_UNDEFINED)
2511 {
2512 break;
2513 }
2514
2515 int elem_off = face_nbr_elements_offset[fn];
2516 int nbr_group = face_nbr_group[fn];
2517 int num_sfaces = gr_sface->RowSize(nbr_group-1);
2518 int *sface = gr_sface->GetRow(nbr_group-1);
2519 int *facedata =
2520 &recv_face_nbr_facedata[send_face_nbr_facedata.GetI()[fn]];
2521
2522 for (int i = 0; i < num_sfaces; i++)
2523 {
2524 const int sf = sface[i];
2525 int lface = s2l_face[sf];
2526 FaceInfo &face_info = faces_info[lface];
2527 face_info.Elem2No = -1 - (facedata[2*i] + elem_off);
2528 int info = facedata[2*i+1];
2529 // change the orientation in info to be relative to the local face
2530 if (Dim < 3)
2531 {
2532 info++; // orientation 0 --> orientation 1
2533 }
2534 else
2535 {
2536 int nbr_ori = info%64, nbr_v[4];
2537 const int *lf_v = faces[lface]->GetVertices();
2538
2539 if (sf < nst) // triangle shared face
2540 {
2541 // apply the nbr_ori to sf_v to get nbr_v
2542 const int *perm = tri_t::Orient[nbr_ori];
2543 const int *sf_v = shared_trias[sf].v;
2544 for (int j = 0; j < 3; j++)
2545 {
2546 nbr_v[perm[j]] = sf_v[j];
2547 }
2548 // get the orientation of nbr_v w.r.t. the local face
2549 nbr_ori = GetTriOrientation(lf_v, nbr_v);
2550 }
2551 else // quad shared face
2552 {
2553 // apply the nbr_ori to sf_v to get nbr_v
2554 const int *perm = quad_t::Orient[nbr_ori];
2555 const int *sf_v = shared_quads[sf-nst].v;
2556 for (int j = 0; j < 4; j++)
2557 {
2558 nbr_v[perm[j]] = sf_v[j];
2559 }
2560 // get the orientation of nbr_v w.r.t. the local face
2561 nbr_ori = GetQuadOrientation(lf_v, nbr_v);
2562 }
2563
2564 info = 64*(info/64) + nbr_ori;
2565 }
2566 face_info.Elem2Inf = info;
2567 }
2568 }
2569
2570 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2571
2572 // allocate the face_nbr_vertices
2573 face_nbr_vertices.SetSize(face_nbr_vertices_offset[num_face_nbrs]);
2574
2575 delete [] nbr_data;
2576
2577 delete [] statuses;
2578 delete [] requests;
2579}
2580
2582{
2583 if (!have_face_nbr_data)
2584 {
2585 ExchangeFaceNbrData(); // calls this method at the end
2586 }
2587 else if (Nodes == NULL)
2588 {
2589 if (Nonconforming())
2590 {
2591 // with ParNCMesh we already have the vertices
2592 return;
2593 }
2594
2595 int num_face_nbrs = GetNFaceNeighbors();
2596
2597 if (!num_face_nbrs) { return; }
2598
2599 MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
2600 MPI_Request *send_requests = requests;
2601 MPI_Request *recv_requests = requests + num_face_nbrs;
2602 MPI_Status *statuses = new MPI_Status[num_face_nbrs];
2603
2604 // allocate buffer and copy the vertices to be sent
2606 for (int i = 0; i < send_vertices.Size(); i++)
2607 {
2608 send_vertices[i] = vertices[send_face_nbr_vertices.GetJ()[i]];
2609 }
2610
2611 // send and receive the vertices
2612 for (int fn = 0; fn < num_face_nbrs; fn++)
2613 {
2614 int nbr_rank = GetFaceNbrRank(fn);
2615 int tag = 0;
2616
2617 MPI_Isend(send_vertices[send_face_nbr_vertices.GetI()[fn]](),
2619 MPITypeMap<real_t>::mpi_type, nbr_rank, tag, MyComm, &send_requests[fn]);
2620
2622 3*(face_nbr_vertices_offset[fn+1] -
2624 MPITypeMap<real_t>::mpi_type, nbr_rank, tag, MyComm, &recv_requests[fn]);
2625 }
2626
2627 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2628 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2629
2630 delete [] statuses;
2631 delete [] requests;
2632 }
2633 else
2634 {
2635 ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
2636 MFEM_VERIFY(pNodes != NULL, "Nodes are not ParGridFunction!");
2637 pNodes->ExchangeFaceNbrData();
2638 }
2639}
2640
2642{
2643 STable3D *sfaces_tbl = new STable3D(face_nbr_vertices.Size());
2644 for (int i = 0; i < face_nbr_elements.Size(); i++)
2645 {
2646 const int *v = face_nbr_elements[i]->GetVertices();
2647 switch (face_nbr_elements[i]->GetType())
2648 {
2650 {
2651 for (int j = 0; j < 4; j++)
2652 {
2653 const int *fv = tet_t::FaceVert[j];
2654 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2655 }
2656 break;
2657 }
2658 case Element::WEDGE:
2659 {
2660 for (int j = 0; j < 2; j++)
2661 {
2662 const int *fv = pri_t::FaceVert[j];
2663 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2664 }
2665 for (int j = 2; j < 5; j++)
2666 {
2667 const int *fv = pri_t::FaceVert[j];
2668 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2669 }
2670 break;
2671 }
2672 case Element::PYRAMID:
2673 {
2674 for (int j = 0; j < 1; j++)
2675 {
2676 const int *fv = pyr_t::FaceVert[j];
2677 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2678 }
2679 for (int j = 1; j < 5; j++)
2680 {
2681 const int *fv = pyr_t::FaceVert[j];
2682 sfaces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
2683 }
2684 break;
2685 }
2687 {
2688 // find the face by the vertices with the smallest 3 numbers
2689 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
2690 for (int j = 0; j < 6; j++)
2691 {
2692 const int *fv = hex_t::FaceVert[j];
2693 sfaces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
2694 }
2695 break;
2696 }
2697 default:
2698 MFEM_ABORT("Unexpected type of Element.");
2699 }
2700 }
2701 return sfaces_tbl;
2702}
2703
2704template <int N>
2705void
2707 const std::unique_ptr<STable3D> &faces,
2708 const std::unique_ptr<STable3D> &shared_faces,
2709 int elem, int start, int end, const int fverts[][N])
2710{
2711 for (int i = start; i < end; ++i)
2712 {
2713 // Reference face vertices.
2714 const auto fv = fverts[i];
2715 // Element specific face vertices.
2716 const Vert3 elem_fv(elem_vertices[fv[0]], elem_vertices[fv[1]],
2717 elem_vertices[fv[2]]);
2718
2719 // Check amongst the faces of elements local to this rank for this set of vertices
2720 const int lf = faces->Index(elem_fv.v[0], elem_fv.v[1], elem_fv.v[2]);
2721
2722 // If the face wasn't found amongst processor local elements, search the
2723 // ghosts for this set of vertices.
2724 const int sf = lf < 0 ? shared_faces->Index(elem_fv.v[0], elem_fv.v[1],
2725 elem_fv.v[2]) : -1;
2726 // If find local face -> use that
2727 // else if find shared face -> shift and use that
2728 // else no face found -> set to -1
2729 const int face_to_add = lf < 0 ? (sf >= 0 ? sf + NumOfFaces : -1) : lf;
2730
2731 MFEM_ASSERT(sf >= 0 ||
2732 lf >= 0, "Face must be from a local or a face neighbor element");
2733
2734 // Add this discovered face to the list of faces of this face neighbor element
2735 face_nbr_el_to_face->Push(elem, face_to_add);
2736 }
2737}
2738
2740{
2741 const auto faces = std::unique_ptr<STable3D>(GetFacesTable());
2742 const auto shared_faces = std::unique_ptr<STable3D>(GetSharedFacesTable());
2743
2744 face_nbr_el_to_face.reset(new Table(face_nbr_elements.Size(), 6));
2745
2746 Array<int> v;
2747
2748 // Helper for adding quadrilateral faces.
2749 auto add_quad_faces = [&faces, &shared_faces, &v, this]
2750 (int elem, int start, int end, const int fverts[][4])
2751 {
2752 for (int i = start; i < end; ++i)
2753 {
2754 const int * const fv = fverts[i];
2755 int k = 0;
2756 int max = v[fv[0]];
2757
2758 if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
2759 if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
2760 if (max < v[fv[3]]) { k = 3; }
2761
2762 int v0 = -1, v1 = -1, v2 = -1;
2763 switch (k)
2764 {
2765 case 0:
2766 v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2767 break;
2768 case 1:
2769 v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
2770 break;
2771 case 2:
2772 v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
2773 break;
2774 case 3:
2775 v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
2776 break;
2777 }
2778 int lf = faces->Index(v0, v1, v2);
2779 if (lf < 0)
2780 {
2781 lf = shared_faces->Index(v0, v1, v2);
2782 if (lf >= 0)
2783 {
2784 lf += NumOfFaces;
2785 }
2786 }
2787 face_nbr_el_to_face->Push(elem, lf);
2788 }
2789 };
2790
2791 for (int i = 0; i < face_nbr_elements.Size(); i++)
2792 {
2793 face_nbr_elements[i]->GetVertices(v);
2794 switch (face_nbr_elements[i]->GetType())
2795 {
2797 {
2798 AddTriFaces(v, faces, shared_faces, i, 0, 4, tet_t::FaceVert);
2799 break;
2800 }
2801 case Element::WEDGE:
2802 {
2803 AddTriFaces(v, faces, shared_faces, i, 0, 2, pri_t::FaceVert);
2804 add_quad_faces(i, 2, 5, pri_t::FaceVert);
2805 break;
2806 }
2807 case Element::PYRAMID:
2808 {
2809 add_quad_faces(i, 0, 1, pyr_t::FaceVert);
2810 AddTriFaces(v, faces, shared_faces, i, 1, 5, pyr_t::FaceVert);
2811 break;
2812 }
2814 {
2815 add_quad_faces(i, 0, 6, hex_t::FaceVert);
2816 break;
2817 }
2818 default:
2819 MFEM_ABORT("Unexpected type of Element.");
2820 }
2821 }
2822 face_nbr_el_to_face->Finalize();
2823}
2824
2826{
2827 if (Conforming())
2828 {
2829 int nbr_group = face_nbr_group[fn];
2830 const int *nbs = gtopo.GetGroup(nbr_group);
2831 int nbr_lproc = (nbs[0]) ? nbs[0] : nbs[1];
2832 int nbr_rank = gtopo.GetNeighborRank(nbr_lproc);
2833 return nbr_rank;
2834 }
2835 else
2836 {
2837 // NC: simplified handling of face neighbor ranks
2838 return face_nbr_group[fn];
2839 }
2840}
2841
2842void
2844 Array<int> &orientations) const
2845{
2846 int el_nbr = i - GetNE();
2847 if (face_nbr_el_to_face != nullptr && el_nbr < face_nbr_el_to_face->Size())
2848 {
2849 face_nbr_el_to_face->GetRow(el_nbr, faces);
2850 }
2851 else
2852 {
2853 MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2854 "face_nbr_el_to_face not generated correctly.");
2855 }
2856
2857 if (face_nbr_el_ori != nullptr && el_nbr < face_nbr_el_ori->Size())
2858 {
2859 face_nbr_el_ori->GetRow(el_nbr, orientations);
2860 }
2861 else
2862 {
2863 MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
2864 "face_nbr_el_ori not generated correctly.");
2865 }
2866}
2867
2869{
2870 const Array<int> *s2l_face;
2871 if (Dim == 1)
2872 {
2873 s2l_face = &svert_lvert;
2874 }
2875 else if (Dim == 2)
2876 {
2877 s2l_face = &sedge_ledge;
2878 }
2879 else
2880 {
2881 s2l_face = &sface_lface;
2882 }
2883
2884 Table *face_elem = new Table;
2885
2886 face_elem->MakeI(faces_info.Size());
2887
2888 for (int i = 0; i < faces_info.Size(); i++)
2889 {
2890 if (faces_info[i].Elem2No >= 0)
2891 {
2892 face_elem->AddColumnsInRow(i, 2);
2893 }
2894 else
2895 {
2896 face_elem->AddAColumnInRow(i);
2897 }
2898 }
2899 for (int i = 0; i < s2l_face->Size(); i++)
2900 {
2901 face_elem->AddAColumnInRow((*s2l_face)[i]);
2902 }
2903
2904 face_elem->MakeJ();
2905
2906 for (int i = 0; i < faces_info.Size(); i++)
2907 {
2908 face_elem->AddConnection(i, faces_info[i].Elem1No);
2909 if (faces_info[i].Elem2No >= 0)
2910 {
2911 face_elem->AddConnection(i, faces_info[i].Elem2No);
2912 }
2913 }
2914 for (int i = 0; i < s2l_face->Size(); i++)
2915 {
2916 int lface = (*s2l_face)[i];
2917 int nbr_elem_idx = -1 - faces_info[lface].Elem2No;
2918 face_elem->AddConnection(lface, NumOfElements + nbr_elem_idx);
2919 }
2920
2921 face_elem->ShiftUpI();
2922
2923 return face_elem;
2924}
2925
2933
2938 int mask) const
2939{
2940 if (FaceNo < GetNumFaces())
2941 {
2942 Mesh::GetFaceElementTransformations(FaceNo, FElTr, ElTr1, ElTr2, mask);
2943 }
2944 else
2945 {
2946 const bool fill2 = mask & 10; // Elem2 and/or Loc2
2947 GetSharedFaceTransformationsByLocalIndex(FaceNo, FElTr, ElTr1, ElTr2,
2948 fill2);
2949 }
2950}
2951
2959
2964 bool fill2) const
2965{
2966 int FaceNo = GetSharedFace(sf);
2967 GetSharedFaceTransformationsByLocalIndex(FaceNo, FElTr, ElTr1, ElTr2, fill2);
2968}
2969
2977
2979 int FaceNo, FaceElementTransformations &FElTr,
2981 bool fill2) const
2982{
2983 const FaceInfo &face_info = faces_info[FaceNo];
2984 MFEM_VERIFY(face_info.Elem2Inf >= 0, "The face must be shared.");
2985
2986 bool is_slave = Nonconforming() && IsSlaveFace(face_info);
2987 bool is_ghost = Nonconforming() && FaceNo >= GetNumFaces();
2988
2989 int mask = 0;
2990 FElTr.SetConfigurationMask(0);
2991 FElTr.Elem1 = NULL;
2992 FElTr.Elem2 = NULL;
2993
2994 int local_face =
2995 is_ghost ? nc_faces_info[face_info.NCFace].MasterFace : FaceNo;
2996 Element::Type face_type = GetFaceElementType(local_face);
2997 Geometry::Type face_geom = GetFaceGeometry(local_face);
2998
2999 // setup the transformation for the first element
3000 FElTr.Elem1No = face_info.Elem1No;
3001 GetElementTransformation(FElTr.Elem1No, &ElTr1);
3002 FElTr.Elem1 = &ElTr1;
3004
3005 // setup the transformation for the second (neighbor) element
3006 int Elem2NbrNo;
3007 if (fill2)
3008 {
3009 Elem2NbrNo = -1 - face_info.Elem2No;
3010 // Store the "shifted index" for element 2 in FElTr.Elem2No.
3011 // `Elem2NbrNo` is the index of the face neighbor (starting from 0),
3012 // and `FElTr.Elem2No` will be offset by the number of (local)
3013 // elements in the mesh.
3014 FElTr.Elem2No = NumOfElements + Elem2NbrNo;
3015 GetFaceNbrElementTransformation(Elem2NbrNo, ElTr2);
3016 FElTr.Elem2 = &ElTr2;
3018 }
3019 else
3020 {
3021 FElTr.Elem2No = -1;
3022 }
3023
3024 // setup the face transformation if the face is not a ghost
3025 if (!is_ghost)
3026 {
3027 GetFaceTransformation(FaceNo, &FElTr);
3028 // NOTE: The above call overwrites FElTr.Loc1
3030 }
3031 else
3032 {
3033 FElTr.SetGeometryType(face_geom);
3034 }
3035
3036 // setup Loc1 & Loc2
3037 int elem_type = GetElementType(face_info.Elem1No);
3038 GetLocalFaceTransformation(face_type, elem_type, FElTr.Loc1.Transf,
3039 face_info.Elem1Inf);
3041
3042 if (fill2)
3043 {
3044 elem_type = face_nbr_elements[Elem2NbrNo]->GetType();
3045 GetLocalFaceTransformation(face_type, elem_type, FElTr.Loc2.Transf,
3046 face_info.Elem2Inf);
3048 }
3049
3050 // adjust Loc1 or Loc2 of the master face if this is a slave face
3051 if (is_slave)
3052 {
3053 if (is_ghost || fill2)
3054 {
3055 // is_ghost -> modify side 1, otherwise -> modify side 2:
3056 ApplyLocalSlaveTransformation(FElTr, face_info, is_ghost);
3057 }
3058 }
3059
3060 // for ghost faces we need a special version of GetFaceTransformation
3061 if (is_ghost)
3062 {
3063 GetGhostFaceTransformation(FaceNo, FElTr);
3065 }
3066
3067 FElTr.SetConfigurationMask(mask);
3068
3069 // This check can be useful for internal debugging, however it will fail on
3070 // periodic boundary faces, so we keep it disabled in general.
3071#if 0
3072#ifdef MFEM_DEBUG
3073 real_t dist = FElTr.CheckConsistency();
3074 if (dist >= 1e-12)
3075 {
3076 mfem::out << "\nInternal error: face id = " << FaceNo
3077 << ", dist = " << dist << ", rank = " << MyRank << '\n';
3078 FElTr.CheckConsistency(1); // print coordinates
3079 MFEM_ABORT("internal error");
3080 }
3081#endif
3082#endif
3083}
3084
3086 int FaceNo, FaceElementTransformations &FElTr) const
3087{
3088 MFEM_ASSERT(FaceNo >= GetNumFaces(), "Not a ghost face.");
3089
3090 // use the local face data
3091 const int LocFaceNo = nc_faces_info[faces_info[FaceNo].NCFace].MasterFace;
3092 FElTr.Attribute = (Dim == 1) ? 1 : faces[LocFaceNo]->GetAttribute();
3093 FElTr.ElementNo = FaceNo;
3095 FElTr.mesh = this;
3096
3097 // calculate composition of FElTr.Loc1 and FElTr.Elem1
3098 DenseMatrix &face_pm = FElTr.GetPointMat();
3099 FElTr.Reset();
3100 if (Nodes == NULL)
3101 {
3102 const Element::Type face_type = GetFaceElementType(LocFaceNo);
3103 FElTr.Elem1->Transform(FElTr.Loc1.Transf.GetPointMat(), face_pm);
3104 FElTr.SetFE(GetTransformationFEforElementType(face_type));
3105 }
3106 else
3107 {
3108 const Geometry::Type face_geom = GetFaceGeometry(LocFaceNo);
3109 const FiniteElement* face_el =
3110 Nodes->FESpace()->GetTraceElement(FElTr.Elem1No, face_geom);
3111 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
3112 "Mesh requires nodal Finite Element.");
3113
3114#if 0 // TODO: handle the case of non-interpolatory Nodes
3115 DenseMatrix I;
3116 face_el->Project(Transformation.GetFE(), FElTr.Loc1.Transf, I);
3117 MultABt(Transformation.GetPointMat(), I, pm_face);
3118#else
3119 IntegrationRule eir(face_el->GetDof());
3120 FElTr.Loc1.Transform(face_el->GetNodes(), eir);
3121 Nodes->GetVectorValues(*FElTr.Elem1, eir, face_pm);
3122#endif
3123 FElTr.SetFE(face_el);
3124 }
3125}
3126
3132
3134 int FaceNo, IsoparametricTransformation &ElTr) const
3135{
3136 DenseMatrix &pointmat = ElTr.GetPointMat();
3137 Element *elem = face_nbr_elements[FaceNo];
3138
3139 ElTr.Attribute = elem->GetAttribute();
3140 ElTr.ElementNo = NumOfElements + FaceNo;
3142 ElTr.mesh = this;
3143 ElTr.Reset();
3144
3145 if (Nodes == NULL)
3146 {
3147 const int nv = elem->GetNVertices();
3148 const int *v = elem->GetVertices();
3149
3150 pointmat.SetSize(spaceDim, nv);
3151 for (int k = 0; k < spaceDim; k++)
3152 {
3153 for (int j = 0; j < nv; j++)
3154 {
3155 pointmat(k, j) = face_nbr_vertices[v[j]](k);
3156 }
3157 }
3158
3160 }
3161 else
3162 {
3163 Array<int> vdofs;
3164 ParGridFunction *pNodes = dynamic_cast<ParGridFunction *>(Nodes);
3165 if (pNodes)
3166 {
3167 pNodes->ParFESpace()->GetFaceNbrElementVDofs(FaceNo, vdofs);
3168 int n = vdofs.Size()/spaceDim;
3169 pointmat.SetSize(spaceDim, n);
3170 pNodes->FaceNbrData().HostRead();
3171 for (int k = 0; k < spaceDim; k++)
3172 {
3173 for (int j = 0; j < n; j++)
3174 {
3175 pointmat(k,j) = AsConst(pNodes->FaceNbrData())(vdofs[n*k+j]);
3176 }
3177 }
3178
3179 ElTr.SetFE(pNodes->ParFESpace()->GetFaceNbrFE(FaceNo));
3180 }
3181 else
3182 {
3183 MFEM_ABORT("Nodes are not ParGridFunction!");
3184 }
3185 }
3186}
3187
3192
3194{
3195 if (Conforming())
3196 {
3197 switch (Dim)
3198 {
3199 case 1: return svert_lvert.Size();
3200 case 2: return sedge_ledge.Size();
3201 default: return sface_lface.Size();
3202 }
3203 }
3204 else
3205 {
3206 MFEM_ASSERT(Dim > 1, "");
3207 const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3208 return shared.conforming.Size() + shared.slaves.Size();
3209 }
3210}
3211
3212int ParMesh::GetSharedFace(int sface) const
3213{
3214 if (Conforming())
3215 {
3216 switch (Dim)
3217 {
3218 case 1: return svert_lvert[sface];
3219 case 2: return sedge_ledge[sface];
3220 default: return sface_lface[sface];
3221 }
3222 }
3223 else
3224 {
3225 MFEM_ASSERT(Dim > 1, "");
3226 const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
3227 int csize = shared.conforming.Size();
3228 return sface < csize
3229 ? shared.conforming[sface].index
3230 : shared.slaves[sface - csize].index;
3231 }
3232}
3233
3235{
3236 const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
3237 return Mesh::GetNFbyType(type);
3238}
3239
3240// shift cyclically 3 integers a, b, c, so that the smallest of
3241// order[a], order[b], order[c] is first
3242static inline
3243void Rotate3Indirect(int &a, int &b, int &c,
3244 const Array<std::int64_t> &order)
3245{
3246 if (order[a] < order[b])
3247 {
3248 if (order[a] > order[c])
3249 {
3250 ShiftRight(a, b, c);
3251 }
3252 }
3253 else
3254 {
3255 if (order[b] < order[c])
3256 {
3257 ShiftRight(c, b, a);
3258 }
3259 else
3260 {
3261 ShiftRight(a, b, c);
3262 }
3263 }
3264}
3265
3267{
3268 if (Dim != 3 || !(meshgen & 1))
3269 {
3270 return;
3271 }
3272
3273 ResetLazyData();
3274
3275 DSTable *old_v_to_v = NULL;
3276 Table *old_elem_vert = NULL;
3277
3278 if (Nodes)
3279 {
3280 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3281 }
3282
3283 // create a GroupCommunicator over shared vertices
3284 GroupCommunicator svert_comm(gtopo);
3285 GetSharedVertexCommunicator(0, svert_comm);
3286
3287 // communicate the local index of each shared vertex from the group master to
3288 // other ranks in the group
3289 Array<int> svert_master_rank(svert_lvert.Size());
3290 Array<int> svert_master_index(svert_lvert);
3291 for (int i = 0; i < group_svert.Size(); i++)
3292 {
3293 int rank = gtopo.GetGroupMasterRank(i+1);
3294 for (int j = 0; j < group_svert.RowSize(i); j++)
3295 {
3296 svert_master_rank[group_svert.GetRow(i)[j]] = rank;
3297 }
3298 }
3299 svert_comm.Bcast(svert_master_index);
3300
3301 // the pairs (master rank, master local index) define a globally consistent
3302 // vertex ordering
3303 Array<std::int64_t> glob_vert_order(vertices.Size());
3304 {
3305 Array<int> lvert_svert(vertices.Size());
3306 lvert_svert = -1;
3307 for (int i = 0; i < svert_lvert.Size(); i++)
3308 {
3309 lvert_svert[svert_lvert[i]] = i;
3310 }
3311
3312 for (int i = 0; i < vertices.Size(); i++)
3313 {
3314 int s = lvert_svert[i];
3315 if (s >= 0)
3316 {
3317 glob_vert_order[i] =
3318 (std::int64_t(svert_master_rank[s]) << 32) + svert_master_index[s];
3319 }
3320 else
3321 {
3322 glob_vert_order[i] = (std::int64_t(MyRank) << 32) + i;
3323 }
3324 }
3325 }
3326
3327 // rotate tetrahedra so that vertex zero is the lowest (global) index vertex,
3328 // vertex 1 is the second lowest (global) index and vertices 2 and 3 preserve
3329 // positive orientation of the element
3330 for (int i = 0; i < NumOfElements; i++)
3331 {
3333 {
3334 int *v = elements[i]->GetVertices();
3335
3336 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3337
3338 if (glob_vert_order[v[0]] < glob_vert_order[v[3]])
3339 {
3340 Rotate3Indirect(v[1], v[2], v[3], glob_vert_order);
3341 }
3342 else
3343 {
3344 ShiftRight(v[0], v[1], v[3]);
3345 }
3346 }
3347 }
3348
3349 // rotate also boundary triangles
3350 for (int i = 0; i < NumOfBdrElements; i++)
3351 {
3353 {
3354 int *v = boundary[i]->GetVertices();
3355
3356 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3357 }
3358 }
3359
3360 const bool check_consistency = true;
3361 if (check_consistency)
3362 {
3363 // create a GroupCommunicator on the shared triangles
3364 GroupCommunicator stria_comm(gtopo);
3365 GetSharedTriCommunicator(0, stria_comm);
3366
3367 Array<int> stria_flag(shared_trias.Size());
3368 for (int i = 0; i < stria_flag.Size(); i++)
3369 {
3370 const int *v = shared_trias[i].v;
3371 if (glob_vert_order[v[0]] < glob_vert_order[v[1]])
3372 {
3373 stria_flag[i] = (glob_vert_order[v[0]] < glob_vert_order[v[2]]) ? 0 : 2;
3374 }
3375 else // v[1] < v[0]
3376 {
3377 stria_flag[i] = (glob_vert_order[v[1]] < glob_vert_order[v[2]]) ? 1 : 2;
3378 }
3379 }
3380
3381 Array<int> stria_master_flag(stria_flag);
3382 stria_comm.Bcast(stria_master_flag);
3383 for (int i = 0; i < stria_flag.Size(); i++)
3384 {
3385 const int *v = shared_trias[i].v;
3386 MFEM_VERIFY(stria_flag[i] == stria_master_flag[i],
3387 "inconsistent vertex ordering found, shared triangle "
3388 << i << ": ("
3389 << v[0] << ", " << v[1] << ", " << v[2] << "), "
3390 << "local flag: " << stria_flag[i]
3391 << ", master flag: " << stria_master_flag[i]);
3392 }
3393 }
3394
3395 // rotate shared triangle faces
3396 for (int i = 0; i < shared_trias.Size(); i++)
3397 {
3398 int *v = shared_trias[i].v;
3399
3400 Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
3401 }
3402
3403 // finalize
3404 if (!Nodes)
3405 {
3407 GenerateFaces();
3408 if (el_to_edge)
3409 {
3411 }
3412 }
3413 else
3414 {
3415 DoNodeReorder(old_v_to_v, old_elem_vert);
3416 delete old_elem_vert;
3417 delete old_v_to_v;
3418 }
3419
3420 // the local edge and face numbering is changed therefore we need to
3421 // update sedge_ledge and sface_lface.
3423}
3424
3425void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
3426{
3427 if (pncmesh)
3428 {
3429 MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
3430 }
3431
3433
3435
3436 if (Dim == 3)
3437 {
3438 int uniform_refinement = 0;
3439 if (type < 0)
3440 {
3441 type = -type;
3442 uniform_refinement = 1;
3443 }
3444
3445 // 1. Hash table of vertex to vertex connections corresponding to refined
3446 // edges.
3447 HashTable<Hashed2> v_to_v;
3448
3449 // 2. Do the red refinement.
3450 switch (type)
3451 {
3452 case 1:
3453 for (int i = 0; i < marked_el.Size(); i++)
3454 {
3455 Bisection(marked_el[i], v_to_v);
3456 }
3457 break;
3458 case 2:
3459 for (int i = 0; i < marked_el.Size(); i++)
3460 {
3461 Bisection(marked_el[i], v_to_v);
3462
3463 Bisection(NumOfElements - 1, v_to_v);
3464 Bisection(marked_el[i], v_to_v);
3465 }
3466 break;
3467 case 3:
3468 for (int i = 0; i < marked_el.Size(); i++)
3469 {
3470 Bisection(marked_el[i], v_to_v);
3471
3472 int j = NumOfElements - 1;
3473 Bisection(j, v_to_v);
3474 Bisection(NumOfElements - 1, v_to_v);
3475 Bisection(j, v_to_v);
3476
3477 Bisection(marked_el[i], v_to_v);
3478 Bisection(NumOfElements-1, v_to_v);
3479 Bisection(marked_el[i], v_to_v);
3480 }
3481 break;
3482 }
3483
3484 // 3. Do the green refinement (to get conforming mesh).
3485 int need_refinement;
3486 int max_faces_in_group = 0;
3487 // face_splittings identify how the shared faces have been split
3488 Array<unsigned> *face_splittings = new Array<unsigned>[GetNGroups()-1];
3489 for (int i = 0; i < GetNGroups()-1; i++)
3490 {
3491 const int faces_in_group = GroupNTriangles(i+1);
3492 face_splittings[i].Reserve(faces_in_group);
3493 if (faces_in_group > max_faces_in_group)
3494 {
3495 max_faces_in_group = faces_in_group;
3496 }
3497 }
3498 int neighbor;
3499 Array<unsigned> iBuf(max_faces_in_group);
3500
3501 MPI_Request *requests = new MPI_Request[GetNGroups()-1];
3502 MPI_Status status;
3503
3504#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3505 int ref_loops_all = 0, ref_loops_par = 0;
3506#endif
3507 do
3508 {
3509 need_refinement = 0;
3510 for (int i = 0; i < NumOfElements; i++)
3511 {
3512 if (elements[i]->NeedRefinement(v_to_v))
3513 {
3514 need_refinement = 1;
3515 Bisection(i, v_to_v);
3516 }
3517 }
3518#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3519 ref_loops_all++;
3520#endif
3521
3522 if (uniform_refinement)
3523 {
3524 continue;
3525 }
3526
3527 // if the mesh is locally conforming start making it globally
3528 // conforming
3529 if (need_refinement == 0)
3530 {
3531#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3532 ref_loops_par++;
3533#endif
3534 // MPI_Barrier(MyComm);
3535 const int tag = 293;
3536
3537 // (a) send the type of interface splitting
3538 int req_count = 0;
3539 for (int i = 0; i < GetNGroups()-1; i++)
3540 {
3541 const int *group_faces = group_stria.GetRow(i);
3542 const int faces_in_group = group_stria.RowSize(i);
3543 // it is enough to communicate through the faces
3544 if (faces_in_group == 0) { continue; }
3545
3546 face_splittings[i].SetSize(0);
3547 for (int j = 0; j < faces_in_group; j++)
3548 {
3549 GetFaceSplittings(shared_trias[group_faces[j]].v, v_to_v,
3550 face_splittings[i]);
3551 }
3552 const int *nbs = gtopo.GetGroup(i+1);
3553 neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3554 MPI_Isend(face_splittings[i], face_splittings[i].Size(),
3555 MPI_UNSIGNED, neighbor, tag, MyComm,
3556 &requests[req_count++]);
3557 }
3558
3559 // (b) receive the type of interface splitting
3560 for (int i = 0; i < GetNGroups()-1; i++)
3561 {
3562 const int *group_faces = group_stria.GetRow(i);
3563 const int faces_in_group = group_stria.RowSize(i);
3564 if (faces_in_group == 0) { continue; }
3565
3566 const int *nbs = gtopo.GetGroup(i+1);
3567 neighbor = gtopo.GetNeighborRank(nbs[0] ? nbs[0] : nbs[1]);
3568 MPI_Probe(neighbor, tag, MyComm, &status);
3569 int count;
3570 MPI_Get_count(&status, MPI_UNSIGNED, &count);
3571 iBuf.SetSize(count);
3572 MPI_Recv(iBuf, count, MPI_UNSIGNED, neighbor, tag, MyComm,
3573 MPI_STATUS_IGNORE);
3574
3575 for (int j = 0, pos = 0; j < faces_in_group; j++)
3576 {
3577 const int *v = shared_trias[group_faces[j]].v;
3578 need_refinement |= DecodeFaceSplittings(v_to_v, v, iBuf, pos);
3579 }
3580 }
3581
3582 int nr = need_refinement;
3583 MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3584
3585 MPI_Waitall(req_count, requests, MPI_STATUSES_IGNORE);
3586 }
3587 }
3588 while (need_refinement == 1);
3589
3590#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3591 {
3592 int i = ref_loops_all;
3593 MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3594 if (MyRank == 0)
3595 {
3596 mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3597 << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3598 << '\n' << endl;
3599 }
3600 }
3601#endif
3602
3603 delete [] requests;
3604 iBuf.DeleteAll();
3605 delete [] face_splittings;
3606
3607 // 4. Update the boundary elements.
3608 do
3609 {
3610 need_refinement = 0;
3611 for (int i = 0; i < NumOfBdrElements; i++)
3612 {
3613 if (boundary[i]->NeedRefinement(v_to_v))
3614 {
3615 need_refinement = 1;
3616 BdrBisection(i, v_to_v);
3617 }
3618 }
3619 }
3620 while (need_refinement == 1);
3621
3622 if (NumOfBdrElements != boundary.Size())
3623 {
3624 mfem_error("ParMesh::LocalRefinement :"
3625 " (NumOfBdrElements != boundary.Size())");
3626 }
3627
3628 ResetLazyData();
3629
3630 const int old_nv = NumOfVertices;
3631 NumOfVertices = vertices.Size();
3632
3633 RefineGroups(old_nv, v_to_v);
3634
3635 // 5. Update the groups after refinement.
3636 if (el_to_face != NULL)
3637 {
3639 GenerateFaces();
3640 }
3641
3642 // 6. Update element-to-edge relations.
3643 if (el_to_edge != NULL)
3644 {
3646 }
3647 } // 'if (Dim == 3)'
3648
3649
3650 if (Dim == 2)
3651 {
3652 int uniform_refinement = 0;
3653 if (type < 0)
3654 {
3655 // type = -type; // not used
3656 uniform_refinement = 1;
3657 }
3658
3659 // 1. Get table of vertex to vertex connections.
3660 DSTable v_to_v(NumOfVertices);
3661 GetVertexToVertexTable(v_to_v);
3662
3663 // 2. Get edge to element connections in arrays edge1 and edge2
3664 int nedges = v_to_v.NumberOfEntries();
3665 int *edge1 = new int[nedges];
3666 int *edge2 = new int[nedges];
3667 int *middle = new int[nedges];
3668
3669 for (int i = 0; i < nedges; i++)
3670 {
3671 edge1[i] = edge2[i] = middle[i] = -1;
3672 }
3673
3674 for (int i = 0; i < NumOfElements; i++)
3675 {
3676 int *v = elements[i]->GetVertices();
3677 for (int j = 0; j < 3; j++)
3678 {
3679 int ind = v_to_v(v[j], v[(j+1)%3]);
3680 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
3681 }
3682 }
3683
3684 // 3. Do the red refinement.
3685 for (int i = 0; i < marked_el.Size(); i++)
3686 {
3687 RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
3688 }
3689
3690 // 4. Do the green refinement (to get conforming mesh).
3691 int need_refinement;
3692 int edges_in_group, max_edges_in_group = 0;
3693 // edge_splittings identify how the shared edges have been split
3694 int **edge_splittings = new int*[GetNGroups()-1];
3695 for (int i = 0; i < GetNGroups()-1; i++)
3696 {
3697 edges_in_group = GroupNEdges(i+1);
3698 edge_splittings[i] = new int[edges_in_group];
3699 if (edges_in_group > max_edges_in_group)
3700 {
3701 max_edges_in_group = edges_in_group;
3702 }
3703 }
3704 int neighbor, *iBuf = new int[max_edges_in_group];
3705
3706 Array<int> group_edges;
3707
3708 MPI_Request request;
3709 MPI_Status status;
3710 Vertex V;
3711 V(2) = 0.0;
3712
3713#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3714 int ref_loops_all = 0, ref_loops_par = 0;
3715#endif
3716 do
3717 {
3718 need_refinement = 0;
3719 for (int i = 0; i < nedges; i++)
3720 {
3721 if (middle[i] != -1 && edge1[i] != -1)
3722 {
3723 need_refinement = 1;
3724 GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
3725 }
3726 }
3727#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3728 ref_loops_all++;
3729#endif
3730
3731 if (uniform_refinement)
3732 {
3733 continue;
3734 }
3735
3736 // if the mesh is locally conforming start making it globally
3737 // conforming
3738 if (need_refinement == 0)
3739 {
3740#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3741 ref_loops_par++;
3742#endif
3743 // MPI_Barrier(MyComm);
3744
3745 // (a) send the type of interface splitting
3746 for (int i = 0; i < GetNGroups()-1; i++)
3747 {
3748 group_sedge.GetRow(i, group_edges);
3749 edges_in_group = group_edges.Size();
3750 // it is enough to communicate through the edges
3751 if (edges_in_group != 0)
3752 {
3753 for (int j = 0; j < edges_in_group; j++)
3754 {
3755 edge_splittings[i][j] =
3756 GetEdgeSplittings(shared_edges[group_edges[j]], v_to_v,
3757 middle);
3758 }
3759 const int *nbs = gtopo.GetGroup(i+1);
3760 if (nbs[0] == 0)
3761 {
3762 neighbor = gtopo.GetNeighborRank(nbs[1]);
3763 }
3764 else
3765 {
3766 neighbor = gtopo.GetNeighborRank(nbs[0]);
3767 }
3768 MPI_Isend(edge_splittings[i], edges_in_group, MPI_INT,
3769 neighbor, 0, MyComm, &request);
3770 }
3771 }
3772
3773 // (b) receive the type of interface splitting
3774 for (int i = 0; i < GetNGroups()-1; i++)
3775 {
3776 group_sedge.GetRow(i, group_edges);
3777 edges_in_group = group_edges.Size();
3778 if (edges_in_group != 0)
3779 {
3780 const int *nbs = gtopo.GetGroup(i+1);
3781 if (nbs[0] == 0)
3782 {
3783 neighbor = gtopo.GetNeighborRank(nbs[1]);
3784 }
3785 else
3786 {
3787 neighbor = gtopo.GetNeighborRank(nbs[0]);
3788 }
3789 MPI_Recv(iBuf, edges_in_group, MPI_INT, neighbor,
3790 MPI_ANY_TAG, MyComm, &status);
3791
3792 for (int j = 0; j < edges_in_group; j++)
3793 {
3794 if (iBuf[j] == 1 && edge_splittings[i][j] == 0)
3795 {
3796 int *v = shared_edges[group_edges[j]]->GetVertices();
3797 int ii = v_to_v(v[0], v[1]);
3798#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3799 if (middle[ii] != -1)
3800 {
3801 mfem_error("ParMesh::LocalRefinement (triangles) : "
3802 "Oops!");
3803 }
3804#endif
3805 need_refinement = 1;
3806 middle[ii] = NumOfVertices++;
3807 for (int c = 0; c < spaceDim; c++)
3808 {
3809 V(c) = 0.5 * (vertices[v[0]](c) + vertices[v[1]](c));
3810 }
3811 vertices.Append(V);
3812 }
3813 }
3814 }
3815 }
3816
3817 int nr = need_refinement;
3818 MPI_Allreduce(&nr, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
3819 }
3820 }
3821 while (need_refinement == 1);
3822
3823#ifdef MFEM_DEBUG_PARMESH_LOCALREF
3824 {
3825 int i = ref_loops_all;
3826 MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
3827 if (MyRank == 0)
3828 {
3829 mfem::out << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
3830 << ref_loops_all << ", ref_loops_par = " << ref_loops_par
3831 << '\n' << endl;
3832 }
3833 }
3834#endif
3835
3836 for (int i = 0; i < GetNGroups()-1; i++)
3837 {
3838 delete [] edge_splittings[i];
3839 }
3840 delete [] edge_splittings;
3841
3842 delete [] iBuf;
3843
3844 // 5. Update the boundary elements.
3845 int v1[2], v2[2], bisect, temp;
3846 temp = NumOfBdrElements;
3847 for (int i = 0; i < temp; i++)
3848 {
3849 int *v = boundary[i]->GetVertices();
3850 bisect = v_to_v(v[0], v[1]);
3851 if (middle[bisect] != -1)
3852 {
3853 // the element was refined (needs updating)
3854 if (boundary[i]->GetType() == Element::SEGMENT)
3855 {
3856 v1[0] = v[0]; v1[1] = middle[bisect];
3857 v2[0] = middle[bisect]; v2[1] = v[1];
3858
3859 boundary[i]->SetVertices(v1);
3860 boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
3861 }
3862 else
3863 {
3864 mfem_error("Only bisection of segment is implemented for bdr"
3865 " elem.");
3866 }
3867 }
3868 }
3869 NumOfBdrElements = boundary.Size();
3870
3871 ResetLazyData();
3872
3873 // 5a. Update the groups after refinement.
3874 RefineGroups(v_to_v, middle);
3875
3876 // 6. Free the allocated memory.
3877 delete [] edge1;
3878 delete [] edge2;
3879 delete [] middle;
3880
3881 if (el_to_edge != NULL)
3882 {
3884 GenerateFaces();
3885 }
3886 } // 'if (Dim == 2)'
3887
3888 if (Dim == 1) // --------------------------------------------------------
3889 {
3890 int cne = NumOfElements, cnv = NumOfVertices;
3891 NumOfVertices += marked_el.Size();
3892 NumOfElements += marked_el.Size();
3893 vertices.SetSize(NumOfVertices);
3894 elements.SetSize(NumOfElements);
3896
3897 for (int j = 0; j < marked_el.Size(); j++)
3898 {
3899 int i = marked_el[j];
3900 Segment *c_seg = (Segment *)elements[i];
3901 int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
3902 int new_v = cnv + j, new_e = cne + j;
3903 AverageVertices(vert, 2, new_v);
3904 elements[new_e] = new Segment(new_v, vert[1], attr);
3905 vert[1] = new_v;
3906
3909 }
3910
3911 static real_t seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
3913 UseExternalData(seg_children, 1, 2, 3);
3914
3915 GenerateFaces();
3916 } // end of 'if (Dim == 1)'
3917
3919 sequence++;
3920
3921 UpdateNodes();
3922
3923#ifdef MFEM_DEBUG
3926#endif
3927}
3928
3930 std::set<int> &conflicts) const
3931{
3932 MFEM_VERIFY(pncmesh, "AnisotropicConflict should be called only for NCMesh");
3933 return pncmesh->AnisotropicConflict(refinements, conflicts);
3934}
3935
3937 int nc_limit)
3938{
3939 if (NURBSext)
3940 {
3941 MFEM_ABORT("NURBS meshes are not supported. Please project the "
3942 "NURBS to Nodes first with SetCurvature().");
3943 }
3944
3945 if (!pncmesh)
3946 {
3947 MFEM_ABORT("Can't convert conforming ParMesh to nonconforming ParMesh "
3948 "(you need to initialize the ParMesh from a nonconforming "
3949 "serial Mesh)");
3950 }
3951
3952 ResetLazyData();
3953
3955
3956 // NOTE: no check of !refinements.Size(), in parallel we would have to reduce
3957
3958 // do the refinements
3960 pncmesh->Refine(refinements);
3961
3962 if (nc_limit > 0)
3963 {
3964 pncmesh->LimitNCLevel(nc_limit);
3965 }
3966
3967 // create a second mesh containing the finest elements from 'pncmesh'
3968 ParMesh* pmesh2 = new ParMesh(*pncmesh);
3969 pncmesh->OnMeshUpdated(pmesh2);
3970
3971 attributes.Copy(pmesh2->attributes);
3973
3974 // Copy attribute and bdr_attribute names
3977
3978 // now swap the meshes, the second mesh will become the old coarse mesh
3979 // and this mesh will be the new fine mesh
3980 Mesh::Swap(*pmesh2, false);
3981
3982 delete pmesh2; // NOTE: old face neighbors destroyed here
3983
3985
3987
3989 sequence++;
3990
3991 UpdateNodes();
3992}
3993
3995 real_t threshold, int nc_limit, int op)
3996{
3997 MFEM_VERIFY(pncmesh, "Only supported for non-conforming meshes.");
3998 MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
3999 "Project the NURBS to Nodes first.");
4000
4001 const Table &dt = pncmesh->GetDerefinementTable();
4002
4003 pncmesh->SynchronizeDerefinementData(elem_error, dt);
4004
4005 Array<int> level_ok;
4006 if (nc_limit > 0)
4007 {
4008 pncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
4009 }
4010
4011 Array<int> derefs;
4012 for (int i = 0; i < dt.Size(); i++)
4013 {
4014 if (nc_limit > 0 && !level_ok[i]) { continue; }
4015
4016 real_t error =
4017 AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
4018
4019 if (error < threshold) { derefs.Append(i); }
4020 }
4021
4022 long long glob_size = ReduceInt(derefs.Size());
4023 if (!glob_size) { return false; }
4024
4025 // Destroy face-neighbor data only when actually de-refining.
4027
4028 pncmesh->Derefine(derefs);
4029
4030 ParMesh* mesh2 = new ParMesh(*pncmesh);
4031 pncmesh->OnMeshUpdated(mesh2);
4032
4033 attributes.Copy(mesh2->attributes);
4035
4036 // Copy attribute and bdr_attribute names
4039
4040 Mesh::Swap(*mesh2, false);
4041 delete mesh2;
4042
4044
4046
4048 sequence++;
4049
4050 UpdateNodes();
4051
4052 return true;
4053}
4054
4055
4057{
4058 RebalanceImpl(NULL); // default SFC-based partition
4059}
4060
4061void ParMesh::Rebalance(const Array<int> &partition)
4062{
4063 RebalanceImpl(&partition);
4064}
4065
4067{
4068 if (Conforming())
4069 {
4070 MFEM_ABORT("Load balancing is currently not supported for conforming"
4071 " meshes.");
4072 }
4073
4074 if (Nodes)
4075 {
4076 // check that Nodes use a parallel FE space, so we can call UpdateNodes()
4077 MFEM_VERIFY(dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace())
4078 != NULL, "internal error");
4079 }
4080
4082
4083 pncmesh->Rebalance(partition);
4084
4085 ParMesh* pmesh2 = new ParMesh(*pncmesh);
4086 pncmesh->OnMeshUpdated(pmesh2);
4087
4088 attributes.Copy(pmesh2->attributes);
4090
4091 // Copy attribute and bdr_attribute names
4094
4095 Mesh::Swap(*pmesh2, false);
4096 delete pmesh2;
4097
4099
4101
4103 sequence++;
4104
4105 UpdateNodes();
4106}
4107
4108void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
4109{
4110 // Refine groups after LocalRefinement in 2D (triangle meshes)
4111
4112 MFEM_ASSERT(Dim == 2 && meshgen == 1, "internal error");
4113
4114 Array<int> group_verts, group_edges;
4115
4116 // To update the groups after a refinement, we observe that:
4117 // - every (new and old) vertex, edge and face belongs to exactly one group
4118 // - the refinement does not create new groups
4119 // - a new vertex appears only as the middle of a refined edge
4120 // - a face can be refined 2, 3 or 4 times producing new edges and faces
4121
4122 int *I_group_svert, *J_group_svert;
4123 int *I_group_sedge, *J_group_sedge;
4124
4125 I_group_svert = Memory<int>(GetNGroups()+1);
4126 I_group_sedge = Memory<int>(GetNGroups()+1);
4127
4128 I_group_svert[0] = I_group_svert[1] = 0;
4129 I_group_sedge[0] = I_group_sedge[1] = 0;
4130
4131 // overestimate the size of the J arrays
4132 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4134 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4135
4136 for (int group = 0; group < GetNGroups()-1; group++)
4137 {
4138 // Get the group shared objects
4139 group_svert.GetRow(group, group_verts);
4140 group_sedge.GetRow(group, group_edges);
4141
4142 // Check which edges have been refined
4143 for (int i = 0; i < group_sedge.RowSize(group); i++)
4144 {
4145 int *v = shared_edges[group_edges[i]]->GetVertices();
4146 const int ind = middle[v_to_v(v[0], v[1])];
4147 if (ind != -1)
4148 {
4149 // add a vertex
4150 group_verts.Append(svert_lvert.Append(ind)-1);
4151 // update the edges
4152 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4153 shared_edges.Append(new Segment(v[1], ind, attr));
4154 group_edges.Append(sedge_ledge.Append(-1)-1);
4155 v[1] = ind;
4156 }
4157 }
4158
4159 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4160 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4161
4162 int *J;
4163 J = J_group_svert+I_group_svert[group];
4164 for (int i = 0; i < group_verts.Size(); i++)
4165 {
4166 J[i] = group_verts[i];
4167 }
4168 J = J_group_sedge+I_group_sedge[group];
4169 for (int i = 0; i < group_edges.Size(); i++)
4170 {
4171 J[i] = group_edges[i];
4172 }
4173 }
4174
4176
4177 group_svert.SetIJ(I_group_svert, J_group_svert);
4178 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4179}
4180
4181void ParMesh::RefineGroups(int old_nv, const HashTable<Hashed2> &v_to_v)
4182{
4183 // Refine groups after LocalRefinement in 3D (tetrahedral meshes)
4184
4185 MFEM_ASSERT(Dim == 3 && meshgen == 1, "internal error");
4186
4187 Array<int> group_verts, group_edges, group_trias;
4188
4189 // To update the groups after a refinement, we observe that:
4190 // - every (new and old) vertex, edge and face belongs to exactly one group
4191 // - the refinement does not create new groups
4192 // - a new vertex appears only as the middle of a refined edge
4193 // - a face can be refined multiple times producing new edges and faces
4194
4195 Array<Segment *> sedge_stack;
4196 Array<Vert3> sface_stack;
4197
4198 Array<int> I_group_svert, J_group_svert;
4199 Array<int> I_group_sedge, J_group_sedge;
4200 Array<int> I_group_stria, J_group_stria;
4201
4202 I_group_svert.SetSize(GetNGroups());
4203 I_group_sedge.SetSize(GetNGroups());
4204 I_group_stria.SetSize(GetNGroups());
4205
4206 I_group_svert[0] = 0;
4207 I_group_sedge[0] = 0;
4208 I_group_stria[0] = 0;
4209
4210 for (int group = 0; group < GetNGroups()-1; group++)
4211 {
4212 // Get the group shared objects
4213 group_svert.GetRow(group, group_verts);
4214 group_sedge.GetRow(group, group_edges);
4215 group_stria.GetRow(group, group_trias);
4216
4217 // Check which edges have been refined
4218 for (int i = 0; i < group_sedge.RowSize(group); i++)
4219 {
4220 int *v = shared_edges[group_edges[i]]->GetVertices();
4221 int ind = v_to_v.FindId(v[0], v[1]);
4222 if (ind == -1) { continue; }
4223
4224 // This shared edge is refined: walk the whole refinement tree
4225 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4226 do
4227 {
4228 ind += old_nv;
4229 // Add new shared vertex
4230 group_verts.Append(svert_lvert.Append(ind)-1);
4231 // Put the right sub-edge on top of the stack
4232 sedge_stack.Append(new Segment(ind, v[1], attr));
4233 // The left sub-edge replaces the original edge
4234 v[1] = ind;
4235 ind = v_to_v.FindId(v[0], ind);
4236 }
4237 while (ind != -1);
4238 // Process all edges in the edge stack
4239 do
4240 {
4241 Segment *se = sedge_stack.Last();
4242 v = se->GetVertices();
4243 ind = v_to_v.FindId(v[0], v[1]);
4244 if (ind == -1)
4245 {
4246 // The edge 'se' is not refined
4247 sedge_stack.DeleteLast();
4248 // Add new shared edge
4249 shared_edges.Append(se);
4250 group_edges.Append(sedge_ledge.Append(-1)-1);
4251 }
4252 else
4253 {
4254 // The edge 'se' is refined
4255 ind += old_nv;
4256 // Add new shared vertex
4257 group_verts.Append(svert_lvert.Append(ind)-1);
4258 // Put the left sub-edge on top of the stack
4259 sedge_stack.Append(new Segment(v[0], ind, attr));
4260 // The right sub-edge replaces the original edge
4261 v[0] = ind;
4262 }
4263 }
4264 while (sedge_stack.Size() > 0);
4265 }
4266
4267 // Check which triangles have been refined
4268 for (int i = 0; i < group_stria.RowSize(group); i++)
4269 {
4270 int *v = shared_trias[group_trias[i]].v;
4271 int ind = v_to_v.FindId(v[0], v[1]);
4272 if (ind == -1) { continue; }
4273
4274 // This shared face is refined: walk the whole refinement tree
4275 const int edge_attr = 1;
4276 do
4277 {
4278 ind += old_nv;
4279 // Add the refinement edge to the edge stack
4280 sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4281 // Put the right sub-triangle on top of the face stack
4282 sface_stack.Append(Vert3(v[1], v[2], ind));
4283 // The left sub-triangle replaces the original one
4284 v[1] = v[0]; v[0] = v[2]; v[2] = ind;
4285 ind = v_to_v.FindId(v[0], v[1]);
4286 }
4287 while (ind != -1);
4288 // Process all faces (triangles) in the face stack
4289 do
4290 {
4291 Vert3 &st = sface_stack.Last();
4292 v = st.v;
4293 ind = v_to_v.FindId(v[0], v[1]);
4294 if (ind == -1)
4295 {
4296 // The triangle 'st' is not refined
4297 // Add new shared face
4298 shared_trias.Append(st);
4299 group_trias.Append(sface_lface.Append(-1)-1);
4300 sface_stack.DeleteLast();
4301 }
4302 else
4303 {
4304 // The triangle 'st' is refined
4305 ind += old_nv;
4306 // Add the refinement edge to the edge stack
4307 sedge_stack.Append(new Segment(v[2], ind, edge_attr));
4308 // Put the left sub-triangle on top of the face stack
4309 sface_stack.Append(Vert3(v[2], v[0], ind));
4310 // Note that the above Append() may invalidate 'v'
4311 v = sface_stack[sface_stack.Size()-2].v;
4312 // The right sub-triangle replaces the original one
4313 v[0] = v[1]; v[1] = v[2]; v[2] = ind;
4314 }
4315 }
4316 while (sface_stack.Size() > 0);
4317 // Process all edges in the edge stack (same code as above)
4318 do
4319 {
4320 Segment *se = sedge_stack.Last();
4321 v = se->GetVertices();
4322 ind = v_to_v.FindId(v[0], v[1]);
4323 if (ind == -1)
4324 {
4325 // The edge 'se' is not refined
4326 sedge_stack.DeleteLast();
4327 // Add new shared edge
4328 shared_edges.Append(se);
4329 group_edges.Append(sedge_ledge.Append(-1)-1);
4330 }
4331 else
4332 {
4333 // The edge 'se' is refined
4334 ind += old_nv;
4335 // Add new shared vertex
4336 group_verts.Append(svert_lvert.Append(ind)-1);
4337 // Put the left sub-edge on top of the stack
4338 sedge_stack.Append(new Segment(v[0], ind, edge_attr));
4339 // The right sub-edge replaces the original edge
4340 v[0] = ind;
4341 }
4342 }
4343 while (sedge_stack.Size() > 0);
4344 }
4345
4346 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4347 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4348 I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4349
4350 J_group_svert.Append(group_verts);
4351 J_group_sedge.Append(group_edges);
4352 J_group_stria.Append(group_trias);
4353 }
4354
4356
4357 group_svert.SetIJ(I_group_svert, J_group_svert);
4358 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4359 group_stria.SetIJ(I_group_stria, J_group_stria);
4360 I_group_svert.LoseData(); J_group_svert.LoseData();
4361 I_group_sedge.LoseData(); J_group_sedge.LoseData();
4362 I_group_stria.LoseData(); J_group_stria.LoseData();
4363}
4364
4366{
4367 Array<int> sverts, sedges;
4368
4369 int *I_group_svert, *J_group_svert;
4370 int *I_group_sedge, *J_group_sedge;
4371
4372 I_group_svert = Memory<int>(GetNGroups());
4373 I_group_sedge = Memory<int>(GetNGroups());
4374
4375 I_group_svert[0] = 0;
4376 I_group_sedge[0] = 0;
4377
4378 // compute the size of the J arrays
4379 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4381 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections());
4382
4383 for (int group = 0; group < GetNGroups()-1; group++)
4384 {
4385 // Get the group shared objects
4386 group_svert.GetRow(group, sverts);
4387 group_sedge.GetRow(group, sedges);
4388
4389 // Process all the edges
4390 for (int i = 0; i < group_sedge.RowSize(group); i++)
4391 {
4392 int *v = shared_edges[sedges[i]]->GetVertices();
4393 const int ind = old_nv + sedge_ledge[sedges[i]];
4394 // add a vertex
4395 sverts.Append(svert_lvert.Append(ind)-1);
4396 // update the edges
4397 const int attr = shared_edges[sedges[i]]->GetAttribute();
4398 shared_edges.Append(new Segment(v[1], ind, attr));
4399 sedges.Append(sedge_ledge.Append(-1)-1);
4400 v[1] = ind;
4401 }
4402
4403 I_group_svert[group+1] = I_group_svert[group] + sverts.Size();
4404 I_group_sedge[group+1] = I_group_sedge[group] + sedges.Size();
4405
4406 sverts.CopyTo(J_group_svert + I_group_svert[group]);
4407 sedges.CopyTo(J_group_sedge + I_group_sedge[group]);
4408 }
4409
4411
4412 group_svert.SetIJ(I_group_svert, J_group_svert);
4413 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4414}
4415
4416void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
4417 const DSTable &old_v_to_v,
4418 const STable3D &old_faces,
4419 Array<int> *f2qf)
4420{
4421 // f2qf can be NULL if all faces are quads or there are no quad faces
4422
4423 Array<int> group_verts, group_edges, group_trias, group_quads;
4424
4425 int *I_group_svert, *J_group_svert;
4426 int *I_group_sedge, *J_group_sedge;
4427 int *I_group_stria, *J_group_stria;
4428 int *I_group_squad, *J_group_squad;
4429
4430 I_group_svert = Memory<int>(GetNGroups());
4431 I_group_sedge = Memory<int>(GetNGroups());
4432 I_group_stria = Memory<int>(GetNGroups());
4433 I_group_squad = Memory<int>(GetNGroups());
4434
4435 I_group_svert[0] = 0;
4436 I_group_sedge[0] = 0;
4437 I_group_stria[0] = 0;
4438 I_group_squad[0] = 0;
4439
4440 // compute the size of the J arrays
4441 J_group_svert = Memory<int>(group_svert.Size_of_connections() +
4444 J_group_sedge = Memory<int>(2*group_sedge.Size_of_connections() +
4447 J_group_stria = Memory<int>(4*group_stria.Size_of_connections());
4448 J_group_squad = Memory<int>(4*group_squad.Size_of_connections());
4449
4450 const int oface = old_nv + old_nedges;
4451
4452 for (int group = 0; group < GetNGroups()-1; group++)
4453 {
4454 // Get the group shared objects
4455 group_svert.GetRow(group, group_verts);
4456 group_sedge.GetRow(group, group_edges);
4457 group_stria.GetRow(group, group_trias);
4458 group_squad.GetRow(group, group_quads);
4459
4460 // Process the edges that have been refined
4461 for (int i = 0; i < group_sedge.RowSize(group); i++)
4462 {
4463 int *v = shared_edges[group_edges[i]]->GetVertices();
4464 const int ind = old_nv + old_v_to_v(v[0], v[1]);
4465 // add a vertex
4466 group_verts.Append(svert_lvert.Append(ind)-1);
4467 // update the edges
4468 const int attr = shared_edges[group_edges[i]]->GetAttribute();
4469 shared_edges.Append(new Segment(v[1], ind, attr));
4470 group_edges.Append(sedge_ledge.Append(-1)-1);
4471 v[1] = ind; // v[0] remains the same
4472 }
4473
4474 // Process the triangles that have been refined
4475 for (int i = 0; i < group_stria.RowSize(group); i++)
4476 {
4477 int m[3];
4478 const int stria = group_trias[i];
4479 int *v = shared_trias[stria].v;
4480 // add the refinement edges
4481 m[0] = old_nv + old_v_to_v(v[0], v[1]);
4482 m[1] = old_nv + old_v_to_v(v[1], v[2]);
4483 m[2] = old_nv + old_v_to_v(v[2], v[0]);
4484 const int edge_attr = 1;
4485 shared_edges.Append(new Segment(m[0], m[1], edge_attr));
4486 group_edges.Append(sedge_ledge.Append(-1)-1);
4487 shared_edges.Append(new Segment(m[1], m[2], edge_attr));
4488 group_edges.Append(sedge_ledge.Append(-1)-1);
4489 shared_edges.Append(new Segment(m[0], m[2], edge_attr));
4490 group_edges.Append(sedge_ledge.Append(-1)-1);
4491 // update faces
4492 const int nst = shared_trias.Size();
4493 shared_trias.SetSize(nst+3);
4494 // The above SetSize() may invalidate 'v'
4495 v = shared_trias[stria].v;
4496 shared_trias[nst+0].Set(m[1],m[2],m[0]);
4497 shared_trias[nst+1].Set(m[0],v[1],m[1]);
4498 shared_trias[nst+2].Set(m[2],m[1],v[2]);
4499 v[1] = m[0]; v[2] = m[2]; // v[0] remains the same
4500 group_trias.Append(nst+0);
4501 group_trias.Append(nst+1);
4502 group_trias.Append(nst+2);
4503 // sface_lface is set later
4504 }
4505
4506 // Process the quads that have been refined
4507 for (int i = 0; i < group_squad.RowSize(group); i++)
4508 {
4509 int m[5];
4510 const int squad = group_quads[i];
4511 int *v = shared_quads[squad].v;
4512 const int olf = old_faces(v[0], v[1], v[2], v[3]);
4513 // f2qf can be NULL if all faces are quads
4514 m[0] = oface + (f2qf ? (*f2qf)[olf] : olf);
4515 // add a vertex
4516 group_verts.Append(svert_lvert.Append(m[0])-1);
4517 // add the refinement edges
4518 m[1] = old_nv + old_v_to_v(v[0], v[1]);
4519 m[2] = old_nv + old_v_to_v(v[1], v[2]);
4520 m[3] = old_nv + old_v_to_v(v[2], v[3]);
4521 m[4] = old_nv + old_v_to_v(v[3], v[0]);
4522 const int edge_attr = 1;
4523 shared_edges.Append(new Segment(m[1], m[0], edge_attr));
4524 group_edges.Append(sedge_ledge.Append(-1)-1);
4525 shared_edges.Append(new Segment(m[2], m[0], edge_attr));
4526 group_edges.Append(sedge_ledge.Append(-1)-1);
4527 shared_edges.Append(new Segment(m[3], m[0], edge_attr));
4528 group_edges.Append(sedge_ledge.Append(-1)-1);
4529 shared_edges.Append(new Segment(m[4], m[0], edge_attr));
4530 group_edges.Append(sedge_ledge.Append(-1)-1);
4531 // update faces
4532 const int nsq = shared_quads.Size();
4533 shared_quads.SetSize(nsq+3);
4534 // The above SetSize() may invalidate 'v'
4535 v = shared_quads[squad].v;
4536 shared_quads[nsq+0].Set(m[1],v[1],m[2],m[0]);
4537 shared_quads[nsq+1].Set(m[0],m[2],v[2],m[3]);
4538 shared_quads[nsq+2].Set(m[4],m[0],m[3],v[3]);
4539 v[1] = m[1]; v[2] = m[0]; v[3] = m[4]; // v[0] remains the same
4540 group_quads.Append(nsq+0);
4541 group_quads.Append(nsq+1);
4542 group_quads.Append(nsq+2);
4543 // sface_lface is set later
4544 }
4545
4546 I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
4547 I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
4548 I_group_stria[group+1] = I_group_stria[group] + group_trias.Size();
4549 I_group_squad[group+1] = I_group_squad[group] + group_quads.Size();
4550
4551 group_verts.CopyTo(J_group_svert + I_group_svert[group]);
4552 group_edges.CopyTo(J_group_sedge + I_group_sedge[group]);
4553 group_trias.CopyTo(J_group_stria + I_group_stria[group]);
4554 group_quads.CopyTo(J_group_squad + I_group_squad[group]);
4555 }
4556
4558
4559 group_svert.SetIJ(I_group_svert, J_group_svert);
4560 group_sedge.SetIJ(I_group_sedge, J_group_sedge);
4561 group_stria.SetIJ(I_group_stria, J_group_stria);
4562 group_squad.SetIJ(I_group_squad, J_group_squad);
4563}
4564
4566{
4568
4569 const int old_nv = NumOfVertices;
4570
4571 // call Mesh::UniformRefinement2D so that it won't update the nodes
4572 {
4573 const bool update_nodes = false;
4574 Mesh::UniformRefinement2D_base(update_nodes);
4575 }
4576
4577 // update the groups
4578 UniformRefineGroups2D(old_nv);
4579
4580 UpdateNodes();
4581
4582#ifdef MFEM_DEBUG
4583 // If there are no Nodes, the orientation is checked in the call to
4584 // UniformRefinement2D_base() above.
4585 if (Nodes) { CheckElementOrientation(false); }
4586#endif
4587}
4588
4590{
4592
4593 const int old_nv = NumOfVertices;
4594 const int old_nedges = NumOfEdges;
4595
4596 DSTable v_to_v(NumOfVertices);
4597 GetVertexToVertexTable(v_to_v);
4598 auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
4599
4600 // call Mesh::UniformRefinement3D_base so that it won't update the nodes
4601 Array<int> f2qf;
4602 {
4603 const bool update_nodes = false;
4604 UniformRefinement3D_base(&f2qf, &v_to_v, update_nodes);
4605 // Note: for meshes that have triangular faces, v_to_v is modified by the
4606 // above call to return different edge indices - this is used when
4607 // updating the groups. This is needed by ReorientTetMesh().
4608 }
4609
4610 // update the groups
4611 UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
4612 f2qf.Size() ? &f2qf : NULL);
4613
4614 UpdateNodes();
4615}
4616
4618{
4619 if (MyRank == 0)
4620 {
4621 mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
4622 }
4623}
4624
4626{
4627 if (MyRank == 0)
4628 {
4629 mfem::out << "\nParMesh::NURBSUniformRefinement : Not supported yet!\n";
4630 }
4631}
4632
4633void ParMesh::RefineNURBSWithKVFactors(int rf, const std::string &kvf)
4634{
4635 if (MyRank == 0)
4636 {
4637 mfem::out << "\nRefineNURBSWithKVFactors : Not supported yet!\n";
4638 }
4639}
4640
4641void ParMesh::PrintXG(std::ostream &os) const
4642{
4643 MFEM_ASSERT(Dim == spaceDim, "2D manifolds not supported");
4644 if (Dim == 3 && meshgen == 1)
4645 {
4646 int i, j, nv;
4647 const int *ind;
4648
4649 os << "NETGEN_Neutral_Format\n";
4650 // print the vertices
4651 os << NumOfVertices << '\n';
4652 for (i = 0; i < NumOfVertices; i++)
4653 {
4654 for (j = 0; j < Dim; j++)
4655 {
4656 os << " " << vertices[i](j);
4657 }
4658 os << '\n';
4659 }
4660
4661 // print the elements
4662 os << NumOfElements << '\n';
4663 for (i = 0; i < NumOfElements; i++)
4664 {
4665 nv = elements[i]->GetNVertices();
4666 ind = elements[i]->GetVertices();
4667 os << elements[i]->GetAttribute();
4668 for (j = 0; j < nv; j++)
4669 {
4670 os << " " << ind[j]+1;
4671 }
4672 os << '\n';
4673 }
4674
4675 // print the boundary + shared faces information
4676 os << NumOfBdrElements + sface_lface.Size() << '\n';
4677 // boundary
4678 for (i = 0; i < NumOfBdrElements; i++)
4679 {
4680 nv = boundary[i]->GetNVertices();
4681 ind = boundary[i]->GetVertices();
4682 os << boundary[i]->GetAttribute();
4683 for (j = 0; j < nv; j++)
4684 {
4685 os << " " << ind[j]+1;
4686 }
4687 os << '\n';
4688 }
4689 // shared faces
4690 const int sf_attr =
4691 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4692 for (i = 0; i < shared_trias.Size(); i++)
4693 {
4694 ind = shared_trias[i].v;
4695 os << sf_attr;
4696 for (j = 0; j < 3; j++)
4697 {
4698 os << ' ' << ind[j]+1;
4699 }
4700 os << '\n';
4701 }
4702 // There are no quad shared faces
4703 }
4704
4705 if (Dim == 3 && meshgen == 2)
4706 {
4707 int i, j, nv;
4708 const int *ind;
4709
4710 os << "TrueGrid\n"
4711 << "1 " << NumOfVertices << " " << NumOfElements
4712 << " 0 0 0 0 0 0 0\n"
4713 << "0 0 0 1 0 0 0 0 0 0 0\n"
4714 << "0 0 " << NumOfBdrElements+sface_lface.Size()
4715 << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
4716 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
4717 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
4718
4719 // print the vertices
4720 for (i = 0; i < NumOfVertices; i++)
4721 {
4722 os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
4723 << " " << vertices[i](2) << " 0.0\n";
4724 }
4725
4726 // print the elements
4727 for (i = 0; i < NumOfElements; i++)
4728 {
4729 nv = elements[i]->GetNVertices();
4730 ind = elements[i]->GetVertices();
4731 os << i+1 << " " << elements[i]->GetAttribute();
4732 for (j = 0; j < nv; j++)
4733 {
4734 os << " " << ind[j]+1;
4735 }
4736 os << '\n';
4737 }
4738
4739 // print the boundary information
4740 for (i = 0; i < NumOfBdrElements; i++)
4741 {
4742 nv = boundary[i]->GetNVertices();
4743 ind = boundary[i]->GetVertices();
4744 os << boundary[i]->GetAttribute();
4745 for (j = 0; j < nv; j++)
4746 {
4747 os << " " << ind[j]+1;
4748 }
4749 os << " 1.0 1.0 1.0 1.0\n";
4750 }
4751
4752 // print the shared faces information
4753 const int sf_attr =
4754 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
4755 // There are no shared triangle faces
4756 for (i = 0; i < shared_quads.Size(); i++)
4757 {
4758 ind = shared_quads[i].v;
4759 os << sf_attr;
4760 for (j = 0; j < 4; j++)
4761 {
4762 os << ' ' << ind[j]+1;
4763 }
4764 os << " 1.0 1.0 1.0 1.0\n";
4765 }
4766 }
4767
4768 if (Dim == 2)
4769 {
4770 int i, j, attr;
4771 Array<int> v;
4772
4773 os << "areamesh2\n\n";
4774
4775 // print the boundary + shared edges information
4776 os << NumOfBdrElements + shared_edges.Size() << '\n';
4777 // boundary
4778 for (i = 0; i < NumOfBdrElements; i++)
4779 {
4780 attr = boundary[i]->GetAttribute();
4781 boundary[i]->GetVertices(v);
4782 os << attr << " ";
4783 for (j = 0; j < v.Size(); j++)
4784 {
4785 os << v[j] + 1 << " ";
4786 }
4787 os << '\n';
4788 }
4789 // shared edges
4790 for (i = 0; i < shared_edges.Size(); i++)
4791 {
4792 attr = shared_edges[i]->GetAttribute();
4793 shared_edges[i]->GetVertices(v);
4794 os << attr << " ";
4795 for (j = 0; j < v.Size(); j++)
4796 {
4797 os << v[j] + 1 << " ";
4798 }
4799 os << '\n';
4800 }
4801
4802 // print the elements
4803 os << NumOfElements << '\n';
4804 for (i = 0; i < NumOfElements; i++)
4805 {
4806 attr = elements[i]->GetAttribute();
4807 elements[i]->GetVertices(v);
4808
4809 os << attr << " ";
4810 if ((j = GetElementType(i)) == Element::TRIANGLE)
4811 {
4812 os << 3 << " ";
4813 }
4814 else if (j == Element::QUADRILATERAL)
4815 {
4816 os << 4 << " ";
4817 }
4818 else if (j == Element::SEGMENT)
4819 {
4820 os << 2 << " ";
4821 }
4822 for (j = 0; j < v.Size(); j++)
4823 {
4824 os << v[j] + 1 << " ";
4825 }
4826 os << '\n';
4827 }
4828
4829 // print the vertices
4830 os << NumOfVertices << '\n';
4831 for (i = 0; i < NumOfVertices; i++)
4832 {
4833 for (j = 0; j < Dim; j++)
4834 {
4835 os << vertices[i](j) << " ";
4836 }
4837 os << '\n';
4838 }
4839 }
4840 os.flush();
4841}
4842
4844{
4845 // In 2D, this is a workaround for a CPU boundary rendering artifact. We need
4846 // to skip a shared master edge if one of its slaves has the same rank.
4847
4848 const NCMesh::NCList &list = pncmesh->GetEdgeList();
4849 for (int i = master.slaves_begin; i < master.slaves_end; i++)
4850 {
4851 if (!pncmesh->IsGhost(1, list.slaves[i].index)) { return true; }
4852 }
4853 return false;
4854}
4855
4856void ParMesh::Print(std::ostream &os, const std::string &comments) const
4857{
4858 int shared_bdr_attr;
4859 Array<int> nc_shared_faces;
4860 Array<int> interface_faces;
4861 int interface_bdr_attr = 0;
4862
4863 if (NURBSext)
4864 {
4865 Printer(os, "", comments); // does not print shared boundary
4866 return;
4867 }
4868
4870 {
4871 // For nodes scaling, we write the file in the format MFEM NC mesh v1.1.
4872 Printer(os, "", comments);
4873 return;
4874 }
4875
4876 const Array<int>* s2l_face;
4877 if (!pncmesh)
4878 {
4879 s2l_face = ((Dim == 1) ? &svert_lvert :
4880 ((Dim == 2) ? &sedge_ledge : &sface_lface));
4881 }
4882 else
4883 {
4884 s2l_face = &nc_shared_faces;
4885 if (Dim >= 2)
4886 {
4887 // get a list of all shared non-ghost faces
4888 const NCMesh::NCList& sfaces =
4890 const int nfaces = GetNumFaces();
4891 for (int i = 0; i < sfaces.conforming.Size(); i++)
4892 {
4893 int index = sfaces.conforming[i].index;
4894 if (index < nfaces) { nc_shared_faces.Append(index); }
4895 }
4896 for (int i = 0; i < sfaces.masters.Size(); i++)
4897 {
4898 if (Dim == 2 && WantSkipSharedMaster(sfaces.masters[i])) { continue; }
4899 int index = sfaces.masters[i].index;
4900 if (index < nfaces) { nc_shared_faces.Append(index); }
4901 }
4902 for (int i = 0; i < sfaces.slaves.Size(); i++)
4903 {
4904 int index = sfaces.slaves[i].index;
4905 if (index < nfaces) { nc_shared_faces.Append(index); }
4906 }
4907 }
4908 }
4909
4910 const bool set_names = attribute_sets.SetsExist() ||
4912
4913 // Add material interfaces as boundary elements for visualization. We build a
4914 // list of local faces to print as extra boundary elements. This does not
4915 // modify the ParMesh object, only the printed mesh.
4916 if (print_interfaces && Dim > 1)
4917 {
4918 FindInterface(interface_faces);
4919
4920 // Choose a boundary attribute that does not collide with existing ones,
4921 // including those introduced by print_shared.
4922 const int max_bdr_attr = bdr_attributes.Size() ? bdr_attributes.Max() : 0;
4923 interface_bdr_attr = max_bdr_attr + 1 + (print_shared ? NRanks : 0);
4924 }
4925
4926 os << (!set_names ? "MFEM mesh v1.0\n" : "MFEM mesh v1.3\n");
4927
4928 if (!comments.empty()) { os << '\n' << comments << '\n'; }
4929
4930 // optional
4931 os <<
4932 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
4933 "# POINT = 0\n"
4934 "# SEGMENT = 1\n"
4935 "# TRIANGLE = 2\n"
4936 "# SQUARE = 3\n"
4937 "# TETRAHEDRON = 4\n"
4938 "# CUBE = 5\n"
4939 "# PRISM = 6\n"
4940 "#\n";
4941
4942 os << "\ndimension\n" << Dim
4943 << "\n\nelements\n" << NumOfElements << '\n';
4944 for (int i = 0; i < NumOfElements; i++)
4945 {
4946 PrintElement(elements[i], os);
4947 }
4948
4949 if (set_names)
4950 {
4951 os << "\nattribute_sets\n";
4953 }
4954
4955 int num_bdr_elems = NumOfBdrElements;
4956 if (print_shared && Dim > 1)
4957 {
4958 num_bdr_elems += s2l_face->Size();
4959 }
4960 if (print_interfaces && Dim > 1)
4961 {
4962 // in 3D we print two oriented copies for each material interface face
4963 num_bdr_elems += (Dim == 3 ? 2 : 1) * interface_faces.Size();
4964 }
4965 os << "\nboundary\n" << num_bdr_elems << '\n';
4966 for (int i = 0; i < NumOfBdrElements; i++)
4967 {
4968 PrintElement(boundary[i], os);
4969 }
4970
4971 if (print_shared && Dim > 1)
4972 {
4973 if (bdr_attributes.Size())
4974 {
4975 shared_bdr_attr = bdr_attributes.Max() + MyRank + 1;
4976 }
4977 else
4978 {
4979 shared_bdr_attr = MyRank + 1;
4980 }
4981 for (int i = 0; i < s2l_face->Size(); i++)
4982 {
4983 // Modify the attributes of the faces (not used otherwise?)
4984 faces[(*s2l_face)[i]]->SetAttribute(shared_bdr_attr);
4985 PrintElement(faces[(*s2l_face)[i]], os);
4986 }
4987 }
4988
4989 // Print interface faces as additional boundary elements. In 3D we print two
4990 // copies of each face, with opposite orientation, so material subdomains can
4991 // be pulled apart with F11/F12 in GLVis.
4992 if (print_interfaces && Dim > 1)
4993 {
4994 for (int i = 0; i < interface_faces.Size(); i++)
4995 {
4996 const int f = interface_faces[i];
4997 const int *fv = faces[f]->GetVertices();
4998 if (Dim == 2)
4999 {
5000 Segment seg(fv, interface_bdr_attr);
5001 PrintElement(&seg, os);
5002 }
5003 else // Dim == 3
5004 {
5005 const Geometry::Type geom = faces[f]->GetGeometryType();
5006 if (geom == Geometry::TRIANGLE)
5007 {
5008 int v0[3] = { fv[0], fv[1], fv[2] };
5009 int v1[3] = { fv[0], fv[2], fv[1] };
5010 Triangle t0(v0, interface_bdr_attr);
5011 Triangle t1(v1, interface_bdr_attr);
5012 PrintElement(&t0, os);
5013 PrintElement(&t1, os);
5014 }
5015 else if (geom == Geometry::SQUARE)
5016 {
5017 int v0[4] = { fv[0], fv[1], fv[2], fv[3] };
5018 int v1[4] = { fv[0], fv[3], fv[2], fv[1] };
5019 Quadrilateral q0(v0, interface_bdr_attr);
5020 Quadrilateral q1(v1, interface_bdr_attr);
5021 PrintElement(&q0, os);
5022 PrintElement(&q1, os);
5023 }
5024 else
5025 {
5026 MFEM_ABORT("unsupported 3D face geometry type '"
5027 << Geometry::Name[geom]
5028 << "' while printing interface boundaries.");
5029 }
5030 }
5031 }
5032 }
5033
5034 if (set_names)
5035 {
5036 os << "\nbdr_attribute_sets\n";
5038 }
5039
5040 os << "\nvertices\n" << NumOfVertices << '\n';
5041 if (Nodes == NULL)
5042 {
5043 os << spaceDim << '\n';
5044 for (int i = 0; i < NumOfVertices; i++)
5045 {
5046 os << vertices[i](0);
5047 for (int j = 1; j < spaceDim; j++)
5048 {
5049 os << ' ' << vertices[i](j);
5050 }
5051 os << '\n';
5052 }
5053 os.flush();
5054 }
5055 else
5056 {
5057 os << "\nnodes\n";
5058 Nodes->Save(os);
5059 }
5060
5061 if (set_names)
5062 {
5063 os << "\nmfem_mesh_end" << endl;
5064 }
5065}
5066
5067void ParMesh::Save(const std::string &fname, int precision) const
5068{
5069 ostringstream fname_with_suffix;
5070 fname_with_suffix << fname << "." << setfill('0') << setw(6) << MyRank;
5071 ofstream ofs(fname_with_suffix.str().c_str());
5072 ofs.precision(precision);
5073 Print(ofs);
5074}
5075
5076#ifdef MFEM_USE_ADIOS2
5078{
5079 Mesh::Print(os);
5080}
5081#endif
5082
5083static void dump_element_with_attr(const Element* elem, Array<int> &data)
5084{
5085 data.Append(elem->GetAttribute());
5086 data.Append(elem->GetGeometryType());
5087
5088 int nv = elem->GetNVertices();
5089 const int *v = elem->GetVertices();
5090 for (int i = 0; i < nv; i++)
5091 {
5092 data.Append(v[i]);
5093 }
5094}
5095
5097{
5098 // We need face neighbor elements to determine if shared faces in parallel
5099 // are on material interfaces.
5100 const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
5101
5102 interface.SetSize(0); // clear 'interface' since we Append to it
5103 const int nf = GetNumFaces();
5104 for (int f = 0; f < nf; f++)
5105 {
5106 if (!FaceIsTrueInterior(f)) { continue; } // skip true boundary
5107
5108 const int e1 = faces_info[f].Elem1No;
5109 if (e1 < 0) { continue; }
5110 const int a1 = elements[e1]->GetAttribute();
5111
5112 int a2 = a1;
5113 if (faces_info[f].Elem2No >= 0)
5114 {
5115 a2 = elements[faces_info[f].Elem2No]->GetAttribute();
5116 }
5117 else
5118 {
5119 // Shared face: element 2 is a face-neighbor element with index
5120 // -1-Elem2No, i.e. FlipIndexSign(Elem2No) (see Mesh::FaceInfo).
5121 const int nbr_el = FlipIndexSign(faces_info[f].Elem2No);
5122 MFEM_ASSERT(0 <= nbr_el && nbr_el < face_nbr_elements.Size(),
5123 "invalid face-neighbor index");
5124 a2 = face_nbr_elements[nbr_el]->GetAttribute();
5125 }
5126
5127 if (a1 != a2) { interface.Append(f); }
5128 }
5129}
5130
5131void ParMesh::PrintAsOne(std::ostream &os, const std::string &comments) const
5132{
5133 int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1];
5134 long long vc; // global vertex offset
5135 const int *v;
5136 MPI_Status status;
5137 Array<real_t> vert;
5138 Array<int> ints;
5139 Array<int> interface_faces;
5140 int interface_bdr_attr = 0;
5141
5142 if (MyRank == 0)
5143 {
5144 os << "MFEM mesh v1.0\n";
5145
5146 if (!comments.empty()) { os << '\n' << comments << '\n'; }
5147
5148 // optional
5149 os <<
5150 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
5151 "# POINT = 0\n"
5152 "# SEGMENT = 1\n"
5153 "# TRIANGLE = 2\n"
5154 "# SQUARE = 3\n"
5155 "# TETRAHEDRON = 4\n"
5156 "# CUBE = 5\n"
5157 "# PRISM = 6\n"
5158 "#\n";
5159
5160 os << "\ndimension\n" << Dim;
5161 }
5162
5163 long long loc_ne = NumOfElements, glob_ne = 0;
5164 MPI_Reduce(&loc_ne, &glob_ne, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
5165 if (MyRank == 0)
5166 {
5167 MFEM_VERIFY(static_cast<int>(glob_ne) == glob_ne,
5168 "integer overflow detected!");
5169 os << "\n\nelements\n" << glob_ne << '\n';
5170 for (i = 0; i < NumOfElements; i++)
5171 {
5172 // Print attribute + geometry:
5173 // * if print_shared != 0, use processor number + 1 as attribute
5174 // * otherwise, use the real attribute
5175 os << (print_shared ? 1 : GetAttribute(i))
5176 << ' ' << elements[i]->GetGeometryType();
5177 // vertices
5178 nv = elements[i]->GetNVertices();
5179 v = elements[i]->GetVertices();
5180 for (j = 0; j < nv; j++)
5181 {
5182 os << ' ' << v[j];
5183 }
5184 os << '\n';
5185 }
5186 vc = NumOfVertices; // global offset for vertex indices
5187 for (p = 1; p < NRanks; p++)
5188 {
5189 MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
5190 ints.SetSize(ne);
5191 if (ne)
5192 {
5193 // Receive an array that contains attribute + geometry + vertices
5194 // for each element.
5195 MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
5196 }
5197 MFEM_VERIFY(static_cast<int>(vc + nv) == (vc + nv),
5198 "integer overflow detected!");
5199 for (i = 0; i < ne; )
5200 {
5201 // Print attribute + geometry:
5202 // * if print_shared != 0, use processor number + 1 as attribute
5203 // * otherwise, use the real attribute
5204 os << (print_shared ? p+1 : ints[i]) << ' ' << ints[i+1];
5205 i++;
5206 // vertices
5207 k = Geometries.GetVertices(ints[i++])->GetNPoints();
5208 for (j = 0; j < k; j++)
5209 {
5210 os << ' ' << vc + ints[i++];
5211 }
5212 os << '\n';
5213 }
5214 vc += nv;
5215 }
5216 }
5217 else
5218 {
5219 // for each element send its attribute, geometry type and vertices
5220 ne = 0;
5221 for (i = 0; i < NumOfElements; i++)
5222 {
5223 ne += 2 + elements[i]->GetNVertices(); // attribute + geom + vertices
5224 }
5225 nv = NumOfVertices;
5226 MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
5227
5228 ints.Reserve(ne);
5229 ints.SetSize(0);
5230 for (i = 0; i < NumOfElements; i++)
5231 {
5232 dump_element_with_attr(elements[i], ints);
5233 }
5234 MFEM_ASSERT(ints.Size() == ne, "");
5235 if (ne)
5236 {
5237 MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm);
5238 }
5239 }
5240
5241 // Add material interfaces as boundary elements for visualization. We build a
5242 // list of local faces to print as extra boundary elements. This does not
5243 // modify the ParMesh object, only the printed mesh.
5244 if (print_interfaces && Dim > 1)
5245 {
5246 FindInterface(interface_faces);
5247
5248 // Choose a boundary attribute that does not collide with existing ones.
5249 const int max_bdr_attr = bdr_attributes.Size() ? bdr_attributes.Max() : 0;
5250 // If print_shared is enabled, this attribute will be replaced:
5251 interface_bdr_attr = max_bdr_attr + 1;
5252 }
5253
5254 // boundary + (optionally) shared faces + (optionally) interface faces
5255 ne = NumOfBdrElements;
5256 if (print_shared && !pncmesh)
5257 {
5258 ne += GetNSharedFaces();
5259 }
5260 if (print_shared && pncmesh && Dim > 1)
5261 {
5262 const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5263 ne += list.conforming.Size() + list.masters.Size() + list.slaves.Size();
5264 // In addition to the number returned by GetNSharedFaces(), include the
5265 // the master shared faces as well.
5266 }
5267 if (print_interfaces && Dim > 1)
5268 {
5269 // In 3D we print two oriented copies for each material interface face:
5270 ne += (Dim == 3 ? 2 : 1) * interface_faces.Size();
5271 }
5272 ints.Reserve(ne * (2 + (1 << Dim))); // just an upper bound
5273 ints.SetSize(0);
5274
5275 // For each boundary, (optionally) shared face, and (optionally) interface
5276 // face send its attribute, geometry type, and vertices.
5277 ne = 0;
5278 for (i = 0; i < NumOfBdrElements; i++)
5279 {
5280 dump_element_with_attr(boundary[i], ints); ne++;
5281 }
5282 if (print_shared && !pncmesh)
5283 {
5284 // Attribute for visualized shared faces on parallel interfaces:
5285 constexpr int shared_attribute = 1;
5286 switch (Dim)
5287 {
5288 case 1:
5289 for (i = 0; i < svert_lvert.Size(); i++)
5290 {
5291 ints.Append(shared_attribute);
5292 ints.Append(Geometry::POINT);
5293 ints.Append(svert_lvert[i]);
5294 ne++;
5295 }
5296 break;
5297
5298 case 2:
5299 for (i = 0; i < shared_edges.Size(); i++)
5300 {
5301 dump_element_with_attr(shared_edges[i], ints); ne++;
5302 }
5303 break;
5304
5305 case 3:
5306 for (i = 0; i < shared_trias.Size(); i++)
5307 {
5308 ints.Append(shared_attribute);
5310 ints.Append(shared_trias[i].v, 3);
5311 ne++;
5312 }
5313 for (i = 0; i < shared_quads.Size(); i++)
5314 {
5315 ints.Append(shared_attribute);
5317 ints.Append(shared_quads[i].v, 4);
5318 ne++;
5319 }
5320 break;
5321
5322 default:
5323 MFEM_ABORT("invalid dimension: " << Dim);
5324 }
5325 }
5326 if (print_shared && pncmesh && Dim > 1)
5327 {
5328 const NCMesh::NCList &list = pncmesh->GetSharedList(Dim - 1);
5329 const int nfaces = GetNumFaces();
5330 for (i = 0; i < list.conforming.Size(); i++)
5331 {
5332 int index = list.conforming[i].index;
5333 if (index < nfaces)
5334 {
5335 dump_element_with_attr(faces[index], ints); ne++;
5336 }
5337 }
5338 for (i = 0; i < list.masters.Size(); i++)
5339 {
5340 int index = list.masters[i].index;
5341 if (index < nfaces)
5342 {
5343 dump_element_with_attr(faces[index], ints); ne++;
5344 }
5345 }
5346 for (i = 0; i < list.slaves.Size(); i++)
5347 {
5348 int index = list.slaves[i].index;
5349 if (index < nfaces)
5350 {
5351 dump_element_with_attr(faces[index], ints); ne++;
5352 }
5353 }
5354 }
5355 if (print_interfaces && Dim > 1)
5356 {
5357 for (i = 0; i < interface_faces.Size(); i++)
5358 {
5359 const int f = interface_faces[i];
5360 const int *fv = faces[f]->GetVertices();
5361 if (Dim == 2)
5362 {
5363 Segment seg(fv, interface_bdr_attr);
5364 dump_element_with_attr(&seg, ints); ne++;
5365 }
5366 else // Dim == 3
5367 {
5368 const Geometry::Type geom = faces[f]->GetGeometryType();
5369 if (geom == Geometry::TRIANGLE)
5370 {
5371 int v0[3] = { fv[0], fv[1], fv[2] };
5372 int v1[3] = { fv[0], fv[2], fv[1] };
5373 Triangle t0(v0, interface_bdr_attr);
5374 Triangle t1(v1, interface_bdr_attr);
5375 dump_element_with_attr(&t0, ints); ne++;
5376 dump_element_with_attr(&t1, ints); ne++;
5377 }
5378 else if (geom == Geometry::SQUARE)
5379 {
5380 int v0[4] = { fv[0], fv[1], fv[2], fv[3] };
5381 int v1[4] = { fv[0], fv[3], fv[2], fv[1] };
5382 Quadrilateral q0(v0, interface_bdr_attr);
5383 Quadrilateral q1(v1, interface_bdr_attr);
5384 dump_element_with_attr(&q0, ints); ne++;
5385 dump_element_with_attr(&q1, ints); ne++;
5386 }
5387 else
5388 {
5389 MFEM_ABORT("unsupported 3D face geometry type '"
5390 << Geometry::Name[geom]
5391 << "' while printing interface boundaries.");
5392 }
5393 }
5394 }
5395 }
5396
5397 long long loc_nb = ne, glob_nb = 0;
5398 MPI_Reduce(&loc_nb, &glob_nb, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
5399 if (MyRank == 0)
5400 {
5401 MFEM_VERIFY(static_cast<int>(glob_nb) == glob_nb,
5402 "integer overflow detected!");
5403 os << "\nboundary\n" << glob_nb << '\n';
5404 vc = 0; // global vertex offset
5405 for (p = 0; p < NRanks; p++)
5406 {
5407 if (p)
5408 {
5409 MPI_Recv(nv_ne, 2, MPI_INT, p, 446, MyComm, &status);
5410 ints.SetSize(ne);
5411 if (ne)
5412 {
5413 MPI_Recv(ints.GetData(), ne, MPI_INT, p, 447, MyComm, &status);
5414 }
5415 }
5416 else
5417 {
5418 ne = ints.Size();
5419 nv = NumOfVertices;
5420 }
5421 for (i = 0; i < ne; )
5422 {
5423 // Print bdr attribute + bdr geometry:
5424 // * if print_shared != 0, use processor number + 1 as bdr attribute
5425 // * otherwise, use the real bdr attribute
5426 os << (print_shared ? p+1 : ints[i]) << ' ' << ints[i+1];
5427 i++;
5428 k = Geometries.NumVerts[ints[i++]];
5429 // vertices
5430 for (j = 0; j < k; j++)
5431 {
5432 os << ' ' << vc + ints[i++];
5433 }
5434 os << '\n';
5435 }
5436 vc += nv; // checked for overflow above, when printing the elements
5437 }
5438 }
5439 else
5440 {
5441 nv = NumOfVertices;
5442 ne = ints.Size();
5443 MPI_Send(nv_ne, 2, MPI_INT, 0, 446, MyComm);
5444 if (ne)
5445 {
5446 MPI_Send(ints.GetData(), ne, MPI_INT, 0, 447, MyComm);
5447 }
5448 }
5449
5450 // vertices / nodes
5451 // checked for overflow above, when printing the elements:
5452 MPI_Reduce(const_cast<int*>(&NumOfVertices), &nv, 1, MPI_INT, MPI_SUM, 0,
5453 MyComm);
5454 if (MyRank == 0)
5455 {
5456 os << "\nvertices\n" << nv << '\n';
5457 }
5458 if (Nodes == NULL)
5459 {
5460 if (MyRank == 0)
5461 {
5462 os << spaceDim << '\n';
5463 for (i = 0; i < NumOfVertices; i++)
5464 {
5465 os << vertices[i](0);
5466 for (j = 1; j < spaceDim; j++)
5467 {
5468 os << ' ' << vertices[i](j);
5469 }
5470 os << '\n';
5471 }
5472 for (p = 1; p < NRanks; p++)
5473 {
5474 MPI_Recv(&nv, 1, MPI_INT, p, 448, MyComm, &status);
5475 vert.SetSize(nv*spaceDim);
5476 if (nv)
5477 {
5478 MPI_Recv(&vert[0], nv*spaceDim, MPITypeMap<real_t>::mpi_type, p,
5479 449, MyComm, &status);
5480 }
5481 for (i = 0; i < nv; i++)
5482 {
5483 os << vert[i*spaceDim];
5484 for (j = 1; j < spaceDim; j++)
5485 {
5486 os << ' ' << vert[i*spaceDim+j];
5487 }
5488 os << '\n';
5489 }
5490 }
5491 os.flush();
5492 }
5493 else
5494 {
5495 MPI_Send(const_cast<int*>(&NumOfVertices), 1, MPI_INT, 0, 448, MyComm);
5497 for (i = 0; i < NumOfVertices; i++)
5498 {
5499 for (j = 0; j < spaceDim; j++)
5500 {
5501 vert[i*spaceDim+j] = vertices[i](j);
5502 }
5503 }
5504 if (NumOfVertices)
5505 {
5506 MPI_Send(&vert[0], NumOfVertices*spaceDim,
5508 }
5509 }
5510 }
5511 else
5512 {
5513 if (MyRank == 0)
5514 {
5515 os << "\nnodes\n";
5516 }
5517 ParGridFunction *pnodes = dynamic_cast<ParGridFunction *>(Nodes);
5518 if (pnodes)
5519 {
5520 pnodes->SaveAsOne(os);
5521 }
5522 else
5523 {
5524 ParFiniteElementSpace *pfes =
5525 dynamic_cast<ParFiniteElementSpace *>(Nodes->FESpace());
5526 if (pfes)
5527 {
5528 // create a wrapper ParGridFunction
5529 ParGridFunction ParNodes(pfes, Nodes);
5530 ParNodes.SaveAsOne(os);
5531 }
5532 else
5533 {
5534 mfem_error("ParMesh::PrintAsOne : Nodes have no parallel info!");
5535 }
5536 }
5537 }
5538}
5539
5540void ParMesh::PrintAsSerial(std::ostream &os, const std::string &comments) const
5541{
5542 int save_rank = 0;
5543 Mesh serialmesh = GetSerialMesh(save_rank);
5544 if (MyRank == save_rank)
5545 {
5546 serialmesh.Printer(os, "", comments);
5547 }
5548 MPI_Barrier(MyComm);
5549}
5550
5551Mesh ParMesh::GetSerialMesh(int save_rank) const
5552{
5553 if (pncmesh || NURBSext)
5554 {
5555 MFEM_ABORT("Nonconforming meshes and NURBS meshes are not yet supported.");
5556 }
5557
5558 // Define linear H1 space for vertex numbering
5559 H1_FECollection fec_linear(1, Dim);
5560 ParMesh *pm = const_cast<ParMesh *>(this);
5561 ParFiniteElementSpace pfespace_linear(pm, &fec_linear);
5562
5563 long long ne_glob_l = GetGlobalNE(); // needs to be called by all ranks
5564 MFEM_VERIFY(int(ne_glob_l) == ne_glob_l,
5565 "overflow in the number of elements!");
5566 int ne_glob = (save_rank == MyRank) ? int(ne_glob_l) : 0;
5567
5568 long long nvertices = pfespace_linear.GetTrueVSize();
5569 long long nvertices_glob_l = 0;
5570 MPI_Reduce(&nvertices, &nvertices_glob_l, 1, MPI_LONG_LONG, MPI_SUM,
5571 save_rank, MyComm);
5572 int nvertices_glob = int(nvertices_glob_l);
5573 MFEM_VERIFY(nvertices_glob == nvertices_glob_l,
5574 "overflow in the number of vertices!");
5575
5576 long long nbe = NumOfBdrElements;
5577 long long nbe_glob_l = 0;
5578 MPI_Reduce(&nbe, &nbe_glob_l, 1, MPI_LONG_LONG, MPI_SUM, save_rank, MyComm);
5579 int nbe_glob = int(nbe_glob_l);
5580 MFEM_VERIFY(nbe_glob == nbe_glob_l,
5581 "overflow in the number of boundary elements!");
5582
5583 // On ranks other than save_rank, the *_glob variables are 0, so the serial
5584 // mesh is empty.
5585 Mesh serialmesh(Dim, nvertices_glob, ne_glob, nbe_glob, spaceDim);
5586
5587 int n_send_recv;
5588 MPI_Status status;
5589 Array<real_t> vert;
5590 Array<int> ints, dofs;
5591
5592 // First set the connectivity of serial mesh using the True Dofs from
5593 // the linear H1 space.
5594 if (MyRank == save_rank)
5595 {
5596 for (int e = 0; e < NumOfElements; e++)
5597 {
5598 const int attr = elements[e]->GetAttribute();
5599 const int geom_type = elements[e]->GetGeometryType();
5600 pfespace_linear.GetElementDofs(e, dofs);
5601 for (int j = 0; j < dofs.Size(); j++)
5602 {
5603 dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5604 }
5605 Element *elem = serialmesh.NewElement(geom_type);
5606 elem->SetAttribute(attr);
5607 elem->SetVertices(dofs);
5608 serialmesh.AddElement(elem);
5609 }
5610
5611 for (int p = 0; p < NRanks; p++)
5612 {
5613 if (p == save_rank) { continue; }
5614 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 444, MyComm, &status);
5615 ints.SetSize(n_send_recv);
5616 if (n_send_recv)
5617 {
5618 MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 445, MyComm, &status);
5619 }
5620 for (int i = 0; i < n_send_recv; )
5621 {
5622 int attr = ints[i++];
5623 int geom_type = ints[i++];
5624 Element *elem = serialmesh.NewElement(geom_type);
5625 elem->SetAttribute(attr);
5626 elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5627 serialmesh.AddElement(elem);
5628 }
5629 }
5630 }
5631 else
5632 {
5633 n_send_recv = 0;
5634 for (int e = 0; e < NumOfElements; e++)
5635 {
5636 n_send_recv += 2 + elements[e]->GetNVertices();
5637 }
5638 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 444, MyComm);
5639 ints.Reserve(n_send_recv);
5640 ints.SetSize(0);
5641 for (int e = 0; e < NumOfElements; e++)
5642 {
5643 const int attr = elements[e]->GetAttribute();
5644 const int geom_type = elements[e]->GetGeometryType();
5645 ints.Append(attr);
5646 ints.Append(geom_type);
5647 pfespace_linear.GetElementDofs(e, dofs);
5648 for (int j = 0; j < dofs.Size(); j++)
5649 {
5650 ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5651 }
5652 }
5653 if (n_send_recv)
5654 {
5655 MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 445, MyComm);
5656 }
5657 }
5658
5659 // Write out boundary elements
5660 if (MyRank == save_rank)
5661 {
5662 for (int e = 0; e < NumOfBdrElements; e++)
5663 {
5664 const int attr = boundary[e]->GetAttribute();
5665 const int geom_type = boundary[e]->GetGeometryType();
5666 pfespace_linear.GetBdrElementDofs(e, dofs);
5667 for (int j = 0; j < dofs.Size(); j++)
5668 {
5669 dofs[j] = pfespace_linear.GetGlobalTDofNumber(dofs[j]);
5670 }
5671 Element *elem = serialmesh.NewElement(geom_type);
5672 elem->SetAttribute(attr);
5673 elem->SetVertices(dofs);
5674 serialmesh.AddBdrElement(elem);
5675 }
5676
5677 for (int p = 0; p < NRanks; p++)
5678 {
5679 if (p == save_rank) { continue; }
5680 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 446, MyComm, &status);
5681 ints.SetSize(n_send_recv);
5682 if (n_send_recv)
5683 {
5684 MPI_Recv(&ints[0], n_send_recv, MPI_INT, p, 447, MyComm, &status);
5685 }
5686 for (int i = 0; i < n_send_recv; )
5687 {
5688 int attr = ints[i++];
5689 int geom_type = ints[i++];
5690 Element *elem = serialmesh.NewElement(geom_type);
5691 elem->SetAttribute(attr);
5692 elem->SetVertices(&ints[i]); i += Geometry::NumVerts[geom_type];
5693 serialmesh.AddBdrElement(elem);
5694 }
5695 }
5696 } // MyRank == save_rank
5697 else
5698 {
5699 n_send_recv = 0;
5700 for (int e = 0; e < NumOfBdrElements; e++)
5701 {
5702 n_send_recv += 2 + GetBdrElement(e)->GetNVertices();
5703 }
5704 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 446, MyComm);
5705 ints.Reserve(n_send_recv);
5706 ints.SetSize(0);
5707 for (int e = 0; e < NumOfBdrElements; e++)
5708 {
5709 const int attr = boundary[e]->GetAttribute();
5710 const int geom_type = boundary[e]->GetGeometryType();
5711 ints.Append(attr);
5712 ints.Append(geom_type);
5713 pfespace_linear.GetBdrElementDofs(e, dofs);
5714 for (int j = 0; j < dofs.Size(); j++)
5715 {
5716 ints.Append(pfespace_linear.GetGlobalTDofNumber(dofs[j]));
5717 }
5718 }
5719 if (n_send_recv)
5720 {
5721 MPI_Send(&ints[0], n_send_recv, MPI_INT, save_rank, 447, MyComm);
5722 }
5723 } // MyRank != save_rank
5724
5725 if (MyRank == save_rank)
5726 {
5727 for (int v = 0; v < nvertices_glob; v++)
5728 {
5729 serialmesh.AddVertex(0.0); // all other coordinates are 0 by default
5730 }
5731 serialmesh.FinalizeTopology();
5732 }
5733
5734 // From each processor, we send element-wise vertex/dof locations and
5735 // overwrite the vertex/dof locations of the serial mesh.
5736 if (MyRank == save_rank && Nodes)
5737 {
5738 FiniteElementSpace *fespace_serial = NULL;
5739 // Duplicate the FE collection to make sure the serial mesh is completely
5740 // independent of the parallel mesh:
5741 auto fec_serial = FiniteElementCollection::New(
5742 GetNodalFESpace()->FEColl()->Name());
5743 fespace_serial = new FiniteElementSpace(&serialmesh,
5744 fec_serial,
5745 spaceDim,
5746 GetNodalFESpace()->GetOrdering());
5747 serialmesh.SetNodalFESpace(fespace_serial);
5748 serialmesh.GetNodes()->MakeOwner(fec_serial);
5749 // The serial mesh owns its Nodes and they, in turn, own fec_serial and
5750 // fespace_serial.
5751 }
5752
5753 int elem_count = 0; // To keep track of element count in serial mesh
5754 if (MyRank == save_rank)
5755 {
5756 Vector nodeloc;
5757 Array<int> ints_serial;
5758 for (int e = 0; e < NumOfElements; e++)
5759 {
5760 if (Nodes)
5761 {
5762 Nodes->GetElementDofValues(e, nodeloc);
5763 serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5764 serialmesh.GetNodes()->SetSubVector(dofs, nodeloc);
5765 }
5766 else
5767 {
5768 GetElementVertices(e, ints);
5769 serialmesh.GetElementVertices(elem_count++, ints_serial);
5770 for (int i = 0; i < ints.Size(); i++)
5771 {
5772 const real_t *vdata = GetVertex(ints[i]);
5773 real_t *vdata_serial = serialmesh.GetVertex(ints_serial[i]);
5774 for (int d = 0; d < spaceDim; d++)
5775 {
5776 vdata_serial[d] = vdata[d];
5777 }
5778 }
5779 }
5780 }
5781
5782 for (int p = 0; p < NRanks; p++)
5783 {
5784 if (p == save_rank) { continue; }
5785 MPI_Recv(&n_send_recv, 1, MPI_INT, p, 448, MyComm, &status);
5786 vert.SetSize(n_send_recv);
5787 if (n_send_recv)
5788 {
5789 MPI_Recv(&vert[0], n_send_recv, MPITypeMap<real_t>::mpi_type, p, 449, MyComm,
5790 &status);
5791 }
5792 for (int i = 0; i < n_send_recv; )
5793 {
5794 if (Nodes)
5795 {
5796 serialmesh.GetNodalFESpace()->GetElementVDofs(elem_count++, dofs);
5797 serialmesh.GetNodes()->SetSubVector(dofs, &vert[i]);
5798 i += dofs.Size();
5799 }
5800 else
5801 {
5802 serialmesh.GetElementVertices(elem_count++, ints_serial);
5803 for (int j = 0; j < ints_serial.Size(); j++)
5804 {
5805 real_t *vdata_serial = serialmesh.GetVertex(ints_serial[j]);
5806 for (int d = 0; d < spaceDim; d++)
5807 {
5808 vdata_serial[d] = vert[i++];
5809 }
5810 }
5811 }
5812 }
5813 }
5814 } // MyRank == save_rank
5815 else
5816 {
5817 n_send_recv = 0;
5818 Vector nodeloc;
5819 for (int e = 0; e < NumOfElements; e++)
5820 {
5821 if (Nodes)
5822 {
5823 const FiniteElement *fe = Nodes->FESpace()->GetFE(e);
5824 n_send_recv += spaceDim*fe->GetDof();
5825 }
5826 else
5827 {
5828 n_send_recv += elements[e]->GetNVertices()*spaceDim;
5829 }
5830 }
5831 MPI_Send(&n_send_recv, 1, MPI_INT, save_rank, 448, MyComm);
5832 vert.Reserve(n_send_recv);
5833 vert.SetSize(0);
5834 for (int e = 0; e < NumOfElements; e++)
5835 {
5836 if (Nodes)
5837 {
5838 Nodes->GetElementDofValues(e, nodeloc);
5839 for (int j = 0; j < nodeloc.Size(); j++)
5840 {
5841 vert.Append(nodeloc(j));
5842 }
5843 }
5844 else
5845 {
5846 GetElementVertices(e, ints);
5847 for (int i = 0; i < ints.Size(); i++)
5848 {
5849 const real_t *vdata = GetVertex(ints[i]);
5850 for (int d = 0; d < spaceDim; d++)
5851 {
5852 vert.Append(vdata[d]);
5853 }
5854 }
5855 }
5856 }
5857 if (n_send_recv)
5858 {
5859 MPI_Send(&vert[0], n_send_recv, MPITypeMap<real_t>::mpi_type, save_rank, 449,
5860 MyComm);
5861 }
5862 }
5863
5864 if (MyRank == save_rank)
5865 {
5868 }
5869
5870 MPI_Barrier(MyComm);
5871 return serialmesh;
5872}
5873
5874void ParMesh::SaveAsOne(const std::string &fname, int precision) const
5875{
5876 ofstream ofs;
5877 if (MyRank == 0)
5878 {
5879 ofs.open(fname);
5880 ofs.precision(precision);
5881 }
5882 PrintAsOne(ofs);
5883}
5884
5885void ParMesh::PrintAsOneXG(std::ostream &os)
5886{
5887 MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported.");
5888 if (Dim == 3 && meshgen == 1)
5889 {
5890 int i, j, k, nv, ne, p;
5891 const int *ind, *v;
5892 MPI_Status status;
5893 Array<real_t> vert;
5894 Array<int> ints;
5895
5896 if (MyRank == 0)
5897 {
5898 os << "NETGEN_Neutral_Format\n";
5899 // print the vertices
5900 ne = NumOfVertices;
5901 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
5902 os << nv << '\n';
5903 for (i = 0; i < NumOfVertices; i++)
5904 {
5905 for (j = 0; j < Dim; j++)
5906 {
5907 os << " " << vertices[i](j);
5908 }
5909 os << '\n';
5910 }
5911 for (p = 1; p < NRanks; p++)
5912 {
5913 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5914 vert.SetSize(Dim*nv);
5915 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
5916 &status);
5917 for (i = 0; i < nv; i++)
5918 {
5919 for (j = 0; j < Dim; j++)
5920 {
5921 os << " " << vert[Dim*i+j];
5922 }
5923 os << '\n';
5924 }
5925 }
5926
5927 // print the elements
5928 nv = NumOfElements;
5929 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5930 os << ne << '\n';
5931 for (i = 0; i < NumOfElements; i++)
5932 {
5933 nv = elements[i]->GetNVertices();
5934 ind = elements[i]->GetVertices();
5935 os << 1;
5936 for (j = 0; j < nv; j++)
5937 {
5938 os << " " << ind[j]+1;
5939 }
5940 os << '\n';
5941 }
5942 k = NumOfVertices;
5943 for (p = 1; p < NRanks; p++)
5944 {
5945 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5946 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5947 ints.SetSize(4*ne);
5948 MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
5949 for (i = 0; i < ne; i++)
5950 {
5951 os << p+1;
5952 for (j = 0; j < 4; j++)
5953 {
5954 os << " " << k+ints[i*4+j]+1;
5955 }
5956 os << '\n';
5957 }
5958 k += nv;
5959 }
5960 // print the boundary + shared faces information
5962 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
5963 os << ne << '\n';
5964 // boundary
5965 for (i = 0; i < NumOfBdrElements; i++)
5966 {
5967 nv = boundary[i]->GetNVertices();
5968 ind = boundary[i]->GetVertices();
5969 os << 1;
5970 for (j = 0; j < nv; j++)
5971 {
5972 os << " " << ind[j]+1;
5973 }
5974 os << '\n';
5975 }
5976 // shared faces
5977 const int sf_attr =
5978 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
5979 for (i = 0; i < shared_trias.Size(); i++)
5980 {
5981 ind = shared_trias[i].v;
5982 os << sf_attr;
5983 for (j = 0; j < 3; j++)
5984 {
5985 os << ' ' << ind[j]+1;
5986 }
5987 os << '\n';
5988 }
5989 // There are no quad shared faces
5990 k = NumOfVertices;
5991 for (p = 1; p < NRanks; p++)
5992 {
5993 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
5994 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
5995 ints.SetSize(3*ne);
5996 MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
5997 for (i = 0; i < ne; i++)
5998 {
5999 os << p+1;
6000 for (j = 0; j < 3; j++)
6001 {
6002 os << ' ' << k+ints[i*3+j]+1;
6003 }
6004 os << '\n';
6005 }
6006 k += nv;
6007 }
6008 }
6009 else
6010 {
6011 ne = NumOfVertices;
6012 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6013 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6015 for (i = 0; i < NumOfVertices; i++)
6016 for (j = 0; j < Dim; j++)
6017 {
6018 vert[Dim*i+j] = vertices[i](j);
6019 }
6021 0, 445, MyComm);
6022 // elements
6023 ne = NumOfElements;
6024 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6025 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6026 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
6027 ints.SetSize(NumOfElements*4);
6028 for (i = 0; i < NumOfElements; i++)
6029 {
6030 v = elements[i]->GetVertices();
6031 for (j = 0; j < 4; j++)
6032 {
6033 ints[4*i+j] = v[j];
6034 }
6035 }
6036 MPI_Send(&ints[0], 4*NumOfElements, MPI_INT, 0, 447, MyComm);
6037 // boundary + shared faces
6039 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6040 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6042 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
6043 ints.SetSize(3*ne);
6044 for (i = 0; i < NumOfBdrElements; i++)
6045 {
6046 v = boundary[i]->GetVertices();
6047 for (j = 0; j < 3; j++)
6048 {
6049 ints[3*i+j] = v[j];
6050 }
6051 }
6052 for ( ; i < ne; i++)
6053 {
6054 v = shared_trias[i-NumOfBdrElements].v; // tet mesh
6055 for (j = 0; j < 3; j++)
6056 {
6057 ints[3*i+j] = v[j];
6058 }
6059 }
6060 MPI_Send(&ints[0], 3*ne, MPI_INT, 0, 447, MyComm);
6061 }
6062 }
6063
6064 if (Dim == 3 && meshgen == 2)
6065 {
6066 int i, j, k, nv, ne, p;
6067 const int *ind, *v;
6068 MPI_Status status;
6069 Array<real_t> vert;
6070 Array<int> ints;
6071
6072 int TG_nv, TG_ne, TG_nbe;
6073
6074 if (MyRank == 0)
6075 {
6076 MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6077 MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6079 MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
6080
6081 os << "TrueGrid\n"
6082 << "1 " << TG_nv << " " << TG_ne << " 0 0 0 0 0 0 0\n"
6083 << "0 0 0 1 0 0 0 0 0 0 0\n"
6084 << "0 0 " << TG_nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
6085 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
6086 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
6087
6088 // print the vertices
6089 nv = TG_nv;
6090 for (i = 0; i < NumOfVertices; i++)
6091 {
6092 os << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
6093 << " " << vertices[i](2) << " 0.0\n";
6094 }
6095 for (p = 1; p < NRanks; p++)
6096 {
6097 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6098 vert.SetSize(Dim*nv);
6099 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
6100 &status);
6101 for (i = 0; i < nv; i++)
6102 {
6103 os << i+1 << " 0.0 " << vert[Dim*i] << " " << vert[Dim*i+1]
6104 << " " << vert[Dim*i+2] << " 0.0\n";
6105 }
6106 }
6107
6108 // print the elements
6109 ne = TG_ne;
6110 for (i = 0; i < NumOfElements; i++)
6111 {
6112 nv = elements[i]->GetNVertices();
6113 ind = elements[i]->GetVertices();
6114 os << i+1 << " " << 1;
6115 for (j = 0; j < nv; j++)
6116 {
6117 os << " " << ind[j]+1;
6118 }
6119 os << '\n';
6120 }
6121 k = NumOfVertices;
6122 for (p = 1; p < NRanks; p++)
6123 {
6124 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6125 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6126 ints.SetSize(8*ne);
6127 MPI_Recv(&ints[0], 8*ne, MPI_INT, p, 447, MyComm, &status);
6128 for (i = 0; i < ne; i++)
6129 {
6130 os << i+1 << " " << p+1;
6131 for (j = 0; j < 8; j++)
6132 {
6133 os << " " << k+ints[i*8+j]+1;
6134 }
6135 os << '\n';
6136 }
6137 k += nv;
6138 }
6139
6140 // print the boundary + shared faces information
6141 ne = TG_nbe;
6142 // boundary
6143 for (i = 0; i < NumOfBdrElements; i++)
6144 {
6145 nv = boundary[i]->GetNVertices();
6146 ind = boundary[i]->GetVertices();
6147 os << 1;
6148 for (j = 0; j < nv; j++)
6149 {
6150 os << " " << ind[j]+1;
6151 }
6152 os << " 1.0 1.0 1.0 1.0\n";
6153 }
6154 // shared faces
6155 const int sf_attr =
6156 MyRank + 1 + (bdr_attributes.Size() > 0 ? bdr_attributes.Max() : 0);
6157 // There are no shared triangle faces
6158 for (i = 0; i < shared_quads.Size(); i++)
6159 {
6160 ind = shared_quads[i].v;
6161 os << sf_attr;
6162 for (j = 0; j < 4; j++)
6163 {
6164 os << ' ' << ind[j]+1;
6165 }
6166 os << " 1.0 1.0 1.0 1.0\n";
6167 }
6168 k = NumOfVertices;
6169 for (p = 1; p < NRanks; p++)
6170 {
6171 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6172 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6173 ints.SetSize(4*ne);
6174 MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
6175 for (i = 0; i < ne; i++)
6176 {
6177 os << p+1;
6178 for (j = 0; j < 4; j++)
6179 {
6180 os << " " << k+ints[i*4+j]+1;
6181 }
6182 os << " 1.0 1.0 1.0 1.0\n";
6183 }
6184 k += nv;
6185 }
6186 }
6187 else
6188 {
6189 MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6190 MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6192 MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
6193
6194 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6196 for (i = 0; i < NumOfVertices; i++)
6197 for (j = 0; j < Dim; j++)
6198 {
6199 vert[Dim*i+j] = vertices[i](j);
6200 }
6201 MPI_Send(&vert[0], Dim*NumOfVertices, MPITypeMap<real_t>::mpi_type, 0, 445,
6202 MyComm);
6203 // elements
6204 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6205 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
6206 ints.SetSize(NumOfElements*8);
6207 for (i = 0; i < NumOfElements; i++)
6208 {
6209 v = elements[i]->GetVertices();
6210 for (j = 0; j < 8; j++)
6211 {
6212 ints[8*i+j] = v[j];
6213 }
6214 }
6215 MPI_Send(&ints[0], 8*NumOfElements, MPI_INT, 0, 447, MyComm);
6216 // boundary + shared faces
6217 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6219 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
6220 ints.SetSize(4*ne);
6221 for (i = 0; i < NumOfBdrElements; i++)
6222 {
6223 v = boundary[i]->GetVertices();
6224 for (j = 0; j < 4; j++)
6225 {
6226 ints[4*i+j] = v[j];
6227 }
6228 }
6229 for ( ; i < ne; i++)
6230 {
6231 v = shared_quads[i-NumOfBdrElements].v; // hex mesh
6232 for (j = 0; j < 4; j++)
6233 {
6234 ints[4*i+j] = v[j];
6235 }
6236 }
6237 MPI_Send(&ints[0], 4*ne, MPI_INT, 0, 447, MyComm);
6238 }
6239 }
6240
6241 if (Dim == 2)
6242 {
6243 int i, j, k, attr, nv, ne, p;
6244 Array<int> v;
6245 MPI_Status status;
6246 Array<real_t> vert;
6247 Array<int> ints;
6248
6249 if (MyRank == 0)
6250 {
6251 os << "areamesh2\n\n";
6252
6253 // print the boundary + shared edges information
6254 nv = NumOfBdrElements + shared_edges.Size();
6255 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6256 os << ne << '\n';
6257 // boundary
6258 for (i = 0; i < NumOfBdrElements; i++)
6259 {
6260 attr = boundary[i]->GetAttribute();
6261 boundary[i]->GetVertices(v);
6262 os << attr << " ";
6263 for (j = 0; j < v.Size(); j++)
6264 {
6265 os << v[j] + 1 << " ";
6266 }
6267 os << '\n';
6268 }
6269 // shared edges
6270 for (i = 0; i < shared_edges.Size(); i++)
6271 {
6272 attr = shared_edges[i]->GetAttribute();
6273 shared_edges[i]->GetVertices(v);
6274 os << attr << " ";
6275 for (j = 0; j < v.Size(); j++)
6276 {
6277 os << v[j] + 1 << " ";
6278 }
6279 os << '\n';
6280 }
6281 k = NumOfVertices;
6282 for (p = 1; p < NRanks; p++)
6283 {
6284 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6285 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6286 ints.SetSize(2*ne);
6287 MPI_Recv(&ints[0], 2*ne, MPI_INT, p, 447, MyComm, &status);
6288 for (i = 0; i < ne; i++)
6289 {
6290 os << p+1;
6291 for (j = 0; j < 2; j++)
6292 {
6293 os << " " << k+ints[i*2+j]+1;
6294 }
6295 os << '\n';
6296 }
6297 k += nv;
6298 }
6299
6300 // print the elements
6301 nv = NumOfElements;
6302 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6303 os << ne << '\n';
6304 for (i = 0; i < NumOfElements; i++)
6305 {
6306 // attr = elements[i]->GetAttribute(); // not used
6307 elements[i]->GetVertices(v);
6308 os << 1 << " " << 3 << " ";
6309 for (j = 0; j < v.Size(); j++)
6310 {
6311 os << v[j] + 1 << " ";
6312 }
6313 os << '\n';
6314 }
6315 k = NumOfVertices;
6316 for (p = 1; p < NRanks; p++)
6317 {
6318 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6319 MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
6320 ints.SetSize(3*ne);
6321 MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
6322 for (i = 0; i < ne; i++)
6323 {
6324 os << p+1 << " " << 3;
6325 for (j = 0; j < 3; j++)
6326 {
6327 os << " " << k+ints[i*3+j]+1;
6328 }
6329 os << '\n';
6330 }
6331 k += nv;
6332 }
6333
6334 // print the vertices
6335 ne = NumOfVertices;
6336 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6337 os << nv << '\n';
6338 for (i = 0; i < NumOfVertices; i++)
6339 {
6340 for (j = 0; j < Dim; j++)
6341 {
6342 os << vertices[i](j) << " ";
6343 }
6344 os << '\n';
6345 }
6346 for (p = 1; p < NRanks; p++)
6347 {
6348 MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
6349 vert.SetSize(Dim*nv);
6350 MPI_Recv(&vert[0], Dim*nv, MPITypeMap<real_t>::mpi_type, p, 445, MyComm,
6351 &status);
6352 for (i = 0; i < nv; i++)
6353 {
6354 for (j = 0; j < Dim; j++)
6355 {
6356 os << " " << vert[Dim*i+j];
6357 }
6358 os << '\n';
6359 }
6360 }
6361 }
6362 else
6363 {
6364 // boundary + shared faces
6365 nv = NumOfBdrElements + shared_edges.Size();
6366 MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
6367 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6368 ne = NumOfBdrElements + shared_edges.Size();
6369 MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
6370 ints.SetSize(2*ne);
6371 for (i = 0; i < NumOfBdrElements; i++)
6372 {
6373 boundary[i]->GetVertices(v);
6374 for (j = 0; j < 2; j++)
6375 {
6376 ints[2*i+j] = v[j];
6377 }
6378 }
6379 for ( ; i < ne; i++)
6380 {
6381 shared_edges[i-NumOfBdrElements]->GetVertices(v);
6382 for (j = 0; j < 2; j++)
6383 {
6384 ints[2*i+j] = v[j];
6385 }
6386 }
6387 MPI_Send(&ints[0], 2*ne, MPI_INT, 0, 447, MyComm);
6388 // elements
6389 ne = NumOfElements;
6390 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6391 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6392 MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
6393 ints.SetSize(NumOfElements*3);
6394 for (i = 0; i < NumOfElements; i++)
6395 {
6396 elements[i]->GetVertices(v);
6397 for (j = 0; j < 3; j++)
6398 {
6399 ints[3*i+j] = v[j];
6400 }
6401 }
6402 MPI_Send(&ints[0], 3*NumOfElements, MPI_INT, 0, 447, MyComm);
6403 // vertices
6404 ne = NumOfVertices;
6405 MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
6406 MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
6408 for (i = 0; i < NumOfVertices; i++)
6409 for (j = 0; j < Dim; j++)
6410 {
6411 vert[Dim*i+j] = vertices[i](j);
6412 }
6414 0, 445, MyComm);
6415 }
6416 }
6417}
6418
6419void ParMesh::GetBoundingBox(Vector &gp_min, Vector &gp_max, int ref)
6420{
6421 int sdim;
6422 Vector p_min, p_max;
6423
6424 this->Mesh::GetBoundingBox(p_min, p_max, ref);
6425
6426 sdim = SpaceDimension();
6427
6428 gp_min.SetSize(sdim);
6429 gp_max.SetSize(sdim);
6430
6431 MPI_Allreduce(p_min.GetData(), gp_min.GetData(), sdim,
6433 MPI_MIN, MyComm);
6434 MPI_Allreduce(p_max.GetData(), gp_max.GetData(), sdim,
6436 MPI_MAX, MyComm);
6437}
6438
6440 real_t &gk_min, real_t &gk_max)
6441{
6442 real_t h_min, h_max, kappa_min, kappa_max;
6443
6444 this->Mesh::GetCharacteristics(h_min, h_max, kappa_min, kappa_max);
6445
6446 MPI_Allreduce(&h_min, &gh_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN,
6447 MyComm);
6448 MPI_Allreduce(&h_max, &gh_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
6449 MyComm);
6450 MPI_Allreduce(&kappa_min, &gk_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN,
6451 MyComm);
6452 MPI_Allreduce(&kappa_max, &gk_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
6453 MyComm);
6454}
6455
6456void ParMesh::PrintInfo(std::ostream &os)
6457{
6458 int i;
6459 DenseMatrix J(Dim);
6460 real_t h_min, h_max, kappa_min, kappa_max, h, kappa;
6461
6462 if (MyRank == 0)
6463 {
6464 os << "Parallel Mesh Stats:" << '\n';
6465 }
6466
6467 for (i = 0; i < NumOfElements; i++)
6468 {
6469 GetElementJacobian(i, J);
6470 h = pow(fabs(J.Weight()), 1.0/real_t(Dim));
6471 kappa = (Dim == spaceDim) ?
6472 J.CalcSingularvalue(0) / J.CalcSingularvalue(Dim-1) : -1.0;
6473 if (i == 0)
6474 {
6475 h_min = h_max = h;
6476 kappa_min = kappa_max = kappa;
6477 }
6478 else
6479 {
6480 if (h < h_min) { h_min = h; }
6481 if (h > h_max) { h_max = h; }
6482 if (kappa < kappa_min) { kappa_min = kappa; }
6483 if (kappa > kappa_max) { kappa_max = kappa; }
6484 }
6485 }
6486
6487 real_t gh_min, gh_max, gk_min, gk_max;
6488 MPI_Reduce(&h_min, &gh_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN, 0,
6489 MyComm);
6490 MPI_Reduce(&h_max, &gh_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX, 0,
6491 MyComm);
6492 MPI_Reduce(&kappa_min, &gk_min, 1, MPITypeMap<real_t>::mpi_type, MPI_MIN, 0,
6493 MyComm);
6494 MPI_Reduce(&kappa_max, &gk_max, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX, 0,
6495 MyComm);
6496
6497 // TODO: collect and print stats by geometry
6498
6499 long long ldata[5]; // vert, edge, face, elem, neighbors;
6500 long long mindata[5], maxdata[5], sumdata[5];
6501
6502 // count locally owned vertices, edges, and faces
6503 ldata[0] = GetNV();
6504 ldata[1] = GetNEdges();
6505 ldata[2] = GetNFaces();
6506 ldata[3] = GetNE();
6507 ldata[4] = gtopo.GetNumNeighbors()-1;
6508 for (int gr = 1; gr < GetNGroups(); gr++)
6509 {
6510 if (!gtopo.IAmMaster(gr)) // we are not the master
6511 {
6512 ldata[0] -= group_svert.RowSize(gr-1);
6513 ldata[1] -= group_sedge.RowSize(gr-1);
6514 ldata[2] -= group_stria.RowSize(gr-1);
6515 ldata[2] -= group_squad.RowSize(gr-1);
6516 }
6517 }
6518
6519 MPI_Reduce(ldata, mindata, 5, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
6520 MPI_Reduce(ldata, sumdata, 5, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
6521 MPI_Reduce(ldata, maxdata, 5, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
6522
6523 if (MyRank == 0)
6524 {
6525 os << '\n'
6526 << " "
6527 << setw(12) << "minimum"
6528 << setw(12) << "average"
6529 << setw(12) << "maximum"
6530 << setw(12) << "total" << '\n';
6531 os << " vertices "
6532 << setw(12) << mindata[0]
6533 << setw(12) << sumdata[0]/NRanks
6534 << setw(12) << maxdata[0]
6535 << setw(12) << sumdata[0] << '\n';
6536 os << " edges "
6537 << setw(12) << mindata[1]
6538 << setw(12) << sumdata[1]/NRanks
6539 << setw(12) << maxdata[1]
6540 << setw(12) << sumdata[1] << '\n';
6541 if (Dim == 3)
6542 {
6543 os << " faces "
6544 << setw(12) << mindata[2]
6545 << setw(12) << sumdata[2]/NRanks
6546 << setw(12) << maxdata[2]
6547 << setw(12) << sumdata[2] << '\n';
6548 }
6549 os << " elements "
6550 << setw(12) << mindata[3]
6551 << setw(12) << sumdata[3]/NRanks
6552 << setw(12) << maxdata[3]
6553 << setw(12) << sumdata[3] << '\n';
6554 os << " neighbors "
6555 << setw(12) << mindata[4]
6556 << setw(12) << sumdata[4]/NRanks
6557 << setw(12) << maxdata[4] << '\n';
6558 os << '\n'
6559 << " "
6560 << setw(12) << "minimum"
6561 << setw(12) << "maximum" << '\n';
6562 os << " h "
6563 << setw(12) << gh_min
6564 << setw(12) << gh_max << '\n';
6565 os << " kappa "
6566 << setw(12) << gk_min
6567 << setw(12) << gk_max << '\n';
6568 os << std::flush;
6569 }
6570}
6571
6572long long ParMesh::ReduceInt(int value) const
6573{
6574 long long local = value, global;
6575 MPI_Allreduce(&local, &global, 1, MPI_LONG_LONG, MPI_SUM, MyComm);
6576 return global;
6577}
6578
6579void ParMesh::ParPrint(ostream &os, const std::string &comments) const
6580{
6581 if (NURBSext)
6582 {
6583 // TODO: NURBS meshes.
6584 Print(os, comments); // use the serial MFEM v1.0 format for now
6585 return;
6586 }
6587
6588 if (Nonconforming())
6589 {
6590 // the NC mesh format works both in serial and in parallel
6591 Printer(os, "", comments);
6592 return;
6593 }
6594
6595 // Write out serial mesh. Tell serial mesh to delineate the end of its
6596 // output with 'mfem_serial_mesh_end' instead of 'mfem_mesh_end', as we will
6597 // be adding additional parallel mesh information.
6598 Printer(os, "mfem_serial_mesh_end", comments);
6599
6600 // write out group topology info.
6601 gtopo.Save(os);
6602
6603 os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6604 if (Dim >= 2)
6605 {
6606 os << "total_shared_edges " << shared_edges.Size() << '\n';
6607 }
6608 if (Dim >= 3)
6609 {
6610 os << "total_shared_faces " << sface_lface.Size() << '\n';
6611 }
6612 os << "\n# group 0 has no shared entities\n";
6613 for (int gr = 1; gr < GetNGroups(); gr++)
6614 {
6615 {
6616 const int nv = group_svert.RowSize(gr-1);
6617 const int *sv = group_svert.GetRow(gr-1);
6618 os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
6619 for (int i = 0; i < nv; i++)
6620 {
6621 os << svert_lvert[sv[i]] << '\n';
6622 }
6623 }
6624 if (Dim >= 2)
6625 {
6626 const int ne = group_sedge.RowSize(gr-1);
6627 const int *se = group_sedge.GetRow(gr-1);
6628 os << "\nshared_edges " << ne << '\n';
6629 for (int i = 0; i < ne; i++)
6630 {
6631 const int *v = shared_edges[se[i]]->GetVertices();
6632 os << v[0] << ' ' << v[1] << '\n';
6633 }
6634 }
6635 if (Dim >= 3)
6636 {
6637 const int nt = group_stria.RowSize(gr-1);
6638 const int *st = group_stria.GetRow(gr-1);
6639 const int nq = group_squad.RowSize(gr-1);
6640 const int *sq = group_squad.GetRow(gr-1);
6641 os << "\nshared_faces " << nt+nq << '\n';
6642 for (int i = 0; i < nt; i++)
6643 {
6644 os << Geometry::TRIANGLE;
6645 const int *v = shared_trias[st[i]].v;
6646 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6647 os << '\n';
6648 }
6649 for (int i = 0; i < nq; i++)
6650 {
6651 os << Geometry::SQUARE;
6652 const int *v = shared_quads[sq[i]].v;
6653 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6654 os << '\n';
6655 }
6656 }
6657 }
6658
6659 // Write out section end tag for mesh.
6660 os << "\nmfem_mesh_end" << endl;
6661}
6662
6663void ParMesh::PrintVTU(std::string pathname,
6664 VTKFormat format,
6665 bool high_order_output,
6666 int compression_level,
6667 bool bdr_elements)
6668{
6669 int pad_digits_rank = 6;
6671
6672 std::string::size_type pos = pathname.find_last_of('/');
6673 std::string fname
6674 = (pos == std::string::npos) ? pathname : pathname.substr(pos+1);
6675
6676 if (MyRank == 0)
6677 {
6678 std::string pvtu_name = pathname + "/" + fname + ".pvtu";
6679 std::ofstream os(pvtu_name);
6680
6681 std::string data_type = (format == VTKFormat::BINARY32) ? "Float32" : "Float64";
6682 std::string data_format = (format == VTKFormat::ASCII) ? "ascii" : "binary";
6683
6684 os << "<?xml version=\"1.0\"?>\n";
6685 os << "<VTKFile type=\"PUnstructuredGrid\"";
6686 os << " version =\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
6687 os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
6688
6689 os << "<PPoints>\n";
6690 os << "\t<PDataArray type=\"" << data_type << "\" ";
6691 os << " Name=\"Points\" NumberOfComponents=\"3\""
6692 << " format=\"" << data_format << "\"/>\n";
6693 os << "</PPoints>\n";
6694
6695 os << "<PCells>\n";
6696 os << "\t<PDataArray type=\"Int32\" ";
6697 os << " Name=\"connectivity\" NumberOfComponents=\"1\""
6698 << " format=\"" << data_format << "\"/>\n";
6699 os << "\t<PDataArray type=\"Int32\" ";
6700 os << " Name=\"offsets\" NumberOfComponents=\"1\""
6701 << " format=\"" << data_format << "\"/>\n";
6702 os << "\t<PDataArray type=\"UInt8\" ";
6703 os << " Name=\"types\" NumberOfComponents=\"1\""
6704 << " format=\"" << data_format << "\"/>\n";
6705 os << "</PCells>\n";
6706
6707 os << "<PCellData>\n";
6708 os << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
6709 << "\" NumberOfComponents=\"1\""
6710 << " format=\"" << data_format << "\"/>\n";
6711 os << "</PCellData>\n";
6712
6713 for (int ii=0; ii<NRanks; ii++)
6714 {
6715 std::string piece = fname + ".proc"
6716 + to_padded_string(ii, pad_digits_rank) + ".vtu";
6717 os << "<Piece Source=\"" << piece << "\"/>\n";
6718 }
6719
6720 os << "</PUnstructuredGrid>\n";
6721 os << "</VTKFile>\n";
6722 os.close();
6723 }
6724
6725 std::string vtu_fname = pathname + "/" + fname + ".proc"
6726 + to_padded_string(MyRank, pad_digits_rank);
6727 Mesh::PrintVTU(vtu_fname, format, high_order_output, compression_level,
6728 bdr_elements);
6729}
6730
6732 Array<IntegrationPoint>& ip, bool warn,
6734{
6735 const int npts = point_mat.Width();
6736 if (npts == 0) { return 0; }
6737
6738 const bool no_warn = false;
6739 Mesh::FindPoints(point_mat, elem_id, ip, no_warn, inv_trans);
6740
6741 // If multiple processors find the same point, we need to choose only one of
6742 // the processors to mark that point as found.
6743 // Here, we choose the processor with the minimal rank.
6744
6745 Array<int> my_point_rank(npts), glob_point_rank(npts);
6746 for (int k = 0; k < npts; k++)
6747 {
6748 my_point_rank[k] = (elem_id[k] == -1) ? NRanks : MyRank;
6749 }
6750
6751 MPI_Allreduce(my_point_rank.GetData(), glob_point_rank.GetData(), npts,
6752 MPI_INT, MPI_MIN, MyComm);
6753
6754 int pts_found = 0;
6755 for (int k = 0; k < npts; k++)
6756 {
6757 if (glob_point_rank[k] == NRanks) { elem_id[k] = -1; }
6758 else
6759 {
6760 pts_found++;
6761 if (glob_point_rank[k] != MyRank) { elem_id[k] = -2; }
6762 }
6763 }
6764 if (warn && pts_found != npts && MyRank == 0)
6765 {
6766 MFEM_WARNING((npts-pts_found) << " points were not found");
6767 }
6768 return pts_found;
6769}
6770
6771static void PrintVertex(const Vertex &v, int space_dim, ostream &os)
6772{
6773 os << v(0);
6774 for (int d = 1; d < space_dim; d++)
6775 {
6776 os << ' ' << v(d);
6777 }
6778}
6779
6780void ParMesh::PrintSharedEntities(const std::string &fname_prefix) const
6781{
6782 stringstream out_name;
6783 out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
6784 << ".shared_entities";
6785 ofstream os(out_name.str().c_str());
6786 os.precision(16);
6787
6788 gtopo.Save(out);
6789
6790 os << "\ntotal_shared_vertices " << svert_lvert.Size() << '\n';
6791 if (Dim >= 2)
6792 {
6793 os << "total_shared_edges " << shared_edges.Size() << '\n';
6794 }
6795 if (Dim >= 3)
6796 {
6797 os << "total_shared_faces " << sface_lface.Size() << '\n';
6798 }
6799 for (int gr = 1; gr < GetNGroups(); gr++)
6800 {
6801 {
6802 const int nv = group_svert.RowSize(gr-1);
6803 const int *sv = group_svert.GetRow(gr-1);
6804 os << "\n# group " << gr << "\n\nshared_vertices " << nv << '\n';
6805 for (int i = 0; i < nv; i++)
6806 {
6807 const int lvi = svert_lvert[sv[i]];
6808 // os << lvi << '\n';
6809 PrintVertex(vertices[lvi], spaceDim, os);
6810 os << '\n';
6811 }
6812 }
6813 if (Dim >= 2)
6814 {
6815 const int ne = group_sedge.RowSize(gr-1);
6816 const int *se = group_sedge.GetRow(gr-1);
6817 os << "\nshared_edges " << ne << '\n';
6818 for (int i = 0; i < ne; i++)
6819 {
6820 const int *v = shared_edges[se[i]]->GetVertices();
6821 // os << v[0] << ' ' << v[1] << '\n';
6822 PrintVertex(vertices[v[0]], spaceDim, os);
6823 os << " | ";
6824 PrintVertex(vertices[v[1]], spaceDim, os);
6825 os << '\n';
6826 }
6827 }
6828 if (Dim >= 3)
6829 {
6830 const int nt = group_stria.RowSize(gr-1);
6831 const int *st = group_stria.GetRow(gr-1);
6832 const int nq = group_squad.RowSize(gr-1);
6833 const int *sq = group_squad.GetRow(gr-1);
6834 os << "\nshared_faces " << nt+nq << '\n';
6835 for (int i = 0; i < nt; i++)
6836 {
6837 const int *v = shared_trias[st[i]].v;
6838#if 0
6839 os << Geometry::TRIANGLE;
6840 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
6841 os << '\n';
6842#endif
6843 for (int j = 0; j < 3; j++)
6844 {
6845 PrintVertex(vertices[v[j]], spaceDim, os);
6846 (j < 2) ? os << " | " : os << '\n';
6847 }
6848 }
6849 for (int i = 0; i < nq; i++)
6850 {
6851 const int *v = shared_quads[sq[i]].v;
6852#if 0
6853 os << Geometry::SQUARE;
6854 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
6855 os << '\n';
6856#endif
6857 for (int j = 0; j < 4; j++)
6858 {
6859 PrintVertex(vertices[v[j]], spaceDim, os);
6860 (j < 3) ? os << " | " : os << '\n';
6861 }
6862 }
6863 }
6864 }
6865}
6866
6868{
6869 H1_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6870 ParMesh *pm = const_cast<ParMesh *>(this);
6871 ParFiniteElementSpace fespace(pm, &fec);
6872
6873 gi.SetSize(GetNV());
6874
6875 Array<int> dofs;
6876 for (int i=0; i<GetNV(); ++i)
6877 {
6878 fespace.GetVertexDofs(i, dofs);
6879 gi[i] = fespace.GetGlobalTDofNumber(dofs[0]);
6880 }
6881}
6882
6884{
6885 if (Dim == 1)
6886 {
6888 return;
6889 }
6890
6891 ND_FECollection fec(1, Dim); // Order 1, mesh dimension (not spatial dimension).
6892 ParMesh *pm = const_cast<ParMesh *>(this);
6893 ParFiniteElementSpace fespace(pm, &fec);
6894
6895 gi.SetSize(GetNEdges());
6896
6897 Array<int> dofs;
6898 for (int i=0; i<GetNEdges(); ++i)
6899 {
6900 fespace.GetEdgeDofs(i, dofs);
6901 const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6902 gi[i] = fespace.GetGlobalTDofNumber(ldof);
6903 }
6904}
6905
6907{
6908 if (Dim == 2)
6909 {
6911 return;
6912 }
6913 else if (Dim == 1)
6914 {
6916 return;
6917 }
6918
6919 RT_FECollection fec(0, Dim); // Order 0, mesh dimension (not spatial dimension).
6920 ParMesh *pm = const_cast<ParMesh *>(this);
6921 ParFiniteElementSpace fespace(pm, &fec);
6922
6923 gi.SetSize(GetNFaces());
6924
6925 Array<int> dofs;
6926 for (int i=0; i<GetNFaces(); ++i)
6927 {
6928 fespace.GetFaceDofs(i, dofs);
6929 const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
6930 gi[i] = fespace.GetGlobalTDofNumber(ldof);
6931 }
6932}
6933
6935{
6937
6938 // Cast from long long to HYPRE_BigInt
6939 const HYPRE_BigInt offset = glob_elem_offset;
6940
6941 gi.SetSize(GetNE());
6942 for (int i=0; i<GetNE(); ++i)
6943 {
6944 gi[i] = offset + i;
6945 }
6946}
6947
6949{
6950 const_cast<ParMesh*>(this)->ExchangeFaceNbrData();
6951
6952 Mesh::GetExteriorFaceMarker(face_marker);
6953}
6954
6955void ParMesh::UnmarkInternalBoundaries(Array<int> &bdr_marker, bool excl) const
6956{
6957 const int max_bdr_attr = bdr_attributes.Max();
6958
6959 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
6960 "bdr_marker must be at least bdr_attriburtes.Max() in length");
6961
6962 Array<int> ext_face_marker;
6963 GetExteriorFaceMarker(ext_face_marker);
6964
6965 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
6966 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
6967
6968 // Identify attributes which contain local interior faces and those which
6969 // contain local exterior faces.
6970 for (int be = 0; be < boundary.Size(); be++)
6971 {
6972 const int bea = boundary[be]->GetAttribute();
6973
6974 if (bdr_marker[bea-1] != 0)
6975 {
6976 const int f = be_to_face[be];
6977
6978 if (ext_face_marker[f] > 0)
6979 {
6980 exterior_bdr[bea-1] = true;
6981 }
6982 else
6983 {
6984 interior_bdr[bea-1] = true;
6985 }
6986 }
6987 }
6988
6989 Array<bool> glb_interior_bdr(bdr_attributes.Max()); glb_interior_bdr = false;
6990 Array<bool> glb_exterior_bdr(bdr_attributes.Max()); glb_exterior_bdr = false;
6991
6992 MPI_Allreduce(&interior_bdr[0], &glb_interior_bdr[0], bdr_attributes.Max(),
6993 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6994 MPI_Allreduce(&exterior_bdr[0], &glb_exterior_bdr[0], bdr_attributes.Max(),
6995 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
6996
6997 // Unmark attributes which are currently marked, contain interior faces,
6998 // and satisfy the appropriate exclusivity requirement.
6999 for (int b = 0; b < max_bdr_attr; b++)
7000 {
7001 if (bdr_marker[b] != 0 && glb_interior_bdr[b])
7002 {
7003 if (!excl || !glb_exterior_bdr[b])
7004 {
7005 bdr_marker[b] = 0;
7006 }
7007 }
7008 }
7009}
7010
7011void ParMesh::MarkExternalBoundaries(Array<int> &bdr_marker, bool excl) const
7012{
7013 const int max_bdr_attr = bdr_attributes.Max();
7014
7015 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
7016 "bdr_marker must be at least bdr_attriburtes.Max() in length");
7017
7018 Array<int> ext_face_marker;
7019 GetExteriorFaceMarker(ext_face_marker);
7020
7021 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
7022 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
7023
7024 // Identify boundary attributes containing local exterior faces and those
7025 // containing local interior faces.
7026 for (int be = 0; be < boundary.Size(); be++)
7027 {
7028 const int bea = boundary[be]->GetAttribute();
7029
7030 const int f = be_to_face[be];
7031
7032 if (ext_face_marker[f] > 0)
7033 {
7034 exterior_bdr[bea-1] = true;
7035 }
7036 else
7037 {
7038 interior_bdr[bea-1] = true;
7039 }
7040 }
7041
7042 Array<bool> glb_interior_bdr(bdr_attributes.Max()); glb_interior_bdr = false;
7043 Array<bool> glb_exterior_bdr(bdr_attributes.Max()); glb_exterior_bdr = false;
7044
7045 MPI_Allreduce(&interior_bdr[0], &glb_interior_bdr[0], bdr_attributes.Max(),
7046 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
7047 MPI_Allreduce(&exterior_bdr[0], &glb_exterior_bdr[0], bdr_attributes.Max(),
7048 MFEM_MPI_CXX_BOOL, MPI_LOR, MyComm);
7049
7050 // Mark the attributes which are currently unmarked, containing exterior
7051 // faces, and satisfying the necessary exclusivity requirements.
7052 for (int b = 0; b < max_bdr_attr; b++)
7053 {
7054 if (bdr_marker[b] == 0 && glb_exterior_bdr[b])
7055 {
7056 if (!excl || !glb_interior_bdr[b])
7057 {
7058 bdr_marker[b] = 1;
7059 }
7060 }
7061 }
7062}
7063
7065{
7066 Mesh::Swap(other, true);
7067
7068 mfem::Swap(MyComm, other.MyComm);
7069 mfem::Swap(NRanks, other.NRanks);
7070 mfem::Swap(MyRank, other.MyRank);
7071
7074
7075 gtopo.Swap(other.gtopo);
7076
7081
7088
7089 // Swap face-neighbor data
7098 std::swap(face_nbr_el_ori, other.face_nbr_el_ori);
7099 std::swap(face_nbr_el_to_face, other.face_nbr_el_to_face);
7100
7101 // Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
7102 mfem::Swap(pncmesh, other.pncmesh);
7103
7104 // Keep Print() behavior consistent after move/swap operations.
7107}
7108
7110{
7111 delete pncmesh;
7112 ncmesh = pncmesh = NULL;
7113
7115
7116 for (int i = 0; i < shared_edges.Size(); i++)
7117 {
7119 }
7120 shared_edges.DeleteAll();
7121
7122 face_nbr_el_to_face = nullptr;
7123}
7124
7126{
7128
7129 // The Mesh destructor is called automatically
7130}
7131
7132}
7133
7134#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:210
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
void LoseData()
NULL-ifies the data.
Definition array.hpp:186
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
void DeleteAll()
Delete the whole array.
Definition array.hpp:1062
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
T * GetData()
Returns the data.
Definition array.hpp:159
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1071
void CopyTo(U *dest)
STL-like copyTo dest from begin to end.
Definition array.hpp:381
void DeleteLast()
Delete the last entry of the array.
Definition array.hpp:250
T & Last()
Return the last element in the array.
Definition array.hpp:974
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:125
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:210
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3786
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:299
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:3860
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:852
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:3738
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:3993
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:669
Abstract class for all finite elements.
Definition fe_base.hpp:294
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:476
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:410
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
Definition geom.cpp:1136
static const char * Name[NumGeom]
Definition geom.hpp:49
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:162
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:160
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:687
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:291
int DofForGeometry(Geometry::Type GeomType) const override
Definition fe_coll.hpp:307
const int * GetDofMap(Geometry::Type GeomType) const
Get the Cartesian to local H1 dof map.
Definition fe_coll.cpp:2128
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:615
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:706
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:96
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
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:369
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:67
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition mesh.cpp:7346
Array< Vertex > vertices
Definition mesh.hpp:110
void GetEdgeOrdering(const DSTable &v_to_v, Array< int > &order)
Definition mesh.cpp:3141
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:1113
void SetVerticesFromNodes(const GridFunction *nodes)
Helper to set vertex coordinates given a high-order curvature function.
Definition mesh.cpp:7246
int GetElementToEdgeTable(Table &)
Definition mesh.cpp:8551
int meshgen
Definition mesh.hpp:93
Element * NewElement(int geom)
Definition mesh.cpp:4978
IsoparametricTransformation Transformation2
Definition mesh.hpp:260
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1396
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:8369
static void PrintElement(const Element *el, std::ostream &os)
Definition mesh.cpp:5044
Array< FaceInfo > faces_info
Definition mesh.hpp:242
CoarseFineTransformations CoarseFineTr
Definition mesh.hpp:267
void GetElementJacobian(int i, DenseMatrix &J, const IntegrationPoint *ip=NULL)
Definition mesh.cpp:66
int AddBdrElement(Element *elem)
Definition mesh.cpp:2449
virtual FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31)
Definition mesh.cpp:1179
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:309
void RedRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:426
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:317
virtual void GetExteriorFaceMarker(Array< int > &face_marker) const
Populate a marker array identifying exterior faces.
Definition mesh.cpp:1733
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Return FiniteElement for reference element of the specified type.
Definition mesh.cpp:340
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7586
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:8445
int NumOfBdrElements
Definition mesh.hpp:84
void BdrBisection(int i, const HashTable< Hashed2 > &)
Bisect a boundary triangle: boundary element with index i is bisected.
Definition mesh.cpp:12088
Element::Type GetBdrElementType(int i) const
Returns the type of boundary element i.
Definition mesh.cpp:8450
const Table & ElementToEdgeTable() const
Definition mesh.cpp:8635
bool Conforming() const
Definition mesh.cpp:16102
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1651
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:6075
int GetAttribute(int i) const
Return the attribute of element i.
Definition mesh.hpp:1497
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1622
void UniformRefinement3D_base(Array< int > *f2qf=NULL, DSTable *v_to_v_p=NULL, bool update_nodes=true)
Definition mesh.cpp:10377
Array< NCFaceInfo > nc_faces_info
Definition mesh.hpp:243
@ REBALANCE
Definition mesh.hpp:304
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:5738
void MakeRefined_(Mesh &orig_mesh, const Array< int > &ref_factors, int ref_type)
Internal function used in Mesh::MakeRefined.
Definition mesh.cpp:5498
real_t GetLength(int i, int j) const
Return the length of the segment from node i to node j.
Definition mesh.cpp:8489
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:7206
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition mesh.cpp:5103
void GenerateNCFaceInfo()
Definition mesh.cpp:8875
void ApplyLocalSlaveTransformation(FaceElementTransformations &FT, const FaceInfo &fi, bool is_ghost) const
Definition mesh.cpp:1329
Array< Element * > faces
Definition mesh.hpp:112
int Dim
Definition mesh.hpp:81
real_t AggregateError(const Array< real_t > &elem_error, const int *fine, int nfine, int op)
Derefinement helper.
Definition mesh.cpp:11376
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition mesh.cpp:3255
bool Nonconforming() const
Definition mesh.hpp:2539
void GenerateFaces()
Definition mesh.cpp:8768
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:315
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1702
void GetVertices(Vector &vert_coord) const
Definition mesh.cpp:10036
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition mesh.cpp:11476
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:7318
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1447
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7497
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1399
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:3660
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:2610
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition mesh.cpp:3189
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2079
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
const Element * GetFace(int i) const
Return pointer to the i'th face element object.
Definition mesh.hpp:1474
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:142
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
Table * el_to_face
Definition mesh.hpp:246
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1462
void GreenRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:432
void UpdateNodes()
Update the nodes of a curved mesh after the topological part of a Mesh::Operation,...
Definition mesh.cpp:10203
long sequence
Definition mesh.hpp:100
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:111
int AddElement(Element *elem)
Definition mesh.cpp:2442
Table * el_to_edge
Definition mesh.hpp:245
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:361
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition mesh.cpp:8996
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Return the indices of the elements sharing face Face.
Definition mesh.cpp:1632
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:12464
IsoparametricTransformation Transformation
Definition mesh.hpp:260
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:8318
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1317
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:206
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition mesh.cpp:7200
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:2016
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:10112
int NumOfVertices
Definition mesh.hpp:84
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:312
Array< int > be_to_face
Definition mesh.hpp:248
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1387
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:8139
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:12908
GridFunction * Nodes
Definition mesh.hpp:272
Element::Type GetFaceElementType(int Face) const
Definition mesh.cpp:1687
int CheckBdrElementOrientation(bool fix_it=true)
Check the orientation of the boundary elements.
Definition mesh.cpp:7798
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:10182
int NumOfElements
Definition mesh.hpp:84
void Swap(Mesh &other, bool non_geometry)
Definition mesh.cpp:11521
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition mesh.cpp:11879
void ResetLazyData()
Definition mesh.cpp:1996
void UniformRefinement2D_base(bool update_nodes=true)
Definition mesh.cpp:10218
int NumOfFaces
Definition mesh.hpp:85
int spaceDim
Definition mesh.hpp:82
virtual void SetNodalFESpace(FiniteElementSpace *nfes)
Definition mesh.cpp:7153
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1393
long long GetGlobalNE() const
Return the total (global) number of elements.
Definition mesh.hpp:1419
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition mesh.cpp:3766
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:14316
int own_nodes
Definition mesh.hpp:273
bool IsSlaveFace(const FaceInfo &fi) const
Definition mesh.cpp:1324
void FreeElement(Element *E)
Definition mesh.cpp:14291
Array< Element * > boundary
Definition mesh.hpp:111
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:318
void NewNodes(GridFunction &nodes, bool make_owner=false)
Replace the internal node GridFunction with the given GridFunction.
Definition mesh.cpp:10139
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition mesh.hpp:2389
STable3D * GetFacesTable()
Definition mesh.cpp:8933
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition mesh.cpp:5050
FaceElementTransformations FaceElemTr
Definition mesh.hpp:263
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:8526
int NumOfEdges
Definition mesh.hpp:85
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1569
Operation last_operation
Definition mesh.hpp:334
Table * GetVertexToElementTable()
Definition mesh.cpp:8218
void InitRefinementTransforms()
Definition mesh.cpp:12215
void UpdateJacobianDeterminantGF(GridFunction &detgf) const
Update Jacobian determinant values in a given gridfunction.
Definition mesh.cpp:7260
int * GeneratePartitioning(int nparts, int part_method=1)
Definition mesh.cpp:9232
Array< Element * > elements
Definition mesh.hpp:105
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition mesh.hpp:307
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1429
void OnMeshUpdated(Mesh *mesh)
Definition ncmesh.cpp:2887
bool using_scaling
Definition ncmesh.hpp:686
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:526
Class for standard nodal finite elements.
Definition fe_base.hpp:798
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
Abstract parallel finite element space.
Definition pfespace.hpp:31
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
Definition pfespace.cpp:739
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
Definition pfespace.cpp:715
int GetTrueVSize() const override
Return the number of local vector true dofs.
Definition pfespace.hpp:365
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:647
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
Definition pfespace.cpp:764
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:593
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:620
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:35
Mesh GetSerialMesh(int save_rank) const
Definition pmesh.cpp:5551
void GetCharacteristics(real_t &h_min, real_t &h_max, real_t &kappa_min, real_t &kappa_max)
Definition pmesh.cpp:6439
void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0) override
This function is not public anymore. Use GeneralRefinement instead.
Definition pmesh.cpp:3936
int GroupNQuadrilaterals(int group) const
Definition pmesh.hpp:478
ElementTransformation * GetFaceNbrElementTransformation(int FaceNo)
Returns a pointer to the transformation defining the i-th face neighbor.
Definition pmesh.cpp:3127
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:467
Array< int > face_nbr_vertices_offset
Definition pmesh.hpp:463
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:4617
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:403
void PrintXG(std::ostream &out=mfem::out) const override
Definition pmesh.cpp:4641
Array< Element * > shared_edges
Definition pmesh.hpp:70
int GetMyRank() const
Definition pmesh.hpp:405
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:4108
void ParPrint(std::ostream &out, const std::string &comments="") const
Definition pmesh.cpp:6579
friend class ParNCMesh
Definition pmesh.hpp:36
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:3994
int GetNSharedFaces() const
Return the number of shared faces (3D), edges (2D), vertices (1D)
Definition pmesh.cpp:3193
Array< int > sface_lface
Definition pmesh.hpp:87
void SaveAsOne(const std::string &fname, int precision=16) const
Definition pmesh.cpp:5874
void ExchangeFaceNbrData()
Definition pmesh.cpp:2098
Table group_sedge
Definition pmesh.hpp:79
int GetNRanks() const
Definition pmesh.hpp:404
void BuildEdgeGroup(int ngroups, const Table &edge_element)
Definition pmesh.cpp:679
Table group_svert
Shared objects in each group.
Definition pmesh.hpp:78
MFEM_DEPRECATED void ReorientTetMesh() override
See the remarks for the serial version in mesh.hpp.
Definition pmesh.cpp:3266
int GroupVertex(int group, int i) const
Accessors for entities within a shared group structure.
Definition pmesh.hpp:491
void UniformRefinement2D() override
Refine a mixed 2D mesh uniformly.
Definition pmesh.cpp:4565
bool WantSkipSharedMaster(const NCMesh::Master &master) const
Definition pmesh.cpp:4843
FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31) override
Definition pmesh.cpp:2926
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
void FindInterface(Array< int > &interface) const
Definition pmesh.cpp:5096
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:3929
Table * GetFaceToAllElementTable() const
Definition pmesh.cpp:2868
virtual ~ParMesh()
Definition pmesh.cpp:7125
void ReduceMeshGen()
Definition pmesh.cpp:891
void GetGlobalElementIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are supported.
Definition pmesh.cpp:6934
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:2706
void GetGhostFaceTransformation(int FaceNo, FaceElementTransformations &FElTr) const
Definition pmesh.cpp:3085
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:96
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:6456
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:3234
bool FaceIsTrueInterior(int FaceNo) const
Definition mesh.hpp:618
void GetGlobalVertexIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition pmesh.cpp:6867
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:3425
void GetFaceNbrElementFaces(int i, Array< int > &faces, Array< int > &orientation) const
Definition pmesh.cpp:2843
long glob_offset_sequence
Definition pmesh.hpp:97
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:7011
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:6731
void GetSharedQuadCommunicator(int ordering, GroupCommunicator &squad_comm) const
Get the shared face quadrilaterals GroupCommunicator.
Definition pmesh.cpp:1704
void BuildFaceNbrElementToFaceTable()
Definition pmesh.cpp:2739
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:6906
Array< Vertex > face_nbr_vertices
Definition pmesh.hpp:465
void UniformRefineGroups2D(int old_nv)
Definition pmesh.cpp:4365
void GetGlobalEdgeIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Definition pmesh.cpp:6883
void ExchangeFaceNbrNodes()
Definition pmesh.cpp:2581
void SetNodalFESpace(FiniteElementSpace *nfes) override
Definition pmesh.cpp:2057
void UniformRefinement3D() override
Refine a mixed 3D mesh uniformly.
Definition pmesh.cpp:4589
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:6663
void Rebalance()
Definition pmesh.cpp:4056
bool have_face_nbr_data
Definition pmesh.hpp:460
long long ReduceInt(int value) const override
Utility function: sum integers from all processors (Allreduce).
Definition pmesh.cpp:6572
Table send_face_nbr_vertices
Definition pmesh.hpp:468
ParMesh()
Default constructor. Create an empty ParMesh.
Definition pmesh.hpp:335
void GetExteriorFaceMarker(Array< int > &face_marker) const override
Populate a marker array identifying exterior faces.
Definition pmesh.cpp:6948
bool print_interfaces
Definition pmesh.hpp:104
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:46
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:75
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:81
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:84
Array< int > sedge_ledge
Definition pmesh.hpp:85
Array< Element * > face_nbr_elements
Definition pmesh.hpp:464
GroupTopology gtopo
Definition pmesh.hpp:457
void Destroy()
Definition pmesh.cpp:7109
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:2971
void PrintSharedEntities(const std::string &fname_prefix) const
Debugging method.
Definition pmesh.cpp:6780
void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1, int pyrtype=1) override
Set the curvature of the mesh nodes using the given polynomial degree.
Definition pmesh.cpp:2034
int GroupNTriangles(int group) const
Definition pmesh.hpp:477
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:3212
void EnsureParNodes()
If the mesh is curved, make sure 'Nodes' is ParGridFunction.
Definition pmesh.cpp:2078
int GroupNEdges(int group) const
Definition pmesh.hpp:476
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:461
Array< int > face_nbr_elements_offset
Definition pmesh.hpp:462
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
std::unique_ptr< ParGridFunction > GetJacobianDeterminantGF() const
Create a ParGridFunction representing the Jacobian determinant. Parallel counterpart of Mesh::GetJaco...
Definition pmesh.cpp:2017
int GetNFaceNeighbors() const
Definition pmesh.hpp:578
void UniformRefineGroups3D(int old_nv, int old_nedges, const DSTable &old_v_to_v, const STable3D &old_faces, Array< int > *f2qf)
Definition pmesh.cpp:4416
void RebalanceImpl(const Array< int > *partition)
Definition pmesh.cpp:4066
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:5885
std::unique_ptr< Table > face_nbr_el_to_face
Definition pmesh.hpp:91
Array< Vert3 > shared_trias
Definition pmesh.hpp:74
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:5067
void DistributeAttributes(Array< int > &attr)
Ensure that bdr_attributes and attributes agree across processors.
Definition pmesh.cpp:1557
STable3D * GetSharedFacesTable()
Definition pmesh.cpp:2641
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:472
real_t GetFaceNbrElementSize(int i, int type=0)
Definition pmesh.cpp:3188
ParNCMesh * pncmesh
Definition pmesh.hpp:470
void UnmarkInternalBoundaries(Array< int > &bdr_marker, bool excl=true) const override
Unmark boundary attributes of internal boundaries.
Definition pmesh.cpp:6955
std::unique_ptr< Table > face_nbr_el_ori
orientations for each face (from nbr processor)
Definition pmesh.hpp:93
int GetFaceNbrRank(int fn) const
Definition pmesh.cpp:2825
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:2952
void PrintAsSerial(std::ostream &out=mfem::out, const std::string &comments="") const
Definition pmesh.cpp:5540
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:80
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:6419
void PrintAsOne(std::ostream &out=mfem::out, const std::string &comments="") const
Write the mesh to the stream 'out' on Process 0 in a form suitable for visualization.
Definition pmesh.cpp:5131
void Print(std::ostream &out=mfem::out, const std::string &comments="") const override
Definition pmesh.cpp:4856
void RefineNURBSWithKVFactors(int rf, const std::string &kvf) override
Definition pmesh.cpp:4633
void Swap(ParMesh &other)
Definition pmesh.cpp:7064
void GroupEdge(int group, int i, int &edge, int &o) const
Definition pmesh.cpp:1626
int GroupNVertices(int group) const
Definition pmesh.hpp:475
A parallel extension of the NCMesh class.
Definition pncmesh.hpp:63
bool AnisotropicConflict(const Array< Refinement > &refinements, std::set< int > &conflicts)
Definition pncmesh.cpp:1556
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:1036
void LimitNCLevel(int max_nc_level) override
Parallel version of NCMesh::LimitNCLevel.
Definition pncmesh.cpp:2162
void Refine(const Array< Refinement > &refinements) override
Definition pncmesh.cpp:2081
void GetConformingSharedStructures(class ParMesh &pmesh)
Definition pncmesh.cpp:935
void Derefine(const Array< int > &derefs) override
Definition pncmesh.cpp:2211
void Rebalance(const Array< int > *custom_partition=NULL)
Definition pncmesh.cpp:2523
void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level) override
Definition pncmesh.cpp:2465
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:2383
Parallel version of NURBSExtension.
Definition nurbs.hpp:1148
Data type quadrilateral element.
Arbitrary order H(div)-conforming Raviart-Thomas finite elements.
Definition fe_coll.hpp:430
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
Data type triangle element.
Definition triangle.hpp:24
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:633
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:3282
MFEM_HOST_DEVICE int FlipIndexSign(int i)
Signed indices i -> -1 - i are used as a convention to encode orientation.
Definition globals.hpp:117
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:767
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:96
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:453
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
STL namespace.
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:179
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