MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh.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// Implementation of data type mesh
13
14#include "mesh_headers.hpp"
15#include "vtkhdf.hpp"
16#include "../fem/fem.hpp"
19#include "../general/text.hpp"
20#include "../general/device.hpp"
22#include "../general/gecko.hpp"
23#include "../general/kdtree.hpp"
24#include "../general/sets.hpp"
26
27// headers already included by mesh.hpp: <iostream>, <array>, <map>, <memory>
28#include <sstream>
29#include <fstream>
30#include <limits>
31#include <cmath>
32#include <cstring>
33#include <ctime>
34#include <functional>
35#include <set>
36#include <numeric>
37#include <unordered_map>
38#include <unordered_set>
39#include <list>
40
41// Include the METIS header, if using version 5. If using METIS 4, the needed
42// declarations are inlined below, i.e. no header is needed.
43#if defined(MFEM_USE_METIS) && defined(MFEM_USE_METIS_5)
44#include "metis.h"
45#endif
46
47// METIS 4 prototypes
48#if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
49typedef int idx_t;
50typedef int idxtype;
51extern "C" {
53 int*, int*, int*, int*, int*, idxtype*);
55 int*, int*, int*, int*, int*, idxtype*);
57 int*, int*, int*, int*, int*, idxtype*);
58}
59#endif
60
61using namespace std;
62
63namespace mfem
64{
65
67{
70 if (ip == NULL)
71 {
72 eltransf->SetIntPoint(&Geometries.GetCenter(geom));
73 }
74 else
75 {
76 eltransf->SetIntPoint(ip);
77 }
78 Geometries.JacToPerfJac(geom, eltransf->Jacobian(), J);
79}
80
81void Mesh::GetElementCenter(int i, Vector &center)
82{
83 center.SetSize(spaceDim);
84 int geom = GetElementBaseGeometry(i);
86 eltransf->Transform(Geometries.GetCenter(geom), center);
87}
88
90{
92
95 Geometries.JacToPerfJac(geom, T->Jacobian(), J);
96
97 if (type == 0)
98 {
99 return pow(fabs(J.Weight()), 1./Dim);
100 }
101 else if (type == 1)
102 {
103 return J.CalcSingularvalue(Dim-1); // h_min
104 }
105 else
106 {
107 return J.CalcSingularvalue(0); // h_max
108 }
109}
110
112{
114}
115
117{
119 Vector d_hat(Dim);
120 GetElementJacobian(i, J);
121 J.MultTranspose(dir, d_hat);
122 return sqrt((d_hat * d_hat) / (dir * dir));
123}
124
126{
129 et->OrderJ());
130 real_t volume = 0.0;
131 for (int j = 0; j < ir.GetNPoints(); j++)
132 {
133 const IntegrationPoint &ip = ir.IntPoint(j);
134 et->SetIntPoint(&ip);
135 volume += ip.weight * et->Weight();
136 }
137
138 return volume;
139}
140
141// Similar to VisualizationSceneSolution3d::FindNewBox in GLVis
142void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
143{
144 min.SetSize(spaceDim);
145 max.SetSize(spaceDim);
146
147 for (int d = 0; d < spaceDim; d++)
148 {
149 min(d) = infinity();
150 max(d) = -infinity();
151 }
152
153 if (Nodes == NULL)
154 {
155 real_t *coord;
156 for (int i = 0; i < NumOfVertices; i++)
157 {
158 coord = GetVertex(i);
159 for (int d = 0; d < spaceDim; d++)
160 {
161 if (coord[d] < min(d)) { min(d) = coord[d]; }
162 if (coord[d] > max(d)) { max(d) = coord[d]; }
163 }
164 }
165 }
166 else
167 {
168 const bool use_boundary = false; // make this a parameter?
169 int ne = use_boundary ? GetNBE() : GetNE();
170 int fn, fo;
171 DenseMatrix pointmat;
172 RefinedGeometry *RefG;
173 IntegrationRule eir;
176
177 for (int i = 0; i < ne; i++)
178 {
179 if (use_boundary)
180 {
181 GetBdrElementFace(i, &fn, &fo);
184 eir.SetSize(RefG->RefPts.GetNPoints());
185 Tr->Loc1.Transform(RefG->RefPts, eir);
186 Tr->Elem1->Transform(eir, pointmat);
187 }
188 else
189 {
192 T->Transform(RefG->RefPts, pointmat);
193 }
194 for (int j = 0; j < pointmat.Width(); j++)
195 {
196 for (int d = 0; d < pointmat.Height(); d++)
197 {
198 if (pointmat(d,j) < min(d)) { min(d) = pointmat(d,j); }
199 if (pointmat(d,j) > max(d)) { max(d) = pointmat(d,j); }
200 }
201 }
202 }
203 }
204}
205
207 real_t &kappa_min, real_t &kappa_max,
208 Vector *Vh, Vector *Vk)
209{
210 int i, dim, sdim;
211 DenseMatrix J;
212 real_t h, kappa;
213
214 dim = Dimension();
215 sdim = SpaceDimension();
216
217 if (Vh) { Vh->SetSize(NumOfElements); }
218 if (Vk) { Vk->SetSize(NumOfElements); }
219
220 h_min = kappa_min = infinity();
221 h_max = kappa_max = -h_min;
222 if (dim == 0) { if (Vh) { *Vh = 1.0; } if (Vk) {*Vk = 1.0; } return; }
223 J.SetSize(sdim, dim);
224 for (i = 0; i < NumOfElements; i++)
225 {
226 GetElementJacobian(i, J);
227 h = pow(fabs(J.Weight()), 1.0/real_t(dim));
228 kappa = (dim == sdim) ?
229 J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1) : -1.0;
230 if (Vh) { (*Vh)(i) = h; }
231 if (Vk) { (*Vk)(i) = kappa; }
232
233 if (h < h_min) { h_min = h; }
234 if (h > h_max) { h_max = h; }
235 if (kappa < kappa_min) { kappa_min = kappa; }
236 if (kappa > kappa_max) { kappa_max = kappa; }
237 }
238}
239
240// static method
242 const Array<int> &num_elems_by_geom,
243 std::ostream &os)
244{
245 for (int g = Geometry::DimStart[dim], first = 1;
246 g < Geometry::DimStart[dim+1]; g++)
247 {
248 if (!num_elems_by_geom[g]) { continue; }
249 if (!first) { os << " + "; }
250 else { first = 0; }
251 os << num_elems_by_geom[g] << ' ' << Geometry::Name[g] << "(s)";
252 }
253}
254
255void Mesh::PrintCharacteristics(Vector *Vh, Vector *Vk, std::ostream &os)
256{
257 real_t h_min, h_max, kappa_min, kappa_max;
258
259 os << "Mesh Characteristics:";
260
261 this->GetCharacteristics(h_min, h_max, kappa_min, kappa_max, Vh, Vk);
262
263 Array<int> num_elems_by_geom(Geometry::NumGeom);
264 num_elems_by_geom = 0;
265 for (int i = 0; i < GetNE(); i++)
266 {
267 num_elems_by_geom[GetElementBaseGeometry(i)]++;
268 }
269
270 os << '\n'
271 << "Dimension : " << Dimension() << '\n'
272 << "Space dimension : " << SpaceDimension();
273 if (Dim == 0)
274 {
275 os << '\n'
276 << "Number of vertices : " << GetNV() << '\n'
277 << "Number of elements : " << GetNE() << '\n'
278 << "Number of bdr elem : " << GetNBE() << '\n';
279 }
280 else if (Dim == 1)
281 {
282 os << '\n'
283 << "Number of vertices : " << GetNV() << '\n'
284 << "Number of elements : " << GetNE() << '\n'
285 << "Number of bdr elem : " << GetNBE() << '\n'
286 << "h_min : " << h_min << '\n'
287 << "h_max : " << h_max << '\n';
288 }
289 else if (Dim == 2)
290 {
291 os << '\n'
292 << "Number of vertices : " << GetNV() << '\n'
293 << "Number of edges : " << GetNEdges() << '\n'
294 << "Number of elements : " << GetNE() << " -- ";
295 PrintElementsByGeometry(2, num_elems_by_geom, os);
296 os << '\n'
297 << "Number of bdr elem : " << GetNBE() << '\n'
298 << "Euler Number : " << EulerNumber2D() << '\n'
299 << "h_min : " << h_min << '\n'
300 << "h_max : " << h_max << '\n'
301 << "kappa_min : " << kappa_min << '\n'
302 << "kappa_max : " << kappa_max << '\n';
303 }
304 else
305 {
306 Array<int> num_bdr_elems_by_geom(Geometry::NumGeom);
307 num_bdr_elems_by_geom = 0;
308 for (int i = 0; i < GetNBE(); i++)
309 {
310 num_bdr_elems_by_geom[GetBdrElementGeometry(i)]++;
311 }
312 Array<int> num_faces_by_geom(Geometry::NumGeom);
313 num_faces_by_geom = 0;
314 for (int i = 0; i < GetNFaces(); i++)
315 {
316 num_faces_by_geom[GetFaceGeometry(i)]++;
317 }
318
319 os << '\n'
320 << "Number of vertices : " << GetNV() << '\n'
321 << "Number of edges : " << GetNEdges() << '\n'
322 << "Number of faces : " << GetNFaces() << " -- ";
323 PrintElementsByGeometry(Dim-1, num_faces_by_geom, os);
324 os << '\n'
325 << "Number of elements : " << GetNE() << " -- ";
326 PrintElementsByGeometry(Dim, num_elems_by_geom, os);
327 os << '\n'
328 << "Number of bdr elem : " << GetNBE() << " -- ";
329 PrintElementsByGeometry(Dim-1, num_bdr_elems_by_geom, os);
330 os << '\n'
331 << "Euler Number : " << EulerNumber() << '\n'
332 << "h_min : " << h_min << '\n'
333 << "h_max : " << h_max << '\n'
334 << "kappa_min : " << kappa_min << '\n'
335 << "kappa_max : " << kappa_max << '\n';
336 }
337 os << '\n' << std::flush;
338}
339
341{
342 switch (ElemType)
343 {
344 case Element::POINT : return &PointFE;
345 case Element::SEGMENT : return &SegmentFE;
346 case Element::TRIANGLE : return &TriangleFE;
348 case Element::TETRAHEDRON : return &TetrahedronFE;
349 case Element::HEXAHEDRON : return &HexahedronFE;
350 case Element::WEDGE : return &WedgeFE;
351 case Element::PYRAMID : return &PyramidFE;
352 default:
353 MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
354 break;
355 }
356 MFEM_ABORT("Unknown element type");
357 return NULL;
358}
359
360
362 IsoparametricTransformation *ElTr) const
363{
364 ElTr->Attribute = GetAttribute(i);
365 ElTr->ElementNo = i;
367 ElTr->mesh = this;
368 ElTr->Reset();
369 if (Nodes == NULL)
370 {
371 GetPointMatrix(i, ElTr->GetPointMat());
373 }
374 else
375 {
376 DenseMatrix &pm = ElTr->GetPointMat();
377 Array<int> vdofs;
378 Nodes->FESpace()->GetElementVDofs(i, vdofs);
379 Nodes->HostRead();
380 const GridFunction &nodes = *Nodes;
381 int n = vdofs.Size()/spaceDim;
382 pm.SetSize(spaceDim, n);
383 for (int k = 0; k < spaceDim; k++)
384 {
385 for (int j = 0; j < n; j++)
386 {
387 pm(k,j) = nodes(vdofs[n*k+j]);
388 }
389 }
390 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
391 }
392}
393
416
422
424 IsoparametricTransformation *ElTr) const
425{
426 ElTr->Attribute = GetAttribute(i);
427 ElTr->ElementNo = i;
429 ElTr->mesh = this;
430 DenseMatrix &pm = ElTr->GetPointMat();
431 ElTr->Reset();
432 nodes.HostRead();
433 if (Nodes == NULL)
434 {
435 MFEM_ASSERT(nodes.Size() == spaceDim*GetNV(), "");
436 int nv = elements[i]->GetNVertices();
437 const int *v = elements[i]->GetVertices();
438 int n = vertices.Size();
439 pm.SetSize(spaceDim, nv);
440 for (int k = 0; k < spaceDim; k++)
441 {
442 for (int j = 0; j < nv; j++)
443 {
444 pm(k, j) = nodes(k*n+v[j]);
445 }
446 }
448 }
449 else
450 {
451 MFEM_ASSERT(nodes.Size() == Nodes->Size(), "");
452 Array<int> vdofs;
453 Nodes->FESpace()->GetElementVDofs(i, vdofs);
454 int n = vdofs.Size()/spaceDim;
455 pm.SetSize(spaceDim, n);
456 for (int k = 0; k < spaceDim; k++)
457 {
458 for (int j = 0; j < n; j++)
459 {
460 pm(k,j) = nodes(vdofs[n*k+j]);
461 }
462 }
463 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
464 }
465}
466
468 IsoparametricTransformation* ElTr) const
469{
470 ElTr->Attribute = GetBdrAttribute(i);
471 ElTr->ElementNo = i; // boundary element number
473 ElTr->mesh = this;
474 DenseMatrix &pm = ElTr->GetPointMat();
475 ElTr->Reset();
476 if (Nodes == NULL)
477 {
478 GetBdrPointMatrix(i, pm);
480 }
481 else
482 {
483 const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
484 Nodes->HostRead();
485 const GridFunction &nodes = *Nodes;
486 if (bdr_el)
487 {
488 Array<int> vdofs;
489 Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
490 int n = vdofs.Size()/spaceDim;
491 pm.SetSize(spaceDim, n);
492 for (int k = 0; k < spaceDim; k++)
493 {
494 for (int j = 0; j < n; j++)
495 {
496 pm(k,j) = nodes(UnsignIndex(vdofs[n*k+j]));
497 }
498 }
499 ElTr->SetFE(bdr_el);
500 }
501 else // L2 Nodes (e.g., periodic mesh)
502 {
503 int elem_id, face_info;
504 GetBdrElementAdjacentElement(i, elem_id, face_info);
506 face_info = EncodeFaceInfo(
507 DecodeFaceInfoLocalIndex(face_info),
509 face_geom, DecodeFaceInfoOrientation(face_info))
510 );
511
514 GetElementType(elem_id),
515 Loc1.Transf, face_info);
516 const FiniteElement *face_el =
517 Nodes->FESpace()->GetTraceElement(elem_id, face_geom);
518 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
519 "Mesh requires nodal Finite Element.");
520
521 IntegrationRule eir(face_el->GetDof());
522 Loc1.Transf.ElementNo = elem_id;
523 Loc1.Transf.mesh = this;
525 Loc1.Transform(face_el->GetNodes(), eir);
526 Nodes->GetVectorValues(Loc1.Transf, eir, pm);
527
528 ElTr->SetFE(face_el);
529 }
530 }
531}
532
538
541{
542 FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
543 FTr->ElementNo = FaceNo;
545 FTr->mesh = this;
546 DenseMatrix &pm = FTr->GetPointMat();
547 FTr->Reset();
548 if (Nodes == NULL)
549 {
550 const int *v = (Dim == 1) ? &FaceNo : faces[FaceNo]->GetVertices();
551 const int nv = (Dim == 1) ? 1 : faces[FaceNo]->GetNVertices();
552 pm.SetSize(spaceDim, nv);
553 for (int i = 0; i < spaceDim; i++)
554 {
555 for (int j = 0; j < nv; j++)
556 {
557 pm(i, j) = vertices[v[j]](i);
558 }
559 }
561 }
562 else // curved mesh
563 {
564 const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
565 Nodes->HostRead();
566 const GridFunction &nodes = *Nodes;
567 if (face_el)
568 {
569 Array<int> vdofs;
570 Nodes->FESpace()->GetFaceVDofs(FaceNo, vdofs);
571 int n = vdofs.Size()/spaceDim;
572 pm.SetSize(spaceDim, n);
573 for (int i = 0; i < spaceDim; i++)
574 {
575 for (int j = 0; j < n; j++)
576 {
577 pm(i, j) = nodes(vdofs[n*i+j]);
578 }
579 }
580 FTr->SetFE(face_el);
581 }
582 else // L2 Nodes (e.g., periodic mesh), go through the volume of Elem1
583 {
584 const FaceInfo &face_info = faces_info[FaceNo];
585 Geometry::Type face_geom = GetFaceGeometry(FaceNo);
586 Element::Type face_type = GetFaceElementType(FaceNo);
587
590 GetElementType(face_info.Elem1No),
591 Loc1.Transf, face_info.Elem1Inf);
592
593 face_el = Nodes->FESpace()->GetTraceElement(face_info.Elem1No,
594 face_geom);
595 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
596 "Mesh requires nodal Finite Element.");
597
598 IntegrationRule eir(face_el->GetDof());
599 Loc1.Transf.ElementNo = face_info.Elem1No;
601 Loc1.Transf.mesh = this;
602 Loc1.Transform(face_el->GetNodes(), eir);
603 Nodes->GetVectorValues(Loc1.Transf, eir, pm);
604
605 FTr->SetFE(face_el);
606 }
607 }
608}
609
615
617 IsoparametricTransformation *EdTr) const
618{
619 if (Dim == 2)
620 {
621 GetFaceTransformation(EdgeNo, EdTr);
622 return;
623 }
624 if (Dim == 1)
625 {
626 mfem_error("Mesh::GetEdgeTransformation not defined in 1D \n");
627 }
628
629 EdTr->Attribute = 1;
630 EdTr->ElementNo = EdgeNo;
632 EdTr->mesh = this;
633 DenseMatrix &pm = EdTr->GetPointMat();
634 EdTr->Reset();
635 if (Nodes == NULL)
636 {
637 Array<int> v;
638 GetEdgeVertices(EdgeNo, v);
639 const int nv = 2;
640 pm.SetSize(spaceDim, nv);
641 for (int i = 0; i < spaceDim; i++)
642 {
643 for (int j = 0; j < nv; j++)
644 {
645 pm(i, j) = vertices[v[j]](i);
646 }
647 }
649 }
650 else
651 {
652 const FiniteElement *edge_el = Nodes->FESpace()->GetEdgeElement(EdgeNo);
653 Nodes->HostRead();
654 const GridFunction &nodes = *Nodes;
655 if (edge_el)
656 {
657 Array<int> vdofs;
658 Nodes->FESpace()->GetEdgeVDofs(EdgeNo, vdofs);
659 int n = vdofs.Size()/spaceDim;
660 pm.SetSize(spaceDim, n);
661 for (int i = 0; i < spaceDim; i++)
662 {
663 for (int j = 0; j < n; j++)
664 {
665 pm(i, j) = nodes(vdofs[n*i+j]);
666 }
667 }
668 EdTr->SetFE(edge_el);
669 }
670 else // L2 Nodes (e.g., periodic mesh), go through the face containing the edge
671 {
672 // Search for a face that contains this edge
674
675 Array<int> faces_e;
676 edge_face->GetRow(EdgeNo, faces_e);
677
678 MFEM_VERIFY(faces_e.Size() > 0, "Edge not found in any face!");
679 const int face_no = faces_e[0];
680
681 // Get edge local index and orientation
682 Array<int> edges_f, oris_f;
683 GetFaceEdges(face_no, edges_f, oris_f);
684 const int local_idx = edges_f.Find(EdgeNo);
685 MFEM_ASSERT(local_idx >= 0, "Edge not found on the face!");
686 const int edge_ori = oris_f[local_idx] > 0 ? 0 : 1;
687
688 // Get face information
689 const FaceInfo &face_info = faces_info[face_no];
690
691 // Get transformation from face to edge
693 int edge_info = EncodeFaceInfo(local_idx, edge_ori);
694 Element::Type face_type = GetFaceElementType(face_no);
695
696 switch (face_type)
697 {
699 GetLocalSegToTriTransformation(LocEdge.Transf, edge_info);
700 break;
702 GetLocalSegToQuadTransformation(LocEdge.Transf, edge_info);
703 break;
704 default:
705 MFEM_ABORT("Unsupported face type for edge transformation!");
706 }
707
708 // Get edge element
709 const int order = Nodes->FESpace()->GetElementOrder(face_info.Elem1No);
710 const L2_FECollection *l2_fec = dynamic_cast<const L2_FECollection*>
711 (Nodes->FESpace()->FEColl());
712 if (l2_fec)
713 {
714 // L2 elements do not have a defined trace space
715 if (!EdgeTransfElement || EdgeTransfElement->GetOrder() != order
716 || EdgeTransfElement->GetBasisType() != l2_fec->GetBasisType())
717 {
718 EdgeTransfElement = make_unique<L2_SegmentElement>(
719 order, l2_fec->GetBasisType());
720 }
721 edge_el = EdgeTransfElement.get();
722 }
723 else
724 {
725 MFEM_ABORT("Unsupported finite element collection.");
726 }
727
728 // Map edge nodes to face reference space
729 IntegrationRule face_ir(edge_el->GetDof());
730 LocEdge.Transform(edge_el->GetNodes(), face_ir);
731
732 // Then, map from face to element
735 GetElementType(face_info.Elem1No),
736 Loc1.Transf, face_info.Elem1Inf);
737
738 IntegrationRule elem_ir(edge_el->GetDof());
739 Loc1.Transf.ElementNo = face_info.Elem1No;
741 Loc1.Transf.mesh = this;
742 Loc1.Transform(face_ir, elem_ir);
743
744 // Finally, get the physical coordinates
745 Nodes->GetVectorValues(Loc1.Transf, elem_ir, pm);
746
747 EdTr->SetFE(edge_el);
748 }
749 }
750}
751
757
758
760 IsoparametricTransformation &Transf, int i) const
761{
762 const IntegrationRule *SegVert;
763 DenseMatrix &locpm = Transf.GetPointMat();
764 Transf.Reset();
765
766 Transf.SetFE(&PointFE);
768 locpm.SetSize(1, 1);
769 locpm(0, 0) = SegVert->IntPoint(i/64).x;
770 // (i/64) is the local face no. in the segment
771 // (i%64) is the orientation of the point (not used)
772}
773
775 IsoparametricTransformation &Transf, int i) const
776{
777 const int *tv, *so;
778 const IntegrationRule *TriVert;
779 DenseMatrix &locpm = Transf.GetPointMat();
780 Transf.Reset();
781
782 Transf.SetFE(&SegmentFE);
783 tv = tri_t::Edges[i/64]; // (i/64) is the local face no. in the triangle
784 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
786 locpm.SetSize(2, 2);
787 for (int j = 0; j < 2; j++)
788 {
789 locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
790 locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
791 }
792}
793
795 IsoparametricTransformation &Transf, int i) const
796{
797 const int *qv, *so;
798 const IntegrationRule *QuadVert;
799 DenseMatrix &locpm = Transf.GetPointMat();
800 Transf.Reset();
801
802 Transf.SetFE(&SegmentFE);
803 qv = quad_t::Edges[i/64]; // (i/64) is the local face no. in the quad
804 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
806 locpm.SetSize(2, 2);
807 for (int j = 0; j < 2; j++)
808 {
809 locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
810 locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
811 }
812}
813
815 IsoparametricTransformation &Transf, int i) const
816{
817 DenseMatrix &locpm = Transf.GetPointMat();
818 Transf.Reset();
819
820 Transf.SetFE(&TriangleFE);
821 // (i/64) is the local face no. in the tet
822 const int *tv = tet_t::FaceVert[i/64];
823 // (i%64) is the orientation of the tetrahedron face
824 // w.r.t. the face element
825 const int *to = tri_t::Orient[i%64];
826 const IntegrationRule *TetVert =
828 locpm.SetSize(3, 3);
829 for (int j = 0; j < 3; j++)
830 {
831 const IntegrationPoint &vert = TetVert->IntPoint(tv[to[j]]);
832 locpm(0, j) = vert.x;
833 locpm(1, j) = vert.y;
834 locpm(2, j) = vert.z;
835 }
836}
837
839 IsoparametricTransformation &Transf, int i) const
840{
841 DenseMatrix &locpm = Transf.GetPointMat();
842 Transf.Reset();
843
844 Transf.SetFE(&TriangleFE);
845 // (i/64) is the local face no. in the pri
846 MFEM_VERIFY(i < 128, "Local face index " << i/64
847 << " is not a triangular face of a wedge.");
848 const int *pv = pri_t::FaceVert[i/64];
849 // (i%64) is the orientation of the wedge face
850 // w.r.t. the face element
851 const int *to = tri_t::Orient[i%64];
852 const IntegrationRule *PriVert =
854 locpm.SetSize(3, 3);
855 for (int j = 0; j < 3; j++)
856 {
857 const IntegrationPoint &vert = PriVert->IntPoint(pv[to[j]]);
858 locpm(0, j) = vert.x;
859 locpm(1, j) = vert.y;
860 locpm(2, j) = vert.z;
861 }
862}
863
865 IsoparametricTransformation &Transf, int i) const
866{
867 DenseMatrix &locpm = Transf.GetPointMat();
868
869 Transf.SetFE(&TriangleFE);
870 // (i/64) is the local face no. in the pyr
871 MFEM_VERIFY(i >= 64, "Local face index " << i/64
872 << " is not a triangular face of a pyramid.");
873 const int *pv = pyr_t::FaceVert[i/64];
874 // (i%64) is the orientation of the pyramid face
875 // w.r.t. the face element
876 const int *to = tri_t::Orient[i%64];
877 const IntegrationRule *PyrVert =
879 locpm.SetSize(3, 3);
880 for (int j = 0; j < 3; j++)
881 {
882 const IntegrationPoint &vert = PyrVert->IntPoint(pv[to[j]]);
883 locpm(0, j) = vert.x;
884 locpm(1, j) = vert.y;
885 locpm(2, j) = vert.z;
886 }
887}
888
890 IsoparametricTransformation &Transf, int i) const
891{
892 DenseMatrix &locpm = Transf.GetPointMat();
893 Transf.Reset();
894
895 Transf.SetFE(&QuadrilateralFE);
896 // (i/64) is the local face no. in the hex
897 const int *hv = hex_t::FaceVert[i/64];
898 // (i%64) is the orientation of the quad
899 const int *qo = quad_t::Orient[i%64];
901 locpm.SetSize(3, 4);
902 for (int j = 0; j < 4; j++)
903 {
904 const IntegrationPoint &vert = HexVert->IntPoint(hv[qo[j]]);
905 locpm(0, j) = vert.x;
906 locpm(1, j) = vert.y;
907 locpm(2, j) = vert.z;
908 }
909}
910
912 IsoparametricTransformation &Transf, int i) const
913{
914 DenseMatrix &locpm = Transf.GetPointMat();
915 Transf.Reset();
916
917 Transf.SetFE(&QuadrilateralFE);
918 // (i/64) is the local face no. in the pri
919 MFEM_VERIFY(i >= 128, "Local face index " << i/64
920 << " is not a quadrilateral face of a wedge.");
921 const int *pv = pri_t::FaceVert[i/64];
922 // (i%64) is the orientation of the quad
923 const int *qo = quad_t::Orient[i%64];
925 locpm.SetSize(3, 4);
926 for (int j = 0; j < 4; j++)
927 {
928 const IntegrationPoint &vert = PriVert->IntPoint(pv[qo[j]]);
929 locpm(0, j) = vert.x;
930 locpm(1, j) = vert.y;
931 locpm(2, j) = vert.z;
932 }
933}
934
936 IsoparametricTransformation &Transf, int i) const
937{
938 DenseMatrix &locpm = Transf.GetPointMat();
939
940 Transf.SetFE(&QuadrilateralFE);
941 // (i/64) is the local face no. in the pyr
942 MFEM_VERIFY(i < 64, "Local face index " << i/64
943 << " is not a quadrilateral face of a pyramid.");
944 const int *pv = pyr_t::FaceVert[i/64];
945 // (i%64) is the orientation of the quad
946 const int *qo = quad_t::Orient[i%64];
948 locpm.SetSize(3, 4);
949 for (int j = 0; j < 4; j++)
950 {
951 const IntegrationPoint &vert = PyrVert->IntPoint(pv[qo[j]]);
952 locpm(0, j) = vert.x;
953 locpm(1, j) = vert.y;
954 locpm(2, j) = vert.z;
955 }
956}
957
959 const int flags,
960 MemoryType d_mt)
961{
962 for (int i = 0; i < geom_factors.Size(); i++)
963 {
965 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags)
966 {
967 return gf;
968 }
969 }
970
971 this->EnsureNodes();
972
973 GeometricFactors *gf = new GeometricFactors(this, ir, flags, d_mt);
974 geom_factors.Append(gf);
975 return gf;
976}
977
979 const IntegrationRule& ir,
980 const int flags, FaceType type, MemoryType d_mt)
981{
982 for (int i = 0; i < face_geom_factors.Size(); i++)
983 {
985 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags &&
986 gf->type==type)
987 {
988 return gf;
989 }
990 }
991
992 this->EnsureNodes();
993
994 FaceGeometricFactors *gf = new FaceGeometricFactors(this, ir, flags, type,
995 d_mt);
996 face_geom_factors.Append(gf);
997 return gf;
998}
999
1001{
1002 if (bdr_face_attrs_cache.Size() == 0)
1003 {
1004 std::unordered_map<int, int> f_to_be;
1005 for (int i = 0; i < GetNBE(); ++i)
1006 {
1007 const int f = GetBdrElementFaceIndex(i);
1008 f_to_be[f] = i;
1009 }
1010 const int nf_bdr = GetNFbyType(FaceType::Boundary);
1011 // MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
1013 int f_ind = 0;
1014 const int nf = GetNumFaces();
1015 for (int f = 0; f < nf; ++f)
1016 {
1017 if (!GetFaceInformation(f).IsOfFaceType(FaceType::Boundary))
1018 {
1019 continue;
1020 }
1021 int attribute = -1; // default value
1022 auto iter = f_to_be.find(f);
1023 if (iter != f_to_be.end())
1024 {
1025 const int be = iter->second;
1026 attribute = GetBdrAttribute(be);
1027 }
1028 else
1029 {
1030 // If a boundary face does not correspond to the a boundary element,
1031 // we assign it the default attribute of -1.
1032 }
1033 bdr_face_attrs_cache[f_ind] = attribute;
1034 ++f_ind;
1035 }
1036 }
1037 return bdr_face_attrs_cache;
1038}
1039
1041{
1042 if (elem_attrs_cache.Size() == 0)
1043 {
1044 // re-compute cache
1047 for (int i = 0; i < GetNE(); ++i)
1048 {
1050 MFEM_ASSERT(elem_attrs_cache[i] > 0,
1051 "Negative attribute on element " << i);
1052 }
1053 }
1054 return elem_attrs_cache;
1055}
1056
1058{
1059 auto &fidcs = face_indices[static_cast<int>(ftype)];
1060 auto &ifidcs = inv_face_indices[static_cast<int>(ftype)];
1061 fidcs.SetSize(GetNFbyType(ftype));
1062 fidcs.HostWrite();
1063 ifidcs.reserve(fidcs.Size());
1064 int f_idx = 0;
1065 for (int i = 0; i < GetNumFacesWithGhost(); ++i)
1066 {
1067 const FaceInformation face = GetFaceInformation(i);
1068 if (face.IsNonconformingCoarse() || !face.IsOfFaceType(ftype))
1069 {
1070 continue;
1071 }
1072 fidcs[f_idx] = i;
1073 ifidcs[i] = f_idx;
1074 ++f_idx;
1075 }
1076}
1077
1079{
1080 if (face_indices[static_cast<int>(ftype)].Size() == 0)
1081 {
1082 ComputeFaceInfo(ftype);
1083 }
1084 return face_indices[static_cast<int>(ftype)];
1085}
1086
1087const std::unordered_map<int, int> &
1089{
1090 if (inv_face_indices[static_cast<int>(ftype)].empty())
1091 {
1092 ComputeFaceInfo(ftype);
1093 }
1094 return inv_face_indices[static_cast<int>(ftype)];
1095}
1096
1098{
1099 for (int i = 0; i < geom_factors.Size(); i++)
1100 {
1101 delete geom_factors[i];
1102 }
1103 geom_factors.SetSize(0);
1104 for (int i = 0; i < face_geom_factors.Size(); i++)
1105 {
1106 delete face_geom_factors[i];
1107 }
1108 face_geom_factors.SetSize(0);
1109
1111}
1112
1113void Mesh::GetLocalFaceTransformation(int face_type, int elem_type,
1115 int info) const
1116{
1117 switch (face_type)
1118 {
1119 case Element::POINT:
1120 GetLocalPtToSegTransformation(Transf, info);
1121 break;
1122
1123 case Element::SEGMENT:
1124 if (elem_type == Element::TRIANGLE)
1125 {
1126 GetLocalSegToTriTransformation(Transf, info);
1127 }
1128 else
1129 {
1130 MFEM_ASSERT(elem_type == Element::QUADRILATERAL, "");
1131 GetLocalSegToQuadTransformation(Transf, info);
1132 }
1133 break;
1134
1135 case Element::TRIANGLE:
1136 if (elem_type == Element::TETRAHEDRON)
1137 {
1138 GetLocalTriToTetTransformation(Transf, info);
1139 }
1140 else if (elem_type == Element::WEDGE)
1141 {
1142 GetLocalTriToWdgTransformation(Transf, info);
1143 }
1144 else if (elem_type == Element::PYRAMID)
1145 {
1146 GetLocalTriToPyrTransformation(Transf, info);
1147 }
1148 else
1149 {
1150 MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
1151 "face type " << face_type
1152 << " and element type " << elem_type << "\n");
1153 }
1154 break;
1155
1157 if (elem_type == Element::HEXAHEDRON)
1158 {
1159 GetLocalQuadToHexTransformation(Transf, info);
1160 }
1161 else if (elem_type == Element::WEDGE)
1162 {
1163 GetLocalQuadToWdgTransformation(Transf, info);
1164 }
1165 else if (elem_type == Element::PYRAMID)
1166 {
1167 GetLocalQuadToPyrTransformation(Transf, info);
1168 }
1169 else
1170 {
1171 MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
1172 "face type " << face_type
1173 << " and element type " << elem_type << "\n");
1174 }
1175 break;
1176 }
1177}
1178
1186
1191 int mask) const
1192{
1193 const FaceInfo &face_info = faces_info[FaceNo];
1194
1195 int cmask = 0;
1196 FElTr.SetConfigurationMask(cmask);
1197 FElTr.Elem1 = NULL;
1198 FElTr.Elem2 = NULL;
1199
1200 // setup the transformation for the first element
1201 FElTr.Elem1No = face_info.Elem1No;
1203 {
1204 GetElementTransformation(FElTr.Elem1No, &ElTr1);
1205 FElTr.Elem1 = &ElTr1;
1206 cmask |= 1;
1207 }
1208
1209 // setup the transformation for the second element
1210 // return NULL in the Elem2 field if there's no second element, i.e.
1211 // the face is on the "boundary"
1212 FElTr.Elem2No = face_info.Elem2No;
1214 FElTr.Elem2No >= 0)
1215 {
1216#ifdef MFEM_DEBUG
1218 { MFEM_ABORT("NURBS mesh not supported!"); }
1219#endif
1220 GetElementTransformation(FElTr.Elem2No, &ElTr2);
1221 FElTr.Elem2 = &ElTr2;
1222 cmask |= 2;
1223 }
1224
1225 // setup the face transformation
1227 {
1228 GetFaceTransformation(FaceNo, &FElTr);
1229 cmask |= 16;
1230 }
1231 else
1232 {
1233 FElTr.SetGeometryType(GetFaceGeometry(FaceNo));
1234 }
1235
1236 // setup Loc1 & Loc2
1237 int face_type = GetFaceElementType(FaceNo);
1239 {
1240 int elem_type = GetElementType(face_info.Elem1No);
1241 GetLocalFaceTransformation(face_type, elem_type,
1242 FElTr.Loc1.Transf, face_info.Elem1Inf);
1243 cmask |= 4;
1244 }
1246 FElTr.Elem2No >= 0)
1247 {
1248 int elem_type = GetElementType(face_info.Elem2No);
1249 GetLocalFaceTransformation(face_type, elem_type,
1250 FElTr.Loc2.Transf, face_info.Elem2Inf);
1251
1252 // NC meshes: prepend slave edge/face transformation to Loc2
1253 if (Nonconforming() && IsSlaveFace(face_info))
1254 {
1255 ApplyLocalSlaveTransformation(FElTr, face_info, false);
1256 }
1257 cmask |= 8;
1258 }
1259
1260 FElTr.SetConfigurationMask(cmask);
1261
1262 // This check can be useful for internal debugging, however it will fail on
1263 // periodic boundary faces, so we keep it disabled in general.
1264#if 0
1265#ifdef MFEM_DEBUG
1266 real_t dist = FElTr.CheckConsistency();
1267 if (dist >= 1e-12)
1268 {
1269 mfem::out << "\nInternal error: face id = " << FaceNo
1270 << ", dist = " << dist << '\n';
1271 FElTr.CheckConsistency(1); // print coordinates
1272 MFEM_ABORT("internal error");
1273 }
1274#endif
1275#endif
1276}
1277
1284
1288 IsoparametricTransformation &ElTr2) const
1289{
1290 if (faces_info[FaceNo].Elem2No < 0)
1291 {
1293 return;
1294 }
1295 GetFaceElementTransformations(FaceNo, FElTr, ElTr1, ElTr2);
1296}
1297
1304
1308 IsoparametricTransformation &ElTr2) const
1309{
1310 // Check if the face is interior, shared, or nonconforming.
1311 int fn = GetBdrElementFaceIndex(BdrElemNo);
1312 if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
1313 {
1315 return;
1316 }
1317 GetFaceElementTransformations(fn, FElTr, ElTr1, ElTr2, 21);
1318 FElTr.Attribute = boundary[BdrElemNo]->GetAttribute();
1319 FElTr.ElementNo = BdrElemNo;
1321 FElTr.mesh = this;
1322}
1323
1324bool Mesh::IsSlaveFace(const FaceInfo &fi) const
1325{
1326 return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
1327}
1328
1330 const FaceInfo &fi, bool is_ghost) const
1331{
1332#ifdef MFEM_THREAD_SAFE
1333 DenseMatrix composition;
1334#else
1335 static DenseMatrix composition;
1336#endif
1337 MFEM_ASSERT(fi.NCFace >= 0, "");
1338 MFEM_ASSERT(nc_faces_info[fi.NCFace].Slave, "internal error");
1339 if (!is_ghost)
1340 {
1341 // side 1 -> child side, side 2 -> parent side
1343 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1344 // In 2D, we need to flip the point matrix since it is aligned with the
1345 // parent side.
1346 if (Dim == 2)
1347 {
1348 // swap points (columns) 0 and 1
1349 std::swap(composition(0,0), composition(0,1));
1350 std::swap(composition(1,0), composition(1,1));
1351 }
1352 LT.SetPointMat(composition);
1353 }
1354 else // is_ghost == true
1355 {
1356 // side 1 -> parent side, side 2 -> child side
1358 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1359 // In 2D, there is no need to flip the point matrix since it is already
1360 // aligned with the parent side, see also ParNCMesh::GetFaceNeighbors.
1361 // In 3D the point matrix was flipped during construction in
1362 // ParNCMesh::GetFaceNeighbors and due to that it is already aligned with
1363 // the parent side.
1364 LT.SetPointMat(composition);
1365 }
1366}
1367
1369{
1370 FaceInformation face;
1371 int e1, e2;
1372 int inf1, inf2;
1373 int ncface;
1374 GetFaceElements(f, &e1, &e2);
1375 GetFaceInfos(f, &inf1, &inf2, &ncface);
1376 face.element[0].index = e1;
1378 face.element[0].orientation = inf1%64;
1379 face.element[0].local_face_id = inf1/64;
1380 face.element[1].local_face_id = inf2/64;
1381 face.ncface = ncface;
1382 face.point_matrix = nullptr;
1383 // The following figures out face.location, face.conformity,
1384 // face.element[1].index, and face.element[1].orientation.
1385 if (f < GetNumFaces()) // Non-ghost face
1386 {
1387 if (e2>=0)
1388 {
1389 if (ncface==-1)
1390 {
1396 face.element[1].index = e2;
1397 face.element[1].orientation = inf2%64;
1398 }
1399 else // ncface >= 0
1400 {
1406 face.element[1].index = e2;
1407 MFEM_ASSERT(inf2%64==0, "unexpected slave face orientation.");
1408 face.element[1].orientation = inf2%64;
1409 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1410 }
1411 }
1412 else // e2<0
1413 {
1414 if (ncface==-1)
1415 {
1416 if (inf2<0)
1417 {
1423 face.element[1].index = -1;
1424 face.element[1].orientation = -1;
1425 }
1426 else // inf2 >= 0
1427 {
1433 face.element[1].index = FlipIndexSign(e2);
1434 face.element[1].orientation = inf2%64;
1435 }
1436 }
1437 else // ncface >= 0
1438 {
1439 if (inf2 < 0)
1440 {
1446 face.element[1].index = -1;
1447 face.element[1].orientation = -1;
1448 }
1449 else
1450 {
1456 face.element[1].index = FlipIndexSign(e2);
1457 face.element[1].orientation = inf2%64;
1458 }
1459 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1460 }
1461 }
1462 }
1463 else // Ghost face
1464 {
1465 if (e1==-1)
1466 {
1472 face.element[1].index = -1;
1473 face.element[1].orientation = -1;
1474 }
1475 else
1476 {
1482 face.element[1].index = FlipIndexSign(e2);
1483 face.element[1].orientation = inf2%64;
1484 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1485 }
1486 }
1487 return face;
1488}
1489
1490Mesh::FaceInformation::operator Mesh::FaceInfo() const
1491{
1492 FaceInfo res {-1, -1, -1, -1, -1};
1493 switch (tag)
1494 {
1496 res.Elem1No = element[0].index;
1497 res.Elem2No = element[1].index;
1498 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1499 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1500 res.NCFace = ncface;
1501 break;
1503 res.Elem1No = element[0].index;
1504 res.Elem2No = element[1].index;
1505 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1506 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1507 res.NCFace = ncface;
1508 break;
1510 res.Elem1No = element[0].index;
1511 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1512 break;
1514 res.Elem1No = element[0].index;
1515 res.Elem2No = FlipIndexSign(element[1].index);
1516 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1517 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1518 break;
1520 res.Elem1No = element[0].index;
1521 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1522 break;
1524 res.Elem1No = element[0].index;
1525 res.Elem2No = FlipIndexSign(element[1].index);
1526 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1527 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1528 break;
1530 break;
1532 res.Elem1No = element[0].index;
1533 res.Elem2No = FlipIndexSign(element[1].index);
1534 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1535 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1536 break;
1537 }
1538 return res;
1539}
1540
1541std::ostream &operator<<(std::ostream &os, const Mesh::FaceInformation& info)
1542{
1543 os << "face topology=";
1544 switch (info.topology)
1545 {
1547 os << "Boundary";
1548 break;
1550 os << "Conforming";
1551 break;
1553 os << "Non-conforming";
1554 break;
1556 os << "NA";
1557 break;
1558 }
1559 os << '\n';
1560 os << "element[0].location=";
1561 switch (info.element[0].location)
1562 {
1564 os << "Local";
1565 break;
1567 os << "FaceNbr";
1568 break;
1570 os << "NA";
1571 break;
1572 }
1573 os << '\n';
1574 os << "element[1].location=";
1575 switch (info.element[1].location)
1576 {
1578 os << "Local";
1579 break;
1581 os << "FaceNbr";
1582 break;
1584 os << "NA";
1585 break;
1586 }
1587 os << '\n';
1588 os << "element[0].conformity=";
1589 switch (info.element[0].conformity)
1590 {
1592 os << "Coincident";
1593 break;
1595 os << "Superset";
1596 break;
1598 os << "Subset";
1599 break;
1601 os << "NA";
1602 break;
1603 }
1604 os << '\n';
1605 os << "element[1].conformity=";
1606 switch (info.element[1].conformity)
1607 {
1609 os << "Coincident";
1610 break;
1612 os << "Superset";
1613 break;
1615 os << "Subset";
1616 break;
1618 os << "NA";
1619 break;
1620 }
1621 os << '\n';
1622 os << "element[0].index=" << info.element[0].index << '\n'
1623 << "element[1].index=" << info.element[1].index << '\n'
1624 << "element[0].local_face_id=" << info.element[0].local_face_id << '\n'
1625 << "element[1].local_face_id=" << info.element[1].local_face_id << '\n'
1626 << "element[0].orientation=" << info.element[0].orientation << '\n'
1627 << "element[1].orientation=" << info.element[1].orientation << '\n'
1628 << "ncface=" << info.ncface << std::endl;
1629 return os;
1630}
1631
1632void Mesh::GetFaceElements(int Face, int *Elem1, int *Elem2) const
1633{
1634 *Elem1 = faces_info[Face].Elem1No;
1635 *Elem2 = faces_info[Face].Elem2No;
1636}
1637
1638void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
1639{
1640 *Inf1 = faces_info[Face].Elem1Inf;
1641 *Inf2 = faces_info[Face].Elem2Inf;
1642}
1643
1644void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
1645{
1646 *Inf1 = faces_info[Face].Elem1Inf;
1647 *Inf2 = faces_info[Face].Elem2Inf;
1648 *NCFace = faces_info[Face].NCFace;
1649}
1650
1652{
1653 switch (Dim)
1654 {
1655 case 1: return Geometry::POINT;
1656 case 2: return Geometry::SEGMENT;
1657 case 3:
1658 if (Face < NumOfFaces) // local (non-ghost) face
1659 {
1660 return faces[Face]->GetGeometryType();
1661 }
1662 // ghost face
1663 const int nc_face_id = faces_info[Face].NCFace;
1664
1665 MFEM_ASSERT(nc_face_id >= 0, "parent ghost faces are not supported");
1666 return faces[nc_faces_info[nc_face_id].MasterFace]->GetGeometryType();
1667 }
1668 return Geometry::INVALID;
1669}
1670
1672{
1674 switch (elem_geom)
1675 {
1680 case Geometry::CUBE: return Geometry::SQUARE;
1683 default: return Geometry::INVALID;
1684 }
1685}
1686
1688{
1689 return (Dim == 1) ? Element::POINT : faces[Face]->GetType();
1690}
1691
1693{
1694 Array<int> face_to_be(Dim == 1 ? NumOfVertices :
1695 Dim == 2 ? NumOfEdges :
1696 Dim == 3 ? NumOfFaces : 0);
1697 face_to_be = -1;
1698 for (int i = 0; i < NumOfBdrElements; i++)
1699 {
1700 face_to_be[GetBdrElementFaceIndex(i)] = i;
1701 }
1702 return face_to_be;
1703}
1704
1706{
1707 if (GetNE() > 0) { return GetElementGeometry(0); }
1708
1709 const int dim = Dimension();
1710 if (dim == 1)
1711 {
1712 return Geometry::SEGMENT;
1713 }
1715 if (dim == 2)
1716 {
1717 geom = ((meshgen & 1) ? Geometry::TRIANGLE :
1719 }
1720 else if (dim == 3)
1721 {
1722 geom = ((meshgen & 1) ? Geometry::TETRAHEDRON :
1723 ((meshgen & 2) ? Geometry::CUBE :
1724 ((meshgen & 4) ? Geometry::PRISM :
1726 }
1727 MFEM_VERIFY(geom != Geometry::INVALID,
1728 "Could not determine a typical element Geometry!");
1729 return geom;
1730}
1731
1732
1734{
1735 const int num_faces = GetNumFaces();
1736
1737 face_marker.SetSize(num_faces);
1738
1739 for (int f = 0; f < num_faces; f++)
1740 {
1741 if (FaceIsTrueInterior(f))
1742 {
1743 face_marker[f] = 0;
1744 }
1745 else
1746 {
1747 face_marker[f] = 1;
1748 }
1749 }
1750}
1751
1752void Mesh::UnmarkInternalBoundaries(Array<int> &bdr_marker, bool excl) const
1753{
1754 const int max_bdr_attr = bdr_attributes.Max();
1755
1756 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1757 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1758
1759 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
1760 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
1761
1762 // Identify attributes which contain interior faces and those which
1763 // contain exterior faces.
1764 for (int be = 0; be < boundary.Size(); be++)
1765 {
1766 const int bea = boundary[be]->GetAttribute();
1767
1768 if (bdr_marker[bea-1] != 0)
1769 {
1770 const int f = be_to_face[be];
1771
1772 if (FaceIsTrueInterior(f))
1773 {
1774 interior_bdr[bea-1] = true;
1775 }
1776 else
1777 {
1778 exterior_bdr[bea-1] = true;
1779 }
1780 }
1781 }
1782
1783 // Unmark attributes which are currently marked, contain interior faces,
1784 // and satisfy the appropriate exclusivity requirement.
1785 for (int b = 0; b < max_bdr_attr; b++)
1786 {
1787 if (bdr_marker[b] != 0 && interior_bdr[b])
1788 {
1789 if (!excl || !exterior_bdr[b])
1790 {
1791 bdr_marker[b] = 0;
1792 }
1793 }
1794 }
1795}
1796
1797void Mesh::UnmarkNamedBoundaries(const std::string &set_name,
1798 Array<int> &bdr_marker) const
1799{
1800 const int max_bdr_attr = bdr_attributes.Max();
1801
1802 MFEM_VERIFY(bdr_attribute_sets.AttributeSetExists(set_name),
1803 "Named set is not defined in this mesh!");
1804 MFEM_VERIFY(bdr_marker.Size() >= bdr_attributes.Max(),
1805 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1806
1808
1809 for (int b = 0; b < max_bdr_attr; b++)
1810 {
1811 if (set_marker[b])
1812 {
1813 bdr_marker[b] = 0;
1814 }
1815 }
1816}
1817
1818void Mesh::MarkExternalBoundaries(Array<int> &bdr_marker, bool excl) const
1819{
1820 const int max_bdr_attr = bdr_attributes.Max();
1821
1822 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1823 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1824
1825 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
1826 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
1827
1828 // Mark boundary attributes containing exterior faces while keeping track of
1829 // those which also contain interior faces.
1830 for (int be = 0; be < boundary.Size(); be++)
1831 {
1832 const int bea = boundary[be]->GetAttribute();
1833
1834 const int f = be_to_face[be];
1835
1836 if (FaceIsTrueInterior(f))
1837 {
1838 interior_bdr[bea-1] = true;
1839 }
1840 else
1841 {
1842 exterior_bdr[bea-1] = true;
1843 }
1844 }
1845
1846 // Mark attributes which were found to contain exterior faces and satisfy
1847 // the appropriate exclusivity requirement.
1848 for (int b = 0; b < max_bdr_attr; b++)
1849 {
1850 if (bdr_marker[b] == 0 && exterior_bdr[b])
1851 {
1852 if (!excl || !interior_bdr[b])
1853 {
1854 bdr_marker[b] = 1;
1855 }
1856 }
1857 }
1858}
1859
1860void Mesh::MarkNamedBoundaries(const std::string &set_name,
1861 Array<int> &bdr_marker) const
1862{
1863 const int max_bdr_attr = bdr_attributes.Max();
1864
1865 MFEM_VERIFY(bdr_attribute_sets.AttributeSetExists(set_name),
1866 "Named set is not defined in this mesh!");
1867 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1868 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1869
1871
1872 for (int b = 0; b < max_bdr_attr; b++)
1873 {
1874 if (set_marker[b])
1875 {
1876 bdr_marker[b] = 1;
1877 }
1878 }
1879}
1880
1882{
1883 // in order of declaration:
1884 Dim = spaceDim = 0;
1885 NumOfVertices = -1;
1887 NumOfEdges = NumOfFaces = 0;
1888 nbInteriorFaces = -1;
1889 nbBoundaryFaces = -1;
1890 meshgen = mesh_geoms = 0;
1891 sequence = 0;
1892 nodes_sequence = 0;
1893 Nodes = NULL;
1894 own_nodes = 1;
1895 NURBSext = NULL;
1896 ncmesh = NULL;
1898}
1899
1901{
1903 face_edge = edge_face = edge_vertex = NULL;
1904 face_to_elem = NULL;
1905}
1906
1908{
1909 Init();
1910 InitTables();
1911}
1912
1914{
1915 delete el_to_edge;
1916 delete el_to_face;
1917 delete el_to_el;
1919
1920 if (Dim == 3)
1921 {
1922 delete bel_to_edge;
1923 }
1924
1925 delete face_edge;
1926 delete edge_face;
1927 delete edge_vertex;
1928
1929 delete face_to_elem;
1930 face_to_elem = NULL;
1931}
1932
1934{
1935 if (own_nodes) { delete Nodes; }
1936
1937 delete ncmesh;
1938
1939 delete NURBSext;
1940
1941 for (int i = 0; i < NumOfElements; i++)
1942 {
1944 }
1945
1946 for (int i = 0; i < NumOfBdrElements; i++)
1947 {
1949 }
1950
1951 for (int i = 0; i < faces.Size(); i++)
1952 {
1953 FreeElement(faces[i]);
1954 }
1955
1956 DestroyTables();
1957}
1958
1960{
1962
1963 elements.DeleteAll();
1964 vertices.DeleteAll();
1965 boundary.DeleteAll();
1966 faces.DeleteAll();
1967 faces_info.DeleteAll();
1968 nc_faces_info.DeleteAll();
1970
1971 // TODO:
1972 // IsoparametricTransformations
1973 // Transformation, Transformation2, BdrTransformation, FaceTransformation,
1974 // EdgeTransformation;
1975 // FaceElementTransformations FaceElemTr;
1976
1978
1979#ifdef MFEM_USE_MEMALLOC
1980 TetMemory.Clear();
1981#endif
1982
1987
1990 // force de-allocation so after this mesh has the smallest memory footprint
1991 // possible
1992 inv_face_indices[0] = std::unordered_map<int, int>();
1993 inv_face_indices[1] = std::unordered_map<int, int>();
1994}
1995
1997{
1998 delete el_to_el; el_to_el = NULL;
1999 delete face_edge; face_edge = NULL;
2000 delete edge_face; edge_face = NULL;
2001 delete face_to_elem; face_to_elem = NULL;
2002 delete edge_vertex; edge_vertex = NULL;
2004 nbInteriorFaces = -1;
2005 nbBoundaryFaces = -1;
2006 // set size to 0 so re-computations can potentially avoid a new allocation
2009
2010 face_indices[0].SetSize(0);
2011 face_indices[1].SetSize(0);
2012 inv_face_indices[0].clear();
2013 inv_face_indices[1].clear();
2014}
2015
2016void Mesh::SetAttributes(bool elem_attrs_changed, bool bdr_face_attrs_changed)
2017{
2018 if (bdr_face_attrs_changed)
2019 {
2020 bdr_face_attrs_cache.SetSize(0); // Invalidate the cache
2021
2022 // Get sorted list of unique boundary element attributes
2023 std::set<int> attribs;
2024 for (int i = 0; i < GetNBE(); i++)
2025 {
2026 attribs.emplace(GetBdrAttribute(i));
2027 }
2028
2029 bdr_attributes.SetSize(attribs.size());
2031 std::copy(attribs.begin(), attribs.end(), bdr_attributes.begin());
2032 if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
2033 {
2034 MFEM_WARNING("Non-positive attributes on the boundary!");
2035 }
2036 }
2037
2038 if (elem_attrs_changed)
2039 {
2040 // Re-compute the attributes cache
2043 // Get sorted list of unique element attributes
2044 std::set<int> attribs(elem_attrs_cache.begin(), elem_attrs_cache.end());
2045 attributes.SetSize(attribs.size());
2047 std::copy(attribs.begin(), attribs.end(), attributes.begin());
2048
2049 if (attributes.Size() > 0 && attributes[0] <= 0)
2050 {
2051 MFEM_WARNING("Non-positive attributes in the domain!");
2052 }
2053 }
2054}
2055
2056void Mesh::InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
2057{
2058 SetEmpty();
2059
2060 Dim = Dim_;
2061 spaceDim = spaceDim_;
2062
2063 NumOfVertices = 0;
2064 vertices.SetSize(NVert); // just allocate space for vertices
2065
2066 NumOfElements = 0;
2067 elements.SetSize(NElem); // just allocate space for Element *
2068
2069 NumOfBdrElements = 0;
2070 boundary.SetSize(NBdrElem); // just allocate space for Element *
2071}
2072
2073template<typename T>
2074static void CheckEnlarge(Array<T> &array, int size)
2075{
2076 if (size >= array.Size()) { array.SetSize(size + 1); }
2077}
2078
2080{
2081 CheckEnlarge(vertices, NumOfVertices);
2083 v[0] = x;
2084 v[1] = y;
2085 v[2] = z;
2086 return NumOfVertices++;
2087}
2088
2089int Mesh::AddVertex(const real_t *coords)
2090{
2091 CheckEnlarge(vertices, NumOfVertices);
2092 vertices[NumOfVertices].SetCoords(spaceDim, coords);
2093 return NumOfVertices++;
2094}
2095
2096int Mesh::AddVertex(const Vector &coords)
2097{
2098 MFEM_ASSERT(coords.Size() >= spaceDim,
2099 "invalid 'coords' size: " << coords.Size());
2100 return AddVertex(coords.GetData());
2101}
2102
2103void Mesh::AddVertexParents(int i, int p1, int p2)
2104{
2105 tmp_vertex_parents.Append(Triple<int, int, int>(i, p1, p2));
2106
2107 // if vertex coordinates are defined, make sure the hanging vertex has the
2108 // correct position
2109 if (i < vertices.Size())
2110 {
2111 real_t *vi = vertices[i](), *vp1 = vertices[p1](), *vp2 = vertices[p2]();
2112 for (int j = 0; j < 3; j++)
2113 {
2114 vi[j] = (vp1[j] + vp2[j]) * 0.5;
2115 }
2116 }
2117}
2118
2119int Mesh::AddVertexAtMeanCenter(const int *vi, int nverts, int dim)
2120{
2121 Vector vii(dim);
2122 vii = 0.0;
2123 for (int i = 0; i < nverts; i++)
2124 {
2125 real_t *vp = vertices[vi[i]]();
2126 for (int j = 0; j < dim; j++)
2127 {
2128 vii(j) += vp[j];
2129 }
2130 }
2131 vii /= nverts;
2132 AddVertex(vii);
2133 return NumOfVertices;
2134}
2135
2136int Mesh::AddSegment(int v1, int v2, int attr)
2137{
2138 CheckEnlarge(elements, NumOfElements);
2139 elements[NumOfElements] = new Segment(v1, v2, attr);
2140 return NumOfElements++;
2141}
2142
2143int Mesh::AddSegment(const int *vi, int attr)
2144{
2145 CheckEnlarge(elements, NumOfElements);
2146 elements[NumOfElements] = new Segment(vi, attr);
2147 return NumOfElements++;
2148}
2149
2150int Mesh::AddTriangle(int v1, int v2, int v3, int attr)
2151{
2152 CheckEnlarge(elements, NumOfElements);
2153 elements[NumOfElements] = new Triangle(v1, v2, v3, attr);
2154 return NumOfElements++;
2155}
2156
2157int Mesh::AddTriangle(const int *vi, int attr)
2158{
2159 CheckEnlarge(elements, NumOfElements);
2160 elements[NumOfElements] = new Triangle(vi, attr);
2161 return NumOfElements++;
2162}
2163
2164int Mesh::AddQuad(int v1, int v2, int v3, int v4, int attr)
2165{
2166 CheckEnlarge(elements, NumOfElements);
2167 elements[NumOfElements] = new Quadrilateral(v1, v2, v3, v4, attr);
2168 return NumOfElements++;
2169}
2170
2171int Mesh::AddQuad(const int *vi, int attr)
2172{
2173 CheckEnlarge(elements, NumOfElements);
2174 elements[NumOfElements] = new Quadrilateral(vi, attr);
2175 return NumOfElements++;
2176}
2177
2178int Mesh::AddTet(int v1, int v2, int v3, int v4, int attr)
2179{
2180 int vi[4] = {v1, v2, v3, v4};
2181 return AddTet(vi, attr);
2182}
2183
2184int Mesh::AddTet(const int *vi, int attr)
2185{
2186 CheckEnlarge(elements, NumOfElements);
2187#ifdef MFEM_USE_MEMALLOC
2188 Tetrahedron *tet;
2189 tet = TetMemory.Alloc();
2190 tet->SetVertices(vi);
2191 tet->SetAttribute(attr);
2192 elements[NumOfElements] = tet;
2193#else
2194 elements[NumOfElements] = new Tetrahedron(vi, attr);
2195#endif
2196 return NumOfElements++;
2197}
2198
2199int Mesh::AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr)
2200{
2201 CheckEnlarge(elements, NumOfElements);
2202 elements[NumOfElements] = new Wedge(v1, v2, v3, v4, v5, v6, attr);
2203 return NumOfElements++;
2204}
2205
2206int Mesh::AddWedge(const int *vi, int attr)
2207{
2208 CheckEnlarge(elements, NumOfElements);
2209 elements[NumOfElements] = new Wedge(vi, attr);
2210 return NumOfElements++;
2211}
2212
2213int Mesh::AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr)
2214{
2215 CheckEnlarge(elements, NumOfElements);
2216 elements[NumOfElements] = new Pyramid(v1, v2, v3, v4, v5, attr);
2217 return NumOfElements++;
2218}
2219
2220int Mesh::AddPyramid(const int *vi, int attr)
2221{
2222 CheckEnlarge(elements, NumOfElements);
2223 elements[NumOfElements] = new Pyramid(vi, attr);
2224 return NumOfElements++;
2225}
2226
2227int Mesh::AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
2228 int attr)
2229{
2230 CheckEnlarge(elements, NumOfElements);
2232 new Hexahedron(v1, v2, v3, v4, v5, v6, v7, v8, attr);
2233 return NumOfElements++;
2234}
2235
2236int Mesh::AddHex(const int *vi, int attr)
2237{
2238 CheckEnlarge(elements, NumOfElements);
2239 elements[NumOfElements] = new Hexahedron(vi, attr);
2240 return NumOfElements++;
2241}
2242
2243void Mesh::AddHexAsTets(const int *vi, int attr)
2244{
2245 static const int hex_to_tet[6][4] =
2246 {
2247 { 0, 1, 2, 6 }, { 0, 5, 1, 6 }, { 0, 4, 5, 6 },
2248 { 0, 2, 3, 6 }, { 0, 3, 7, 6 }, { 0, 7, 4, 6 }
2249 };
2250 int ti[4];
2251
2252 for (int i = 0; i < 6; i++)
2253 {
2254 for (int j = 0; j < 4; j++)
2255 {
2256 ti[j] = vi[hex_to_tet[i][j]];
2257 }
2258 AddTet(ti, attr);
2259 }
2260}
2261
2262void Mesh::AddHexAsWedges(const int *vi, int attr)
2263{
2264 static const int hex_to_wdg[2][6] =
2265 {
2266 { 0, 1, 2, 4, 5, 6 }, { 0, 2, 3, 4, 6, 7 }
2267 };
2268 int ti[6];
2269
2270 for (int i = 0; i < 2; i++)
2271 {
2272 for (int j = 0; j < 6; j++)
2273 {
2274 ti[j] = vi[hex_to_wdg[i][j]];
2275 }
2276 AddWedge(ti, attr);
2277 }
2278}
2279
2280void Mesh::AddHexAsPyramids(const int *vi, int attr)
2281{
2282 static const int hex_to_pyr[6][5] =
2283 {
2284 { 0, 1, 2, 3, 8 }, { 0, 4, 5, 1, 8 }, { 1, 5, 6, 2, 8 },
2285 { 2, 6, 7, 3, 8 }, { 3, 7, 4, 0, 8 }, { 7, 6, 5, 4, 8 }
2286 };
2287 int ti[5];
2288
2289 for (int i = 0; i < 6; i++)
2290 {
2291 for (int j = 0; j < 5; j++)
2292 {
2293 ti[j] = vi[hex_to_pyr[i][j]];
2294 }
2295 AddPyramid(ti, attr);
2296 }
2297}
2298
2299void Mesh::AddQuadAs4TrisWithPoints(int *vi, int attr)
2300{
2301 int num_faces = 4;
2302 static const int quad_to_tri[4][2] =
2303 {
2304 {0, 1}, {1, 2}, {2, 3}, {3, 0}
2305 };
2306
2307 int elem_center_index = AddVertexAtMeanCenter(vi, 4, 2) - 1;
2308
2309 int ti[3];
2310 ti[2] = elem_center_index;
2311 for (int i = 0; i < num_faces; i++)
2312 {
2313 for (int j = 0; j < 2; j++)
2314 {
2315 ti[j] = vi[quad_to_tri[i][j]];
2316 }
2317 AddTri(ti, attr);
2318 }
2319}
2320
2321void Mesh::AddQuadAs5QuadsWithPoints(int *vi, int attr)
2322{
2323 int num_faces = 4;
2324 static const int quad_faces[4][2] =
2325 {
2326 {0, 1}, {1, 2}, {2, 3}, {3, 0}
2327 };
2328
2329 Vector px(4), py(4);
2330 for (int i = 0; i < 4; i++)
2331 {
2332 real_t *vp = vertices[vi[i]]();
2333 px(i) = vp[0];
2334 py(i) = vp[1];
2335 }
2336
2337 int vnew_index[4];
2338 real_t vnew[2];
2339 real_t r = 0.25, s = 0.25;
2340 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2341 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2342 AddVertex(vnew);
2343 vnew_index[0] = NumOfVertices-1;
2344
2345 r = 0.75, s = 0.25;
2346 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2347 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2348 AddVertex(vnew);
2349 vnew_index[1] = NumOfVertices-1;
2350
2351 r = 0.75, s = 0.75;
2352 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2353 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2354 AddVertex(vnew);
2355 vnew_index[2] = NumOfVertices-1;
2356
2357 r = 0.25, s = 0.75;
2358 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2359 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2360 AddVertex(vnew);
2361 vnew_index[3] = NumOfVertices-1;
2362
2363 static const int quad_faces_new[4][2] =
2364 {
2365 { 1, 0}, { 2, 1}, { 3, 2}, { 0, 3}
2366 };
2367
2368 int ti[4];
2369 for (int i = 0; i < num_faces; i++)
2370 {
2371 for (int j = 0; j < 2; j++)
2372 {
2373 ti[j] = vi[quad_faces[i][j]];
2374 ti[j+2] = vnew_index[quad_faces_new[i][j]];
2375 }
2376 AddQuad(ti, attr);
2377 }
2378 AddQuad(vnew_index, attr);
2379}
2380
2382 std::map<std::array<int, 4>, int> &hex_face_verts,
2383 int attr)
2384{
2385 auto get4arraysorted = [](Array<int> v)
2386 {
2387 v.Sort();
2388 return std::array<int, 4> {v[0], v[1], v[2], v[3]};
2389 };
2390
2391 int num_faces = 6;
2392 static const int hex_to_tet[6][4] =
2393 {
2394 { 0, 1, 2, 3 }, { 1, 2, 6, 5 }, { 5, 4, 7, 6},
2395 { 0, 1, 5, 4 }, { 2, 3, 7, 6 }, { 0,3, 7, 4}
2396 };
2397
2398 int elem_center_index = AddVertexAtMeanCenter(vi, 8, 3) - 1;
2399
2400 Array<int> flist(4);
2401
2402 // local vertex indices for each of the 4 edges of the face
2403 static const int tet_face[4][2] =
2404 {
2405 {0, 1}, {1, 2}, {3, 2}, {3, 0}
2406 };
2407
2408 for (int i = 0; i < num_faces; i++)
2409 {
2410 for (int j = 0; j < 4; j++)
2411 {
2412 flist[j] = vi[hex_to_tet[i][j]];
2413 }
2414 int face_center_index;
2415
2416 auto t = get4arraysorted(flist);
2417 auto it = hex_face_verts.find(t);
2418 if (it == hex_face_verts.end())
2419 {
2420 face_center_index = AddVertexAtMeanCenter(flist.GetData(),
2421 flist.Size(), 3) - 1;
2422 hex_face_verts.insert({t, face_center_index});
2423 }
2424 else
2425 {
2426 face_center_index = it->second;
2427 }
2428 int fti[4];
2429 fti[2] = face_center_index;
2430 fti[3] = elem_center_index;
2431 for (int j = 0; j < 4; j++)
2432 {
2433 for (int k = 0; k < 2; k++)
2434 {
2435 fti[k] = flist[tet_face[j][k]];
2436 }
2437 AddTet(fti, attr);
2438 }
2439 }
2440}
2441
2443{
2444 CheckEnlarge(elements, NumOfElements);
2445 elements[NumOfElements] = elem;
2446 return NumOfElements++;
2447}
2448
2450{
2451 CheckEnlarge(boundary, NumOfBdrElements);
2452 boundary[NumOfBdrElements] = elem;
2453 return NumOfBdrElements++;
2454}
2455
2457 const Array<int> &new_be_to_face)
2458{
2459 boundary.Reserve(boundary.Size() + bdr_elems.Size());
2460 MFEM_ASSERT(bdr_elems.Size() == new_be_to_face.Size(), "wrong size");
2461 for (int i = 0; i < bdr_elems.Size(); i++)
2462 {
2463 AddBdrElement(bdr_elems[i]);
2464 }
2465 be_to_face.Append(new_be_to_face);
2466}
2467
2468int Mesh::AddBdrSegment(int v1, int v2, int attr)
2469{
2470 CheckEnlarge(boundary, NumOfBdrElements);
2471 boundary[NumOfBdrElements] = new Segment(v1, v2, attr);
2472 return NumOfBdrElements++;
2473}
2474
2475int Mesh::AddBdrSegment(const int *vi, int attr)
2476{
2477 CheckEnlarge(boundary, NumOfBdrElements);
2478 boundary[NumOfBdrElements] = new Segment(vi, attr);
2479 return NumOfBdrElements++;
2480}
2481
2482int Mesh::AddBdrTriangle(int v1, int v2, int v3, int attr)
2483{
2484 CheckEnlarge(boundary, NumOfBdrElements);
2485 boundary[NumOfBdrElements] = new Triangle(v1, v2, v3, attr);
2486 return NumOfBdrElements++;
2487}
2488
2489int Mesh::AddBdrTriangle(const int *vi, int attr)
2490{
2491 CheckEnlarge(boundary, NumOfBdrElements);
2492 boundary[NumOfBdrElements] = new Triangle(vi, attr);
2493 return NumOfBdrElements++;
2494}
2495
2496int Mesh::AddBdrQuad(int v1, int v2, int v3, int v4, int attr)
2497{
2498 CheckEnlarge(boundary, NumOfBdrElements);
2499 boundary[NumOfBdrElements] = new Quadrilateral(v1, v2, v3, v4, attr);
2500 return NumOfBdrElements++;
2501}
2502
2503int Mesh::AddBdrQuad(const int *vi, int attr)
2504{
2505 CheckEnlarge(boundary, NumOfBdrElements);
2506 boundary[NumOfBdrElements] = new Quadrilateral(vi, attr);
2507 return NumOfBdrElements++;
2508}
2509
2510void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
2511{
2512 static const int quad_to_tri[2][3] = { { 0, 1, 2 }, { 0, 2, 3 } };
2513 int ti[3];
2514
2515 for (int i = 0; i < 2; i++)
2516 {
2517 for (int j = 0; j < 3; j++)
2518 {
2519 ti[j] = vi[quad_to_tri[i][j]];
2520 }
2521 AddBdrTriangle(ti, attr);
2522 }
2523}
2524
2525int Mesh::AddBdrPoint(int v, int attr)
2526{
2527 CheckEnlarge(boundary, NumOfBdrElements);
2528 boundary[NumOfBdrElements] = new Point(&v, attr);
2529 return NumOfBdrElements++;
2530}
2531
2533{
2534 for (auto &b : boundary)
2535 {
2536 FreeElement(b);
2537 }
2538
2539 if (Dim == 3)
2540 {
2541 delete bel_to_edge;
2542 bel_to_edge = NULL;
2543 }
2544
2545 // count the 'NumOfBdrElements'
2546 NumOfBdrElements = 0;
2547 for (const auto &fi : faces_info)
2548 {
2549 if (fi.Elem2No < 0) { ++NumOfBdrElements; }
2550 }
2551
2552 // Add the boundary elements
2555 for (int i = 0, j = 0; i < faces_info.Size(); i++)
2556 {
2557 if (faces_info[i].Elem2No < 0)
2558 {
2559 boundary[j] = faces[i]->Duplicate(this);
2560 be_to_face[j++] = i;
2561 }
2562 }
2563
2564 // Note: in 3D, 'bel_to_edge' is destroyed but it's not updated.
2565}
2566
2568{
2569 MFEM_VERIFY(vertices.Size() == NumOfVertices ||
2570 vertices.Size() == 0,
2571 "incorrect number of vertices: preallocated: " << vertices.Size()
2572 << ", actually added: " << NumOfVertices);
2573 MFEM_VERIFY(elements.Size() == NumOfElements,
2574 "incorrect number of elements: preallocated: " << elements.Size()
2575 << ", actually added: " << NumOfElements);
2576 MFEM_VERIFY(boundary.Size() == NumOfBdrElements,
2577 "incorrect number of boundary elements: preallocated: "
2578 << boundary.Size() << ", actually added: " << NumOfBdrElements);
2579}
2580
2581void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
2582{
2583 FinalizeCheck();
2584 CheckElementOrientation(fix_orientation);
2585
2586 if (refine)
2587 {
2589 }
2590
2591 if (generate_edges)
2592 {
2593 el_to_edge = new Table;
2595 GenerateFaces();
2597 }
2598 else
2599 {
2600 NumOfEdges = 0;
2601 }
2602
2603 NumOfFaces = 0;
2604
2605 SetAttributes();
2606
2607 SetMeshGen();
2608}
2609
2610void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
2611 bool fix_orientation)
2612{
2613 FinalizeCheck();
2614 if (fix_orientation)
2615 {
2616 CheckElementOrientation(fix_orientation);
2617 }
2618
2619 if (generate_edges)
2620 {
2621 el_to_edge = new Table;
2623 GenerateFaces();
2625 }
2626 else
2627 {
2628 NumOfEdges = 0;
2629 }
2630
2631 NumOfFaces = 0;
2632
2633 SetAttributes();
2634
2635 SetMeshGen();
2636}
2637
2638
2639class GeckoProgress : public Gecko::Progress
2640{
2641 real_t limit;
2642 mutable StopWatch sw;
2643public:
2644 GeckoProgress(real_t limit) : limit(limit) { sw.Start(); }
2645 bool quit() const override { return limit > 0 && sw.UserTime() > limit; }
2646};
2647
2648class GeckoVerboseProgress : public GeckoProgress
2649{
2650 using Float = Gecko::Float;
2651 using Graph = Gecko::Graph;
2652 using uint = Gecko::uint;
2653public:
2654 GeckoVerboseProgress(real_t limit) : GeckoProgress(limit) {}
2655
2656 void beginorder(const Graph* graph, Float cost) const override
2657 { mfem::out << "Begin Gecko ordering, cost = " << cost << std::endl; }
2658 void endorder(const Graph* graph, Float cost) const override
2659 { mfem::out << "End ordering, cost = " << cost << std::endl; }
2660
2661 void beginiter(const Graph* graph,
2662 uint iter, uint maxiter, uint window) const override
2663 {
2664 mfem::out << "Iteration " << iter << "/" << maxiter << ", window "
2665 << window << std::flush;
2666 }
2667 void enditer(const Graph* graph, Float mincost, Float cost) const override
2668 { mfem::out << ", cost = " << cost << endl; }
2669};
2670
2671
2673 int iterations, int window,
2674 int period, int seed, bool verbose,
2675 real_t time_limit)
2676{
2677 Gecko::Graph graph;
2678 Gecko::FunctionalGeometric functional; // edge product cost
2679
2680 GeckoProgress progress(time_limit);
2681 GeckoVerboseProgress vprogress(time_limit);
2682
2683 // insert elements as nodes in the graph
2684 for (int elemid = 0; elemid < GetNE(); ++elemid)
2685 {
2686 graph.insert_node();
2687 }
2688
2689 // insert graph edges for element neighbors
2690 // NOTE: indices in Gecko are 1 based hence the +1 on insertion
2691 const Table &my_el_to_el = ElementToElementTable();
2692 for (int elemid = 0; elemid < GetNE(); ++elemid)
2693 {
2694 const int *neighid = my_el_to_el.GetRow(elemid);
2695 for (int i = 0; i < my_el_to_el.RowSize(elemid); ++i)
2696 {
2697 graph.insert_arc(elemid + 1, neighid[i] + 1);
2698 }
2699 }
2700
2701 // get the ordering from Gecko and copy it into the Array<int>
2702 graph.order(&functional, iterations, window, period, seed,
2703 verbose ? &vprogress : &progress);
2704
2705 ordering.SetSize(GetNE());
2707 for (Gecko::Node::Index gnodeid = 1; gnodeid <= NE; ++gnodeid)
2708 {
2709 ordering[gnodeid - 1] = graph.rank(gnodeid);
2710 }
2711
2712 return graph.cost();
2713}
2714
2715
2716struct HilbertCmp
2717{
2718 int coord;
2719 bool dir;
2720 const Array<real_t> &points;
2721 real_t mid;
2722
2723 HilbertCmp(int coord, bool dir, const Array<real_t> &points, real_t mid)
2724 : coord(coord), dir(dir), points(points), mid(mid) {}
2725
2726 bool operator()(int i) const
2727 {
2728 return (points[3*i + coord] < mid) != dir;
2729 }
2730};
2731
2732static void HilbertSort2D(int coord1, // major coordinate to sort points by
2733 bool dir1, // sort coord1 ascending/descending?
2734 bool dir2, // sort coord2 ascending/descending?
2735 const Array<real_t> &points, int *beg, int *end,
2736 real_t xmin, real_t ymin, real_t xmax, real_t ymax)
2737{
2738 if (end - beg <= 1) { return; }
2739
2740 real_t xmid = (xmin + xmax)*0.5;
2741 real_t ymid = (ymin + ymax)*0.5;
2742
2743 int coord2 = (coord1 + 1) % 2; // the 'other' coordinate
2744
2745 // sort (partition) points into four quadrants
2746 int *p0 = beg, *p4 = end;
2747 int *p2 = std::partition(p0, p4, HilbertCmp(coord1, dir1, points, xmid));
2748 int *p1 = std::partition(p0, p2, HilbertCmp(coord2, dir2, points, ymid));
2749 int *p3 = std::partition(p2, p4, HilbertCmp(coord2, !dir2, points, ymid));
2750
2751 if (p1 != p4)
2752 {
2753 HilbertSort2D(coord2, dir2, dir1, points, p0, p1,
2754 ymin, xmin, ymid, xmid);
2755 }
2756 if (p1 != p0 || p2 != p4)
2757 {
2758 HilbertSort2D(coord1, dir1, dir2, points, p1, p2,
2759 xmin, ymid, xmid, ymax);
2760 }
2761 if (p2 != p0 || p3 != p4)
2762 {
2763 HilbertSort2D(coord1, dir1, dir2, points, p2, p3,
2764 xmid, ymid, xmax, ymax);
2765 }
2766 if (p3 != p0)
2767 {
2768 HilbertSort2D(coord2, !dir2, !dir1, points, p3, p4,
2769 ymid, xmax, ymin, xmid);
2770 }
2771}
2772
2773static void HilbertSort3D(int coord1, bool dir1, bool dir2, bool dir3,
2774 const Array<real_t> &points, int *beg, int *end,
2775 real_t xmin, real_t ymin, real_t zmin,
2776 real_t xmax, real_t ymax, real_t zmax)
2777{
2778 if (end - beg <= 1) { return; }
2779
2780 real_t xmid = (xmin + xmax)*0.5;
2781 real_t ymid = (ymin + ymax)*0.5;
2782 real_t zmid = (zmin + zmax)*0.5;
2783
2784 int coord2 = (coord1 + 1) % 3;
2785 int coord3 = (coord1 + 2) % 3;
2786
2787 // sort (partition) points into eight octants
2788 int *p0 = beg, *p8 = end;
2789 int *p4 = std::partition(p0, p8, HilbertCmp(coord1, dir1, points, xmid));
2790 int *p2 = std::partition(p0, p4, HilbertCmp(coord2, dir2, points, ymid));
2791 int *p6 = std::partition(p4, p8, HilbertCmp(coord2, !dir2, points, ymid));
2792 int *p1 = std::partition(p0, p2, HilbertCmp(coord3, dir3, points, zmid));
2793 int *p3 = std::partition(p2, p4, HilbertCmp(coord3, !dir3, points, zmid));
2794 int *p5 = std::partition(p4, p6, HilbertCmp(coord3, dir3, points, zmid));
2795 int *p7 = std::partition(p6, p8, HilbertCmp(coord3, !dir3, points, zmid));
2796
2797 if (p1 != p8)
2798 {
2799 HilbertSort3D(coord3, dir3, dir1, dir2, points, p0, p1,
2800 zmin, xmin, ymin, zmid, xmid, ymid);
2801 }
2802 if (p1 != p0 || p2 != p8)
2803 {
2804 HilbertSort3D(coord2, dir2, dir3, dir1, points, p1, p2,
2805 ymin, zmid, xmin, ymid, zmax, xmid);
2806 }
2807 if (p2 != p0 || p3 != p8)
2808 {
2809 HilbertSort3D(coord2, dir2, dir3, dir1, points, p2, p3,
2810 ymid, zmid, xmin, ymax, zmax, xmid);
2811 }
2812 if (p3 != p0 || p4 != p8)
2813 {
2814 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p3, p4,
2815 xmin, ymax, zmid, xmid, ymid, zmin);
2816 }
2817 if (p4 != p0 || p5 != p8)
2818 {
2819 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p4, p5,
2820 xmid, ymax, zmid, xmax, ymid, zmin);
2821 }
2822 if (p5 != p0 || p6 != p8)
2823 {
2824 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p5, p6,
2825 ymax, zmid, xmax, ymid, zmax, xmid);
2826 }
2827 if (p6 != p0 || p7 != p8)
2828 {
2829 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p6, p7,
2830 ymid, zmid, xmax, ymin, zmax, xmid);
2831 }
2832 if (p7 != p0)
2833 {
2834 HilbertSort3D(coord3, !dir3, !dir1, dir2, points, p7, p8,
2835 zmid, xmax, ymin, zmin, xmid, ymid);
2836 }
2837}
2838
2840{
2841 MFEM_VERIFY(spaceDim <= 3, "");
2842
2843 Vector min, max, center;
2844 GetBoundingBox(min, max);
2845
2846 Array<int> indices(GetNE());
2847 Array<real_t> points(3*GetNE());
2848
2849 if (spaceDim < 3) { points = 0.0; }
2850
2851 // calculate element centers
2852 for (int i = 0; i < GetNE(); i++)
2853 {
2854 GetElementCenter(i, center);
2855 for (int j = 0; j < spaceDim; j++)
2856 {
2857 points[3*i + j] = center(j);
2858 }
2859 indices[i] = i;
2860 }
2861
2862 if (spaceDim == 1)
2863 {
2864 indices.Sort([&](int a, int b)
2865 { return points[3*a] < points[3*b]; });
2866 }
2867 else if (spaceDim == 2)
2868 {
2869 // recursively partition the points in 2D
2870 HilbertSort2D(0, false, false,
2871 points, indices.begin(), indices.end(),
2872 min(0), min(1), max(0), max(1));
2873 }
2874 else
2875 {
2876 // recursively partition the points in 3D
2877 HilbertSort3D(0, false, false, false,
2878 points, indices.begin(), indices.end(),
2879 min(0), min(1), min(2), max(0), max(1), max(2));
2880 }
2881
2882 // return ordering in the format required by ReorderElements
2883 ordering.SetSize(GetNE());
2884 for (int i = 0; i < GetNE(); i++)
2885 {
2886 ordering[indices[i]] = i;
2887 }
2888}
2889
2890
2891void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
2892{
2893 if (NURBSext)
2894 {
2895 MFEM_WARNING("element reordering of NURBS meshes is not supported.");
2896 return;
2897 }
2898 if (ncmesh)
2899 {
2900 MFEM_WARNING("element reordering of non-conforming meshes is not"
2901 " supported.");
2902 return;
2903 }
2904 MFEM_VERIFY(ordering.Size() == GetNE(), "invalid reordering array.")
2905
2906 // Data members that need to be updated:
2907
2908 // - elements - reorder of the pointers and the vertex ids if reordering
2909 // the vertices
2910 // - vertices - if reordering the vertices
2911 // - boundary - update the vertex ids if reordering the vertices; reorder
2912 // the array (Dim > 1) by face index so the result matches
2913 // what GenerateBoundaryElements would produce on a mesh that
2914 // was originally stored in the new element order
2915 // - faces - regenerate
2916 // - faces_info - regenerate
2917
2918 // Deleted by DeleteTables():
2919 // - el_to_edge - rebuild in 2D and 3D only
2920 // - el_to_face - rebuild in 3D only
2921 // - bel_to_edge - rebuild in 3D only; rows then permuted to match the new
2922 // boundary element ordering
2923 // - el_to_el - no need to rebuild
2924 // - face_edge - no need to rebuild
2925 // - edge_face - no need to rebuild
2926 // - edge_vertex - no need to rebuild
2927 // - geom_factors - no need to rebuild
2928
2929 // - be_to_face - rebuild (Dim > 1); then permuted to match the new
2930 // boundary element ordering
2931
2932 // - Nodes
2933
2934 // Save the locations of the Nodes so we can rebuild them later
2935 Array<Vector*> old_elem_node_vals;
2936 FiniteElementSpace *nodes_fes = NULL;
2937 if (Nodes)
2938 {
2939 old_elem_node_vals.SetSize(GetNE());
2940 nodes_fes = Nodes->FESpace();
2941 Array<int> old_dofs;
2942 Vector vals;
2943 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2944 {
2945 nodes_fes->GetElementVDofs(old_elid, old_dofs);
2946 Nodes->GetSubVector(old_dofs, vals);
2947 old_elem_node_vals[old_elid] = new Vector(vals);
2948 }
2949 }
2950
2951 // Get the newly ordered elements
2952 Array<Element *> new_elements(GetNE());
2953 for (int old_elid = 0; old_elid < ordering.Size(); ++old_elid)
2954 {
2955 int new_elid = ordering[old_elid];
2956 new_elements[new_elid] = elements[old_elid];
2957 }
2958 mfem::Swap(elements, new_elements);
2959 new_elements.DeleteAll();
2960
2961 if (reorder_vertices)
2962 {
2963 // Get the new vertex ordering permutation vectors and fill the new
2964 // vertices
2965 Array<int> vertex_ordering(GetNV());
2966 vertex_ordering = -1;
2967 Array<Vertex> new_vertices(GetNV());
2968 int new_vertex_ind = 0;
2969 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2970 {
2971 int *elem_vert = elements[new_elid]->GetVertices();
2972 int nv = elements[new_elid]->GetNVertices();
2973 for (int vi = 0; vi < nv; ++vi)
2974 {
2975 int old_vertex_ind = elem_vert[vi];
2976 if (vertex_ordering[old_vertex_ind] == -1)
2977 {
2978 vertex_ordering[old_vertex_ind] = new_vertex_ind;
2979 new_vertices[new_vertex_ind] = vertices[old_vertex_ind];
2980 new_vertex_ind++;
2981 }
2982 }
2983 }
2984 mfem::Swap(vertices, new_vertices);
2985 new_vertices.DeleteAll();
2986
2987 // Replace the vertex ids in the elements with the reordered vertex
2988 // numbers
2989 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2990 {
2991 int *elem_vert = elements[new_elid]->GetVertices();
2992 int nv = elements[new_elid]->GetNVertices();
2993 for (int vi = 0; vi < nv; ++vi)
2994 {
2995 elem_vert[vi] = vertex_ordering[elem_vert[vi]];
2996 }
2997 }
2998
2999 // Replace the vertex ids in the boundary with reordered vertex numbers
3000 for (int belid = 0; belid < GetNBE(); ++belid)
3001 {
3002 int *be_vert = boundary[belid]->GetVertices();
3003 int nv = boundary[belid]->GetNVertices();
3004 for (int vi = 0; vi < nv; ++vi)
3005 {
3006 be_vert[vi] = vertex_ordering[be_vert[vi]];
3007 }
3008 }
3009 }
3010
3011 // Destroy tables that need to be rebuild
3012 DeleteTables();
3013
3014 if (Dim > 1)
3015 {
3016 // generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
3017 el_to_edge = new Table;
3019 }
3020 if (Dim > 2)
3021 {
3022 // generate el_to_face, be_to_face
3024 }
3025 // Update faces and faces_info
3026 GenerateFaces();
3027
3028 // Reorder boundary elements
3029 if (Dim > 1)
3030 {
3031 // Build a sort permutation: boundary element i goes to position
3032 // bdr_perm[i]. Sort by face index (be_to_face[i]) rather than just
3033 // adjacent element index: after GetElementToFaceTable face indices are
3034 // assigned in element order, so be_to_face encodes both the adjacent
3035 // element and its local face position within that element. This makes
3036 // the result identical to what GenerateBoundaryElements would produce on
3037 // a mesh that was originally written in Hilbert element order.
3038 Array<int> bdr_perm(NumOfBdrElements);
3039 for (int i = 0; i < NumOfBdrElements; ++i) { bdr_perm[i] = i; }
3040 bdr_perm.Sort([this](int a, int b)
3041 {
3042 return be_to_face[a] < be_to_face[b];
3043 });
3044
3045 // Apply permutation to the boundary element array and be_to_face
3046 Array<Element *> new_boundary(NumOfBdrElements);
3047 Array<int> new_be_to_face(NumOfBdrElements);
3048 for (int new_i = 0; new_i < NumOfBdrElements; ++new_i)
3049 {
3050 new_boundary[new_i] = boundary[bdr_perm[new_i]];
3051 new_be_to_face[new_i] = be_to_face[bdr_perm[new_i]];
3052 }
3053 mfem::Swap(boundary, new_boundary);
3054 new_boundary.DeleteAll(); // pointers are now owned by boundary; just free container
3055 mfem::Swap(be_to_face, new_be_to_face);
3056
3057 // For 3D meshes bel_to_edge maps boundary element index -> edges.
3058 // Permute its rows so the mapping stays consistent with the new boundary
3059 // element ordering.
3060 if (Dim == 3 && bel_to_edge)
3061 {
3062 int total_nnz = 0;
3063 for (int new_i = 0; new_i < NumOfBdrElements; ++new_i)
3064 {
3065 total_nnz += bel_to_edge->RowSize(bdr_perm[new_i]);
3066 }
3067 Table *new_bel_to_edge = new Table;
3068 new_bel_to_edge->SetDims(NumOfBdrElements, total_nnz);
3069 int *new_I = new_bel_to_edge->GetI();
3070 int *new_J = new_bel_to_edge->GetJ();
3071 new_I[0] = 0;
3072 for (int new_i = 0; new_i < NumOfBdrElements; ++new_i)
3073 {
3074 const int old_i = bdr_perm[new_i];
3075 const int nrow = bel_to_edge->RowSize(old_i);
3076 const int *old_J = bel_to_edge->GetRow(old_i);
3077 for (int k = 0; k < nrow; ++k)
3078 {
3079 new_J[new_I[new_i] + k] = old_J[k];
3080 }
3081 new_I[new_i + 1] = new_I[new_i] + nrow;
3082 }
3083 delete bel_to_edge;
3084 bel_to_edge = new_bel_to_edge;
3085 }
3086 }
3087
3088 // Build the nodes from the saved locations if they were around before
3089 if (Nodes)
3090 {
3091 // To force FE space update, we need to increase 'sequence':
3092 sequence++;
3095 nodes_fes->Update(false); // want_transform = false
3096 Nodes->Update(); // just needed to update Nodes->sequence
3097 Array<int> new_dofs;
3098 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
3099 {
3100 int new_elid = ordering[old_elid];
3101 nodes_fes->GetElementVDofs(new_elid, new_dofs);
3102 Nodes->SetSubVector(new_dofs, *(old_elem_node_vals[old_elid]));
3103 delete old_elem_node_vals[old_elid];
3104 }
3105 }
3106}
3107
3108
3110{
3111 if (meshgen & 1)
3112 {
3113 if (Dim == 2)
3114 {
3116 }
3117 else if (Dim == 3)
3118 {
3119 DSTable v_to_v(NumOfVertices);
3120 GetVertexToVertexTable(v_to_v);
3122 }
3123 }
3124}
3125
3127{
3128 // Mark the longest triangle edge by rotating the indices so that
3129 // vertex 0 - vertex 1 is the longest edge in the triangle.
3130 DenseMatrix pmat;
3131 for (int i = 0; i < NumOfElements; i++)
3132 {
3133 if (elements[i]->GetType() == Element::TRIANGLE)
3134 {
3135 GetPointMatrix(i, pmat);
3136 static_cast<Triangle*>(elements[i])->MarkEdge(pmat);
3137 }
3138 }
3139}
3140
3141void Mesh::GetEdgeOrdering(const DSTable &v_to_v, Array<int> &order)
3142{
3143 NumOfEdges = v_to_v.NumberOfEntries();
3144 order.SetSize(NumOfEdges);
3145 Array<Pair<real_t, int> > length_idx(NumOfEdges);
3146
3147 for (int i = 0; i < NumOfVertices; i++)
3148 {
3149 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
3150 {
3151 int j = it.Index();
3152 length_idx[j].one = GetLength(i, it.Column());
3153 length_idx[j].two = j;
3154 }
3155 }
3156
3157 // Sort by increasing edge-length.
3158 length_idx.Sort();
3159
3160 for (int i = 0; i < NumOfEdges; i++)
3161 {
3162 order[length_idx[i].two] = i;
3163 }
3164}
3165
3167{
3168 // Mark the longest tetrahedral edge by rotating the indices so that
3169 // vertex 0 - vertex 1 is the longest edge in the element.
3170 Array<int> order;
3171 GetEdgeOrdering(v_to_v, order);
3172
3173 for (int i = 0; i < NumOfElements; i++)
3174 {
3175 if (elements[i]->GetType() == Element::TETRAHEDRON)
3176 {
3177 elements[i]->MarkEdge(v_to_v, order);
3178 }
3179 }
3180 for (int i = 0; i < NumOfBdrElements; i++)
3181 {
3182 if (boundary[i]->GetType() == Element::TRIANGLE)
3183 {
3184 boundary[i]->MarkEdge(v_to_v, order);
3185 }
3186 }
3187}
3188
3189void Mesh::PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
3190{
3191 if (*old_v_to_v && *old_elem_vert)
3192 {
3193 return;
3194 }
3195
3197
3198 if (*old_v_to_v == NULL)
3199 {
3200 bool need_v_to_v = false;
3201 Array<int> dofs;
3202 for (int i = 0; i < GetNEdges(); i++)
3203 {
3204 // Since edge indices may change, we need to permute edge interior dofs
3205 // any time an edge index changes and there is at least one dof on that
3206 // edge.
3207 fes->GetEdgeInteriorDofs(i, dofs);
3208 if (dofs.Size() > 0)
3209 {
3210 need_v_to_v = true;
3211 break;
3212 }
3213 }
3214 if (need_v_to_v)
3215 {
3216 *old_v_to_v = new DSTable(NumOfVertices);
3217 GetVertexToVertexTable(*(*old_v_to_v));
3218 }
3219 }
3220 if (*old_elem_vert == NULL)
3221 {
3222 bool need_elem_vert = false;
3223 Array<int> dofs;
3224 for (int i = 0; i < GetNE(); i++)
3225 {
3226 // Since element indices do not change, we need to permute element
3227 // interior dofs only when there are at least 2 interior dofs in an
3228 // element (assuming the nodal dofs are non-directional).
3229 fes->GetElementInteriorDofs(i, dofs);
3230 if (dofs.Size() > 1)
3231 {
3232 need_elem_vert = true;
3233 break;
3234 }
3235 }
3236 if (need_elem_vert)
3237 {
3238 *old_elem_vert = new Table;
3239 (*old_elem_vert)->MakeI(GetNE());
3240 for (int i = 0; i < GetNE(); i++)
3241 {
3242 (*old_elem_vert)->AddColumnsInRow(i, elements[i]->GetNVertices());
3243 }
3244 (*old_elem_vert)->MakeJ();
3245 for (int i = 0; i < GetNE(); i++)
3246 {
3247 (*old_elem_vert)->AddConnections(i, elements[i]->GetVertices(),
3248 elements[i]->GetNVertices());
3249 }
3250 (*old_elem_vert)->ShiftUpI();
3251 }
3252 }
3253}
3254
3255void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
3256{
3258 const FiniteElementCollection *fec = fes->FEColl();
3259 Array<int> old_dofs, new_dofs;
3260
3261 // assuming that all edges have the same number of dofs
3262 if (NumOfEdges) { fes->GetEdgeInteriorDofs(0, old_dofs); }
3263 const int num_edge_dofs = old_dofs.Size();
3264
3265 // Save the original nodes
3266 Nodes->HostReadWrite(); // for "(*Nodes)() = "
3267 const Vector onodes = *Nodes;
3268
3269 // vertex dofs do not need to be moved
3270 fes->GetVertexDofs(0, old_dofs);
3271 int offset = NumOfVertices * old_dofs.Size();
3272
3273 // edge dofs:
3274 // edge enumeration may be different but edge orientation is the same
3275 if (num_edge_dofs > 0)
3276 {
3277 DSTable new_v_to_v(NumOfVertices);
3278 GetVertexToVertexTable(new_v_to_v);
3279
3280 for (int i = 0; i < NumOfVertices; i++)
3281 {
3282 for (DSTable::RowIterator it(new_v_to_v, i); !it; ++it)
3283 {
3284 const int old_i = (*old_v_to_v)(i, it.Column());
3285 const int new_i = it.Index();
3286 if (new_i == old_i) { continue; }
3287
3288 old_dofs.SetSize(num_edge_dofs);
3289 new_dofs.SetSize(num_edge_dofs);
3290 for (int j = 0; j < num_edge_dofs; j++)
3291 {
3292 old_dofs[j] = offset + old_i * num_edge_dofs + j;
3293 new_dofs[j] = offset + new_i * num_edge_dofs + j;
3294 }
3295 fes->DofsToVDofs(old_dofs);
3296 fes->DofsToVDofs(new_dofs);
3297 for (int j = 0; j < old_dofs.Size(); j++)
3298 {
3299 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3300 }
3301 }
3302 }
3303 offset += NumOfEdges * num_edge_dofs;
3304 }
3305
3306 // face dofs:
3307 // both enumeration and orientation of the faces may be different
3308 if (fes->GetNFDofs() > 0)
3309 {
3310 // generate the old face-vertex table using the unmodified 'faces'
3311 Table old_face_vertex;
3312 old_face_vertex.MakeI(NumOfFaces);
3313 for (int i = 0; i < NumOfFaces; i++)
3314 {
3315 old_face_vertex.AddColumnsInRow(i, faces[i]->GetNVertices());
3316 }
3317 old_face_vertex.MakeJ();
3318 for (int i = 0; i < NumOfFaces; i++)
3319 old_face_vertex.AddConnections(i, faces[i]->GetVertices(),
3320 faces[i]->GetNVertices());
3321 old_face_vertex.ShiftUpI();
3322
3323 // update 'el_to_face', 'be_to_face', 'faces', and 'faces_info'
3324 STable3D *faces_tbl = GetElementToFaceTable(1);
3325 GenerateFaces();
3326
3327 // compute the new face dof offsets
3328 Array<int> new_fdofs(NumOfFaces+1);
3329 new_fdofs[0] = 0;
3330 for (int i = 0; i < NumOfFaces; i++) // i = old face index
3331 {
3332 const int *old_v = old_face_vertex.GetRow(i);
3333 int new_i; // new face index
3334 switch (old_face_vertex.RowSize(i))
3335 {
3336 case 3:
3337 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
3338 break;
3339 case 4:
3340 default:
3341 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
3342 break;
3343 }
3344 fes->GetFaceInteriorDofs(i, old_dofs);
3345 new_fdofs[new_i+1] = old_dofs.Size();
3346 }
3347 new_fdofs.PartialSum();
3348
3349 // loop over the old face numbers
3350 for (int i = 0; i < NumOfFaces; i++)
3351 {
3352 const int *old_v = old_face_vertex.GetRow(i), *new_v;
3353 const int *dof_ord;
3354 int new_i, new_or;
3355 switch (old_face_vertex.RowSize(i))
3356 {
3357 case 3:
3358 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
3359 new_v = faces[new_i]->GetVertices();
3360 new_or = GetTriOrientation(old_v, new_v);
3361 dof_ord = fec->DofOrderForOrientation(Geometry::TRIANGLE, new_or);
3362 break;
3363 case 4:
3364 default:
3365 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
3366 new_v = faces[new_i]->GetVertices();
3367 new_or = GetQuadOrientation(old_v, new_v);
3368 dof_ord = fec->DofOrderForOrientation(Geometry::SQUARE, new_or);
3369 break;
3370 }
3371
3372 fes->GetFaceInteriorDofs(i, old_dofs);
3373 new_dofs.SetSize(old_dofs.Size());
3374 for (int j = 0; j < old_dofs.Size(); j++)
3375 {
3376 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
3377 const int old_j = dof_ord[j];
3378 new_dofs[old_j] = offset + new_fdofs[new_i] + j;
3379 }
3380 fes->DofsToVDofs(old_dofs);
3381 fes->DofsToVDofs(new_dofs);
3382 for (int j = 0; j < old_dofs.Size(); j++)
3383 {
3384 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3385 }
3386 }
3387
3388 offset += fes->GetNFDofs();
3389 delete faces_tbl;
3390 }
3391
3392 // element dofs:
3393 // element orientation may be different
3394 if (old_elem_vert) // have elements with 2 or more dofs
3395 {
3396 // matters when the 'fec' is
3397 // (this code is executed only for triangles/tets)
3398 // - Pk on triangles, k >= 4
3399 // - Qk on quads, k >= 3
3400 // - Pk on tets, k >= 5
3401 // - Qk on hexes, k >= 3
3402 // - DG spaces
3403 // - ...
3404
3405 // loop over all elements
3406 for (int i = 0; i < GetNE(); i++)
3407 {
3408 fes->GetElementInteriorDofs(i, old_dofs);
3409 // No need to permute the dofs if there are fewer than two
3410 if (old_dofs.Size() < 2)
3411 {
3412 offset += old_dofs.Size();
3413 continue;
3414 }
3415
3416 const int *old_v = old_elem_vert->GetRow(i);
3417 const int *new_v = elements[i]->GetVertices();
3418 const int *dof_ord;
3419 int new_or;
3420 const Geometry::Type geom = elements[i]->GetGeometryType();
3421 if (geom == Geometry::CUBE || geom == Geometry::PRISM ||
3422 geom == Geometry::PYRAMID)
3423 {
3424 offset += old_dofs.Size();
3425 continue;
3426 }
3427 switch (geom)
3428 {
3429 case Geometry::SEGMENT:
3430 new_or = (old_v[0] == new_v[0]) ? +1 : -1;
3431 break;
3432 case Geometry::TRIANGLE:
3433 new_or = GetTriOrientation(old_v, new_v);
3434 break;
3435 case Geometry::SQUARE:
3436 new_or = GetQuadOrientation(old_v, new_v);
3437 break;
3439 new_or = GetTetOrientation(old_v, new_v);
3440 break;
3441 default:
3442 new_or = 0;
3443 MFEM_ABORT(Geometry::Name[geom] << " elements (" << fec->Name()
3444 << " FE collection) are not supported yet!");
3445 break;
3446 }
3447 dof_ord = fec->DofOrderForOrientation(geom, new_or);
3448 MFEM_VERIFY(dof_ord != NULL,
3449 "FE collection '" << fec->Name()
3450 << "' does not define reordering (" << new_or << ") for "
3451 << Geometry::Name[geom] << " elements!");
3452 new_dofs.SetSize(old_dofs.Size());
3453 for (int j = 0; j < new_dofs.Size(); j++)
3454 {
3455 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
3456 const int old_j = dof_ord[j];
3457 new_dofs[old_j] = offset + j;
3458 }
3459 offset += new_dofs.Size();
3460 fes->DofsToVDofs(old_dofs);
3461 fes->DofsToVDofs(new_dofs);
3462 for (int j = 0; j < old_dofs.Size(); j++)
3463 {
3464 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3465 }
3466 }
3467 }
3468
3469 // Update Tables, faces, etc
3470 if (Dim > 2)
3471 {
3472 if (fes->GetNFDofs() == 0)
3473 {
3474 // needed for FE spaces that have face dofs, even if
3475 // the 'Nodes' do not have face dofs.
3477 GenerateFaces();
3478 }
3480 }
3481 if (el_to_edge)
3482 {
3483 // update 'el_to_edge', 'be_to_face' (2D), 'bel_to_edge' (3D)
3485 if (Dim == 2)
3486 {
3487 // update 'faces' and 'faces_info'
3488 GenerateFaces();
3490 }
3491 }
3492 // To force FE space update, we need to increase 'sequence':
3493 sequence++;
3496 fes->Update(false); // want_transform = false
3497 Nodes->Update(); // just needed to update Nodes->sequence
3498}
3499
3500void Mesh::SetPatchAttribute(int i, int attr)
3501{
3502 MFEM_ASSERT(NURBSext, "SetPatchAttribute is only for NURBS meshes");
3503 NURBSext->SetPatchAttribute(i, attr);
3504 const Array<int>& elems = NURBSext->GetPatchElements(i);
3505 for (auto e : elems)
3506 {
3507 SetAttribute(e, attr);
3508 }
3509}
3510
3512{
3513 MFEM_ASSERT(NURBSext, "GetPatchAttribute is only for NURBS meshes");
3514 return NURBSext->GetPatchAttribute(i);
3515}
3516
3517void Mesh::SetPatchBdrAttribute(int i, int attr)
3518{
3519 MFEM_ASSERT(NURBSext, "SetPatchBdrAttribute is only for NURBS meshes");
3521
3522 const Array<int>& bdryelems = NURBSext->GetPatchBdrElements(i);
3523 for (auto be : bdryelems)
3524 {
3525 SetBdrAttribute(be, attr);
3526 }
3527}
3528
3530{
3531 MFEM_ASSERT(NURBSext, "GetBdrPatchBdrAttribute is only for NURBS meshes");
3532 return NURBSext->GetPatchBdrAttribute(i);
3533}
3534
3536{
3537 MFEM_VERIFY(NURBSext, "Must be a NURBS mesh");
3538 // This sets the data in NURBSPatch(es) from the control points (Nodes)
3540
3541 // Deep copy patches
3542 NURBSext->GetPatches(patches);
3543
3544 // Among other things, this deletes patches in NURBSext
3545 UpdateNURBS();
3546}
3547
3548void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
3549{
3550 FinalizeCheck();
3551 CheckElementOrientation(fix_orientation);
3552
3553 if (!HasBoundaryElements())
3554 {
3556 GenerateFaces();
3558 }
3559
3560 if (refine)
3561 {
3562 DSTable v_to_v(NumOfVertices);
3563 GetVertexToVertexTable(v_to_v);
3565 }
3566
3568 GenerateFaces();
3569
3571
3572 if (generate_edges == 1)
3573 {
3574 el_to_edge = new Table;
3576 }
3577 else
3578 {
3579 el_to_edge = NULL; // Not really necessary -- InitTables was called
3580 bel_to_edge = NULL;
3581 NumOfEdges = 0;
3582 }
3583
3584 SetAttributes();
3585
3586 SetMeshGen();
3587}
3588
3589void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
3590 bool fix_orientation)
3591{
3592 FinalizeCheck();
3593 CheckElementOrientation(fix_orientation);
3594
3595 if (!HasBoundaryElements())
3596 {
3598 GenerateFaces();
3600 }
3601
3603 GenerateFaces();
3604
3606
3607 if (generate_edges == 1)
3608 {
3609 el_to_edge = new Table;
3611 }
3612 else
3613 {
3614 el_to_edge = NULL; // Not really necessary -- InitTables was called
3615 bel_to_edge = NULL;
3616 NumOfEdges = 0;
3617 }
3618
3619 SetAttributes();
3620
3621 SetMeshGen();
3622}
3623
3624void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
3625{
3626 FinalizeCheck();
3627 CheckElementOrientation(fix_orientation);
3628
3630 GenerateFaces();
3631
3632 if (!HasBoundaryElements())
3633 {
3635 }
3636
3638
3639 if (generate_edges)
3640 {
3641 el_to_edge = new Table;
3643 }
3644 else
3645 {
3646 NumOfEdges = 0;
3647 }
3648
3649 SetAttributes();
3650
3651 SetMeshGen();
3652}
3653
3654void Mesh::FinalizeMesh(int refine, bool fix_orientation)
3655{
3657 Finalize(refine, fix_orientation);
3658}
3659
3660void Mesh::FinalizeTopology(bool generate_bdr)
3661{
3662 // Requirements: the following should be defined:
3663 // 1) Dim
3664 // 2) NumOfElements, elements
3665 // 3) NumOfBdrElements, boundary
3666 // 4) NumOfVertices
3667 // Optional:
3668 // 2) ncmesh may be defined
3669 // 3) el_to_edge may be allocated (it will be re-computed)
3670
3671 FinalizeCheck();
3672 bool generate_edges = true;
3673
3674 if (spaceDim == 0) { spaceDim = Dim; }
3675 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
3676
3677 // if the user defined any hanging nodes (see AddVertexParent),
3678 // we're initializing a non-conforming mesh
3679 if (tmp_vertex_parents.Size())
3680 {
3681 MFEM_VERIFY(ncmesh == NULL, "");
3682 ncmesh = new NCMesh(this);
3683
3684 // we need to recreate the Mesh because NCMesh reorders the vertices
3685 // (see NCMesh::UpdateVertices())
3687 ncmesh->OnMeshUpdated(this);
3689
3690 SetAttributes();
3691
3692 tmp_vertex_parents.DeleteAll();
3693 return;
3694 }
3695
3696 // set the mesh type: 'meshgen', ...
3697 SetMeshGen();
3698
3699 // generate the faces
3700 if (Dim > 2)
3701 {
3703 GenerateFaces();
3704 if (!HasBoundaryElements() && generate_bdr)
3705 {
3707 GetElementToFaceTable(); // update be_to_face
3708 }
3709 }
3710 else
3711 {
3712 NumOfFaces = 0;
3713 }
3714
3715 // generate edges if requested
3716 if (Dim > 1 && generate_edges)
3717 {
3718 // el_to_edge may already be allocated (P2 VTK meshes)
3719 if (!el_to_edge) { el_to_edge = new Table; }
3721 if (Dim == 2)
3722 {
3723 GenerateFaces(); // 'Faces' in 2D refers to the edges
3724 if (!HasBoundaryElements() && generate_bdr)
3725 {
3727 }
3728 }
3729 }
3730 else
3731 {
3732 NumOfEdges = 0;
3733 }
3734
3735 if (Dim == 1)
3736 {
3737 GenerateFaces();
3738 if (!HasBoundaryElements() && generate_bdr)
3739 {
3740 // be_to_face will be set inside GenerateBoundaryElements
3742 }
3743 else
3744 {
3746 for (int i = 0; i < NumOfBdrElements; ++i)
3747 {
3748 be_to_face[i] = boundary[i]->GetVertices()[0];
3749 }
3750 }
3751 }
3752
3753 if (ncmesh)
3754 {
3755 // tell NCMesh the numbering of edges/faces
3756 ncmesh->OnMeshUpdated(this);
3757
3758 // update faces_info with NC relations
3760 }
3761
3762 // generate the arrays 'attributes' and 'bdr_attributes'
3763 SetAttributes();
3764}
3765
3766void Mesh::Finalize(bool refine, bool fix_orientation)
3767{
3768 if (NURBSext || ncmesh)
3769 {
3770 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
3771 MFEM_ASSERT(CheckBdrElementOrientation() == 0, "");
3772 return;
3773 }
3774
3775 // Requirements:
3776 // 1) FinalizeTopology() or equivalent was called
3777 // 2) if (Nodes == NULL), vertices must be defined
3778 // 3) if (Nodes != NULL), Nodes must be defined
3779
3780 const bool check_orientation = true; // for regular elements, not boundary
3781 const bool curved = (Nodes != NULL);
3782 const bool may_change_topology =
3783 ( refine && (Dim > 1 && (meshgen & 1)) ) ||
3784 ( check_orientation && fix_orientation &&
3785 (Dim == 2 || (Dim == 3 && (meshgen & 1))) );
3786
3787 DSTable *old_v_to_v = NULL;
3788 Table *old_elem_vert = NULL;
3789
3790 if (curved && may_change_topology)
3791 {
3792 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3793 }
3794
3795 if (check_orientation)
3796 {
3797 // check and optionally fix element orientation
3798 CheckElementOrientation(fix_orientation);
3799 }
3800 if (refine)
3801 {
3802 MarkForRefinement(); // may change topology!
3803 }
3804
3805 if (may_change_topology)
3806 {
3807 if (curved)
3808 {
3809 DoNodeReorder(old_v_to_v, old_elem_vert); // updates the mesh topology
3810 delete old_elem_vert;
3811 delete old_v_to_v;
3812 }
3813 else
3814 {
3815 FinalizeTopology(); // Re-computes some data unnecessarily.
3816 }
3817
3818 // TODO: maybe introduce Mesh::NODE_REORDER operation and FESpace::
3819 // NodeReorderMatrix and do Nodes->Update() instead of DoNodeReorder?
3820 }
3821
3822 // check and fix boundary element orientation
3824
3825#ifdef MFEM_DEBUG
3826 // For non-orientable surfaces/manifolds, the check below will fail, so we
3827 // only perform it when Dim == spaceDim.
3828 if (Dim >= 2 && Dim == spaceDim)
3829 {
3830 const int num_faces = GetNumFaces();
3831 for (int i = 0; i < num_faces; i++)
3832 {
3833 MFEM_VERIFY(faces_info[i].Elem2No < 0 ||
3834 faces_info[i].Elem2Inf%2 != 0, "Invalid mesh topology."
3835 " Interior face with incompatible orientations.");
3836 }
3837 }
3838#endif
3839}
3840
3841void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
3842 real_t sx, real_t sy, real_t sz, bool sfc_ordering)
3843{
3844 int x, y, z;
3845
3846 int NVert, NElem, NBdrElem;
3847
3848 NVert = (nx+1) * (ny+1) * (nz+1);
3849 NElem = nx * ny * nz;
3850 NBdrElem = 2*(nx*ny+nx*nz+ny*nz);
3851 if (type == Element::TETRAHEDRON)
3852 {
3853 NElem *= 6;
3854 NBdrElem *= 2;
3855 }
3856 else if (type == Element::WEDGE)
3857 {
3858 NElem *= 2;
3859 NBdrElem += 2*nx*ny;
3860 }
3861 else if (type == Element::PYRAMID)
3862 {
3863 NElem *= 6;
3864 NVert += nx * ny * nz;
3865 }
3866
3867 InitMesh(3, 3, NVert, NElem, NBdrElem);
3868
3869 real_t coord[3];
3870 int ind[9];
3871
3872 // Sets vertices and the corresponding coordinates
3873 for (z = 0; z <= nz; z++)
3874 {
3875 coord[2] = ((real_t) z / nz) * sz;
3876 for (y = 0; y <= ny; y++)
3877 {
3878 coord[1] = ((real_t) y / ny) * sy;
3879 for (x = 0; x <= nx; x++)
3880 {
3881 coord[0] = ((real_t) x / nx) * sx;
3882 AddVertex(coord);
3883 }
3884 }
3885 }
3886 if (type == Element::PYRAMID)
3887 {
3888 for (z = 0; z < nz; z++)
3889 {
3890 coord[2] = (((real_t) z + 0.5) / nz) * sz;
3891 for (y = 0; y < ny; y++)
3892 {
3893 coord[1] = (((real_t) y + 0.5) / ny) * sy;
3894 for (x = 0; x < nx; x++)
3895 {
3896 coord[0] = (((real_t) x + 0.5) / nx) * sx;
3897 AddVertex(coord);
3898 }
3899 }
3900 }
3901 }
3902
3903#define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
3904#define VTXP(XC, YC, ZC) ((nx+1)*(ny+1)*(nz+1)+(XC)+((YC)+(ZC)*ny)*nx)
3905
3906 // Sets elements and the corresponding indices of vertices
3907 if (sfc_ordering && type == Element::HEXAHEDRON)
3908 {
3909 Array<int> sfc;
3910 NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
3911 MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
3912
3913 for (int k = 0; k < nx*ny*nz; k++)
3914 {
3915 x = sfc[3*k + 0];
3916 y = sfc[3*k + 1];
3917 z = sfc[3*k + 2];
3918
3919 // *INDENT-OFF*
3920 ind[0] = VTX(x , y , z );
3921 ind[1] = VTX(x+1, y , z );
3922 ind[2] = VTX(x+1, y+1, z );
3923 ind[3] = VTX(x , y+1, z );
3924 ind[4] = VTX(x , y , z+1);
3925 ind[5] = VTX(x+1, y , z+1);
3926 ind[6] = VTX(x+1, y+1, z+1);
3927 ind[7] = VTX(x , y+1, z+1);
3928 // *INDENT-ON*
3929
3930 AddHex(ind, 1);
3931 }
3932 }
3933 else
3934 {
3935 for (z = 0; z < nz; z++)
3936 {
3937 for (y = 0; y < ny; y++)
3938 {
3939 for (x = 0; x < nx; x++)
3940 {
3941 // *INDENT-OFF*
3942 ind[0] = VTX(x , y , z );
3943 ind[1] = VTX(x+1, y , z );
3944 ind[2] = VTX(x+1, y+1, z );
3945 ind[3] = VTX(x , y+1, z );
3946 ind[4] = VTX(x , y , z+1);
3947 ind[5] = VTX(x+1, y , z+1);
3948 ind[6] = VTX(x+1, y+1, z+1);
3949 ind[7] = VTX( x, y+1, z+1);
3950 // *INDENT-ON*
3951 if (type == Element::TETRAHEDRON)
3952 {
3953 AddHexAsTets(ind, 1);
3954 }
3955 else if (type == Element::WEDGE)
3956 {
3957 AddHexAsWedges(ind, 1);
3958 }
3959 else if (type == Element::PYRAMID)
3960 {
3961 ind[8] = VTXP(x, y, z);
3962 AddHexAsPyramids(ind, 1);
3963 }
3964 else
3965 {
3966 AddHex(ind, 1);
3967 }
3968 }
3969 }
3970 }
3971 }
3972
3973 // Sets boundary elements and the corresponding indices of vertices
3974 // bottom, bdr. attribute 1
3975 for (y = 0; y < ny; y++)
3976 {
3977 for (x = 0; x < nx; x++)
3978 {
3979 // *INDENT-OFF*
3980 ind[0] = VTX(x , y , 0);
3981 ind[1] = VTX(x , y+1, 0);
3982 ind[2] = VTX(x+1, y+1, 0);
3983 ind[3] = VTX(x+1, y , 0);
3984 // *INDENT-ON*
3985 if (type == Element::TETRAHEDRON)
3986 {
3987 AddBdrQuadAsTriangles(ind, 1);
3988 }
3989 else if (type == Element::WEDGE)
3990 {
3991 AddBdrQuadAsTriangles(ind, 1);
3992 }
3993 else
3994 {
3995 AddBdrQuad(ind, 1);
3996 }
3997 }
3998 }
3999 // top, bdr. attribute 6
4000 for (y = 0; y < ny; y++)
4001 {
4002 for (x = 0; x < nx; x++)
4003 {
4004 // *INDENT-OFF*
4005 ind[0] = VTX(x , y , nz);
4006 ind[1] = VTX(x+1, y , nz);
4007 ind[2] = VTX(x+1, y+1, nz);
4008 ind[3] = VTX(x , y+1, nz);
4009 // *INDENT-ON*
4010 if (type == Element::TETRAHEDRON)
4011 {
4012 AddBdrQuadAsTriangles(ind, 6);
4013 }
4014 else if (type == Element::WEDGE)
4015 {
4016 AddBdrQuadAsTriangles(ind, 6);
4017 }
4018 else
4019 {
4020 AddBdrQuad(ind, 6);
4021 }
4022 }
4023 }
4024 // left, bdr. attribute 5
4025 for (z = 0; z < nz; z++)
4026 {
4027 for (y = 0; y < ny; y++)
4028 {
4029 // *INDENT-OFF*
4030 ind[0] = VTX(0 , y , z );
4031 ind[1] = VTX(0 , y , z+1);
4032 ind[2] = VTX(0 , y+1, z+1);
4033 ind[3] = VTX(0 , y+1, z );
4034 // *INDENT-ON*
4035 if (type == Element::TETRAHEDRON)
4036 {
4037 AddBdrQuadAsTriangles(ind, 5);
4038 }
4039 else
4040 {
4041 AddBdrQuad(ind, 5);
4042 }
4043 }
4044 }
4045 // right, bdr. attribute 3
4046 for (z = 0; z < nz; z++)
4047 {
4048 for (y = 0; y < ny; y++)
4049 {
4050 // *INDENT-OFF*
4051 ind[0] = VTX(nx, y , z );
4052 ind[1] = VTX(nx, y+1, z );
4053 ind[2] = VTX(nx, y+1, z+1);
4054 ind[3] = VTX(nx, y , z+1);
4055 // *INDENT-ON*
4056 if (type == Element::TETRAHEDRON)
4057 {
4058 AddBdrQuadAsTriangles(ind, 3);
4059 }
4060 else
4061 {
4062 AddBdrQuad(ind, 3);
4063 }
4064 }
4065 }
4066 // front, bdr. attribute 2
4067 for (x = 0; x < nx; x++)
4068 {
4069 for (z = 0; z < nz; z++)
4070 {
4071 // *INDENT-OFF*
4072 ind[0] = VTX(x , 0, z );
4073 ind[1] = VTX(x+1, 0, z );
4074 ind[2] = VTX(x+1, 0, z+1);
4075 ind[3] = VTX(x , 0, z+1);
4076 // *INDENT-ON*
4077 if (type == Element::TETRAHEDRON)
4078 {
4079 AddBdrQuadAsTriangles(ind, 2);
4080 }
4081 else
4082 {
4083 AddBdrQuad(ind, 2);
4084 }
4085 }
4086 }
4087 // back, bdr. attribute 4
4088 for (x = 0; x < nx; x++)
4089 {
4090 for (z = 0; z < nz; z++)
4091 {
4092 // *INDENT-OFF*
4093 ind[0] = VTX(x , ny, z );
4094 ind[1] = VTX(x , ny, z+1);
4095 ind[2] = VTX(x+1, ny, z+1);
4096 ind[3] = VTX(x+1, ny, z );
4097 // *INDENT-ON*
4098 if (type == Element::TETRAHEDRON)
4099 {
4100 AddBdrQuadAsTriangles(ind, 4);
4101 }
4102 else
4103 {
4104 AddBdrQuad(ind, 4);
4105 }
4106 }
4107 }
4108
4109#undef VTX
4110#undef VTXP
4111
4112#if 0
4113 ofstream test_stream("debug.mesh");
4114 Print(test_stream);
4115 test_stream.close();
4116#endif
4117
4119
4120 // Finalize(...) can be called after this method, if needed
4121}
4122
4123
4124void Mesh::Make2D4TrisFromQuad(int nx, int ny, real_t sx, real_t sy)
4125{
4126 SetEmpty();
4127
4128 Dim = 2;
4129 spaceDim = 2;
4130
4131 NumOfVertices = (nx+1) * (ny+1);
4132 NumOfElements = nx * ny * 4;
4133 NumOfBdrElements = (2 * nx + 2 * ny);
4134 vertices.SetSize(NumOfVertices);
4135 elements.SetSize(NumOfElements);
4136 boundary.SetSize(NumOfBdrElements);
4137 NumOfElements = 0;
4138
4139 int ind[4];
4140
4141 // Sets vertices and the corresponding coordinates
4142 int k = 0;
4143 for (real_t j = 0; j < ny+1; j++)
4144 {
4145 real_t cy = (j / ny) * sy;
4146 for (real_t i = 0; i < nx+1; i++)
4147 {
4148 real_t cx = (i / nx) * sx;
4149 vertices[k](0) = cx;
4150 vertices[k](1) = cy;
4151 k++;
4152 }
4153 }
4154
4155 for (int y = 0; y < ny; y++)
4156 {
4157 for (int x = 0; x < nx; x++)
4158 {
4159 ind[0] = x + y*(nx+1);
4160 ind[1] = x + 1 +y*(nx+1);
4161 ind[2] = x + 1 + (y+1)*(nx+1);
4162 ind[3] = x + (y+1)*(nx+1);
4164 }
4165 }
4166
4167 int m = (nx+1)*ny;
4168 for (int i = 0; i < nx; i++)
4169 {
4170 boundary[i] = new Segment(i, i+1, 1);
4171 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4172 }
4173 m = nx+1;
4174 for (int j = 0; j < ny; j++)
4175 {
4176 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4177 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4178 }
4179
4180 SetMeshGen();
4182
4183 el_to_edge = new Table;
4185 GenerateFaces();
4187
4188 NumOfFaces = 0;
4189
4190 attributes.Append(1);
4193
4195}
4196
4197void Mesh::Make2D5QuadsFromQuad(int nx, int ny,
4198 real_t sx, real_t sy)
4199{
4200 SetEmpty();
4201
4202 Dim = 2;
4203 spaceDim = 2;
4204
4205 NumOfElements = nx * ny * 5;
4206 NumOfVertices = (nx+1) * (ny+1); //it will be enlarged later on
4207 NumOfBdrElements = (2 * nx + 2 * ny);
4208 vertices.SetSize(NumOfVertices);
4209 elements.SetSize(NumOfElements);
4210 boundary.SetSize(NumOfBdrElements);
4211 NumOfElements = 0;
4212
4213 int ind[4];
4214
4215 // Sets vertices and the corresponding coordinates
4216 int k = 0;
4217 for (real_t j = 0; j < ny+1; j++)
4218 {
4219 real_t cy = (j / ny) * sy;
4220 for (real_t i = 0; i < nx+1; i++)
4221 {
4222 real_t cx = (i / nx) * sx;
4223 vertices[k](0) = cx;
4224 vertices[k](1) = cy;
4225 k++;
4226 }
4227 }
4228
4229 for (int y = 0; y < ny; y++)
4230 {
4231 for (int x = 0; x < nx; x++)
4232 {
4233 ind[0] = x + y*(nx+1);
4234 ind[1] = x + 1 +y*(nx+1);
4235 ind[2] = x + 1 + (y+1)*(nx+1);
4236 ind[3] = x + (y+1)*(nx+1);
4238 }
4239 }
4240
4241 int m = (nx+1)*ny;
4242 for (int i = 0; i < nx; i++)
4243 {
4244 boundary[i] = new Segment(i, i+1, 1);
4245 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4246 }
4247 m = nx+1;
4248 for (int j = 0; j < ny; j++)
4249 {
4250 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4251 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4252 }
4253
4254 SetMeshGen();
4256
4257 el_to_edge = new Table;
4259 GenerateFaces();
4261
4262 NumOfFaces = 0;
4263
4264 attributes.Append(1);
4267
4269}
4270
4271void Mesh::Make3D24TetsFromHex(int nx, int ny, int nz,
4272 real_t sx, real_t sy, real_t sz)
4273{
4274 const int NVert = (nx+1) * (ny+1) * (nz+1);
4275 const int NElem = nx * ny * nz * 24;
4276 const int NBdrElem = 2*(nx*ny+nx*nz+ny*nz)*4;
4277
4278 InitMesh(3, 3, NVert, NElem, NBdrElem);
4279
4280 real_t coord[3];
4281
4282 // Sets vertices and the corresponding coordinates
4283 for (real_t z = 0; z <= nz; z++)
4284 {
4285 coord[2] = ( z / nz) * sz;
4286 for (real_t y = 0; y <= ny; y++)
4287 {
4288 coord[1] = (y / ny) * sy;
4289 for (real_t x = 0; x <= nx; x++)
4290 {
4291 coord[0] = (x / nx) * sx;
4292 AddVertex(coord);
4293 }
4294 }
4295 }
4296
4297 std::map<std::array<int, 4>, int> hex_face_verts;
4298 auto VertexIndex = [nx, ny](int xc, int yc, int zc)
4299 {
4300 return xc + (yc + zc*(ny+1))*(nx+1);
4301 };
4302
4303 int ind[9];
4304 for (int z = 0; z < nz; z++)
4305 {
4306 for (int y = 0; y < ny; y++)
4307 {
4308 for (int x = 0; x < nx; x++)
4309 {
4310 // *INDENT-OFF*
4311 ind[0] = VertexIndex(x , y , z );
4312 ind[1] = VertexIndex(x+1, y , z );
4313 ind[2] = VertexIndex(x+1, y+1, z );
4314 ind[3] = VertexIndex(x , y+1, z );
4315 ind[4] = VertexIndex(x , y , z+1);
4316 ind[5] = VertexIndex(x+1, y , z+1);
4317 ind[6] = VertexIndex(x+1, y+1, z+1);
4318 ind[7] = VertexIndex( x, y+1, z+1);
4319 // *INDENT-ON*
4320
4321 AddHexAs24TetsWithPoints(ind, hex_face_verts, 1);
4322 }
4323 }
4324 }
4325
4326 hex_face_verts.clear();
4328
4329 // Done adding Tets
4330 // Now figure out elements that are on the boundary
4331 GetElementToFaceTable(false);
4332 GenerateFaces();
4333
4334 // Map to count number of tets sharing a face
4335 std::map<std::array<int, 3>, int> tet_face_count;
4336 // Map from tet face defined by three vertices to the local face number
4337 std::map<std::array<int, 3>, int> face_count_map;
4338
4339 auto get3array = [](Array<int> v)
4340 {
4341 v.Sort();
4342 return std::array<int, 3> {v[0], v[1], v[2]};
4343 };
4344
4345 Array<int> el_faces;
4346 Array<int> ori;
4347 Array<int> vertidxs;
4348 for (int i = 0; i < el_to_face->Size(); i++)
4349 {
4350 el_to_face->GetRow(i, el_faces);
4351 for (int j = 0; j < el_faces.Size(); j++)
4352 {
4353 GetFaceVertices(el_faces[j], vertidxs);
4354 auto t = get3array(vertidxs);
4355 auto it = tet_face_count.find(t);
4356 if (it == tet_face_count.end()) //edge does not already exist
4357 {
4358 tet_face_count.insert({t, 1});
4359 face_count_map.insert({t, el_faces[j]});
4360 }
4361 else
4362 {
4363 it->second++; // increase edge count value by 1.
4364 }
4365 }
4366 }
4367
4368 for (const auto &edge : tet_face_count)
4369 {
4370 if (edge.second == 1) //if this only appears once, it is a boundary edge
4371 {
4372 int facenum = (face_count_map.find(edge.first))->second;
4373 GetFaceVertices(facenum, vertidxs);
4374 AddBdrTriangle(vertidxs, 1);
4375 }
4376 }
4377
4378#if 0
4379 ofstream test_stream("debug.mesh");
4380 Print(test_stream);
4381 test_stream.close();
4382#endif
4383
4385 // Finalize(...) can be called after this method, if needed
4386}
4387
4388void Mesh::Make2D(int nx, int ny, Element::Type type,
4389 real_t sx, real_t sy,
4390 bool generate_edges, bool sfc_ordering)
4391{
4392 int i, j, k;
4393
4394 SetEmpty();
4395
4396 Dim = spaceDim = 2;
4397
4398 // Creates quadrilateral mesh
4399 if (type == Element::QUADRILATERAL)
4400 {
4401 NumOfVertices = (nx+1) * (ny+1);
4402 NumOfElements = nx * ny;
4403 NumOfBdrElements = 2 * nx + 2 * ny;
4404
4405 vertices.SetSize(NumOfVertices);
4406 elements.SetSize(NumOfElements);
4407 boundary.SetSize(NumOfBdrElements);
4408
4409 real_t cx, cy;
4410 int ind[4];
4411
4412 // Sets vertices and the corresponding coordinates
4413 k = 0;
4414 for (j = 0; j < ny+1; j++)
4415 {
4416 cy = ((real_t) j / ny) * sy;
4417 for (i = 0; i < nx+1; i++)
4418 {
4419 cx = ((real_t) i / nx) * sx;
4420 vertices[k](0) = cx;
4421 vertices[k](1) = cy;
4422 k++;
4423 }
4424 }
4425
4426 // Sets elements and the corresponding indices of vertices
4427 if (sfc_ordering)
4428 {
4429 Array<int> sfc;
4430 NCMesh::GridSfcOrdering2D(nx, ny, sfc);
4431 MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
4432
4433 for (k = 0; k < nx*ny; k++)
4434 {
4435 i = sfc[2*k + 0];
4436 j = sfc[2*k + 1];
4437 ind[0] = i + j*(nx+1);
4438 ind[1] = i + 1 +j*(nx+1);
4439 ind[2] = i + 1 + (j+1)*(nx+1);
4440 ind[3] = i + (j+1)*(nx+1);
4441 elements[k] = new Quadrilateral(ind);
4442 }
4443 }
4444 else
4445 {
4446 k = 0;
4447 for (j = 0; j < ny; j++)
4448 {
4449 for (i = 0; i < nx; i++)
4450 {
4451 ind[0] = i + j*(nx+1);
4452 ind[1] = i + 1 +j*(nx+1);
4453 ind[2] = i + 1 + (j+1)*(nx+1);
4454 ind[3] = i + (j+1)*(nx+1);
4455 elements[k] = new Quadrilateral(ind);
4456 k++;
4457 }
4458 }
4459 }
4460
4461 // Sets boundary elements and the corresponding indices of vertices
4462 int m = (nx+1)*ny;
4463 for (i = 0; i < nx; i++)
4464 {
4465 boundary[i] = new Segment(i, i+1, 1);
4466 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4467 }
4468 m = nx+1;
4469 for (j = 0; j < ny; j++)
4470 {
4471 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4472 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4473 }
4474 }
4475 // Creates triangular mesh
4476 else if (type == Element::TRIANGLE)
4477 {
4478 NumOfVertices = (nx+1) * (ny+1);
4479 NumOfElements = 2 * nx * ny;
4480 NumOfBdrElements = 2 * nx + 2 * ny;
4481
4482 vertices.SetSize(NumOfVertices);
4483 elements.SetSize(NumOfElements);
4484 boundary.SetSize(NumOfBdrElements);
4485
4486 real_t cx, cy;
4487 int ind[3];
4488
4489 // Sets vertices and the corresponding coordinates
4490 k = 0;
4491 for (j = 0; j < ny+1; j++)
4492 {
4493 cy = ((real_t) j / ny) * sy;
4494 for (i = 0; i < nx+1; i++)
4495 {
4496 cx = ((real_t) i / nx) * sx;
4497 vertices[k](0) = cx;
4498 vertices[k](1) = cy;
4499 k++;
4500 }
4501 }
4502
4503 // Sets the elements and the corresponding indices of vertices
4504 k = 0;
4505 for (j = 0; j < ny; j++)
4506 {
4507 for (i = 0; i < nx; i++)
4508 {
4509 ind[0] = i + j*(nx+1);
4510 ind[1] = i + 1 + (j+1)*(nx+1);
4511 ind[2] = i + (j+1)*(nx+1);
4512 elements[k] = new Triangle(ind);
4513 k++;
4514 ind[1] = i + 1 + j*(nx+1);
4515 ind[2] = i + 1 + (j+1)*(nx+1);
4516 elements[k] = new Triangle(ind);
4517 k++;
4518 }
4519 }
4520
4521 // Sets boundary elements and the corresponding indices of vertices
4522 int m = (nx+1)*ny;
4523 for (i = 0; i < nx; i++)
4524 {
4525 boundary[i] = new Segment(i, i+1, 1);
4526 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4527 }
4528 m = nx+1;
4529 for (j = 0; j < ny; j++)
4530 {
4531 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4532 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4533 }
4534
4535 // MarkTriMeshForRefinement(); // done in Finalize(...)
4536 }
4537 else
4538 {
4539 MFEM_ABORT("Unsupported element type.");
4540 }
4541
4542 SetMeshGen();
4544
4545 if (generate_edges == 1)
4546 {
4547 el_to_edge = new Table;
4549 GenerateFaces();
4551 }
4552 else
4553 {
4554 NumOfEdges = 0;
4555 }
4556
4557 NumOfFaces = 0;
4558
4559 attributes.Append(1);
4562
4563 // Finalize(...) can be called after this method, if needed
4564}
4565
4566void Mesh::Make1D(int n, real_t sx)
4567{
4568 int j, ind[1];
4569
4570 SetEmpty();
4571
4572 Dim = 1;
4573 spaceDim = 1;
4574
4575 NumOfVertices = n + 1;
4576 NumOfElements = n;
4577 NumOfBdrElements = 2;
4578 vertices.SetSize(NumOfVertices);
4579 elements.SetSize(NumOfElements);
4580 boundary.SetSize(NumOfBdrElements);
4581
4582 // Sets vertices and the corresponding coordinates
4583 for (j = 0; j < n+1; j++)
4584 {
4585 vertices[j](0) = ((real_t) j / n) * sx;
4586 }
4587
4588 // Sets elements and the corresponding indices of vertices
4589 for (j = 0; j < n; j++)
4590 {
4591 elements[j] = new Segment(j, j+1, 1);
4592 }
4593
4594 // Sets the boundary elements
4595 ind[0] = 0;
4596 boundary[0] = new Point(ind, 1);
4597 ind[0] = n;
4598 boundary[1] = new Point(ind, 2);
4599
4600 NumOfEdges = 0;
4601 NumOfFaces = 0;
4602
4603 SetMeshGen();
4604 GenerateFaces();
4605
4606 // Set be_to_face
4608 be_to_face[0] = 0;
4609 be_to_face[1] = n;
4610
4611 attributes.Append(1);
4613}
4614
4615Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
4616 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4617{
4618 Dim = mesh.Dim;
4619 spaceDim = mesh.spaceDim;
4620
4624 NumOfEdges = mesh.NumOfEdges;
4625 NumOfFaces = mesh.NumOfFaces;
4628
4629 meshgen = mesh.meshgen;
4630 mesh_geoms = mesh.mesh_geoms;
4631
4632 // Create the new Mesh instance without a record of its refinement history
4633 sequence = 0;
4634 nodes_sequence = 0;
4636
4637 // Duplicate the elements
4638 elements.SetSize(NumOfElements);
4639 for (int i = 0; i < NumOfElements; i++)
4640 {
4641 elements[i] = mesh.elements[i]->Duplicate(this);
4642 }
4643
4644 // Copy the vertices
4645 mesh.vertices.Copy(vertices);
4646
4647 // Duplicate the boundary
4648 boundary.SetSize(NumOfBdrElements);
4649 for (int i = 0; i < NumOfBdrElements; i++)
4650 {
4651 boundary[i] = mesh.boundary[i]->Duplicate(this);
4652 }
4653
4654 // Copy the element-to-face Table, el_to_face
4655 el_to_face = (mesh.el_to_face) ? new Table(*mesh.el_to_face) : NULL;
4656
4657 // Copy the boundary-to-face Array, be_to_face.
4659
4660 // Copy the element-to-edge Table, el_to_edge
4661 el_to_edge = (mesh.el_to_edge) ? new Table(*mesh.el_to_edge) : NULL;
4662
4663 // Copy the boundary-to-edge Table, bel_to_edge (3D)
4664 bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
4665
4666 // Duplicate the faces and faces_info.
4667 faces.SetSize(mesh.faces.Size());
4668 for (int i = 0; i < faces.Size(); i++)
4669 {
4670 Element *face = mesh.faces[i]; // in 1D the faces are NULL
4671 faces[i] = (face) ? face->Duplicate(this) : NULL;
4672 }
4673 mesh.faces_info.Copy(faces_info);
4674 mesh.nc_faces_info.Copy(nc_faces_info);
4675
4676 // Do NOT copy the element-to-element Table, el_to_el
4677 el_to_el = NULL;
4678
4679 // Do NOT copy the face-to-edge Table, face_edge and edge_face
4680 face_edge = NULL;
4681 edge_face = NULL;
4682 face_to_elem = NULL;
4683
4684 // Copy the edge-to-vertex Table, edge_vertex
4685 edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
4686
4687 // Copy the attributes and bdr_attributes
4690
4691 // Copy attribute and bdr_attribute names
4694
4695 // Deep copy the NURBSExtension.
4696#ifdef MFEM_USE_MPI
4697 ParNURBSExtension *pNURBSext =
4698 dynamic_cast<ParNURBSExtension *>(mesh.NURBSext);
4699 if (pNURBSext)
4700 {
4701 NURBSext = new ParNURBSExtension(*pNURBSext);
4702 }
4703 else
4704#endif
4705 {
4706 NURBSext = mesh.NURBSext ? new NURBSExtension(*mesh.NURBSext) : NULL;
4707 }
4708
4709 // Deep copy the NCMesh.
4710#ifdef MFEM_USE_MPI
4711 if (dynamic_cast<const ParMesh*>(&mesh))
4712 {
4713 ncmesh = NULL; // skip; will be done in ParMesh copy ctor
4714 }
4715 else
4716#endif
4717 {
4718 ncmesh = mesh.ncmesh ? new NCMesh(*mesh.ncmesh) : NULL;
4719 }
4720
4721 // Duplicate the Nodes, including the FiniteElementCollection and the
4722 // FiniteElementSpace
4723 if (mesh.Nodes && copy_nodes)
4724 {
4725 FiniteElementSpace *fes = mesh.Nodes->FESpace();
4726 const FiniteElementCollection *fec = fes->FEColl();
4727 FiniteElementCollection *fec_copy =
4729 FiniteElementSpace *fes_copy =
4730 new FiniteElementSpace(*fes, this, fec_copy);
4731 Nodes = new GridFunction(fes_copy);
4732 Nodes->MakeOwner(fec_copy);
4733 *Nodes = *mesh.Nodes;
4734 own_nodes = 1;
4735 }
4736 else
4737 {
4738 Nodes = mesh.Nodes;
4739 own_nodes = 0;
4740 }
4741
4742 // copy attribute caches
4745}
4746
4748{
4749 Swap(mesh, true);
4750}
4751
4753{
4754 Swap(mesh, true);
4755 return *this;
4756}
4757
4758Mesh Mesh::LoadFromFile(const std::string &filename, int generate_edges,
4759 int refine, bool fix_orientation)
4760{
4761 Mesh mesh;
4762 named_ifgzstream imesh(filename);
4763 if (!imesh) { MFEM_ABORT("Mesh file not found: " << filename << '\n'); }
4764 else { mesh.Load(imesh, generate_edges, refine, fix_orientation); }
4765 return mesh;
4766}
4767
4769{
4770 Mesh mesh;
4771 mesh.Make1D(n, sx);
4772 // mesh.Finalize(); not needed in this case
4773 return mesh;
4774}
4775
4777 int nx, int ny, Element::Type type, bool generate_edges,
4778 real_t sx, real_t sy, bool sfc_ordering)
4779{
4780 Mesh mesh;
4781 mesh.Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
4782 mesh.Finalize(true); // refine = true
4783 return mesh;
4784}
4785
4787 int nx, int ny, int nz, Element::Type type,
4788 real_t sx, real_t sy, real_t sz, bool sfc_ordering)
4789{
4790 Mesh mesh;
4791 mesh.Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
4792 mesh.Finalize(true); // refine = true
4793 return mesh;
4794}
4795
4797 real_t sx, real_t sy, real_t sz)
4798{
4799 Mesh mesh;
4800 mesh.Make3D24TetsFromHex(nx, ny, nz, sx, sy, sz);
4801 mesh.Finalize(false, false);
4802 return mesh;
4803}
4804
4806 real_t sx, real_t sy)
4807{
4808 Mesh mesh;
4809 mesh.Make2D4TrisFromQuad(nx, ny, sx, sy);
4810 mesh.Finalize(false, false);
4811 return mesh;
4812}
4813
4815 real_t sx, real_t sy)
4816{
4817 Mesh mesh;
4818 mesh.Make2D5QuadsFromQuad(nx, ny, sx, sy);
4819 mesh.Finalize(false, false);
4820 return mesh;
4821}
4822
4823Mesh Mesh::MakeRefined(Mesh &orig_mesh, int ref_factor, int ref_type)
4824{
4825 Mesh mesh;
4826 Array<int> ref_factors(orig_mesh.GetNE());
4827 ref_factors = ref_factor;
4828 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
4829 return mesh;
4830}
4831
4832Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
4833 int ref_type)
4834{
4835 Mesh mesh;
4836 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
4837 return mesh;
4838}
4839
4840Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
4841 bool fix_orientation)
4842 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4843{
4844 // Initialization as in the default constructor
4845 SetEmpty();
4846
4847 named_ifgzstream imesh(filename);
4848 if (!imesh)
4849 {
4850 // Abort with an error message.
4851 MFEM_ABORT("Mesh file not found: " << filename << '\n');
4852 }
4853 else
4854 {
4855 Load(imesh, generate_edges, refine, fix_orientation);
4856 }
4857}
4858
4859Mesh::Mesh(std::istream &input, int generate_edges, int refine,
4860 bool fix_orientation)
4861 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4862{
4863 SetEmpty();
4864 Load(input, generate_edges, refine, fix_orientation);
4865}
4866
4867void Mesh::ChangeVertexDataOwnership(real_t *vertex_data, int len_vertex_data,
4868 bool zerocopy)
4869{
4870 // A dimension of 3 is now required since we use mfem::Vertex objects as PODs
4871 // and these object have a hardcoded double[3] entry
4872 MFEM_VERIFY(len_vertex_data >= NumOfVertices * 3,
4873 "Not enough vertices in external array : "
4874 "len_vertex_data = "<< len_vertex_data << ", "
4875 "NumOfVertices * 3 = " << NumOfVertices * 3);
4876 // Allow multiple calls to this method with the same vertex_data
4877 if (vertex_data == (real_t *)(vertices.GetData()))
4878 {
4879 MFEM_ASSERT(!vertices.OwnsData(), "invalid ownership");
4880 return;
4881 }
4882 if (!zerocopy)
4883 {
4884 memcpy(vertex_data, vertices.GetData(),
4885 NumOfVertices * 3 * sizeof(real_t));
4886 }
4887 // Vertex is POD double[3]
4888 vertices.MakeRef(reinterpret_cast<Vertex*>(vertex_data), NumOfVertices);
4889}
4890
4891Mesh::Mesh(real_t *vertices_, int num_vertices,
4892 int *element_indices, Geometry::Type element_type,
4893 int *element_attributes, int num_elements,
4894 int *boundary_indices, Geometry::Type boundary_type,
4895 int *boundary_attributes, int num_boundary_elements,
4896 int dimension, int space_dimension)
4897 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4898{
4899 if (space_dimension == -1)
4900 {
4901 space_dimension = dimension;
4902 }
4903
4904 InitMesh(dimension, space_dimension, /*num_vertices*/ 0, num_elements,
4905 num_boundary_elements);
4906
4907 int element_index_stride = Geometry::NumVerts[element_type];
4908 int boundary_index_stride = num_boundary_elements > 0 ?
4909 Geometry::NumVerts[boundary_type] : 0;
4910
4911 // assuming Vertex is POD
4912 vertices.MakeRef(reinterpret_cast<Vertex*>(vertices_), num_vertices);
4913 NumOfVertices = num_vertices;
4914
4915 for (int i = 0; i < num_elements; i++)
4916 {
4917 elements[i] = NewElement(element_type);
4918 elements[i]->SetVertices(element_indices + i * element_index_stride);
4919 elements[i]->SetAttribute(element_attributes[i]);
4920 }
4921 NumOfElements = num_elements;
4922
4923 for (int i = 0; i < num_boundary_elements; i++)
4924 {
4925 boundary[i] = NewElement(boundary_type);
4926 boundary[i]->SetVertices(boundary_indices + i * boundary_index_stride);
4927 boundary[i]->SetAttribute(boundary_attributes[i]);
4928 }
4929 NumOfBdrElements = num_boundary_elements;
4930
4932}
4933
4935 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4936{
4937 SetEmpty();
4938 /// make an internal copy of the NURBSExtension
4939 NURBSext = new NURBSExtension(ext);
4940
4941 Dim = NURBSext->Dimension();
4945
4948
4949 vertices.SetSize(NumOfVertices);
4950 if (NURBSext->HavePatches())
4951 {
4953 const int vdim = NURBSext->GetPatchSpaceDimension();
4954 FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
4956 Nodes = new GridFunction(fes);
4957 Nodes->MakeOwner(fec);
4959 own_nodes = 1;
4961 for (int i = 0; i < spaceDim; i++)
4962 {
4963 Vector vert_val;
4964 Nodes->GetNodalValues(vert_val, i+1);
4965 for (int j = 0; j < NumOfVertices; j++)
4966 {
4967 vertices[j](i) = vert_val(j);
4968 }
4969 }
4970 }
4971 else
4972 {
4973 MFEM_ABORT("NURBS mesh has no patches.");
4974 }
4975 FinalizeMesh();
4976}
4977
4979{
4980 switch (geom)
4981 {
4982 case Geometry::POINT: return (new Point);
4983 case Geometry::SEGMENT: return (new Segment);
4984 case Geometry::TRIANGLE: return (new Triangle);
4985 case Geometry::SQUARE: return (new Quadrilateral);
4987#ifdef MFEM_USE_MEMALLOC
4988 return TetMemory.Alloc();
4989#else
4990 return (new Tetrahedron);
4991#endif
4992 case Geometry::CUBE: return (new Hexahedron);
4993 case Geometry::PRISM: return (new Wedge);
4994 case Geometry::PYRAMID: return (new Pyramid);
4995 default:
4996 MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
4997 }
4998
4999 return NULL;
5000}
5001
5003{
5004 int geom, nv, *v;
5005 Element *el;
5006
5007 input >> geom;
5008 el = NewElement(geom);
5009 MFEM_VERIFY(el, "Unsupported element type: " << geom);
5010 nv = el->GetNVertices();
5011 v = el->GetVertices();
5012 for (int i = 0; i < nv; i++)
5013 {
5014 input >> v[i];
5015 }
5016
5017 return el;
5018}
5019
5020void Mesh::PrintElementWithoutAttr(const Element *el, std::ostream &os)
5021{
5022 os << el->GetGeometryType();
5023 const int nv = el->GetNVertices();
5024 const int *v = el->GetVertices();
5025 for (int j = 0; j < nv; j++)
5026 {
5027 os << ' ' << v[j];
5028 }
5029 os << '\n';
5030}
5031
5032Element *Mesh::ReadElement(std::istream &input)
5033{
5034 int attr;
5035 Element *el;
5036
5037 input >> attr;
5038 el = ReadElementWithoutAttr(input);
5039 el->SetAttribute(attr);
5040
5041 return el;
5042}
5043
5044void Mesh::PrintElement(const Element *el, std::ostream &os)
5045{
5046 os << el->GetAttribute() << ' ';
5048}
5049
5051{
5052 meshgen = mesh_geoms = 0;
5053 for (int i = 0; i < NumOfElements; i++)
5054 {
5055 const Element::Type type = GetElement(i)->GetType();
5056 switch (type)
5057 {
5060 case Element::TRIANGLE:
5062 case Element::SEGMENT:
5063 mesh_geoms |= (1 << Geometry::SEGMENT);
5064 case Element::POINT:
5065 mesh_geoms |= (1 << Geometry::POINT);
5066 meshgen |= 1;
5067 break;
5068
5070 mesh_geoms |= (1 << Geometry::CUBE);
5072 mesh_geoms |= (1 << Geometry::SQUARE);
5073 mesh_geoms |= (1 << Geometry::SEGMENT);
5074 mesh_geoms |= (1 << Geometry::POINT);
5075 meshgen |= 2;
5076 break;
5077
5078 case Element::WEDGE:
5079 mesh_geoms |= (1 << Geometry::PRISM);
5080 mesh_geoms |= (1 << Geometry::SQUARE);
5082 mesh_geoms |= (1 << Geometry::SEGMENT);
5083 mesh_geoms |= (1 << Geometry::POINT);
5084 meshgen |= 4;
5085 break;
5086
5087 case Element::PYRAMID:
5088 mesh_geoms |= (1 << Geometry::PYRAMID);
5089 mesh_geoms |= (1 << Geometry::SQUARE);
5091 mesh_geoms |= (1 << Geometry::SEGMENT);
5092 mesh_geoms |= (1 << Geometry::POINT);
5093 meshgen |= 8;
5094 break;
5095
5096 default:
5097 MFEM_ABORT("invalid element type: " << type);
5098 break;
5099 }
5100 }
5101}
5102
5103void Mesh::Loader(std::istream &input, int generate_edges,
5104 std::string parse_tag)
5105{
5106 int curved = 0, read_gf = 1;
5107 bool finalize_topo = true;
5108
5109 if (!input)
5110 {
5111 MFEM_ABORT("Input stream is not open");
5112 }
5113
5114 Clear();
5115
5116 string mesh_type;
5117 input >> ws;
5118 getline(input, mesh_type);
5119 filter_dos(mesh_type);
5120
5121 // MFEM's conforming mesh formats
5122 int mfem_version = 0;
5123 if (mesh_type == "MFEM mesh v1.0") { mfem_version = 10; } // serial
5124 else if (mesh_type == "MFEM mesh v1.2") { mfem_version = 12; } // parallel
5125 else if (mesh_type == "MFEM mesh v1.3") { mfem_version = 13; } // attr sets
5126
5127 // MFEM nonconforming mesh format
5128 // (NOTE: previous v1.1 is now under this branch for backward compatibility)
5129 int mfem_nc_version = 0;
5130 if (mesh_type == "MFEM NC mesh v1.0") { mfem_nc_version = 10; }
5131 else if (mesh_type == "MFEM NC mesh v1.1") { mfem_nc_version = 11; }
5132 else if (mesh_type == "MFEM mesh v1.1") { mfem_nc_version = 1 /*legacy*/; }
5133
5134 if (mfem_version)
5135 {
5136 // Formats mfem_v12 and newer have a tag indicating the end of the mesh
5137 // section in the stream. A user provided parse tag can also be provided
5138 // via the arguments. For example, if this is called from parallel mesh
5139 // object, it can indicate to read until parallel mesh section begins.
5140 if (mfem_version >= 12 && parse_tag.empty())
5141 {
5142 parse_tag = "mfem_mesh_end";
5143 }
5144 ReadMFEMMesh(input, mfem_version, curved);
5145 }
5146 else if (mfem_nc_version)
5147 {
5148 MFEM_ASSERT(ncmesh == NULL, "internal error");
5149 int is_nc = 1;
5150
5151#ifdef MFEM_USE_MPI
5152 ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
5153 if (pmesh)
5154 {
5155 MFEM_VERIFY(mfem_nc_version >= 10,
5156 "Legacy nonconforming format (MFEM mesh v1.1) cannot be "
5157 "used to load a parallel nonconforming mesh, sorry.");
5158
5159 ncmesh = new ParNCMesh(pmesh->GetComm(),
5160 input, mfem_nc_version, curved, is_nc);
5161 }
5162 else
5163#endif
5164 {
5165 ncmesh = new NCMesh(input, mfem_nc_version, curved, is_nc);
5166 }
5168
5169 if (!is_nc)
5170 {
5171 // special case for backward compatibility with MFEM <=4.2:
5172 // if the "vertex_parents" section is missing in the v1.1 format,
5173 // the mesh is treated as conforming
5174 delete ncmesh;
5175 ncmesh = NULL;
5176 }
5177 }
5178 else if (mesh_type == "linemesh") // 1D mesh
5179 {
5180 ReadLineMesh(input);
5181 }
5182 else if (mesh_type == "areamesh2" || mesh_type == "curved_areamesh2")
5183 {
5184 if (mesh_type == "curved_areamesh2")
5185 {
5186 curved = 1;
5187 }
5188 ReadNetgen2DMesh(input, curved);
5189 }
5190 else if (mesh_type == "NETGEN" || mesh_type == "NETGEN_Neutral_Format")
5191 {
5192 ReadNetgen3DMesh(input);
5193 }
5194 else if (mesh_type == "TrueGrid")
5195 {
5196 ReadTrueGridMesh(input);
5197 }
5198 else if (mesh_type.rfind("# vtk DataFile Version") == 0)
5199 {
5200 int major_vtk_version = mesh_type[mesh_type.length()-3] - '0';
5201 // int minor_vtk_version = mesh_type[mesh_type.length()-1] - '0';
5202 MFEM_VERIFY(major_vtk_version >= 2 && major_vtk_version <= 4,
5203 "Unsupported VTK format");
5204 ReadVTKMesh(input, curved, read_gf, finalize_topo);
5205 }
5206 else if (mesh_type.rfind("<VTKFile ") == 0 || mesh_type.rfind("<?xml") == 0)
5207 {
5208 ReadXML_VTKMesh(input, curved, read_gf, finalize_topo, mesh_type);
5209 }
5210 else if (mesh_type == "MFEM NURBS mesh v1.0")
5211 {
5212 ReadNURBSMesh(input, curved, read_gf);
5213 }
5214 else if (mesh_type == "MFEM NURBS NC-patch mesh v1.0")
5215 {
5216 ReadNURBSMesh(input, curved, read_gf, true, true); // Spacing is required
5217 }
5218 else if (mesh_type == "MFEM NURBS mesh v1.1")
5219 {
5220 ReadNURBSMesh(input, curved, read_gf, true);
5221 }
5222 else if (mesh_type == "MFEM INLINE mesh v1.0")
5223 {
5224 ReadInlineMesh(input, generate_edges);
5225 return; // done with inline mesh construction
5226 }
5227 else if (mesh_type == "$MeshFormat") // Gmsh
5228 {
5229 ReadGmshMesh(input);
5230 finalize_topo = false; // Gmsh mesh reader already finalizes the topology
5231 curved = Nodes != nullptr;
5232 read_gf = false;
5233 }
5234 else if
5235 ((mesh_type.size() > 2 &&
5236 mesh_type[0] == 'C' && mesh_type[1] == 'D' && mesh_type[2] == 'F') ||
5237 (mesh_type.size() > 3 &&
5238 mesh_type[1] == 'H' && mesh_type[2] == 'D' && mesh_type[3] == 'F'))
5239 {
5240 named_ifgzstream *mesh_input = dynamic_cast<named_ifgzstream *>(&input);
5241 if (mesh_input)
5242 {
5243#ifdef MFEM_USE_NETCDF
5244 ReadCubit(mesh_input->filename, curved, read_gf);
5245#else
5246 MFEM_ABORT("NetCDF support requires configuration with"
5247 " MFEM_USE_NETCDF=YES");
5248 return;
5249#endif
5250 }
5251 else
5252 {
5253 MFEM_ABORT("Can not determine Cubit mesh filename!"
5254 " Use mfem::named_ifgzstream for input.");
5255 return;
5256 }
5257 }
5258 else
5259 {
5260 MFEM_ABORT("Unknown input mesh format: " << mesh_type);
5261 return;
5262 }
5263
5264 // at this point the following should be defined:
5265 // 1) Dim
5266 // 2) NumOfElements, elements
5267 // 3) NumOfBdrElements, boundary
5268 // 4) NumOfVertices, with allocated space in vertices
5269 // 5) curved
5270 // 5a) if curved == 0, vertices must be defined
5271 // 5b) if curved != 0 and read_gf != 0,
5272 // 'input' must point to a GridFunction
5273 // 5c) if curved != 0 and read_gf == 0,
5274 // vertices and Nodes must be defined
5275 // optional:
5276 // 1) el_to_edge may be allocated (as in the case of P2 VTK meshes)
5277 // 2) ncmesh may be allocated
5278
5279 // FinalizeTopology() will:
5280 // - assume that generate_edges is true
5281 // - assume that refine is false
5282 // - does not check the orientation of regular and boundary elements
5283 if (finalize_topo)
5284 {
5285 // don't generate any boundary elements, especially in parallel
5286 bool generate_bdr = false;
5287
5288 FinalizeTopology(generate_bdr);
5289 }
5290
5291 if (curved && read_gf)
5292 {
5293 Nodes = new GridFunction(this, input);
5294
5295 own_nodes = 1;
5297 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
5298
5299 // Set vertex coordinates from the 'Nodes'
5301 }
5302
5303 // If a parse tag was supplied, keep reading the stream until the tag is
5304 // encountered.
5305 if (mfem_version >= 12)
5306 {
5307 string line;
5308 do
5309 {
5310 skip_comment_lines(input, '#');
5311 MFEM_VERIFY(input.good(), "Required mesh-end tag not found");
5312 getline(input, line);
5313 filter_dos(line);
5314 // mfem v1.2 may not have parse_tag in it, e.g. if trying to read a
5315 // serial mfem v1.2 mesh as parallel with "mfem_serial_mesh_end" as
5316 // parse_tag. That's why, regardless of parse_tag, we stop reading if
5317 // we find "mfem_mesh_end" which is required by mfem v1.2 format.
5318 if (line == "mfem_mesh_end") { break; }
5319 }
5320 while (line != parse_tag);
5321 }
5322 else if (mfem_nc_version >= 10)
5323 {
5324 string ident;
5325 skip_comment_lines(input, '#');
5326 input >> ident;
5327 MFEM_VERIFY(ident == "mfem_mesh_end",
5328 "invalid mesh: end of file tag not found");
5329 }
5330
5332 {
5333 string ident;
5334 skip_comment_lines(input, '#');
5335 // Check for the optional section "patch_cp"
5336 if (input.peek() == 'p')
5337 {
5338 input >> ident;
5339 MFEM_VERIFY(ident == "patch_cp", "Invalid mesh format");
5341 }
5342 }
5343
5344 // Finalize(...) should be called after this, if needed.
5345}
5346
5347Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
5348 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
5349{
5350 int i, j, ie, ib, iv, *v, nv;
5351 Element *el;
5352 Mesh *m;
5353
5354 SetEmpty();
5355
5356 Dim = mesh_array[0]->Dimension();
5357 spaceDim = mesh_array[0]->SpaceDimension();
5358
5359 if (mesh_array[0]->NURBSext)
5360 {
5361 // assuming the pieces form a partition of a NURBS mesh
5362 NURBSext = new NURBSExtension(mesh_array, num_pieces);
5363
5366
5368
5369 // NumOfBdrElements = NURBSext->GetNBE();
5370 // NURBSext->GetBdrElementTopo(boundary);
5371
5372 Array<int> lvert_vert, lelem_elem;
5373
5374 // Here, for visualization purposes, we copy the boundary elements from
5375 // the individual pieces which include the interior boundaries. This
5376 // creates 'boundary' array that is different from the one generated by
5377 // the NURBSExtension which, in particular, makes the boundary-dof table
5378 // invalid. This, in turn, causes GetBdrElementTransformation to not
5379 // function properly.
5380 NumOfBdrElements = 0;
5381 for (i = 0; i < num_pieces; i++)
5382 {
5383 NumOfBdrElements += mesh_array[i]->GetNBE();
5384 }
5385 boundary.SetSize(NumOfBdrElements);
5386 vertices.SetSize(NumOfVertices);
5387 ib = 0;
5388 for (i = 0; i < num_pieces; i++)
5389 {
5390 m = mesh_array[i];
5391 m->NURBSext->GetVertexLocalToGlobal(lvert_vert);
5392 m->NURBSext->GetElementLocalToGlobal(lelem_elem);
5393 // copy the element attributes
5394 for (j = 0; j < m->GetNE(); j++)
5395 {
5396 elements[lelem_elem[j]]->SetAttribute(m->GetAttribute(j));
5397 }
5398 // copy the boundary
5399 for (j = 0; j < m->GetNBE(); j++)
5400 {
5401 el = m->GetBdrElement(j)->Duplicate(this);
5402 v = el->GetVertices();
5403 nv = el->GetNVertices();
5404 for (int k = 0; k < nv; k++)
5405 {
5406 v[k] = lvert_vert[v[k]];
5407 }
5408 boundary[ib++] = el;
5409 }
5410 // copy the vertices
5411 for (j = 0; j < m->GetNV(); j++)
5412 {
5413 vertices[lvert_vert[j]].SetCoords(m->SpaceDimension(),
5414 m->GetVertex(j));
5415 }
5416 }
5417 }
5418 else // not a NURBS mesh
5419 {
5420 NumOfElements = 0;
5421 NumOfBdrElements = 0;
5422 NumOfVertices = 0;
5423 for (i = 0; i < num_pieces; i++)
5424 {
5425 m = mesh_array[i];
5426 NumOfElements += m->GetNE();
5427 NumOfBdrElements += m->GetNBE();
5428 NumOfVertices += m->GetNV();
5429 }
5430 elements.SetSize(NumOfElements);
5431 boundary.SetSize(NumOfBdrElements);
5432 vertices.SetSize(NumOfVertices);
5433 ie = ib = iv = 0;
5434 for (i = 0; i < num_pieces; i++)
5435 {
5436 m = mesh_array[i];
5437 // copy the elements
5438 for (j = 0; j < m->GetNE(); j++)
5439 {
5440 el = m->GetElement(j)->Duplicate(this);
5441 v = el->GetVertices();
5442 nv = el->GetNVertices();
5443 for (int k = 0; k < nv; k++)
5444 {
5445 v[k] += iv;
5446 }
5447 elements[ie++] = el;
5448 }
5449 // copy the boundary elements
5450 for (j = 0; j < m->GetNBE(); j++)
5451 {
5452 el = m->GetBdrElement(j)->Duplicate(this);
5453 v = el->GetVertices();
5454 nv = el->GetNVertices();
5455 for (int k = 0; k < nv; k++)
5456 {
5457 v[k] += iv;
5458 }
5459 boundary[ib++] = el;
5460 }
5461 // copy the vertices
5462 for (j = 0; j < m->GetNV(); j++)
5463 {
5464 vertices[iv++].SetCoords(m->SpaceDimension(), m->GetVertex(j));
5465 }
5466 }
5467 }
5468
5470
5471 // copy the nodes (curvilinear meshes)
5472 GridFunction *g = mesh_array[0]->GetNodes();
5473 if (g)
5474 {
5475 Array<GridFunction *> gf_array(num_pieces);
5476 for (i = 0; i < num_pieces; i++)
5477 {
5478 gf_array[i] = mesh_array[i]->GetNodes();
5479 }
5480 Nodes = new GridFunction(this, gf_array, num_pieces);
5481 own_nodes = 1;
5482 }
5483
5484#ifdef MFEM_DEBUG
5487#endif
5488}
5489
5490Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
5491 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
5492{
5493 Array<int> ref_factors(orig_mesh->GetNE());
5494 ref_factors = ref_factor;
5495 MakeRefined_(*orig_mesh, ref_factors, ref_type);
5496}
5497
5498void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> &ref_factors,
5499 int ref_type)
5500{
5501 SetEmpty();
5502 Dim = orig_mesh.Dimension();
5503 spaceDim = orig_mesh.SpaceDimension();
5504
5505 int orig_ne = orig_mesh.GetNE();
5506 MFEM_VERIFY(ref_factors.Size() == orig_ne,
5507 "Number of refinement factors must equal number of elements")
5508 MFEM_VERIFY(orig_ne == 0 ||
5509 ref_factors.Min() >= 1, "Refinement factor must be >= 1");
5510 const int q_type = BasisType::GetQuadrature1D(ref_type);
5511 MFEM_VERIFY(Quadrature1D::CheckClosed(q_type) != Quadrature1D::Invalid,
5512 "Invalid refinement type. Must use closed basis type.");
5513
5514 int min_ref = orig_ne > 0 ? ref_factors.Min() : 1;
5515 int max_ref = orig_ne > 0 ? ref_factors.Max() : 1;
5516
5517 bool var_order = (min_ref != max_ref);
5518
5519 // variable order space can only be constructed over an NC mesh
5520 if (var_order) { orig_mesh.EnsureNCMesh(true); }
5521
5522 // Construct a scalar H1 FE space of order ref_factor and use its dofs as
5523 // the indices of the new, refined vertices.
5524 H1_FECollection rfec(min_ref, Dim, ref_type);
5525 FiniteElementSpace rfes(&orig_mesh, &rfec);
5526
5527 if (var_order)
5528 {
5529 rfes.SetRelaxedHpConformity(false);
5530 for (int i = 0; i < orig_ne; i++)
5531 {
5532 rfes.SetElementOrder(i, ref_factors[i]);
5533 }
5534 rfes.Update(false);
5535 }
5536
5537 // Set the number of vertices, set the actual coordinates later
5538 NumOfVertices = rfes.GetNDofs();
5539 vertices.SetSize(NumOfVertices);
5540
5541 Array<int> rdofs;
5542 DenseMatrix phys_pts;
5543 GeometryRefiner refiner(q_type);
5544
5545 // Add refined elements and set vertex coordinates
5546 for (int el = 0; el < orig_ne; el++)
5547 {
5548 Geometry::Type geom = orig_mesh.GetElementGeometry(el);
5549 int attrib = orig_mesh.GetAttribute(el);
5550 int nvert = Geometry::NumVerts[geom];
5551 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el]);
5552
5553 rfes.GetElementDofs(el, rdofs);
5554 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
5555 const FiniteElement *rfe = rfes.GetFE(el);
5556 orig_mesh.GetElementTransformation(el)->Transform(rfe->GetNodes(),
5557 phys_pts);
5558 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[el]);
5559 for (int i = 0; i < phys_pts.Width(); i++)
5560 {
5561 vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
5562 }
5563 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5564 {
5565 Element *elem = NewElement(geom);
5566 elem->SetAttribute(attrib);
5567 int *v = elem->GetVertices();
5568 for (int k = 0; k < nvert; k++)
5569 {
5570 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5571 v[k] = rdofs[c2h_map[cid]];
5572 }
5573 AddElement(elem);
5574 }
5575 }
5576
5577 if (Dim > 2)
5578 {
5579 GetElementToFaceTable(false);
5580 GenerateFaces();
5581 }
5582
5583 // Add refined boundary elements
5584 for (int el = 0; el < orig_mesh.GetNBE(); el++)
5585 {
5586 int i, info;
5587 orig_mesh.GetBdrElementAdjacentElement(el, i, info);
5588 Geometry::Type geom = orig_mesh.GetBdrElementGeometry(el);
5589 int attrib = orig_mesh.GetBdrAttribute(el);
5590 int nvert = Geometry::NumVerts[geom];
5591 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[i]);
5592
5593 rfes.GetBdrElementDofs(el, rdofs);
5594 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
5595 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[i]);
5596 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5597 {
5598 Element *elem = NewElement(geom);
5599 elem->SetAttribute(attrib);
5600 int *v = elem->GetVertices();
5601 for (int k = 0; k < nvert; k++)
5602 {
5603 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5604 v[k] = rdofs[c2h_map[cid]];
5605 }
5606 AddBdrElement(elem);
5607 }
5608 }
5609 FinalizeTopology(false);
5610 sequence = orig_mesh.GetSequence() + 1;
5612
5613 // Set up the nodes of the new mesh (if the original mesh has nodes). The new
5614 // mesh is always straight-sided (i.e. degree 1 finite element space), but
5615 // the nodes are required for e.g. periodic meshes.
5616 if (orig_mesh.GetNodes())
5617 {
5618 bool discont = orig_mesh.GetNodalFESpace()->IsDGSpace();
5619 Ordering::Type dof_ordering = orig_mesh.GetNodalFESpace()->GetOrdering();
5620 Mesh::SetCurvature(1, discont, spaceDim, dof_ordering);
5621 FiniteElementSpace *nodal_fes = Nodes->FESpace();
5622 const FiniteElementCollection *nodal_fec = nodal_fes->FEColl();
5623 H1_FECollection vertex_fec(1, Dim);
5624 Array<int> dofs;
5625 int el_counter = 0;
5626 for (int iel = 0; iel < orig_ne; iel++)
5627 {
5628 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(iel);
5629 int nvert = Geometry::NumVerts[geom];
5630 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[iel]);
5631 rfes.GetElementDofs(iel, rdofs);
5632 const FiniteElement *rfe = rfes.GetFE(iel);
5633 orig_mesh.GetElementTransformation(iel)->Transform(rfe->GetNodes(),
5634 phys_pts);
5635 const int *node_map = NULL;
5636 const H1_FECollection *h1_fec =
5637 dynamic_cast<const H1_FECollection *>(nodal_fec);
5638 if (h1_fec != NULL) { node_map = h1_fec->GetDofMap(geom); }
5639 const int *vertex_map = vertex_fec.GetDofMap(geom);
5640 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[iel]);
5641 for (int jel = 0; jel < RG.RefGeoms.Size()/nvert; jel++)
5642 {
5643 nodal_fes->GetElementVDofs(el_counter++, dofs);
5644 for (int iv_lex=0; iv_lex<nvert; ++iv_lex)
5645 {
5646 // convert from lexicographic to vertex index
5647 int iv = vertex_map[iv_lex];
5648 // index of vertex of current element in phys_pts matrix
5649 int pt_idx = c2h_map[RG.RefGeoms[iv+nvert*jel]];
5650 // index of current vertex into DOF array
5651 int node_idx = node_map ? node_map[iv_lex] : iv_lex;
5652 for (int d=0; d<spaceDim; ++d)
5653 {
5654 (*Nodes)[dofs[node_idx + d*nvert]] = phys_pts(d,pt_idx);
5655 }
5656 }
5657 }
5658 }
5659 }
5660
5661 // Setup the data for the coarse-fine refinement transformations
5662 CoarseFineTr.embeddings.SetSize(GetNE());
5663 // First, compute total number of point matrices that we need per geometry
5664 // and the offsets into that array
5665 using GeomRef = std::pair<Geometry::Type, int>;
5666 std::map<GeomRef, int> point_matrices_offsets;
5667 int n_point_matrices[Geometry::NumGeom] = {}; // initialize to zero
5668 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
5669 {
5670 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
5671 // Have we seen this pair of (geometry, refinement level) before?
5672 GeomRef id(geom, ref_factors[el_coarse]);
5673 if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
5674 {
5675 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el_coarse]);
5676 int nvert = Geometry::NumVerts[geom];
5677 int nref_el = RG.RefGeoms.Size()/nvert;
5678 // If not, then store the offset and add to the size required
5679 point_matrices_offsets[id] = n_point_matrices[geom];
5680 n_point_matrices[geom] += nref_el;
5681 }
5682 }
5683
5684 // Set up the sizes
5685 for (int geom = 0; geom < Geometry::NumGeom; ++geom)
5686 {
5687 int nmatrices = n_point_matrices[geom];
5688 int nvert = Geometry::NumVerts[geom];
5689 CoarseFineTr.point_matrices[geom].SetSize(Dim, nvert, nmatrices);
5690 }
5691
5692 // Compute the point matrices and embeddings
5693 int el_fine = 0;
5694 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
5695 {
5696 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
5697 int ref = ref_factors[el_coarse];
5698 int offset = point_matrices_offsets[GeomRef(geom, ref)];
5699 int nvert = Geometry::NumVerts[geom];
5700 RefinedGeometry &RG = *refiner.Refine(geom, ref);
5701 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5702 {
5703 DenseMatrix &Pj = CoarseFineTr.point_matrices[geom](offset + j);
5704 for (int k = 0; k < nvert; k++)
5705 {
5706 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5707 const IntegrationPoint &ip = RG.RefPts[cid];
5708 ip.Get(Pj.GetColumn(k), Dim);
5709 }
5710
5711 Embedding &emb = CoarseFineTr.embeddings[el_fine];
5712 emb.geom = geom;
5713 emb.parent = el_coarse;
5714 emb.matrix = offset + j;
5715 ++el_fine;
5716 }
5717 }
5718
5719 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
5720
5721 // The check below is disabled because is fails for parallel meshes with
5722 // interior "boundary" element that, when such "boundary" element is between
5723 // two elements on different processors.
5724 // MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
5725}
5726
5728{
5729 Mesh mesh;
5730 auto parent_elements = mesh.MakeSimplicial_(orig_mesh, NULL);
5731 if (orig_mesh.GetNodes() != nullptr)
5732 {
5733 mesh.MakeHigherOrderSimplicial_(orig_mesh, parent_elements);
5734 }
5735 return mesh;
5736}
5737
5738Array<int> Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
5739{
5740 MFEM_VERIFY(const_cast<Mesh&>(orig_mesh).CheckElementOrientation(false) == 0,
5741 "Mesh::MakeSimplicial requires a properly oriented input mesh");
5742 MFEM_VERIFY(orig_mesh.Conforming(),
5743 "Mesh::MakeSimplicial does not support non-conforming meshes.")
5744
5745 int dim = orig_mesh.Dimension();
5746 int sdim = orig_mesh.SpaceDimension();
5747
5748 if (dim == 1)
5749 {
5750 Mesh copy(orig_mesh);
5751 Swap(copy, true);
5752 Array<int> parent_elements(GetNE());
5753 std::iota(parent_elements.begin(), parent_elements.end(), 0);
5754 return parent_elements;
5755 }
5756
5757 int nv = orig_mesh.GetNV();
5758 int ne = orig_mesh.GetNE();
5759 int nbe = orig_mesh.GetNBE();
5760
5761 static int num_subdivisions[Geometry::NUM_GEOMETRIES];
5762 num_subdivisions[Geometry::POINT] = 1;
5763 num_subdivisions[Geometry::SEGMENT] = 1;
5764 num_subdivisions[Geometry::TRIANGLE] = 1;
5765 num_subdivisions[Geometry::TETRAHEDRON] = 1;
5766 num_subdivisions[Geometry::SQUARE] = 2;
5767 num_subdivisions[Geometry::PRISM] = 3;
5768 num_subdivisions[Geometry::CUBE] = 6;
5769 // NOTE: some hexes may be subdivided into only 5 tets, so this is an
5770 // estimate only. The actual number of created tets may be less, so the
5771 // elements array will need to be shrunk after mesh creation.
5772 int new_ne = 0, new_nbe = 0;
5773 for (int i=0; i<ne; ++i)
5774 {
5775 new_ne += num_subdivisions[orig_mesh.GetElementBaseGeometry(i)];
5776 }
5777 for (int i=0; i<nbe; ++i)
5778 {
5779 new_nbe += num_subdivisions[orig_mesh.GetBdrElementGeometry(i)];
5780 }
5781
5782 InitMesh(dim, sdim, nv, new_ne, new_nbe);
5783
5784 // Vertices of the new mesh are same as the original mesh
5785 NumOfVertices = nv;
5786 for (int i=0; i<nv; ++i)
5787 {
5788 vertices[i].SetCoords(sdim, orig_mesh.vertices[i]());
5789 }
5790
5791 // We need a global vertex numbering to identify which diagonals to split
5792 // (quad faces are split using the diagonal originating from the smallest
5793 // global vertex number). Use the supplied global numbering, if it is
5794 // non-NULL, otherwise use the local numbering.
5795 Array<int> vglobal_id;
5796 if (vglobal == nullptr)
5797 {
5798 vglobal_id.SetSize(nv);
5799 std::iota(vglobal_id.begin(), vglobal_id.end(), 0);
5800 vglobal = vglobal_id.GetData();
5801 }
5802
5803 // Number of vertices per element
5804 constexpr int nv_tri = 3, nv_quad = 4, nv_tet = 4, nv_prism = 6, nv_hex = 8;
5805 constexpr int quad_ntris = 2; // NTriangles per quad
5806 constexpr int prism_ntets = 3; // NTets per prism
5807 // Map verts of quad to verts of tri, in two possible configurations.
5808 // quad_trimap[i][0,2,4] is the first triangle, and quad_trimap[i][1,3,5] is
5809 // the second, for each configuration.
5810 static const int quad_trimap[2][nv_tri*quad_ntris] =
5811 {
5812 {
5813 0, 0,
5814 1, 2,
5815 2, 3
5816 },{
5817 0, 1,
5818 1, 2,
5819 3, 3
5820 }
5821 };
5822 static const int prism_rot[nv_prism*nv_prism] =
5823 {
5824 0, 1, 2, 3, 4, 5,
5825 1, 2, 0, 4, 5, 3,
5826 2, 0, 1, 5, 3, 4,
5827 3, 5, 4, 0, 2, 1,
5828 4, 3, 5, 1, 0, 2,
5829 5, 4, 3, 2, 1, 0
5830 };
5831 static const int prism_f[nv_quad] = {1, 2, 5, 4};
5832 static const int prism_tetmaps[2][nv_prism*prism_ntets] =
5833 {
5834 {
5835 0, 0, 0,
5836 1, 1, 4,
5837 2, 5, 5,
5838 5, 4, 3
5839 },{
5840 0, 0, 0,
5841 1, 4, 4,
5842 2, 2, 5,
5843 4, 5, 3
5844 }
5845 };
5846 static const int hex_rot[nv_hex*nv_hex] =
5847 {
5848 0, 1, 2, 3, 4, 5, 6, 7,
5849 1, 0, 4, 5, 2, 3, 7, 6,
5850 2, 1, 5, 6, 3, 0, 4, 7,
5851 3, 0, 1, 2, 7, 4, 5, 6,
5852 4, 0, 3, 7, 5, 1, 2, 6,
5853 5, 1, 0, 4, 6, 2, 3, 7,
5854 6, 2, 1, 5, 7, 3, 0, 4,
5855 7, 3, 2, 6, 4, 0, 1, 5
5856 };
5857 static const int hex_f0[nv_quad] = {1, 2, 6, 5};
5858 static const int hex_f1[nv_quad] = {2, 3, 7, 6};
5859 static const int hex_f2[nv_quad] = {4, 5, 6, 7};
5860 static const int num_rot[8] = {0, 1, 2, 0, 0, 2, 1, 0};
5861 static const int hex_tetmap0[nv_tet*5] =
5862 {
5863 0, 0, 0, 0, 2,
5864 1, 2, 2, 5, 7,
5865 2, 7, 3, 7, 5,
5866 5, 5, 7, 4, 6
5867 };
5868 static const int hex_tetmap1[nv_tet*6] =
5869 {
5870 0, 0, 1, 0, 0, 1,
5871 5, 1, 6, 7, 7, 7,
5872 7, 7, 7, 2, 1, 6,
5873 4, 5, 5, 3, 2, 2
5874 };
5875 static const int hex_tetmap2[nv_tet*6] =
5876 {
5877 0, 0, 0, 0, 0, 0,
5878 4, 3, 7, 1, 3, 6,
5879 5, 7, 4, 2, 6, 5,
5880 6, 6, 6, 5, 2, 2
5881 };
5882 static const int hex_tetmap3[nv_tet*6] =
5883 {
5884 0, 0, 0, 0, 1, 1,
5885 2, 3, 7, 5, 5, 6,
5886 3, 7, 4, 6, 6, 2,
5887 6, 6, 6, 4, 0, 0
5888 };
5889 static const int *hex_tetmaps[4] =
5890 {
5891 hex_tetmap0, hex_tetmap1, hex_tetmap2, hex_tetmap3
5892 };
5893
5894 auto find_min = [](const int *a, int n) { return std::min_element(a,a+n)-a; };
5895
5896 Array<int> parent_elems;
5897 for (int i=0; i<ne; ++i)
5898 {
5899 const int *v = orig_mesh.elements[i]->GetVertices();
5900 const int attrib = orig_mesh.GetAttribute(i);
5901 const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(i);
5902
5903 if (num_subdivisions[orig_geom] == 1)
5904 {
5905 // (num_subdivisions[orig_geom] == 1) implies that the element does not
5906 // need to be further split (it is either a segment, triangle, or
5907 // tetrahedron), and so it is left unchanged.
5908 Element *e = NewElement(orig_geom);
5909 e->SetAttribute(attrib);
5910 e->SetVertices(v);
5911 AddElement(e);
5912 parent_elems.Append(i);
5913 }
5914 else if (orig_geom == Geometry::SQUARE)
5915 {
5916 for (int itri=0; itri<quad_ntris; ++itri)
5917 {
5919 e->SetAttribute(attrib);
5920 int *v2 = e->GetVertices();
5921 for (int iv=0; iv<nv_tri; ++iv)
5922 {
5923 v2[iv] = v[quad_trimap[0][itri + iv*quad_ntris]];
5924 }
5925 AddElement(e);
5926 parent_elems.Append(i);
5927 }
5928 }
5929 else if (orig_geom == Geometry::PRISM)
5930 {
5931 int vg[nv_prism];
5932 for (int iv=0; iv<nv_prism; ++iv) { vg[iv] = vglobal[v[iv]]; }
5933 // Rotate the vertices of the prism so that the smallest vertex index
5934 // is in the first place
5935 int irot = find_min(vg, nv_prism);
5936 for (int iv=0; iv<nv_prism; ++iv)
5937 {
5938 int jv = prism_rot[iv + irot*nv_prism];
5939 vg[iv] = v[jv];
5940 }
5941 // Two cases according to which diagonal splits third quad face
5942 int q[nv_quad];
5943 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[prism_f[iv]]]; }
5944 int j = find_min(q, nv_quad);
5945 const int *tetmap = (j == 0 || j == 2) ? prism_tetmaps[0] : prism_tetmaps[1];
5946 for (int itet=0; itet<prism_ntets; ++itet)
5947 {
5949 e->SetAttribute(attrib);
5950 int *v2 = e->GetVertices();
5951 for (int iv=0; iv<nv_tet; ++iv)
5952 {
5953 v2[iv] = vg[tetmap[itet + iv*prism_ntets]];
5954 }
5955 AddElement(e);
5956 parent_elems.Append(i);
5957 }
5958 }
5959 else if (orig_geom == Geometry::CUBE)
5960 {
5961 int vg[nv_hex];
5962 for (int iv=0; iv<nv_hex; ++iv) { vg[iv] = vglobal[v[iv]]; }
5963
5964 // Rotate the vertices of the hex so that the smallest vertex index is
5965 // in the first place
5966 int irot = find_min(vg, nv_hex);
5967 for (int iv=0; iv<nv_hex; ++iv)
5968 {
5969 int jv = hex_rot[iv + irot*nv_hex];
5970 vg[iv] = v[jv];
5971 }
5972
5973 int q[nv_quad];
5974 // Bitmask is three binary digits, each digit is 1 if the diagonal of
5975 // the corresponding face goes through the 7th vertex, and 0 if not.
5976 int bitmask = 0;
5977 int j;
5978 // First quad face
5979 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f0[iv]]]; }
5980 j = find_min(q, nv_quad);
5981 if (j == 0 || j == 2) { bitmask += 4; }
5982 // Second quad face
5983 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f1[iv]]]; }
5984 j = find_min(q, nv_quad);
5985 if (j == 1 || j == 3) { bitmask += 2; }
5986 // Third quad face
5987 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f2[iv]]]; }
5988 j = find_min(q, nv_quad);
5989 if (j == 0 || j == 2) { bitmask += 1; }
5990
5991 // Apply rotations
5992 int nrot = num_rot[bitmask];
5993 for (int k=0; k<nrot; ++k)
5994 {
5995 int vtemp;
5996 vtemp = vg[1];
5997 vg[1] = vg[4];
5998 vg[4] = vg[3];
5999 vg[3] = vtemp;
6000 vtemp = vg[5];
6001 vg[5] = vg[7];
6002 vg[7] = vg[2];
6003 vg[2] = vtemp;
6004 }
6005
6006 // Sum up nonzero bits in bitmask
6007 int ndiags = ((bitmask&4) >> 2) + ((bitmask&2) >> 1) + (bitmask&1);
6008 int ntets = (ndiags == 0) ? 5 : 6;
6009 const int *tetmap = hex_tetmaps[ndiags];
6010 for (int itet=0; itet<ntets; ++itet)
6011 {
6013 e->SetAttribute(attrib);
6014 int *v2 = e->GetVertices();
6015 for (int iv=0; iv<nv_tet; ++iv)
6016 {
6017 v2[iv] = vg[tetmap[itet + iv*ntets]];
6018 }
6019 AddElement(e);
6020 parent_elems.Append(i);
6021 }
6022 }
6023 }
6024 // In 3D, shrink the element array because some hexes have only 5 tets
6025 if (dim == 3) { elements.SetSize(NumOfElements); }
6026
6027 for (int i=0; i<nbe; ++i)
6028 {
6029 const int *v = orig_mesh.boundary[i]->GetVertices();
6030 const int attrib = orig_mesh.GetBdrAttribute(i);
6031 const Geometry::Type orig_geom = orig_mesh.GetBdrElementGeometry(i);
6032 if (num_subdivisions[orig_geom] == 1)
6033 {
6034 Element *be = NewElement(orig_geom);
6035 be->SetAttribute(attrib);
6036 be->SetVertices(v);
6037 AddBdrElement(be);
6038 }
6039 else if (orig_geom == Geometry::SQUARE)
6040 {
6041 int vg[nv_quad];
6042 for (int iv=0; iv<nv_quad; ++iv) { vg[iv] = vglobal[v[iv]]; }
6043 // Split quad according the smallest (global) vertex
6044 int iv_min = find_min(vg, nv_quad);
6045 int isplit = (iv_min == 0 || iv_min == 2) ? 0 : 1;
6046 for (int itri=0; itri<quad_ntris; ++itri)
6047 {
6049 be->SetAttribute(attrib);
6050 int *v2 = be->GetVertices();
6051 for (int iv=0; iv<nv_tri; ++iv)
6052 {
6053 v2[iv] = v[quad_trimap[isplit][itri + iv*quad_ntris]];
6054 }
6055 AddBdrElement(be);
6056 }
6057 }
6058 else
6059 {
6060 MFEM_ABORT("Unreachable");
6061 }
6062 }
6063
6064 FinalizeTopology(false);
6065 sequence = orig_mesh.GetSequence();
6066 last_operation = orig_mesh.last_operation;
6067
6068 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
6069 MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
6070
6071 return parent_elems;
6072}
6073
6074
6076 const Array<int> &parent_elements)
6077{
6078 // Higher order associated to vertices are unchanged, and those for
6079 // previously existing edges. DOFs associated to new elements need to be set.
6080 const int sdim = orig_mesh.SpaceDimension();
6081 auto *orig_fespace = orig_mesh.GetNodes()->FESpace();
6082 SetCurvature(orig_fespace->GetMaxElementOrder(), orig_fespace->IsDGSpace(),
6083 orig_mesh.SpaceDimension(), orig_fespace->GetOrdering());
6084
6085 // The dofs associated with vertices are unchanged, but there can be new dofs
6086 // associated to edges, faces and volumes. Additionally, because we know that
6087 // the set of vertices is unchanged by the splitting operation, we can use
6088 // the vertices to map local coordinates of the "child" elements (the new
6089 // simplices introduced), from the "parent" element (the quad, prism, hex
6090 // that was split).
6091
6092 // For segment, triangle and tetrahedron, the dof values are copied directly.
6093 // For the others, we have to construct a map from the Node locations in the
6094 // new simplex to the parent non-simplex element. This could be sped up by
6095 // not repeatedly access the original FE as the accesses will be coherent
6096 // (i.e. all child elems are consecutive).
6097
6098 Array<int> edofs; // element dofs in new element
6099 Array<int> parent_vertices, child_vertices; // vertices of parent and child.
6100 Array<int> node_map; // node indices of parent from child.
6101 Vector edofvals; // values of elements dofs in original element
6102 // Storage for evaluating node function on parent element, at node locations
6103 // of child element
6104 DenseMatrix shape; // ndof_coarse x nnode_refined.
6105 DenseMatrix point_matrix; // sdim x nnode_refined
6107 child_nodes_in_parent; // The parent nodes that correspond to the child nodes
6108 for (int i = 0; i < parent_elements.Size(); i++)
6109 {
6110 const int ip = parent_elements[i];
6111 const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(ip);
6112 orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
6113 switch (orig_geom)
6114 {
6115 case Geometry::Type::SEGMENT : // fall through
6116 case Geometry::Type::TRIANGLE : // fall through
6118 GetNodes()->FESpace()->GetElementVDofs(i, edofs);
6119 GetNodes()->SetSubVector(edofs, edofvals);
6120 break;
6121 case Geometry::Type::CUBE : // fall through
6122 case Geometry::Type::PRISM : // fall through
6123 case Geometry::Type::PYRAMID : // fall through
6125 {
6126 // Extract the vertices of parent and child, can then form the
6127 // map from child reference coordinates to parent reference
6128 // coordinates. Exploit the fact that for Nodes, the vertex
6129 // entries come first, and their indexing matches the vertex
6130 // numbering. Thus we have already have an inverse index map.
6131 orig_mesh.GetElementVertices(ip, parent_vertices);
6132 GetElementVertices(i, child_vertices);
6133 node_map.SetSize(0);
6134 for (auto cv : child_vertices)
6135 for (int ipv = 0; ipv < parent_vertices.Size(); ipv++)
6136 if (cv == parent_vertices[ipv])
6137 {
6138 node_map.Append(ipv);
6139 break;
6140 }
6141 MFEM_ASSERT(node_map.Size() == Geometry::NumVerts[GetElementBaseGeometry(i)],
6142 "!");
6143 // node_map now says which of the parent vertex nodes map to each
6144 // of the child vertex nodes. Using this can build a basis in the
6145 // parent element from child Node values, exploit the linearity
6146 // to then transform all nodes.
6147 child_nodes_in_parent.SetSize(0);
6148 const auto *orig_FE = orig_mesh.GetNodes()->FESpace()->GetFE(ip);
6149 for (auto pn : node_map)
6150 {
6151 child_nodes_in_parent.Append(orig_FE->GetNodes()[pn]);
6152 }
6153 const auto *simplex_FE = GetNodes()->FESpace()->GetFE(i);
6154 shape.SetSize(orig_FE->GetDof(),
6155 simplex_FE->GetDof()); // One set of evaluations per simplex dof.
6156 Vector col;
6157 for (int j = 0; j < simplex_FE->GetNodes().Size(); j++)
6158 {
6159 const auto &simplex_node = simplex_FE->GetNodes()[j];
6160 IntegrationPoint simplex_node_in_orig;
6161 // Handle the 2D vs 3D case by multiplying .z by zero.
6162 simplex_node_in_orig.Set3(
6163 child_nodes_in_parent[0].x +
6164 simplex_node.x * (child_nodes_in_parent[1].x - child_nodes_in_parent[0].x)
6165 + simplex_node.y * (child_nodes_in_parent[2].x - child_nodes_in_parent[0].x)
6166 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].x -
6167 child_nodes_in_parent[0].x),
6168 child_nodes_in_parent[0].y +
6169 simplex_node.x * (child_nodes_in_parent[1].y - child_nodes_in_parent[0].y)
6170 + simplex_node.y * (child_nodes_in_parent[2].y - child_nodes_in_parent[0].y)
6171 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].y -
6172 child_nodes_in_parent[0].y),
6173 child_nodes_in_parent[0].z +
6174 simplex_node.x * (child_nodes_in_parent[1].z - child_nodes_in_parent[0].z)
6175 + simplex_node.y * (child_nodes_in_parent[2].z - child_nodes_in_parent[0].z)
6176 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].z -
6177 child_nodes_in_parent[0].z));
6178 shape.GetColumnReference(j, col);
6179 orig_FE->CalcShape(simplex_node_in_orig, col);
6180 }
6181 // All the non-simplex basis functions have now been evaluated at
6182 // all the simplex basis function node locations. Now evaluate
6183 // the summations and place back into the Nodes vector.
6184 orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
6185 // Dof values are always returned as
6186 // [[x_1,x_2,x_3,...],
6187 // [y_1,y_2,y_3,...],
6188 // [z_1,z_2,z_3,...]]
6189 DenseMatrix edofvals_mat(edofvals.GetData(), orig_FE->GetDof(), sdim);
6190 point_matrix.SetSize(simplex_FE->GetDof(), sdim);
6191 MultAtB(shape, edofvals_mat, point_matrix);
6192 GetNodes()->FESpace()->GetElementVDofs(i, edofs);
6193 GetNodes()->SetSubVector(edofs, point_matrix.GetData());
6194 }
6195 break;
6196 case Geometry::Type::POINT : // fall through
6199 MFEM_ABORT("Internal Error!");
6200 }
6201 }
6202}
6203
6204
6205Mesh Mesh::MakePeriodic(const Mesh &orig_mesh, const std::vector<int> &v2v)
6206{
6207 Mesh periodic_mesh(orig_mesh, true); // Make a copy of the original mesh
6208 const FiniteElementSpace *nodal_fes = orig_mesh.GetNodalFESpace();
6209 int nodal_order = nodal_fes ? nodal_fes->GetMaxElementOrder() : 1;
6210 periodic_mesh.SetCurvature(nodal_order, true);
6211
6212 // renumber element vertices
6213 for (int i = 0; i < periodic_mesh.GetNE(); i++)
6214 {
6215 Element *el = periodic_mesh.GetElement(i);
6216 int *v = el->GetVertices();
6217 int nv = el->GetNVertices();
6218 for (int j = 0; j < nv; j++)
6219 {
6220 v[j] = v2v[v[j]];
6221 }
6222 }
6223 // renumber boundary element vertices
6224 for (int i = 0; i < periodic_mesh.GetNBE(); i++)
6225 {
6226 Element *el = periodic_mesh.GetBdrElement(i);
6227 int *v = el->GetVertices();
6228 int nv = el->GetNVertices();
6229 for (int j = 0; j < nv; j++)
6230 {
6231 v[j] = v2v[v[j]];
6232 }
6233 }
6234
6235 periodic_mesh.RemoveUnusedVertices();
6236 return periodic_mesh;
6237}
6238
6240 const std::vector<Vector> &translations, real_t tol) const
6241{
6242 const int sdim = SpaceDimension();
6243
6244 Vector coord(sdim), at(sdim), dx(sdim);
6245 Vector xMax(sdim), xMin(sdim), xDiff(sdim);
6246 xMax = xMin = xDiff = 0.0;
6247
6248 // Get a list of all vertices on the boundary
6249 unordered_set<int> bdr_v;
6250 for (int be = 0; be < GetNBE(); be++)
6251 {
6252 Array<int> dofs;
6253 GetBdrElementVertices(be,dofs);
6254
6255 for (int i = 0; i < dofs.Size(); i++)
6256 {
6257 bdr_v.insert(dofs[i]);
6258
6259 coord = GetVertex(dofs[i]);
6260 for (int j = 0; j < sdim; j++)
6261 {
6262 xMax[j] = max(xMax[j], coord[j]);
6263 xMin[j] = min(xMin[j], coord[j]);
6264 }
6265 }
6266 }
6267 add(xMax, -1.0, xMin, xDiff);
6268 real_t dia = xDiff.Norml2(); // compute mesh diameter
6269
6270 // We now identify coincident vertices. Several originally distinct vertices
6271 // may become coincident under the periodic mapping. One of these vertices
6272 // will be identified as the "primary" vertex, and all other coincident
6273 // vertices will be considered as "replicas".
6274
6275 // replica2primary[v] is the index of the primary vertex of replica `v`
6276 unordered_map<int, int> replica2primary;
6277 // primary2replicas[v] is a set of indices of replicas of primary vertex `v`
6278 unordered_map<int, unordered_set<int>> primary2replicas;
6279
6280 // Create a KD-tree containing all the boundary vertices
6281 std::unique_ptr<KDTreeBase<int,real_t>> kdtree;
6282 if (sdim == 1) { kdtree.reset(new KDTree1D); }
6283 else if (sdim == 2) { kdtree.reset(new KDTree2D); }
6284 else if (sdim == 3) { kdtree.reset(new KDTree3D); }
6285 else { MFEM_ABORT("Invalid space dimension."); }
6286
6287 // We begin with the assumption that all vertices are primary, and that there
6288 // are no replicas.
6289 for (const int v : bdr_v)
6290 {
6291 primary2replicas[v];
6292 kdtree->AddPoint(GetVertex(v), v);
6293 }
6294
6295 kdtree->Sort();
6296
6297 // Make `r` and all of `r`'s replicas be replicas of `p`. Delete `r` from the
6298 // list of primary vertices.
6299 auto make_replica = [&replica2primary, &primary2replicas](int r, int p)
6300 {
6301 if (r == p) { return; }
6302 primary2replicas[p].insert(r);
6303 replica2primary[r] = p;
6304 for (const int s : primary2replicas[r])
6305 {
6306 primary2replicas[p].insert(s);
6307 replica2primary[s] = p;
6308 }
6309 primary2replicas.erase(r);
6310 };
6311
6312 for (unsigned int i = 0; i < translations.size(); i++)
6313 {
6314 for (int vi : bdr_v)
6315 {
6316 coord = GetVertex(vi);
6317 add(coord, translations[i], at);
6318
6319 const int vj = kdtree->FindClosestPoint(at.GetData());
6320 coord = GetVertex(vj);
6321 add(at, -1.0, coord, dx);
6322
6323 if (dx.Norml2() > dia*tol) { continue; }
6324
6325 // The two vertices vi and vj are coincident.
6326
6327 // Are vertices `vi` and `vj` already primary?
6328 const bool pi = primary2replicas.find(vi) != primary2replicas.end();
6329 const bool pj = primary2replicas.find(vj) != primary2replicas.end();
6330
6331 if (pi && pj)
6332 {
6333 // Both vertices are currently primary
6334 // Demote `vj` to be a replica of `vi`
6335 make_replica(vj, vi);
6336 }
6337 else if (pi && !pj)
6338 {
6339 // `vi` is primary and `vj` is a replica
6340 const int owner_of_vj = replica2primary[vj];
6341 // Make `vi` and its replicas be replicas of `vj`'s owner
6342 make_replica(vi, owner_of_vj);
6343 }
6344 else if (!pi && pj)
6345 {
6346 // `vi` is currently a replica and `vj` is currently primary
6347 // Make `vj` and its replicas be replicas of `vi`'s owner
6348 const int owner_of_vi = replica2primary[vi];
6349 make_replica(vj, owner_of_vi);
6350 }
6351 else
6352 {
6353 // Both vertices are currently replicas
6354 // Make `vj`'s owner and all of its owner's replicas be replicas
6355 // of `vi`'s owner
6356 const int owner_of_vi = replica2primary[vi];
6357 const int owner_of_vj = replica2primary[vj];
6358 make_replica(owner_of_vj, owner_of_vi);
6359 }
6360 }
6361 }
6362
6363 std::vector<int> v2v(GetNV());
6364 for (size_t i = 0; i < v2v.size(); i++)
6365 {
6366 v2v[i] = static_cast<int>(i);
6367 }
6368 for (const auto &r2p : replica2primary)
6369 {
6370 v2v[r2p.first] = r2p.second;
6371 }
6372 return v2v;
6373}
6374
6375void Mesh::RefineNURBSFromFile(std::string ref_file)
6376{
6377 MFEM_VERIFY(NURBSext,"Mesh::RefineNURBSFromFile: Not a NURBS mesh!");
6378 mfem::out<<"Refining NURBS from refinement file: "<<ref_file<<endl;
6379
6380 int nkv;
6381 ifstream input(ref_file);
6382 input >> nkv;
6383
6384 // Check if the number of knot vectors in the refinement file and mesh match
6385 if ( nkv != NURBSext->GetNKV())
6386 {
6387 mfem::out<<endl;
6388 mfem::out<<"Knot vectors in ref_file: "<<nkv<<endl;
6389 mfem::out<<"Knot vectors in NURBSExt: "<<NURBSext->GetNKV()<<endl;
6390 MFEM_ABORT("Refine file does not have the correct number of knot vectors");
6391 }
6392
6393 // Read knot vectors from file
6394 Array<Vector *> knotVec(nkv);
6395 for (int kv = 0; kv < nkv; kv++)
6396 {
6397 knotVec[kv] = new Vector();
6398 knotVec[kv]-> Load(input);
6399 }
6400 input.close();
6401
6402 // Insert knots
6403 KnotInsert(knotVec);
6404
6405 // Delete knots
6406 for (int kv = 0; kv < nkv; kv++)
6407 {
6408 delete knotVec[kv];
6409 }
6410}
6411
6413{
6414 if (NURBSext == NULL)
6415 {
6416 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
6417 }
6418
6419 if (kv.Size() != NURBSext->GetNKV())
6420 {
6421 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
6422 }
6423
6425
6426 NURBSext->KnotInsert(kv);
6427
6428 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6429 sequence++;
6430
6431 UpdateNURBS();
6432}
6433
6435{
6436 if (NURBSext == NULL)
6437 {
6438 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
6439 }
6440
6441 if (kv.Size() != NURBSext->GetNKV())
6442 {
6443 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
6444 }
6445
6447
6448 NURBSext->KnotInsert(kv);
6449
6450 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6451 sequence++;
6452
6453 UpdateNURBS();
6454}
6455
6457{
6458 if (NURBSext == NULL)
6459 {
6460 mfem_error("Mesh::KnotRemove : Not a NURBS mesh!");
6461 }
6462
6463 if (kv.Size() != NURBSext->GetNKV())
6464 {
6465 mfem_error("Mesh::KnotRemove : KnotVector array size mismatch!");
6466 }
6467
6469
6470 NURBSext->KnotRemove(kv);
6471
6472 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6473 sequence++;
6474
6475 UpdateNURBS();
6476}
6477
6478void Mesh::RefineNURBSWithKVFactors(int rf, const std::string &kvf)
6479{
6480 RefineNURBS(true, 0.0, Array<int>(&rf, 1), kvf);
6481}
6482
6484{
6485 Array<int> rf_array(Dim);
6486 rf_array = rf;
6487 NURBSUniformRefinement(rf_array, tol);
6488}
6489
6491{
6492 MFEM_VERIFY(rf.Size() == Dim,
6493 "Refinement factors must be defined for each dimension");
6494
6495 RefineNURBS(false, tol, rf, "");
6496}
6497
6498void Mesh::RefineNURBS(bool usingKVF, real_t tol, const Array<int> &rf,
6499 const std::string &kvf)
6500{
6501 MFEM_VERIFY(NURBSext, "This type of refinement is only for NURBS meshes");
6503
6504 Array<int> cf;
6506
6507 bool cf1 = true;
6508 for (auto f : cf)
6509 {
6510 cf1 = (cf1 && f == 1);
6511 }
6512
6513 if (!cf1 && NURBSext->NonconformingPatches())
6514 {
6516 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6517 }
6518 else if (!cf1 && !NURBSext->NonconformingPatches())
6519 {
6520 MFEM_VERIFY(!usingKVF, "This refinement type is not supported for this"
6521 " NURBS mesh type");
6522 NURBSext->Coarsen(cf, tol);
6523
6524 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6525 sequence++;
6526 UpdateNURBS();
6527
6529 for (int i=0; i<cf.Size(); ++i) { cf[i] *= rf[i]; }
6531 }
6532
6533 if (cf1 || NURBSext->NonconformingPatches())
6534 {
6535 if (usingKVF || NURBSext->NonconformingPatches())
6536 {
6537 NURBSext->RefineWithKVFactors(rf[0], kvf, !cf1);
6538 }
6539 else
6540 {
6542 }
6543 }
6544
6545 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6546 sequence++;
6547
6548 UpdateNURBS();
6549}
6550
6551void Mesh::DegreeElevate(int rel_degree, int degree)
6552{
6553 if (NURBSext == NULL)
6554 {
6555 mfem_error("Mesh::DegreeElevate : Not a NURBS mesh!");
6556 }
6557
6559
6560 NURBSext->DegreeElevate(rel_degree, degree);
6561
6562 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6563 sequence++;
6564
6565 UpdateNURBS();
6566}
6567
6569{
6570 ResetLazyData();
6571
6573
6574 Dim = NURBSext->Dimension();
6575 spaceDim = Nodes->FESpace()->GetVDim();
6576
6577 if (NumOfElements != NURBSext->GetNE())
6578 {
6579 for (int i = 0; i < elements.Size(); i++)
6580 {
6582 }
6585 }
6586
6588 {
6589 for (int i = 0; i < boundary.Size(); i++)
6590 {
6592 }
6595 }
6596
6597 Nodes->FESpace()->Update();
6598 Nodes->Update();
6599 NodesUpdated();
6600 const int vdim = Nodes->FESpace()->GetVDim();
6602
6603 if (NumOfVertices != NURBSext->GetNV())
6604 {
6606 vertices.SetSize(NumOfVertices);
6607 int vd = Nodes->VectorDim();
6608 for (int i = 0; i < vd; i++)
6609 {
6610 Vector vert_val;
6611 Nodes->GetNodalValues(vert_val, i+1);
6612 for (int j = 0; j < NumOfVertices; j++)
6613 {
6614 vertices[j](i) = vert_val(j);
6615 }
6616 }
6617 }
6618
6619 if (el_to_edge)
6620 {
6622 }
6623
6624 if (el_to_face)
6625 {
6627 }
6628 GenerateFaces();
6629}
6630
6631void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
6632{
6633 SetEmpty();
6634
6635 // Read MFEM NURBS mesh v1.0 or 1.1 format
6636 string ident;
6637
6638 skip_comment_lines(input, '#');
6639
6640 input >> ident; // 'dimension'
6641 input >> Dim;
6642 spaceDim = Dim;
6643
6644 skip_comment_lines(input, '#');
6645
6646 input >> ident; // 'elements'
6647 input >> NumOfElements;
6648 elements.SetSize(NumOfElements);
6649 for (int j = 0; j < NumOfElements; j++)
6650 {
6651 elements[j] = ReadElement(input);
6652 }
6653
6654 skip_comment_lines(input, '#');
6655
6656 input >> ident; // 'boundary'
6657 input >> NumOfBdrElements;
6658 boundary.SetSize(NumOfBdrElements);
6659 for (int j = 0; j < NumOfBdrElements; j++)
6660 {
6661 boundary[j] = ReadElement(input);
6662 }
6663
6664 skip_comment_lines(input, '#');
6665
6666 input >> ident; // 'edges'
6667 input >> NumOfEdges;
6668 if (NumOfEdges > 0)
6669 {
6670 edge_vertex = new Table(NumOfEdges, 2);
6671 edge_to_ukv.SetSize(NumOfEdges);
6672 for (int j = 0; j < NumOfEdges; j++)
6673 {
6674 int *v = edge_vertex->GetRow(j);
6675 input >> edge_to_ukv[j] >> v[0] >> v[1];
6676 if (v[0] > v[1])
6677 {
6678 edge_to_ukv[j] = FlipIndexSign(edge_to_ukv[j]);
6679 }
6680 }
6681 }
6682 else
6683 {
6684 edge_to_ukv.SetSize(0);
6685 }
6686
6687 skip_comment_lines(input, '#');
6688
6689 input >> ident; // 'vertices'
6690 input >> NumOfVertices;
6691 vertices.SetSize(0);
6692
6694 CheckBdrElementOrientation(); // check and fix boundary element orientation
6695
6696 /* Generate edge to knotvector mapping if edges are not specified in the
6697 mesh file. See miniapps/nurbs/meshes/two-squares-nurbs-autoedge.mesh
6698 for an example */
6699 if (edge_to_ukv.Size() == 0)
6700 {
6701 Array<int> ukv_to_rpkv;
6702 GetEdgeToUniqueKnotvector(edge_to_ukv, ukv_to_rpkv);
6703 }
6704
6705 CorrectPatchTopoOrientations(edge_to_ukv);
6706}
6707
6709 Array<int> &ukv_to_rpkv) const
6710{
6711 const int dim = Dimension(); // topological (not physical) dimension
6712 const int NP = NumOfElements; // number of patches
6713 const int NPKV = NP * dim; // number of patch knotvectors
6714 constexpr int notset = -9999999;
6715 // Local edge index -> dimension convention
6716 auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
6717
6718 Array<int> v(2); // vertices of an edge
6719
6720 // 1D case is special: edge index = signed element index
6721 // ukv_to_rpkv = Identity
6722 if (dim == 1)
6723 {
6724 edge_to_ukv.SetSize(NP);
6725 ukv_to_rpkv.SetSize(NP);
6726 for (int i = 0; i < NP; i++)
6727 {
6728 GetElementVertices(i, v);
6729 // Sign is based on the edge's vertex indices
6730 edge_to_ukv[i] = (v[1] > v[0]) ? i : FlipIndexSign(i);
6731 ukv_to_rpkv[i] = i;
6732 }
6733 return;
6734 }
6735
6736 // Local (per-patch) variables
6737 Array<int> edges, oedges;
6738 // Edge index -> signed patch knotvector index (p*dim + d)
6739 Array<int> edge_to_pkv(NumOfEdges);
6740 edge_to_pkv.SetSize(NumOfEdges);
6741 edge_to_pkv = notset;
6742
6743 // Initialize pkv_map as identity - this is the storage for the
6744 // disjoint-set/union-find algorithm which will later be used
6745 // to get the map pkv_to_rpkv
6746 Array<int> pkv_map(NPKV);
6747 for (int i = 0; i < NPKV; i++)
6748 {
6749 pkv_map[i] = i;
6750 }
6751 std::function<int(int)> get_root;
6752 get_root = [&pkv_map, &get_root](int i) -> int
6753 {
6754 return (pkv_map[i] == i) ? i : get_root(pkv_map[i]);
6755 };
6756 auto unite = [&pkv_map, &get_root](int i, int j)
6757 {
6758 const int ri = get_root(i);
6759 const int rj = get_root(j);
6760 if (ri == rj) { return; }
6761 // keep the lowest index
6762 (ri < rj) ? pkv_map[rj] = ri : pkv_map[ri] = rj;
6763 };
6764
6765 // Get edge_to_pkv (one edge can link to multiple pkv) and pkv_map
6766 for (int p = 0; p < NP; p++)
6767 {
6768 GetElementEdges(p, edges, oedges);
6769
6770 // First loop checks for if edge has already been set
6771 for (int i = 0; i < edges.Size(); i++)
6772 {
6773 const int edge = edges[i];
6774 const int d = edge_to_dim(i);
6775 const int pkv = p*dim+d;
6776
6777 // We've set this edge already - link this index to it
6778 if (edge_to_pkv[edge] != notset)
6779 {
6780 const int pkv_other = UnsignIndex(edge_to_pkv[edge]);
6781 unite(pkv, pkv_other);
6782 }
6783 else
6784 {
6785 GetEdgeVertices(edge, v);
6786 // Sign is based on the edge's vertex indices
6787 edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : FlipIndexSign(pkv);
6788 }
6789 }
6790 }
6791
6792 // Construct the pkv_to_rpkv map by finding the lowest/root index
6793 Array<int> pkv_to_rpkv(NPKV);
6794 ukv_to_rpkv.SetSize(NPKV);
6795 for (int i = 0; i < NPKV; i++)
6796 {
6797 pkv_to_rpkv[i] = get_root(pkv_map[i]);
6798 ukv_to_rpkv[i] = pkv_to_rpkv[i];
6799 }
6800 ukv_to_rpkv.Sort(); // ukv is just a renumbering of rpkv
6801 ukv_to_rpkv.Unique();
6802
6803 // Create inverse map
6804 std::map<int, int> rpkv_to_ukv;
6805 for (int i = 0; i < ukv_to_rpkv.Size(); i++)
6806 {
6807 rpkv_to_ukv[ukv_to_rpkv[i]] = i;
6808 }
6809
6810 // Get edge_to_ukv = edge_to_pkv -> pkv_to_rpkv -> rpkv_to_ukv
6811 edge_to_ukv.SetSize(NumOfEdges);
6812 for (int i = 0; i < NumOfEdges; i++)
6813 {
6814 const int pkv = UnsignIndex(edge_to_pkv[i]);
6815 const int rpkv = pkv_to_rpkv[pkv];
6816 const int ukv = rpkv_to_ukv[rpkv];
6817 edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? FlipIndexSign(ukv) : ukv;
6818 }
6819
6820 CorrectPatchTopoOrientations(edge_to_ukv);
6821}
6822
6824{
6825 const int dim = Dimension(); // Topological (not physical) dimension
6826 if (dim == 1) { return; }
6827
6828 const Table *face2elem = GetFaceToElementTable();
6829 Array<int> pfaces, orient;
6830 Array<int> fe, feo;
6831
6832 // Finds elements sharing a face containing knotvector kv.
6833 auto faceNeighbors = [&](int p, int kv, std::unordered_set<int> &nghb)
6834 {
6835 if (dim == 2) { GetElementEdges(p, pfaces, orient); }
6836 else { GetElementFaces(p, pfaces, orient); }
6837
6838 for (auto face : pfaces)
6839 {
6840 // Check whether this face contains kv.
6841 GetFaceEdges(face, fe, feo);
6842 bool hasKV = false;
6843 for (auto e : fe)
6844 {
6845 const int skv = edge_to_ukv[e];
6846 if (skv == kv || FlipIndexSign(skv) == kv) { hasKV = true; }
6847 }
6848 if (hasKV)
6849 {
6850 Array<int> row;
6851 face2elem->GetRow(face, row);
6852 for (auto elem : row) { nghb.insert(elem); }
6853 }
6854 }
6855 };
6856
6857 std::vector<std::vector<int>> dir_edges;
6858 if (dim == 2)
6859 {
6860 dir_edges =
6861 {
6862 {0,2},
6863 {1,3}
6864 };
6865 }
6866 else
6867 {
6868 dir_edges =
6869 {
6870 {0,2,4,6},
6871 {1,3,5,7},
6872 {8,9,10,11}
6873 };
6874 }
6875
6876 Array<int> ukvs((dim == 2) ? 4 : 12);
6877 Array<int> pe, oe;
6878 bool initKV = false;
6879
6880 auto setPatchDirections = [&](int p, int kv, Array<bool> &edgeSet,
6881 std::unordered_set<int> &visited)
6882 {
6883 // Edges and orientations for this patch
6884 GetElementEdges(p, pe, oe);
6885
6886 // Get the signed unique knot vector indices
6887 for (int i = 0; i < pe.Size(); i++)
6888 {
6889 ukvs[i] = edge_to_ukv[pe[i]];
6890 ukvs[i] = (oe[i] < 0) ? FlipIndexSign(ukvs[i]) : ukvs[i];
6891 }
6892
6893 // Find the direction with this kv.
6894 int thisDir = -1;
6895 for (int d=0; d<dim; ++d) // Loop over directions.
6896 {
6897 const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
6898 if (skv == kv || FlipIndexSign(skv) == kv)
6899 {
6900 for (auto e : dir_edges[d])
6901 if (!edgeSet[pe[e]])
6902 {
6903 thisDir = d;
6904 }
6905 }
6906 }
6907 if (thisDir == -1)
6908 {
6909 return false;
6910 }
6911
6912 // For this direction, find any edge already set. If no edge is set, we
6913 // arbitrarily take the first.
6914 int ref_edge0 = dir_edges[thisDir][0];
6915 for (auto ref_edge : dir_edges[thisDir])
6916 {
6917 const int edge = pe[ref_edge];
6918 if (edgeSet[edge])
6919 {
6920 ref_edge0 = ref_edge;
6921 }
6922 }
6923
6924 if (initKV && !edgeSet[pe[ref_edge0]])
6925 {
6926 visited.erase(p);
6927 return false; // There is no set edge in this direction on this patch.
6928 }
6929
6930 initKV = true;
6931
6932 // Use ref_edge0 to set other edges in this direction.
6933 edgeSet[pe[ref_edge0]] = true;
6934 for (auto i : dir_edges[thisDir])
6935 {
6936 if (i == ref_edge0)
6937 {
6938 continue;
6939 }
6940
6941 const int edge = pe[i];
6942 if ((dim == 2 && ukvs[i] != FlipIndexSign(ukvs[ref_edge0])) ||
6943 (dim == 3 && ukvs[i] == FlipIndexSign(ukvs[ref_edge0])))
6944 {
6945 // Flip the sign of this edge
6946 MFEM_ASSERT(!edgeSet[edge], "");
6947 edge_to_ukv[edge] = FlipIndexSign(edge_to_ukv[edge]);
6948 }
6949
6950 edgeSet[edge] = true;
6951 }
6952
6953 return true;
6954 };
6955
6956 Array<bool> edgeSet(NumOfEdges); // Whether edge has orientation set
6957 edgeSet = false;
6958
6959 std::unordered_set<int> unset; // Patches with an unset edge
6960 for (int i=0; i<NumOfElements; ++i) { unset.insert(i); }
6961
6962 const int max_iter = 3 * NumOfElements;
6963 for (int iter=0; iter<max_iter; ++iter)
6964 {
6965 // Iteratively choose an unset patch (meaning not all edges have
6966 // orientation set), choose a knotvector index for which the corresponding
6967 // edges on this patch are not set, and sweep over all patches containing
6968 // this knotvector. The patch sweep is ordered, by maintaining an ordered
6969 // list `nextPatches` set by finding face-neighbor patches of visited
6970 // patches, where the common face contains the knotvector. When each patch
6971 // is visited, the edge orientations are set consistently. This iteration
6972 // terminates when all edges have been set on all patches.
6973
6974 std::list<int> nextPatches; // Next patches to visit, ordered
6975 std::unordered_set<int> nextSet; // nextPatches as a set
6976 std::unordered_set<int> visited; // Visit each patch only once
6977
6978 if (unset.size() == 0)
6979 {
6980 break;
6981 }
6982
6983 const int p0 = *unset.begin();
6984 nextPatches.push_back(p0); // Start from arbitrary unset patch
6985 nextSet.insert(p0);
6986
6987 // Choose an arbitrary unset direction for the first patch.
6988 GetElementEdges(p0, pe, oe);
6989 int unsetDim = -1;
6990 for (int d=0; d<dim; ++d) // Loop over dimensions.
6991 {
6992 for (auto e : dir_edges[d])
6993 if (!edgeSet[pe[e]])
6994 {
6995 unsetDim = d;
6996 }
6997 }
6998
6999 if (unsetDim == -1)
7000 {
7001 unset.erase(p0);
7002 continue;
7003 }
7004
7005 const int kv = UnsignIndex(edge_to_ukv[pe[dir_edges[unsetDim][0]]]);
7006
7007 initKV = false;
7008
7009 while (nextPatches.size() > 0)
7010 {
7011 const int p = nextPatches.front();
7012 nextPatches.pop_front();
7013 nextSet.erase(p);
7014 visited.insert(p);
7015
7016 const bool somethingSet = setPatchDirections(p, kv, edgeSet, visited);
7017 if (!somethingSet)
7018 {
7019 continue;
7020 }
7021
7022 // Find neighbors of patch p sharing a conforming face, via face2elem.
7023 std::unordered_set<int> neighbors;
7024 faceNeighbors(p, kv, neighbors);
7025
7026 bool allSet = true;
7027 GetElementEdges(p, pe, oe);
7028 for (auto edge : pe)
7029 {
7030 if (!edgeSet[edge])
7031 {
7032 allSet = false;
7033 }
7034 }
7035 if (allSet)
7036 {
7037 unset.erase(p);
7038 }
7039
7040 // Add neighbors not done to nextPatches.
7041 for (auto n : neighbors)
7042 {
7043 if (n != p && visited.count(n) == 0 && unset.count(n) > 0)
7044 {
7045 if (nextSet.count(n) == 0)
7046 {
7047 nextPatches.push_back(n);
7048 nextSet.insert(n);
7049 }
7050 }
7051 }
7052 }
7053 }
7054
7055#ifdef MFEM_DEBUG
7056 bool allSet = true;
7057 for (auto eset : edgeSet)
7058 {
7059 if (!eset)
7060 {
7061 allSet = false;
7062 }
7063 }
7064 MFEM_ASSERT(allSet && unset.size() == 0, "Some edge is not set");
7065#endif
7066
7067 delete face2elem;
7068}
7069
7070void Mesh::LoadNonconformingPatchTopo(std::istream &input,
7071 Array<int> &edge_to_ukv)
7072{
7073 SetEmpty();
7074
7075 // Read MFEM NURBS NC-patch mesh v1.0 format
7076 int curved = 0;
7077 int is_nc = 1;
7078
7079 ncmesh = new NCMesh(input, 10, curved, is_nc);
7080
7082
7083 skip_comment_lines(input, '#');
7084
7085 string ident;
7086 int inputNumOfEdges = -1;
7087
7088 input >> ident; // 'edges'
7089 input >> inputNumOfEdges;
7090
7091 MFEM_VERIFY(NumOfEdges == inputNumOfEdges, "");
7092
7093 edge_to_ukv.SetSize(NumOfEdges);
7094 for (int j = 0; j < NumOfEdges; j++)
7095 {
7096 int v[2]; // Vertex indices
7097 int ukv; // Unique KnotVector index
7098 input >> ukv >> v[0] >> v[1];
7099
7100 for (int i=0; i<2; ++i)
7101 {
7102 v[i] = ncmesh->vertex_nodeId[v[i]];
7103 }
7104
7105 if (v[0] > v[1])
7106 {
7107 ukv = FlipIndexSign(ukv);
7108 }
7109 edge_to_ukv[j] = ukv;
7110 }
7111
7113 CheckBdrElementOrientation(); // check and fix boundary element orientation
7114}
7115
7117{
7118 if (p.Size() >= v.Size())
7119 {
7120 for (int d = 0; d < v.Size(); d++)
7121 {
7122 v(d) = p(d);
7123 }
7124 }
7125 else
7126 {
7127 int d;
7128 for (d = 0; d < p.Size(); d++)
7129 {
7130 v(d) = p(d);
7131 }
7132 for ( ; d < v.Size(); d++)
7133 {
7134 v(d) = 0.0;
7135 }
7136 }
7137}
7138
7140{
7141 if (Nodes == NULL || Nodes->FESpace() != nodes.FESpace())
7142 {
7143 const int newSpaceDim = nodes.FESpace()->GetVDim();
7145 nodes.ProjectCoefficient(xyz);
7146 }
7147 else
7148 {
7149 nodes = *Nodes;
7150 }
7151}
7152
7158
7160{
7161 if (Nodes)
7162 {
7164 if (dynamic_cast<const H1_FECollection*>(fec)
7165 || dynamic_cast<const L2_FECollection*>(fec))
7166 {
7167 return;
7168 }
7169 else // Mesh using a legacy FE_Collection
7170 {
7171 const int order = GetNodalFESpace()->GetMaxElementOrder();
7172 if (NURBSext)
7173 {
7174#ifndef MFEM_USE_MPI
7175 const bool warn = true;
7176#else
7177 ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
7178 const bool warn = !pmesh || pmesh->GetMyRank() == 0;
7179#endif
7180 if (warn)
7181 {
7182 MFEM_WARNING("converting NURBS mesh to order " << order <<
7183 " H1-continuous mesh!\n "
7184 "If this is the desired behavior, you can silence"
7185 " this warning by converting\n "
7186 "the NURBS mesh to high-order mesh in advance by"
7187 " calling the method\n "
7188 "Mesh::SetCurvature().");
7189 }
7190 }
7191 SetCurvature(order, false, -1, Ordering::byVDIM);
7192 }
7193 }
7194 else // First order H1 mesh
7195 {
7196 SetCurvature(1, false, -1, Ordering::byVDIM);
7197 }
7198}
7199
7201{
7202 GetNodes(*nodes);
7203 NewNodes(*nodes, make_owner);
7204}
7205
7207{
7208 return ((Nodes) ? Nodes->FESpace() : NULL);
7209}
7210
7211void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering,
7212 int pyr_type)
7213{
7214 if (order <= 0)
7215 {
7216 delete Nodes;
7217 Nodes = nullptr;
7218 return;
7219 }
7220 space_dim = (space_dim == -1) ? spaceDim : space_dim;
7222 if (discont)
7223 {
7224 const int type = 1; // Gauss-Lobatto points
7225 nfec = new L2_FECollection(order, Dim, type, FiniteElement::VALUE,
7226 pyr_type);
7227 }
7228 else
7229 {
7230 nfec = new H1_FECollection(order, Dim, BasisType::GaussLobatto, pyr_type);
7231 }
7232 FiniteElementSpace* nfes = new FiniteElementSpace(this, nfec, space_dim,
7233 ordering);
7234
7235 const int old_space_dim = spaceDim;
7236 SetNodalFESpace(nfes);
7237 Nodes->MakeOwner(nfec);
7238
7239 if (spaceDim != old_space_dim)
7240 {
7241 // Fix dimension of the vertices if the space dimension changes
7243 }
7244}
7245
7247{
7248 MFEM_ASSERT(nodes != NULL, "");
7249 for (int i = 0; i < spaceDim; i++)
7250 {
7251 Vector vert_val;
7252 nodes->GetNodalValues(vert_val, i+1);
7253 for (int j = 0; j < NumOfVertices; j++)
7254 {
7255 vertices[j](i) = vert_val(j);
7256 }
7257 }
7258}
7259
7261{
7262 const FiniteElementSpace *fespace_det = detgf.FESpace();
7263 Array<int> dofs;
7265 for (int e = 0; e < GetNE(); e++)
7266 {
7267 const FiniteElement *fe = fespace_det->GetFE(e);
7268 const IntegrationRule ir = fe->GetNodes();
7269 GetElementTransformation(e, &transf);
7270 DenseMatrix Jac(spaceDim, Dim);
7271
7272 Vector detvals(ir.GetNPoints());
7273 for (int q = 0; q < ir.GetNPoints(); q++)
7274 {
7275 IntegrationPoint ip = ir.IntPoint(q);
7276 transf.SetIntPoint(&ip);
7277 Jac = transf.Jacobian();
7278 detvals(q) = Jac.Weight();
7279 }
7280 fespace_det->GetElementDofs(e, dofs);
7281 detgf.SetSubVector(dofs, detvals);
7282 }
7283}
7284
7285std::unique_ptr<GridFunction> Mesh::GetJacobianDeterminantGF() const
7286{
7287 int mesh_poly_deg =
7288 Nodes != NULL ? Nodes->FESpace()->GetMaxElementOrder() : 1;
7289 // determinant order is d*p-1 for tensor product elements and
7290 // d*(p-1) for simplices. We use the former here for simplicity.
7291 int det_order = Dim*mesh_poly_deg-1;
7292 L2_FECollection *fec_det = new L2_FECollection(det_order, Dim,
7294 FiniteElementSpace *fespace_det =
7295 new FiniteElementSpace(const_cast<Mesh *>(this), fec_det);
7296 auto detgf = std::make_unique<GridFunction>(fespace_det);
7297 detgf->MakeOwner(fec_det);
7298 UpdateJacobianDeterminantGF(*detgf.get());
7299 return detgf;
7300}
7301
7303{
7304 switch (Dim)
7305 {
7306 case 1: return GetNV();
7307 case 2: return GetNEdges();
7308 case 3: return GetNFaces();
7309 }
7310 return 0;
7311}
7312
7314{
7315 return faces_info.Size();
7316}
7317
7319{
7320 const bool isInt = type==FaceType::Interior;
7321 int &nf = isInt ? nbInteriorFaces : nbBoundaryFaces;
7322 if (nf<0)
7323 {
7324 nf = 0;
7325 for (int f = 0; f < GetNumFacesWithGhost(); ++f)
7326 {
7328 if (face.IsOfFaceType(type))
7329 {
7330 if (face.IsNonconformingCoarse())
7331 {
7332 // We don't count nonconforming coarse faces.
7333 continue;
7334 }
7335 nf++;
7336 }
7337 }
7338 }
7339 return nf;
7340}
7341
7342#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
7343static const char *fixed_or_not[] = { "fixed", "NOT FIXED" };
7344#endif
7345
7347{
7348 int i, j, k, wo = 0, fo = 0;
7349 real_t *v[4];
7350
7351 if (Dim == 2 && spaceDim == 2)
7352 {
7353 DenseMatrix J(2, 2);
7354
7355 for (i = 0; i < NumOfElements; i++)
7356 {
7357 int *vi = elements[i]->GetVertices();
7358 if (Nodes == NULL)
7359 {
7360 for (j = 0; j < 3; j++)
7361 {
7362 v[j] = vertices[vi[j]]();
7363 }
7364 for (j = 0; j < 2; j++)
7365 for (k = 0; k < 2; k++)
7366 {
7367 J(j, k) = v[j+1][k] - v[0][k];
7368 }
7369 }
7370 else
7371 {
7372 // only check the Jacobian at the center of the element
7373 GetElementJacobian(i, J);
7374 }
7375 if (J.Det() < 0.0)
7376 {
7377 if (fix_it)
7378 {
7379 switch (GetElementType(i))
7380 {
7381 case Element::TRIANGLE:
7382 mfem::Swap(vi[0], vi[1]);
7383 break;
7385 mfem::Swap(vi[1], vi[3]);
7386 break;
7387 default:
7388 MFEM_ABORT("Invalid 2D element type \""
7389 << GetElementType(i) << "\"");
7390 break;
7391 }
7392 fo++;
7393 }
7394 wo++;
7395 }
7396 }
7397 }
7398
7399 if (Dim == 3)
7400 {
7401 DenseMatrix J(3, 3);
7402
7403 for (i = 0; i < NumOfElements; i++)
7404 {
7405 int *vi = elements[i]->GetVertices();
7406 switch (GetElementType(i))
7407 {
7409 if (Nodes == NULL)
7410 {
7411 for (j = 0; j < 4; j++)
7412 {
7413 v[j] = vertices[vi[j]]();
7414 }
7415 for (j = 0; j < 3; j++)
7416 for (k = 0; k < 3; k++)
7417 {
7418 J(j, k) = v[j+1][k] - v[0][k];
7419 }
7420 }
7421 else
7422 {
7423 // only check the Jacobian at the center of the element
7424 GetElementJacobian(i, J);
7425 }
7426 if (J.Det() < 0.0)
7427 {
7428 wo++;
7429 if (fix_it)
7430 {
7431 mfem::Swap(vi[0], vi[1]);
7432 fo++;
7433 }
7434 }
7435 break;
7436
7437 case Element::WEDGE:
7438 // only check the Jacobian at the center of the element
7439 GetElementJacobian(i, J);
7440 if (J.Det() < 0.0)
7441 {
7442 wo++;
7443 if (fix_it)
7444 {
7445 // how?
7446 }
7447 }
7448 break;
7449
7450 case Element::PYRAMID:
7451 // only check the Jacobian at the center of the element
7452 GetElementJacobian(i, J);
7453 if (J.Det() < 0.0)
7454 {
7455 wo++;
7456 if (fix_it)
7457 {
7458 mfem::Swap(vi[1], vi[3]);
7459 fo++;
7460 }
7461 }
7462 break;
7463
7465 // only check the Jacobian at the center of the element
7466 GetElementJacobian(i, J);
7467 if (J.Det() < 0.0)
7468 {
7469 wo++;
7470 if (fix_it)
7471 {
7472 // how?
7473 }
7474 }
7475 break;
7476
7477 default:
7478 MFEM_ABORT("Invalid 3D element type \""
7479 << GetElementType(i) << "\"");
7480 break;
7481 }
7482 }
7483 }
7484#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
7485 if (wo > 0)
7486 {
7487 mfem::out << "Elements with wrong orientation: " << wo << " / "
7488 << NumOfElements << " (" << fixed_or_not[(wo == fo) ? 0 : 1]
7489 << ")" << endl;
7490 }
7491#else
7492 MFEM_CONTRACT_VAR(fo);
7493#endif
7494 return wo;
7495}
7496
7497int Mesh::GetTriOrientation(const int *base, const int *test)
7498{
7499 // Static method.
7500 // This function computes the index 'j' of the permutation that transforms
7501 // test into base: test[tri_orientation[j][i]]=base[i].
7502 // tri_orientation = Geometry::Constants<Geometry::TRIANGLE>::Orient
7503 int orient;
7504
7505 if (test[0] == base[0])
7506 if (test[1] == base[1])
7507 {
7508 orient = 0; // (0, 1, 2)
7509 }
7510 else
7511 {
7512 orient = 5; // (0, 2, 1)
7513 }
7514 else if (test[0] == base[1])
7515 if (test[1] == base[0])
7516 {
7517 orient = 1; // (1, 0, 2)
7518 }
7519 else
7520 {
7521 orient = 2; // (1, 2, 0)
7522 }
7523 else // test[0] == base[2]
7524 if (test[1] == base[0])
7525 {
7526 orient = 4; // (2, 0, 1)
7527 }
7528 else
7529 {
7530 orient = 3; // (2, 1, 0)
7531 }
7532
7533#ifdef MFEM_DEBUG
7534 const int *aor = tri_t::Orient[orient];
7535 for (int j = 0; j < 3; j++)
7536 if (test[aor[j]] != base[j])
7537 {
7538 mfem::err << "Mesh::GetTriOrientation(...)" << endl;
7539 mfem::err << " base = [";
7540 for (int k = 0; k < 3; k++)
7541 {
7542 mfem::err << " " << base[k];
7543 }
7544 mfem::err << " ]\n test = [";
7545 for (int k = 0; k < 3; k++)
7546 {
7547 mfem::err << " " << test[k];
7548 }
7549 mfem::err << " ]" << endl;
7550 mfem_error();
7551 }
7552#endif
7553
7554 return orient;
7555}
7556
7557int Mesh::ComposeTriOrientations(int ori_a_b, int ori_b_c)
7558{
7559 // Static method.
7560 // Given three, possibly different, configurations of triangular face
7561 // vertices: va, vb, and vc. This function returns the relative orientation
7562 // GetTriOrientation(va, vc) by composing previously computed orientations
7563 // ori_a_b = GetTriOrientation(va, vb) and
7564 // ori_b_c = GetTriOrientation(vb, vc) without accessing the vertices.
7565
7566 const int oo[6][6] =
7567 {
7568 {0, 1, 2, 3, 4, 5},
7569 {1, 0, 5, 4, 3, 2},
7570 {2, 3, 4, 5, 0, 1},
7571 {3, 2, 1, 0, 5, 4},
7572 {4, 5, 0, 1, 2, 3},
7573 {5, 4, 3, 2, 1, 0}
7574 };
7575
7576 int ori_a_c = oo[ori_a_b][ori_b_c];
7577 return ori_a_c;
7578}
7579
7581{
7582 const int inv_ori[6] = {0, 1, 4, 3, 2, 5};
7583 return inv_ori[ori];
7584}
7585
7586int Mesh::GetQuadOrientation(const int *base, const int *test)
7587{
7588 int i;
7589
7590 for (i = 0; i < 4; i++)
7591 if (test[i] == base[0])
7592 {
7593 break;
7594 }
7595
7596#ifdef MFEM_DEBUG
7597 int orient;
7598 if (test[(i+1)%4] == base[1])
7599 {
7600 orient = 2*i;
7601 }
7602 else
7603 {
7604 orient = 2*i+1;
7605 }
7606 const int *aor = quad_t::Orient[orient];
7607 for (int j = 0; j < 4; j++)
7608 if (test[aor[j]] != base[j])
7609 {
7610 mfem::err << "Mesh::GetQuadOrientation(...)" << endl;
7611 mfem::err << " base = [";
7612 for (int k = 0; k < 4; k++)
7613 {
7614 mfem::err << " " << base[k];
7615 }
7616 mfem::err << " ]\n test = [";
7617 for (int k = 0; k < 4; k++)
7618 {
7619 mfem::err << " " << test[k];
7620 }
7621 mfem::err << " ]" << endl;
7622 mfem_error();
7623 }
7624#endif
7625
7626 if (test[(i+1)%4] == base[1])
7627 {
7628 return 2*i;
7629 }
7630
7631 return 2*i+1;
7632}
7633
7634int Mesh::ComposeQuadOrientations(int ori_a_b, int ori_b_c)
7635{
7636 // Static method.
7637 // Given three, possibly different, configurations of quadrilateral face
7638 // vertices: va, vb, and vc. This function returns the relative orientation
7639 // GetQuadOrientation(va, vc) by composing previously computed orientations
7640 // ori_a_b = GetQuadOrientation(va, vb) and
7641 // ori_b_c = GetQuadOrientation(vb, vc) without accessing the vertices.
7642
7643 const int oo[8][8] =
7644 {
7645 {0, 1, 2, 3, 4, 5, 6, 7},
7646 {1, 0, 3, 2, 5, 4, 7, 6},
7647 {2, 7, 4, 1, 6, 3, 0, 5},
7648 {3, 6, 5, 0, 7, 2, 1, 4},
7649 {4, 5, 6, 7, 0, 1, 2, 3},
7650 {5, 4, 7, 6, 1, 0, 3, 2},
7651 {6, 3, 0, 5, 2, 7, 4, 1},
7652 {7, 2, 1, 4, 3, 6, 5, 0}
7653 };
7654
7655 int ori_a_c = oo[ori_a_b][ori_b_c];
7656 return ori_a_c;
7657}
7658
7660{
7661 const int inv_ori[8] = {0, 1, 6, 3, 4, 5, 2, 7};
7662 return inv_ori[ori];
7663}
7664
7665int Mesh::GetTetOrientation(const int *base, const int *test)
7666{
7667 // Static method.
7668 // This function computes the index 'j' of the permutation that transforms
7669 // test into base: test[tet_orientation[j][i]]=base[i].
7670 // tet_orientation = Geometry::Constants<Geometry::TETRAHEDRON>::Orient
7671 int orient;
7672
7673 if (test[0] == base[0])
7674 if (test[1] == base[1])
7675 if (test[2] == base[2])
7676 {
7677 orient = 0; // (0, 1, 2, 3)
7678 }
7679 else
7680 {
7681 orient = 1; // (0, 1, 3, 2)
7682 }
7683 else if (test[2] == base[1])
7684 if (test[3] == base[2])
7685 {
7686 orient = 2; // (0, 2, 3, 1)
7687 }
7688 else
7689 {
7690 orient = 3; // (0, 2, 1, 3)
7691 }
7692 else // test[3] == base[1]
7693 if (test[1] == base[2])
7694 {
7695 orient = 4; // (0, 3, 1, 2)
7696 }
7697 else
7698 {
7699 orient = 5; // (0, 3, 2, 1)
7700 }
7701 else if (test[1] == base[0])
7702 if (test[2] == base[1])
7703 if (test[0] == base[2])
7704 {
7705 orient = 6; // (1, 2, 0, 3)
7706 }
7707 else
7708 {
7709 orient = 7; // (1, 2, 3, 0)
7710 }
7711 else if (test[3] == base[1])
7712 if (test[2] == base[2])
7713 {
7714 orient = 8; // (1, 3, 2, 0)
7715 }
7716 else
7717 {
7718 orient = 9; // (1, 3, 0, 2)
7719 }
7720 else // test[0] == base[1]
7721 if (test[3] == base[2])
7722 {
7723 orient = 10; // (1, 0, 3, 2)
7724 }
7725 else
7726 {
7727 orient = 11; // (1, 0, 2, 3)
7728 }
7729 else if (test[2] == base[0])
7730 if (test[3] == base[1])
7731 if (test[0] == base[2])
7732 {
7733 orient = 12; // (2, 3, 0, 1)
7734 }
7735 else
7736 {
7737 orient = 13; // (2, 3, 1, 0)
7738 }
7739 else if (test[0] == base[1])
7740 if (test[1] == base[2])
7741 {
7742 orient = 14; // (2, 0, 1, 3)
7743 }
7744 else
7745 {
7746 orient = 15; // (2, 0, 3, 1)
7747 }
7748 else // test[1] == base[1]
7749 if (test[3] == base[2])
7750 {
7751 orient = 16; // (2, 1, 3, 0)
7752 }
7753 else
7754 {
7755 orient = 17; // (2, 1, 0, 3)
7756 }
7757 else // (test[3] == base[0])
7758 if (test[0] == base[1])
7759 if (test[2] == base[2])
7760 {
7761 orient = 18; // (3, 0, 2, 1)
7762 }
7763 else
7764 {
7765 orient = 19; // (3, 0, 1, 2)
7766 }
7767 else if (test[1] == base[1])
7768 if (test[0] == base[2])
7769 {
7770 orient = 20; // (3, 1, 0, 2)
7771 }
7772 else
7773 {
7774 orient = 21; // (3, 1, 2, 0)
7775 }
7776 else // test[2] == base[1]
7777 if (test[1] == base[2])
7778 {
7779 orient = 22; // (3, 2, 1, 0)
7780 }
7781 else
7782 {
7783 orient = 23; // (3, 2, 0, 1)
7784 }
7785
7786#ifdef MFEM_DEBUG
7787 const int *aor = tet_t::Orient[orient];
7788 for (int j = 0; j < 4; j++)
7789 if (test[aor[j]] != base[j])
7790 {
7791 mfem_error("Mesh::GetTetOrientation(...)");
7792 }
7793#endif
7794
7795 return orient;
7796}
7797
7799{
7800 int wo = 0; // count wrong orientations
7801
7802 if (Dim == 2)
7803 {
7804 if (el_to_edge == NULL) // edges were not generated
7805 {
7806 el_to_edge = new Table;
7808 GenerateFaces(); // 'Faces' in 2D refers to the edges
7809 }
7810 for (int i = 0; i < NumOfBdrElements; i++)
7811 {
7812 if (faces_info[be_to_face[i]].Elem2No < 0) // boundary face
7813 {
7814 int *bv = boundary[i]->GetVertices();
7815 int *fv = faces[be_to_face[i]]->GetVertices();
7816 if (bv[0] != fv[0])
7817 {
7818 if (fix_it)
7819 {
7820 mfem::Swap<int>(bv[0], bv[1]);
7821 }
7822 wo++;
7823 }
7824 }
7825 }
7826 }
7827
7828 if (Dim == 3)
7829 {
7830 for (int i = 0; i < NumOfBdrElements; i++)
7831 {
7832 const int fi = be_to_face[i];
7833
7834 if (faces_info[fi].Elem2No >= 0) { continue; }
7835
7836 // boundary face
7837 int *bv = boundary[i]->GetVertices();
7838 // Make sure the 'faces' are generated:
7839 MFEM_ASSERT(fi < faces.Size(), "internal error");
7840 const int *fv = faces[fi]->GetVertices();
7841 int orientation; // orientation of the bdr. elem. w.r.t. the
7842 // corresponding face element (that's the base)
7843 const Element::Type bdr_type = GetBdrElementType(i);
7844 switch (bdr_type)
7845 {
7846 case Element::TRIANGLE:
7847 {
7848 orientation = GetTriOrientation(fv, bv);
7849 break;
7850 }
7852 {
7853 orientation = GetQuadOrientation(fv, bv);
7854 break;
7855 }
7856 default:
7857 MFEM_ABORT("Invalid 2D boundary element type \""
7858 << bdr_type << "\"");
7859 orientation = 0; // suppress a warning
7860 break;
7861 }
7862
7863 if (orientation % 2 == 0) { continue; }
7864 wo++;
7865 if (!fix_it) { continue; }
7866
7867 switch (bdr_type)
7868 {
7869 case Element::TRIANGLE:
7870 {
7871 // swap vertices 0 and 1 so that we don't change the marked edge:
7872 // (0,1,2) -> (1,0,2)
7873 mfem::Swap(bv[0], bv[1]);
7874 if (bel_to_edge)
7875 {
7876 int *be = bel_to_edge->GetRow(i);
7877 mfem::Swap(be[1], be[2]);
7878 }
7879 break;
7880 }
7882 {
7883 mfem::Swap(bv[0], bv[2]);
7884 if (bel_to_edge)
7885 {
7886 int *be = bel_to_edge->GetRow(i);
7887 mfem::Swap(be[0], be[1]);
7888 mfem::Swap(be[2], be[3]);
7889 }
7890 break;
7891 }
7892 default: // unreachable
7893 break;
7894 }
7895 }
7896 }
7897 // #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
7898#ifdef MFEM_DEBUG
7899 if (wo > 0)
7900 {
7901 mfem::out << "Boundary elements with wrong orientation: " << wo << " / "
7902 << NumOfBdrElements << " (" << fixed_or_not[fix_it ? 0 : 1]
7903 << ")" << endl;
7904 }
7905#endif
7906 return wo;
7907}
7908
7910 const IntegrationPoint &ip)
7911{
7912 IntegrationPoint fip = ip;
7913 if (geom == Geometry::POINT)
7914 {
7915 return fip;
7916 }
7917 else if (geom == Geometry::SEGMENT)
7918 {
7919 MFEM_ASSERT(o >= 0 && o < 2, "Invalid orientation for Geometry::SEGMENT!");
7920 if (o == 0)
7921 {
7922 fip.x = ip.x;
7923 }
7924 else if (o == 1)
7925 {
7926 fip.x = 1.0 - ip.x;
7927 }
7928 }
7929 else if (geom == Geometry::TRIANGLE)
7930 {
7931 MFEM_ASSERT(o >= 0 && o < 6, "Invalid orientation for Geometry::TRIANGLE!");
7932 if (o == 0) // 0, 1, 2
7933 {
7934 fip.x = ip.x;
7935 fip.y = ip.y;
7936 }
7937 else if (o == 5) // 0, 2, 1
7938 {
7939 fip.x = ip.y;
7940 fip.y = ip.x;
7941 }
7942 else if (o == 2) // 1, 2, 0
7943 {
7944 fip.x = 1.0 - ip.x - ip.y;
7945 fip.y = ip.x;
7946 }
7947 else if (o == 1) // 1, 0, 2
7948 {
7949 fip.x = 1.0 - ip.x - ip.y;
7950 fip.y = ip.y;
7951 }
7952 else if (o == 4) // 2, 0, 1
7953 {
7954 fip.x = ip.y;
7955 fip.y = 1.0 - ip.x - ip.y;
7956 }
7957 else if (o == 3) // 2, 1, 0
7958 {
7959 fip.x = ip.x;
7960 fip.y = 1.0 - ip.x - ip.y;
7961 }
7962 }
7963 else if (geom == Geometry::SQUARE)
7964 {
7965 MFEM_ASSERT(o >= 0 && o < 8, "Invalid orientation for Geometry::SQUARE!");
7966 if (o == 0) // 0, 1, 2, 3
7967 {
7968 fip.x = ip.x;
7969 fip.y = ip.y;
7970 }
7971 else if (o == 1) // 0, 3, 2, 1
7972 {
7973 fip.x = ip.y;
7974 fip.y = ip.x;
7975 }
7976 else if (o == 2) // 1, 2, 3, 0
7977 {
7978 fip.x = ip.y;
7979 fip.y = 1.0 - ip.x;
7980 }
7981 else if (o == 3) // 1, 0, 3, 2
7982 {
7983 fip.x = 1.0 - ip.x;
7984 fip.y = ip.y;
7985 }
7986 else if (o == 4) // 2, 3, 0, 1
7987 {
7988 fip.x = 1.0 - ip.x;
7989 fip.y = 1.0 - ip.y;
7990 }
7991 else if (o == 5) // 2, 1, 0, 3
7992 {
7993 fip.x = 1.0 - ip.y;
7994 fip.y = 1.0 - ip.x;
7995 }
7996 else if (o == 6) // 3, 0, 1, 2
7997 {
7998 fip.x = 1.0 - ip.y;
7999 fip.y = ip.x;
8000 }
8001 else if (o == 7) // 3, 2, 1, 0
8002 {
8003 fip.x = ip.x;
8004 fip.y = 1.0 - ip.y;
8005 }
8006 }
8007 else
8008 {
8009 MFEM_ABORT("Unsupported face geometry for TransformBdrElementToFace!");
8010 }
8011 return fip;
8012}
8013
8015{
8016 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
8017 int num_geoms = 0;
8018 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
8019 {
8020 if (HasGeometry(Geometry::Type(g))) { num_geoms++; }
8021 }
8022 return num_geoms;
8023}
8024
8026{
8027 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
8028 el_geoms.SetSize(0);
8029 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
8030 {
8032 {
8033 el_geoms.Append(Geometry::Type(g));
8034 }
8035 }
8036}
8037
8039{
8040 // Return true if meshgen has more than one bit set, zero otherwise
8041 return meshgen & (meshgen - 1);
8042}
8043
8044void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
8045{
8046 if (Dim == 1)
8047 {
8048 // In 1D, elements are segments and can be treated as edges.
8049 edges.SetSize(1);
8050 cor.SetSize(1);
8051 edges[0] = i;
8052 const int *v = elements[i]->GetVertices();
8053 cor[0] = (v[0] < v[1]) ? (1) : (-1);
8054 return;
8055 }
8056
8057 if (el_to_edge)
8058 {
8059 el_to_edge->GetRow(i, edges);
8060 }
8061 else
8062 {
8063 mfem_error("Mesh::GetElementEdges(...) element to edge table "
8064 "is not generated.");
8065 }
8066
8067 const int *v = elements[i]->GetVertices();
8068 const int ne = elements[i]->GetNEdges();
8069 cor.SetSize(ne);
8070 for (int j = 0; j < ne; j++)
8071 {
8072 const int *e = elements[i]->GetEdgeVertices(j);
8073 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
8074 }
8075}
8076
8077void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
8078{
8079 if (Dim == 2)
8080 {
8081 edges.SetSize(1);
8082 cor.SetSize(1);
8083 edges[0] = be_to_face[i];
8084 const int *v = boundary[i]->GetVertices();
8085 cor[0] = (v[0] < v[1]) ? (1) : (-1);
8086 }
8087 else if (Dim == 3)
8088 {
8089 if (bel_to_edge)
8090 {
8091 bel_to_edge->GetRow(i, edges);
8092 }
8093 else
8094 {
8095 mfem_error("Mesh::GetBdrElementEdges(...)");
8096 }
8097
8098 const int *v = boundary[i]->GetVertices();
8099 const int ne = boundary[i]->GetNEdges();
8100 cor.SetSize(ne);
8101 for (int j = 0; j < ne; j++)
8102 {
8103 const int *e = boundary[i]->GetEdgeVertices(j);
8104 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
8105 }
8106 }
8107}
8108
8109void Mesh::GetFaceEdges(int i, Array<int> &edges, Array<int> &o) const
8110{
8111 if (Dim == 2)
8112 {
8113 edges.SetSize(1);
8114 edges[0] = i;
8115 o.SetSize(1);
8116 const int *v = faces[i]->GetVertices();
8117 o[0] = (v[0] < v[1]) ? (1) : (-1);
8118 }
8119
8120 if (Dim != 3)
8121 {
8122 return;
8123 }
8124
8125 GetFaceEdgeTable(); // generate face_edge Table (if not generated)
8126
8127 face_edge->GetRow(i, edges);
8128
8129 const int *v = faces[i]->GetVertices();
8130 const int ne = faces[i]->GetNEdges();
8131 o.SetSize(ne);
8132 for (int j = 0; j < ne; j++)
8133 {
8134 const int *e = faces[i]->GetEdgeVertices(j);
8135 o[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
8136 }
8137}
8138
8139void Mesh::GetEdgeVertices(int i, Array<int> &vert) const
8140{
8141 // the two vertices are sorted: vert[0] < vert[1]
8142 // this is consistent with the global edge orientation
8143 // generate edge_vertex Table (if not generated)
8144 if (!edge_vertex) { GetEdgeVertexTable(); }
8145 edge_vertex->GetRow(i, vert);
8146}
8147
8149{
8150 if (face_edge)
8151 {
8152 return face_edge;
8153 }
8154
8155 if (Dim != 3)
8156 {
8157 return NULL;
8158 }
8159
8160#ifdef MFEM_DEBUG
8161 if (faces.Size() != NumOfFaces)
8162 {
8163 mfem_error("Mesh::GetFaceEdgeTable : faces were not generated!");
8164 }
8165#endif
8166
8167 DSTable v_to_v(NumOfVertices);
8168 GetVertexToVertexTable(v_to_v);
8169
8170 face_edge = new Table;
8172
8173 return (face_edge);
8174}
8175
8177{
8178 if (edge_face)
8179 {
8180 return edge_face;
8181 }
8182
8183 if (Dim != 3)
8184 {
8185 return NULL;
8186 }
8187
8189 return edge_face;
8190}
8191
8193{
8194 if (edge_vertex)
8195 {
8196 return edge_vertex;
8197 }
8198
8199 DSTable v_to_v(NumOfVertices);
8200 GetVertexToVertexTable(v_to_v);
8201
8202 int nedges = v_to_v.NumberOfEntries();
8203 edge_vertex = new Table(nedges, 2);
8204 for (int i = 0; i < NumOfVertices; i++)
8205 {
8206 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
8207 {
8208 int j = it.Index();
8209 edge_vertex->Push(j, i);
8210 edge_vertex->Push(j, it.Column());
8211 }
8212 }
8214
8215 return edge_vertex;
8216}
8217
8219{
8220 Table *vert_elem = new Table;
8221
8222 vert_elem->MakeI(NumOfVertices);
8223
8224 for (int i = 0; i < NumOfElements; i++)
8225 {
8226 const int nv = elements[i]->GetNVertices();
8227 const int *v = elements[i]->GetVertices();
8228 for (int j = 0; j < nv; j++)
8229 {
8230 vert_elem->AddAColumnInRow(v[j]);
8231 }
8232 }
8233
8234 vert_elem->MakeJ();
8235
8236 for (int i = 0; i < NumOfElements; i++)
8237 {
8238 const int nv = elements[i]->GetNVertices();
8239 const int *v = elements[i]->GetVertices();
8240 for (int j = 0; j < nv; j++)
8241 {
8242 vert_elem->AddConnection(v[j], i);
8243 }
8244 }
8245
8246 vert_elem->ShiftUpI();
8247
8248 return vert_elem;
8249}
8250
8252{
8253 Table *vert_bdr_elem = new Table;
8254
8255 vert_bdr_elem->MakeI(NumOfVertices);
8256
8257 for (int i = 0; i < NumOfBdrElements; i++)
8258 {
8259 const int nv = boundary[i]->GetNVertices();
8260 const int *v = boundary[i]->GetVertices();
8261 for (int j = 0; j < nv; j++)
8262 {
8263 vert_bdr_elem->AddAColumnInRow(v[j]);
8264 }
8265 }
8266
8267 vert_bdr_elem->MakeJ();
8268
8269 for (int i = 0; i < NumOfBdrElements; i++)
8270 {
8271 const int nv = boundary[i]->GetNVertices();
8272 const int *v = boundary[i]->GetVertices();
8273 for (int j = 0; j < nv; j++)
8274 {
8275 vert_bdr_elem->AddConnection(v[j], i);
8276 }
8277 }
8278
8279 vert_bdr_elem->ShiftUpI();
8280
8281 return vert_bdr_elem;
8282}
8283
8285{
8286 Table *face_elem = new Table;
8287
8288 face_elem->MakeI(faces_info.Size());
8289
8290 for (int i = 0; i < faces_info.Size(); i++)
8291 {
8292 if (faces_info[i].Elem2No >= 0)
8293 {
8294 face_elem->AddColumnsInRow(i, 2);
8295 }
8296 else
8297 {
8298 face_elem->AddAColumnInRow(i);
8299 }
8300 }
8301
8302 face_elem->MakeJ();
8303
8304 for (int i = 0; i < faces_info.Size(); i++)
8305 {
8306 face_elem->AddConnection(i, faces_info[i].Elem1No);
8307 if (faces_info[i].Elem2No >= 0)
8308 {
8309 face_elem->AddConnection(i, faces_info[i].Elem2No);
8310 }
8311 }
8312
8313 face_elem->ShiftUpI();
8314
8315 return face_elem;
8316}
8317
8318void Mesh::GetElementFaces(int i, Array<int> &el_faces, Array<int> &ori) const
8319{
8320 MFEM_VERIFY(el_to_face != NULL, "el_to_face not generated");
8321
8322 el_to_face->GetRow(i, el_faces);
8323
8324 int n = el_faces.Size();
8325 ori.SetSize(n);
8326
8327 for (int j = 0; j < n; j++)
8328 {
8329 if (faces_info[el_faces[j]].Elem1No == i)
8330 {
8331 ori[j] = faces_info[el_faces[j]].Elem1Inf % 64;
8332 }
8333 else
8334 {
8335 MFEM_ASSERT(faces_info[el_faces[j]].Elem2No == i, "internal error");
8336 ori[j] = faces_info[el_faces[j]].Elem2Inf % 64;
8337 }
8338 }
8339}
8340
8342{
8343 if (face_to_elem == NULL)
8344 {
8346 }
8347
8348 Array<int> elem_faces;
8349 Array<int> ori;
8350 GetElementFaces(elem, elem_faces, ori);
8351
8352 Array<int> nghb;
8353 for (auto f : elem_faces)
8354 {
8355 Array<int> row;
8356 face_to_elem->GetRow(f, row);
8357 for (auto r : row)
8358 {
8359 nghb.Append(r);
8360 }
8361 }
8362
8363 nghb.Sort();
8364 nghb.Unique();
8365
8366 return nghb;
8367}
8368
8369void Mesh::GetBdrElementFace(int i, int *f, int *o) const
8370{
8372
8373 const int *fv = (Dim > 1) ? faces[*f]->GetVertices() : NULL;
8374 const int *bv = boundary[i]->GetVertices();
8375
8376 // find the orientation of the bdr. elem. w.r.t.
8377 // the corresponding face element (that's the base)
8378 switch (GetBdrElementGeometry(i))
8379 {
8380 case Geometry::POINT: *o = 0; break;
8381 case Geometry::SEGMENT: *o = (fv[0] == bv[0]) ? 0 : 1; break;
8382 case Geometry::TRIANGLE: *o = GetTriOrientation(fv, bv); break;
8383 case Geometry::SQUARE: *o = GetQuadOrientation(fv, bv); break;
8384 default: MFEM_ABORT("invalid geometry");
8385 }
8386}
8387
8388void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
8389{
8390 int fid = GetBdrElementFaceIndex(bdr_el);
8391
8392 const FaceInfo &fi = faces_info[fid];
8393 MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
8394
8395 const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
8396 const int *bv = boundary[bdr_el]->GetVertices();
8397 int ori;
8398 switch (GetBdrElementGeometry(bdr_el))
8399 {
8400 case Geometry::POINT: ori = 0; break;
8401 case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
8402 case Geometry::TRIANGLE: ori = GetTriOrientation(fv, bv); break;
8403 case Geometry::SQUARE: ori = GetQuadOrientation(fv, bv); break;
8404 default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
8405 }
8406 el = fi.Elem1No;
8407 info = fi.Elem1Inf + ori;
8408}
8409
8411 int bdr_el, int &el, int &info) const
8412{
8413 int fid = GetBdrElementFaceIndex(bdr_el);
8414
8415 const FaceInfo &fi = faces_info[fid];
8416 MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
8417
8418 const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
8419 const int *bv = boundary[bdr_el]->GetVertices();
8420 int ori;
8421 switch (GetBdrElementGeometry(bdr_el))
8422 {
8423 case Geometry::POINT: ori = 0; break;
8424 case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
8425 case Geometry::TRIANGLE: ori = GetTriOrientation(bv, fv); break;
8426 case Geometry::SQUARE: ori = GetQuadOrientation(bv, fv); break;
8427 default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
8428 }
8429 el = fi.Elem1No;
8430 info = fi.Elem1Inf + ori;
8431}
8432
8433void Mesh::SetAttribute(int i, int attr)
8434{
8435 elements[i]->SetAttribute(attr);
8436 if (elem_attrs_cache.Size() == GetNE())
8437 {
8438 // update the existing cache instead of deleting it
8440 elem_attrs_cache[i] = attr;
8441 }
8442 if (ncmesh) { ncmesh->SetAttribute(i, attr); }
8443}
8444
8446{
8447 return elements[i]->GetType();
8448}
8449
8451{
8452 return boundary[i]->GetType();
8453}
8454
8455void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
8456{
8457 int k, j, nv;
8458 const int *v;
8459
8460 v = elements[i]->GetVertices();
8461 nv = elements[i]->GetNVertices();
8462
8463 pointmat.SetSize(spaceDim, nv);
8464 for (k = 0; k < spaceDim; k++)
8465 {
8466 for (j = 0; j < nv; j++)
8467 {
8468 pointmat(k, j) = vertices[v[j]](k);
8469 }
8470 }
8471}
8472
8473void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
8474{
8475 int k, j, nv;
8476 const int *v;
8477
8478 v = boundary[i]->GetVertices();
8479 nv = boundary[i]->GetNVertices();
8480
8481 pointmat.SetSize(spaceDim, nv);
8482 for (k = 0; k < spaceDim; k++)
8483 for (j = 0; j < nv; j++)
8484 {
8485 pointmat(k, j) = vertices[v[j]](k);
8486 }
8487}
8488
8489real_t Mesh::GetLength(int i, int j) const
8490{
8491 const real_t *vi = vertices[i]();
8492 const real_t *vj = vertices[j]();
8493 real_t length = 0.;
8494
8495 for (int k = 0; k < spaceDim; k++)
8496 {
8497 length += (vi[k]-vj[k])*(vi[k]-vj[k]);
8498 }
8499
8500 return sqrt(length);
8501}
8502
8503// static method
8505 const DSTable &v_to_v, Table &el_to_edge)
8506{
8507 el_to_edge.MakeI(elem_array.Size());
8508 for (int i = 0; i < elem_array.Size(); i++)
8509 {
8510 el_to_edge.AddColumnsInRow(i, elem_array[i]->GetNEdges());
8511 }
8512 el_to_edge.MakeJ();
8513 for (int i = 0; i < elem_array.Size(); i++)
8514 {
8515 const int *v = elem_array[i]->GetVertices();
8516 const int ne = elem_array[i]->GetNEdges();
8517 for (int j = 0; j < ne; j++)
8518 {
8519 const int *e = elem_array[i]->GetEdgeVertices(j);
8520 el_to_edge.AddConnection(i, v_to_v(v[e[0]], v[e[1]]));
8521 }
8522 }
8524}
8525
8527{
8528 if (edge_vertex)
8529 {
8530 for (int i = 0; i < edge_vertex->Size(); i++)
8531 {
8532 const int *v = edge_vertex->GetRow(i);
8533 v_to_v.Push(v[0], v[1]);
8534 }
8535 }
8536 else
8537 {
8538 for (int i = 0; i < NumOfElements; i++)
8539 {
8540 const int *v = elements[i]->GetVertices();
8541 const int ne = elements[i]->GetNEdges();
8542 for (int j = 0; j < ne; j++)
8543 {
8544 const int *e = elements[i]->GetEdgeVertices(j);
8545 v_to_v.Push(v[e[0]], v[e[1]]);
8546 }
8547 }
8548 }
8549}
8550
8552{
8553 int i, NumberOfEdges;
8554
8555 DSTable v_to_v(NumOfVertices);
8556 GetVertexToVertexTable(v_to_v);
8557
8558 NumberOfEdges = v_to_v.NumberOfEntries();
8559
8560 // Fill the element to edge table
8561 GetElementArrayEdgeTable(elements, v_to_v, e_to_f);
8562
8563 if (Dim == 2)
8564 {
8565 // Initialize the indices for the boundary elements.
8567 for (i = 0; i < NumOfBdrElements; i++)
8568 {
8569 const int *v = boundary[i]->GetVertices();
8570 be_to_face[i] = v_to_v(v[0], v[1]);
8571 }
8572 }
8573 else if (Dim == 3)
8574 {
8575 if (bel_to_edge == NULL)
8576 {
8577 bel_to_edge = new Table;
8578 }
8580 }
8581 else
8582 {
8583 mfem_error("1D GetElementToEdgeTable is not yet implemented.");
8584 }
8585
8586 // Return the number of edges
8587 return NumberOfEdges;
8588}
8589
8591{
8592 if (el_to_el)
8593 {
8594 return *el_to_el;
8595 }
8596
8597 // Note that, for ParNCMeshes, faces_info will contain also the ghost faces
8598 MFEM_ASSERT(faces_info.Size() >= GetNumFaces(), "faces were not generated!");
8599
8600 Array<Connection> conn;
8601 conn.Reserve(2*faces_info.Size());
8602
8603 for (int i = 0; i < faces_info.Size(); i++)
8604 {
8605 const FaceInfo &fi = faces_info[i];
8606 if (fi.Elem2No >= 0)
8607 {
8608 conn.Append(Connection(fi.Elem1No, fi.Elem2No));
8609 conn.Append(Connection(fi.Elem2No, fi.Elem1No));
8610 }
8611 else if (fi.Elem2Inf >= 0)
8612 {
8613 int nbr_elem_idx = NumOfElements - 1 - fi.Elem2No;
8614 conn.Append(Connection(fi.Elem1No, nbr_elem_idx));
8615 conn.Append(Connection(nbr_elem_idx, fi.Elem1No));
8616 }
8617 }
8618
8619 conn.Sort();
8620 conn.Unique();
8621 el_to_el = new Table(NumOfElements, conn);
8622
8623 return *el_to_el;
8624}
8625
8627{
8628 if (el_to_face == NULL)
8629 {
8630 mfem_error("Mesh::ElementToFaceTable()");
8631 }
8632 return *el_to_face;
8633}
8634
8636{
8637 if (el_to_edge == NULL)
8638 {
8639 mfem_error("Mesh::ElementToEdgeTable()");
8640 }
8641 return *el_to_edge;
8642}
8643
8644void Mesh::AddPointFaceElement(int lf, int gf, int el)
8645{
8646 if (faces[gf] == NULL) // this will be elem1
8647 {
8648 faces[gf] = new Point(&gf);
8649 faces_info[gf].Elem1No = el;
8650 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8651 faces_info[gf].Elem2No = -1; // in case there's no other side
8652 faces_info[gf].Elem2Inf = -1; // face is not shared
8653 }
8654 else // this will be elem2
8655 {
8656 /* WARNING: Without the following check the mesh faces_info data structure
8657 may contain unreliable data. Normally, the order in which elements are
8658 processed could swap which elements appear as Elem1No and Elem2No. In
8659 branched meshes, where more than two elements can meet at a given node,
8660 the indices stored in Elem1No and Elem2No will be the first and last,
8661 respectively, elements found which touch a given node. This can lead to
8662 inconsistencies in any algorithms which rely on this data structure. To
8663 properly support branched meshes this data structure should be extended
8664 to support multiple elements per face. */
8665 /*
8666 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8667 "Interior point found connecting 1D elements "
8668 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8669 << " and " << el << ".");
8670 */
8671 faces_info[gf].Elem2No = el;
8672 faces_info[gf].Elem2Inf = 64 * lf + 1;
8673 }
8674}
8675
8676void Mesh::AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
8677{
8678 if (faces[gf] == NULL) // this will be elem1
8679 {
8680 faces[gf] = new Segment(v0, v1);
8681 faces_info[gf].Elem1No = el;
8682 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8683 faces_info[gf].Elem2No = -1; // in case there's no other side
8684 faces_info[gf].Elem2Inf = -1; // face is not shared
8685 }
8686 else // this will be elem2
8687 {
8688 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8689 "Interior edge found between 2D elements "
8690 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8691 << " and " << el << ".");
8692 int *v = faces[gf]->GetVertices();
8693 faces_info[gf].Elem2No = el;
8694 if (v[1] == v0 && v[0] == v1)
8695 {
8696 faces_info[gf].Elem2Inf = 64 * lf + 1;
8697 }
8698 else if (v[0] == v0 && v[1] == v1)
8699 {
8700 // Temporarily allow even edge orientations: see the remark in
8701 // AddTriangleFaceElement().
8702 // Also, in a non-orientable surface mesh, the orientation will be even
8703 // for edges that connect elements with opposite orientations.
8704 faces_info[gf].Elem2Inf = 64 * lf;
8705 }
8706 else
8707 {
8708 MFEM_ABORT("internal error");
8709 }
8710 }
8711}
8712
8713void Mesh::AddTriangleFaceElement(int lf, int gf, int el,
8714 int v0, int v1, int v2)
8715{
8716 if (faces[gf] == NULL) // this will be elem1
8717 {
8718 faces[gf] = new Triangle(v0, v1, v2);
8719 faces_info[gf].Elem1No = el;
8720 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8721 faces_info[gf].Elem2No = -1; // in case there's no other side
8722 faces_info[gf].Elem2Inf = -1; // face is not shared
8723 }
8724 else // this will be elem2
8725 {
8726 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8727 "Interior triangular face found connecting elements "
8728 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8729 << " and " << el << ".");
8730 int orientation, vv[3] = { v0, v1, v2 };
8731 orientation = GetTriOrientation(faces[gf]->GetVertices(), vv);
8732 // In a valid mesh, we should have (orientation % 2 != 0), however, if
8733 // one of the adjacent elements has wrong orientation, both face
8734 // orientations can be even, until the element orientations are fixed.
8735 // MFEM_ASSERT(orientation % 2 != 0, "");
8736 faces_info[gf].Elem2No = el;
8737 faces_info[gf].Elem2Inf = 64 * lf + orientation;
8738 }
8739}
8740
8741void Mesh::AddQuadFaceElement(int lf, int gf, int el,
8742 int v0, int v1, int v2, int v3)
8743{
8744 if (faces_info[gf].Elem1No < 0) // this will be elem1
8745 {
8746 faces[gf] = new Quadrilateral(v0, v1, v2, v3);
8747 faces_info[gf].Elem1No = el;
8748 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8749 faces_info[gf].Elem2No = -1; // in case there's no other side
8750 faces_info[gf].Elem2Inf = -1; // face is not shared
8751 }
8752 else // this will be elem2
8753 {
8754 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8755 "Interior quadrilateral face found connecting elements "
8756 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8757 << " and " << el << ".");
8758 int vv[4] = { v0, v1, v2, v3 };
8759 int oo = GetQuadOrientation(faces[gf]->GetVertices(), vv);
8760 // Temporarily allow even face orientations: see the remark in
8761 // AddTriangleFaceElement().
8762 // MFEM_ASSERT(oo % 2 != 0, "");
8763 faces_info[gf].Elem2No = el;
8764 faces_info[gf].Elem2Inf = 64 * lf + oo;
8765 }
8766}
8767
8769{
8770 int nfaces = GetNumFaces();
8771 for (auto &f : faces)
8772 {
8773 FreeElement(f);
8774 }
8775
8776 // delete caches
8777 face_indices[0].SetSize(0);
8778 face_indices[1].SetSize(0);
8779 inv_face_indices[0].clear();
8780 inv_face_indices[1].clear();
8781
8782 // (re)generate the interior faces and the info for them
8783 faces.SetSize(nfaces);
8784 faces_info.SetSize(nfaces);
8785 for (int i = 0; i < nfaces; ++i)
8786 {
8787 faces[i] = NULL;
8788 faces_info[i].Elem1No = -1;
8789 faces_info[i].NCFace = -1;
8790 }
8791
8792 Array<int> v;
8793 for (int i = 0; i < NumOfElements; ++i)
8794 {
8795 elements[i]->GetVertices(v);
8796 if (Dim == 1)
8797 {
8798 AddPointFaceElement(0, v[0], i);
8799 AddPointFaceElement(1, v[1], i);
8800 }
8801 else if (Dim == 2)
8802 {
8803 const int * const ef = el_to_edge->GetRow(i);
8804 const int ne = elements[i]->GetNEdges();
8805 for (int j = 0; j < ne; j++)
8806 {
8807 const int *e = elements[i]->GetEdgeVertices(j);
8808 AddSegmentFaceElement(j, ef[j], i, v[e[0]], v[e[1]]);
8809 }
8810 }
8811 else
8812 {
8813 const int * const ef = el_to_face->GetRow(i);
8814 switch (GetElementType(i))
8815 {
8817 {
8818 for (int j = 0; j < 4; j++)
8819 {
8820 const int *fv = tet_t::FaceVert[j];
8821 AddTriangleFaceElement(j, ef[j], i,
8822 v[fv[0]], v[fv[1]], v[fv[2]]);
8823 }
8824 break;
8825 }
8826 case Element::WEDGE:
8827 {
8828 for (int j = 0; j < 2; j++)
8829 {
8830 const int *fv = pri_t::FaceVert[j];
8831 AddTriangleFaceElement(j, ef[j], i,
8832 v[fv[0]], v[fv[1]], v[fv[2]]);
8833 }
8834 for (int j = 2; j < 5; j++)
8835 {
8836 const int *fv = pri_t::FaceVert[j];
8837 AddQuadFaceElement(j, ef[j], i,
8838 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8839 }
8840 break;
8841 }
8842 case Element::PYRAMID:
8843 {
8844 for (int j = 0; j < 1; j++)
8845 {
8846 const int *fv = pyr_t::FaceVert[j];
8847 AddQuadFaceElement(j, ef[j], i,
8848 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8849 }
8850 for (int j = 1; j < 5; j++)
8851 {
8852 const int *fv = pyr_t::FaceVert[j];
8853 AddTriangleFaceElement(j, ef[j], i,
8854 v[fv[0]], v[fv[1]], v[fv[2]]);
8855 }
8856 break;
8857 }
8859 {
8860 for (int j = 0; j < 6; j++)
8861 {
8862 const int *fv = hex_t::FaceVert[j];
8863 AddQuadFaceElement(j, ef[j], i,
8864 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8865 }
8866 break;
8867 }
8868 default:
8869 MFEM_ABORT("Unexpected type of Element.");
8870 }
8871 }
8872 }
8873}
8874
8876{
8877 MFEM_VERIFY(ncmesh, "missing NCMesh.");
8878
8879 for (auto &x : faces_info)
8880 {
8881 x.NCFace = -1;
8882 }
8883
8884 const NCMesh::NCList &list =
8885 (Dim == 2) ? ncmesh->GetEdgeList() : ncmesh->GetFaceList();
8886
8887 nc_faces_info.SetSize(0);
8888 nc_faces_info.Reserve(list.masters.Size() + list.slaves.Size());
8889
8890 int nfaces = GetNumFaces();
8891
8892 // add records for master faces
8893 for (const NCMesh::Master &master : list.masters)
8894 {
8895 if (master.index >= nfaces) { continue; }
8896
8897 FaceInfo &master_fi = faces_info[master.index];
8898 master_fi.NCFace = nc_faces_info.Size();
8899 nc_faces_info.Append(NCFaceInfo(false, master.local, NULL));
8900 // NOTE: one of the unused members stores local face no. to be used below
8901 MFEM_ASSERT(master_fi.Elem2No == -1, "internal error");
8902 MFEM_ASSERT(master_fi.Elem2Inf == -1, "internal error");
8903 }
8904
8905 // add records for slave faces
8906 for (const NCMesh::Slave &slave : list.slaves)
8907 {
8908 if (slave.index < 0 || // degenerate slave face
8909 slave.index >= nfaces || // ghost slave
8910 slave.master >= nfaces) // has ghost master
8911 {
8912 continue;
8913 }
8914
8915 FaceInfo &slave_fi = faces_info[slave.index];
8916 FaceInfo &master_fi = faces_info[slave.master];
8917 NCFaceInfo &master_nc = nc_faces_info[master_fi.NCFace];
8918
8919 slave_fi.NCFace = nc_faces_info.Size();
8920 slave_fi.Elem2No = master_fi.Elem1No;
8921 slave_fi.Elem2Inf = 64 * master_nc.MasterFace; // get lf no. stored above
8922 // NOTE: In 3D, the orientation part of Elem2Inf is encoded in the point
8923 // matrix. In 2D, the point matrix has the orientation of the parent
8924 // edge, so its columns need to be flipped when applying it, see
8925 // ApplyLocalSlaveTransformation.
8926
8927 nc_faces_info.Append(
8928 NCFaceInfo(true, slave.master,
8929 list.point_matrices[slave.geom][slave.matrix]));
8930 }
8931}
8932
8934{
8935 STable3D *faces_tbl = new STable3D(NumOfVertices);
8936 for (int i = 0; i < NumOfElements; i++)
8937 {
8938 const int *v = elements[i]->GetVertices();
8939 switch (GetElementType(i))
8940 {
8942 {
8943 for (int j = 0; j < 4; j++)
8944 {
8945 const int *fv = tet_t::FaceVert[j];
8946 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8947 }
8948 break;
8949 }
8950 case Element::PYRAMID:
8951 {
8952 for (int j = 0; j < 1; j++)
8953 {
8954 const int *fv = pyr_t::FaceVert[j];
8955 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8956 }
8957 for (int j = 1; j < 5; j++)
8958 {
8959 const int *fv = pyr_t::FaceVert[j];
8960 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8961 }
8962 break;
8963 }
8964 case Element::WEDGE:
8965 {
8966 for (int j = 0; j < 2; j++)
8967 {
8968 const int *fv = pri_t::FaceVert[j];
8969 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8970 }
8971 for (int j = 2; j < 5; j++)
8972 {
8973 const int *fv = pri_t::FaceVert[j];
8974 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8975 }
8976 break;
8977 }
8979 {
8980 // find the face by the vertices with the smallest 3 numbers
8981 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
8982 for (int j = 0; j < 6; j++)
8983 {
8984 const int *fv = hex_t::FaceVert[j];
8985 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8986 }
8987 break;
8988 }
8989 default:
8990 MFEM_ABORT("Unexpected type of Element: " << GetElementType(i));
8991 }
8992 }
8993 return faces_tbl;
8994}
8995
8997{
8998 Array<int> v;
8999 STable3D *faces_tbl;
9000
9001 if (el_to_face != NULL)
9002 {
9003 delete el_to_face;
9004 }
9005 el_to_face = new Table(NumOfElements, 6); // must be 6 for hexahedra
9006 faces_tbl = new STable3D(NumOfVertices);
9007 for (int i = 0; i < NumOfElements; i++)
9008 {
9009 elements[i]->GetVertices(v);
9010 switch (GetElementType(i))
9011 {
9013 {
9014 for (int j = 0; j < 4; j++)
9015 {
9016 const int *fv = tet_t::FaceVert[j];
9018 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
9019 }
9020 break;
9021 }
9022 case Element::WEDGE:
9023 {
9024 for (int j = 0; j < 2; j++)
9025 {
9026 const int *fv = pri_t::FaceVert[j];
9028 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
9029 }
9030 for (int j = 2; j < 5; j++)
9031 {
9032 const int *fv = pri_t::FaceVert[j];
9034 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
9035 }
9036 break;
9037 }
9038 case Element::PYRAMID:
9039 {
9040 for (int j = 0; j < 1; j++)
9041 {
9042 const int *fv = pyr_t::FaceVert[j];
9044 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
9045 }
9046 for (int j = 1; j < 5; j++)
9047 {
9048 const int *fv = pyr_t::FaceVert[j];
9050 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
9051 }
9052 break;
9053 }
9055 {
9056 // find the face by the vertices with the smallest 3 numbers
9057 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
9058 for (int j = 0; j < 6; j++)
9059 {
9060 const int *fv = hex_t::FaceVert[j];
9062 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
9063 }
9064 break;
9065 }
9066 default:
9067 MFEM_ABORT("Unexpected type of Element.");
9068 }
9069 }
9071 NumOfFaces = faces_tbl->NumberOfElements();
9073
9074 for (int i = 0; i < NumOfBdrElements; i++)
9075 {
9076 boundary[i]->GetVertices(v);
9077 switch (GetBdrElementType(i))
9078 {
9079 case Element::TRIANGLE:
9080 {
9081 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2]);
9082 break;
9083 }
9085 {
9086 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
9087 break;
9088 }
9089 default:
9090 MFEM_ABORT("Unexpected type of boundary Element.");
9091 }
9092 }
9093
9094 if (ret_ftbl)
9095 {
9096 return faces_tbl;
9097 }
9098 delete faces_tbl;
9099 return NULL;
9100}
9101
9102// shift cyclically 3 integers so that the smallest is first
9103static inline
9104void Rotate3(int &a, int &b, int &c)
9105{
9106 if (a < b)
9107 {
9108 if (a > c)
9109 {
9110 ShiftRight(a, b, c);
9111 }
9112 }
9113 else
9114 {
9115 if (b < c)
9116 {
9117 ShiftRight(c, b, a);
9118 }
9119 else
9120 {
9121 ShiftRight(a, b, c);
9122 }
9123 }
9124}
9125
9127{
9128 if (Dim != 3 || !(meshgen & 1))
9129 {
9130 return;
9131 }
9132
9133 ResetLazyData();
9134
9135 DSTable *old_v_to_v = NULL;
9136 Table *old_elem_vert = NULL;
9137
9138 if (Nodes)
9139 {
9140 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
9141 }
9142
9143 for (int i = 0; i < NumOfElements; i++)
9144 {
9146 {
9147 int *v = elements[i]->GetVertices();
9148
9149 Rotate3(v[0], v[1], v[2]);
9150 if (v[0] < v[3])
9151 {
9152 Rotate3(v[1], v[2], v[3]);
9153 }
9154 else
9155 {
9156 ShiftRight(v[0], v[1], v[3]);
9157 }
9158 }
9159 }
9160
9161 for (int i = 0; i < NumOfBdrElements; i++)
9162 {
9164 {
9165 int *v = boundary[i]->GetVertices();
9166
9167 Rotate3(v[0], v[1], v[2]);
9168 }
9169 }
9170
9171 if (!Nodes)
9172 {
9174 GenerateFaces();
9175 if (el_to_edge)
9176 {
9178 }
9179 }
9180 else
9181 {
9182 DoNodeReorder(old_v_to_v, old_elem_vert);
9183 delete old_elem_vert;
9184 delete old_v_to_v;
9185 }
9186}
9187
9189{
9190 int *partitioning;
9191 real_t pmin[3] = { infinity(), infinity(), infinity() };
9192 real_t pmax[3] = { -infinity(), -infinity(), -infinity() };
9193 // find a bounding box using the vertices
9194 for (int vi = 0; vi < NumOfVertices; vi++)
9195 {
9196 const real_t *p = vertices[vi]();
9197 for (int i = 0; i < spaceDim; i++)
9198 {
9199 if (p[i] < pmin[i]) { pmin[i] = p[i]; }
9200 if (p[i] > pmax[i]) { pmax[i] = p[i]; }
9201 }
9202 }
9203
9204 partitioning = new int[NumOfElements];
9205
9206 // determine the partitioning using the centers of the elements
9207 real_t ppt[3];
9208 Vector pt(ppt, spaceDim);
9209 for (int el = 0; el < NumOfElements; el++)
9210 {
9211 GetElementTransformation(el)->Transform(
9213 int part = 0;
9214 for (int i = spaceDim-1; i >= 0; i--)
9215 {
9216 int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i])));
9217 if (idx < 0) { idx = 0; }
9218 if (idx >= nxyz[i]) { idx = nxyz[i]-1; }
9219 part = part * nxyz[i] + idx;
9220 }
9221 partitioning[el] = part;
9222 }
9223
9224 return partitioning;
9225}
9226
9227void FindPartitioningComponents(Table &elem_elem,
9228 const Array<int> &partitioning,
9229 Array<int> &component,
9230 Array<int> &num_comp);
9231
9232int *Mesh::GeneratePartitioning(int nparts, int part_method)
9233{
9234#ifdef MFEM_USE_METIS
9235
9236 int print_messages = 1;
9237 // If running in parallel, print messages only from rank 0.
9238#ifdef MFEM_USE_MPI
9239 int init_flag, fin_flag;
9240 MPI_Initialized(&init_flag);
9241 MPI_Finalized(&fin_flag);
9242 if (init_flag && !fin_flag)
9243 {
9244 int rank;
9245 MPI_Comm_rank(GetGlobalMPI_Comm(), &rank);
9246 if (rank != 0) { print_messages = 0; }
9247 }
9248#endif
9249
9250 int i, *partitioning;
9251
9253
9254 partitioning = new int[NumOfElements];
9255
9256 if (nparts == 1)
9257 {
9258 for (i = 0; i < NumOfElements; i++)
9259 {
9260 partitioning[i] = 0;
9261 }
9262 }
9263 else if (NumOfElements <= nparts)
9264 {
9265 for (i = 0; i < NumOfElements; i++)
9266 {
9267 partitioning[i] = i;
9268 }
9269 }
9270 else
9271 {
9272 idx_t *I, *J, n;
9273#ifndef MFEM_USE_METIS_5
9274 idx_t wgtflag = 0;
9275 idx_t numflag = 0;
9276 idx_t options[5];
9277#else
9278 idx_t ncon = 1;
9279 idx_t errflag;
9280 idx_t options[40];
9281#endif
9282 idx_t edgecut;
9283
9284 // In case METIS have been compiled with 64bit indices
9285 bool freedata = false;
9286 idx_t mparts = (idx_t) nparts;
9287 idx_t *mpartitioning;
9288
9289 n = NumOfElements;
9290 if (sizeof(idx_t) == sizeof(int))
9291 {
9292 I = (idx_t*) el_to_el->GetI();
9293 J = (idx_t*) el_to_el->GetJ();
9294 mpartitioning = (idx_t*) partitioning;
9295 }
9296 else
9297 {
9298 int *iI = el_to_el->GetI();
9299 int *iJ = el_to_el->GetJ();
9300 int m = iI[n];
9301 I = new idx_t[n+1];
9302 J = new idx_t[m];
9303 for (int k = 0; k < n+1; k++) { I[k] = iI[k]; }
9304 for (int k = 0; k < m; k++) { J[k] = iJ[k]; }
9305 mpartitioning = new idx_t[n];
9306 freedata = true;
9307 }
9308#ifndef MFEM_USE_METIS_5
9309 options[0] = 0;
9310#else
9311 METIS_SetDefaultOptions(options);
9312 options[METIS_OPTION_CONTIG] = 1; // set METIS_OPTION_CONTIG
9313 // If the mesh is disconnected, disable METIS_OPTION_CONTIG.
9314 {
9315 Array<int> part(partitioning, NumOfElements);
9316 part = 0; // single part for the whole mesh
9317 Array<int> component; // size will be set to num. elem.
9318 Array<int> num_comp; // size will be set to num. parts (1)
9319 FindPartitioningComponents(*el_to_el, part, component, num_comp);
9320 if (num_comp[0] > 1) { options[METIS_OPTION_CONTIG] = 0; }
9321 }
9322#endif
9323
9324 // Sort the neighbor lists
9325 if (part_method >= 0 && part_method <= 2)
9326 {
9327 for (i = 0; i < n; i++)
9328 {
9329 // Sort in increasing order.
9330 // std::sort(J+I[i], J+I[i+1]);
9331
9332 // Sort in decreasing order, as in previous versions of MFEM.
9333 std::sort(J+I[i], J+I[i+1], std::greater<idx_t>());
9334 }
9335 }
9336
9337 // This function should be used to partition a graph into a small
9338 // number of partitions (less than 8).
9339 if (part_method == 0 || part_method == 3)
9340 {
9341#ifndef MFEM_USE_METIS_5
9343 I,
9344 J,
9345 NULL,
9346 NULL,
9347 &wgtflag,
9348 &numflag,
9349 &mparts,
9350 options,
9351 &edgecut,
9352 mpartitioning);
9353#else
9354 errflag = METIS_PartGraphRecursive(&n,
9355 &ncon,
9356 I,
9357 J,
9358 NULL,
9359 NULL,
9360 NULL,
9361 &mparts,
9362 NULL,
9363 NULL,
9364 options,
9365 &edgecut,
9366 mpartitioning);
9367 if (errflag != 1)
9368 {
9369 mfem_error("Mesh::GeneratePartitioning: "
9370 " error in METIS_PartGraphRecursive!");
9371 }
9372#endif
9373 }
9374
9375 // This function should be used to partition a graph into a large
9376 // number of partitions (greater than 8).
9377 if (part_method == 1 || part_method == 4)
9378 {
9379#ifndef MFEM_USE_METIS_5
9381 I,
9382 J,
9383 NULL,
9384 NULL,
9385 &wgtflag,
9386 &numflag,
9387 &mparts,
9388 options,
9389 &edgecut,
9390 mpartitioning);
9391#else
9392 errflag = METIS_PartGraphKway(&n,
9393 &ncon,
9394 I,
9395 J,
9396 NULL,
9397 NULL,
9398 NULL,
9399 &mparts,
9400 NULL,
9401 NULL,
9402 options,
9403 &edgecut,
9404 mpartitioning);
9405 if (errflag != 1)
9406 {
9407 mfem_error("Mesh::GeneratePartitioning: "
9408 " error in METIS_PartGraphKway!");
9409 }
9410#endif
9411 }
9412
9413 // The objective of this partitioning is to minimize the total
9414 // communication volume
9415 if (part_method == 2 || part_method == 5)
9416 {
9417#ifndef MFEM_USE_METIS_5
9419 I,
9420 J,
9421 NULL,
9422 NULL,
9423 &wgtflag,
9424 &numflag,
9425 &mparts,
9426 options,
9427 &edgecut,
9428 mpartitioning);
9429#else
9430 options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL;
9431 errflag = METIS_PartGraphKway(&n,
9432 &ncon,
9433 I,
9434 J,
9435 NULL,
9436 NULL,
9437 NULL,
9438 &mparts,
9439 NULL,
9440 NULL,
9441 options,
9442 &edgecut,
9443 mpartitioning);
9444 if (errflag != 1)
9445 {
9446 mfem_error("Mesh::GeneratePartitioning: "
9447 " error in METIS_PartGraphKway!");
9448 }
9449#endif
9450 }
9451
9452#ifdef MFEM_DEBUG
9453 if (print_messages)
9454 {
9455 mfem::out << "Mesh::GeneratePartitioning(...): edgecut = "
9456 << edgecut << endl;
9457 }
9458#endif
9459 nparts = (int) mparts;
9460 if (mpartitioning != (idx_t*)partitioning)
9461 {
9462 for (int k = 0; k<NumOfElements; k++)
9463 {
9464 partitioning[k] = mpartitioning[k];
9465 }
9466 }
9467 if (freedata)
9468 {
9469 delete[] I;
9470 delete[] J;
9471 delete[] mpartitioning;
9472 }
9473 }
9474
9475 delete el_to_el;
9476 el_to_el = NULL;
9477
9478 // Check for empty partitionings (a "feature" in METIS)
9479 if (nparts > 1 && NumOfElements > nparts)
9480 {
9481 Array< Pair<int,int> > psize(nparts);
9482 int empty_parts;
9483
9484 // Count how many elements are in each partition, and store the result in
9485 // psize, where psize[i].one is the number of elements, and psize[i].two
9486 // is partition index. Keep track of the number of empty parts.
9487 auto count_partition_elements = [&]()
9488 {
9489 for (i = 0; i < nparts; i++)
9490 {
9491 psize[i].one = 0;
9492 psize[i].two = i;
9493 }
9494
9495 for (i = 0; i < NumOfElements; i++)
9496 {
9497 psize[partitioning[i]].one++;
9498 }
9499
9500 empty_parts = 0;
9501 for (i = 0; i < nparts; i++)
9502 {
9503 if (psize[i].one == 0) { empty_parts++; }
9504 }
9505 };
9506
9507 count_partition_elements();
9508
9509 // This code just split the largest partitionings in two.
9510 // Do we need to replace it with something better?
9511 while (empty_parts)
9512 {
9513 if (print_messages)
9514 {
9515 mfem::err << "Mesh::GeneratePartitioning(...): METIS returned "
9516 << empty_parts << " empty parts!"
9517 << " Applying a simple fix ..." << endl;
9518 }
9519
9520 SortPairs<int,int>(psize, nparts);
9521
9522 for (i = nparts-1; i > nparts-1-empty_parts; i--)
9523 {
9524 psize[i].one /= 2;
9525 }
9526
9527 for (int j = 0; j < NumOfElements; j++)
9528 {
9529 for (i = nparts-1; i > nparts-1-empty_parts; i--)
9530 {
9531 if (psize[i].one == 0 || partitioning[j] != psize[i].two)
9532 {
9533 continue;
9534 }
9535 else
9536 {
9537 partitioning[j] = psize[nparts-1-i].two;
9538 psize[i].one--;
9539 }
9540 }
9541 }
9542
9543 // Check for empty partitionings again
9544 count_partition_elements();
9545 }
9546 }
9547
9548 return partitioning;
9549
9550#else
9551
9552 mfem_error("Mesh::GeneratePartitioning(...): "
9553 "MFEM was compiled without Metis.");
9554
9555 return NULL;
9556
9557#endif
9558}
9559
9560/* required: 0 <= partitioning[i] < num_part */
9562 const Array<int> &partitioning,
9563 Array<int> &component,
9564 Array<int> &num_comp)
9565{
9566 int i, j, k;
9567 int num_elem, *i_elem_elem, *j_elem_elem;
9568
9569 num_elem = elem_elem.Size();
9570 i_elem_elem = elem_elem.GetI();
9571 j_elem_elem = elem_elem.GetJ();
9572
9573 component.SetSize(num_elem);
9574
9575 Array<int> elem_stack(num_elem);
9576 int stack_p, stack_top_p, elem;
9577 int num_part;
9578
9579 num_part = -1;
9580 for (i = 0; i < num_elem; i++)
9581 {
9582 if (partitioning[i] > num_part)
9583 {
9584 num_part = partitioning[i];
9585 }
9586 component[i] = -1;
9587 }
9588 num_part++;
9589
9590 num_comp.SetSize(num_part);
9591 for (i = 0; i < num_part; i++)
9592 {
9593 num_comp[i] = 0;
9594 }
9595
9596 stack_p = 0;
9597 stack_top_p = 0; // points to the first unused element in the stack
9598 for (elem = 0; elem < num_elem; elem++)
9599 {
9600 if (component[elem] >= 0)
9601 {
9602 continue;
9603 }
9604
9605 component[elem] = num_comp[partitioning[elem]]++;
9606
9607 elem_stack[stack_top_p++] = elem;
9608
9609 for ( ; stack_p < stack_top_p; stack_p++)
9610 {
9611 i = elem_stack[stack_p];
9612 for (j = i_elem_elem[i]; j < i_elem_elem[i+1]; j++)
9613 {
9614 k = j_elem_elem[j];
9615 if (partitioning[k] == partitioning[i])
9616 {
9617 if (component[k] < 0)
9618 {
9619 component[k] = component[i];
9620 elem_stack[stack_top_p++] = k;
9621 }
9622 else if (component[k] != component[i])
9623 {
9624 mfem_error("FindPartitioningComponents");
9625 }
9626 }
9627 }
9628 }
9629 }
9630}
9631
9632void Mesh::CheckPartitioning(int *partitioning_)
9633{
9634 int i, n_empty, n_mcomp;
9635 Array<int> component, num_comp;
9636 const Array<int> partitioning(partitioning_, GetNE());
9637
9639
9640 FindPartitioningComponents(*el_to_el, partitioning, component, num_comp);
9641
9642 n_empty = n_mcomp = 0;
9643 for (i = 0; i < num_comp.Size(); i++)
9644 if (num_comp[i] == 0)
9645 {
9646 n_empty++;
9647 }
9648 else if (num_comp[i] > 1)
9649 {
9650 n_mcomp++;
9651 }
9652
9653 if (n_empty > 0)
9654 {
9655 mfem::out << "Mesh::CheckPartitioning(...) :\n"
9656 << "The following subdomains are empty :\n";
9657 for (i = 0; i < num_comp.Size(); i++)
9658 if (num_comp[i] == 0)
9659 {
9660 mfem::out << ' ' << i;
9661 }
9662 mfem::out << endl;
9663 }
9664 if (n_mcomp > 0)
9665 {
9666 mfem::out << "Mesh::CheckPartitioning(...) :\n"
9667 << "The following subdomains are NOT connected :\n";
9668 for (i = 0; i < num_comp.Size(); i++)
9669 if (num_comp[i] > 1)
9670 {
9671 mfem::out << ' ' << i;
9672 }
9673 mfem::out << endl;
9674 }
9675 if (n_empty == 0 && n_mcomp == 0)
9676 mfem::out << "Mesh::CheckPartitioning(...) : "
9677 "All subdomains are connected." << endl;
9678
9679 if (el_to_el)
9680 {
9681 delete el_to_el;
9682 }
9683 el_to_el = NULL;
9684}
9685
9686// compute the coefficients of the polynomial in t:
9687// c(0)+c(1)*t+...+c(d)*t^d = det(A+t*B)
9688// where A, B are (d x d), d=2,3
9689void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
9690{
9691 const real_t *a = A.Data();
9692 const real_t *b = B.Data();
9693
9694 c.SetSize(A.Width()+1);
9695 switch (A.Width())
9696 {
9697 case 2:
9698 {
9699 // det(A+t*B) = |a0 a2| / |a0 b2| + |b0 a2| \ |b0 b2|
9700 // |a1 a3| + \ |a1 b3| |b1 a3| / * t + |b1 b3| * t^2
9701 c(0) = a[0]*a[3]-a[1]*a[2];
9702 c(1) = a[0]*b[3]-a[1]*b[2]+b[0]*a[3]-b[1]*a[2];
9703 c(2) = b[0]*b[3]-b[1]*b[2];
9704 }
9705 break;
9706
9707 case 3:
9708 {
9709 /* |a0 a3 a6|
9710 * det(A+t*B) = |a1 a4 a7| +
9711 * |a2 a5 a8|
9712
9713 * / |b0 a3 a6| |a0 b3 a6| |a0 a3 b6| \
9714 * + | |b1 a4 a7| + |a1 b4 a7| + |a1 a4 b7| | * t +
9715 * \ |b2 a5 a8| |a2 b5 a8| |a2 a5 b8| /
9716
9717 * / |a0 b3 b6| |b0 a3 b6| |b0 b3 a6| \
9718 * + | |a1 b4 b7| + |b1 a4 b7| + |b1 b4 a7| | * t^2 +
9719 * \ |a2 b5 b8| |b2 a5 b8| |b2 b5 a8| /
9720
9721 * |b0 b3 b6|
9722 * + |b1 b4 b7| * t^3
9723 * |b2 b5 b8| */
9724 c(0) = (a[0] * (a[4] * a[8] - a[5] * a[7]) +
9725 a[1] * (a[5] * a[6] - a[3] * a[8]) +
9726 a[2] * (a[3] * a[7] - a[4] * a[6]));
9727
9728 c(1) = (b[0] * (a[4] * a[8] - a[5] * a[7]) +
9729 b[1] * (a[5] * a[6] - a[3] * a[8]) +
9730 b[2] * (a[3] * a[7] - a[4] * a[6]) +
9731
9732 a[0] * (b[4] * a[8] - b[5] * a[7]) +
9733 a[1] * (b[5] * a[6] - b[3] * a[8]) +
9734 a[2] * (b[3] * a[7] - b[4] * a[6]) +
9735
9736 a[0] * (a[4] * b[8] - a[5] * b[7]) +
9737 a[1] * (a[5] * b[6] - a[3] * b[8]) +
9738 a[2] * (a[3] * b[7] - a[4] * b[6]));
9739
9740 c(2) = (a[0] * (b[4] * b[8] - b[5] * b[7]) +
9741 a[1] * (b[5] * b[6] - b[3] * b[8]) +
9742 a[2] * (b[3] * b[7] - b[4] * b[6]) +
9743
9744 b[0] * (a[4] * b[8] - a[5] * b[7]) +
9745 b[1] * (a[5] * b[6] - a[3] * b[8]) +
9746 b[2] * (a[3] * b[7] - a[4] * b[6]) +
9747
9748 b[0] * (b[4] * a[8] - b[5] * a[7]) +
9749 b[1] * (b[5] * a[6] - b[3] * a[8]) +
9750 b[2] * (b[3] * a[7] - b[4] * a[6]));
9751
9752 c(3) = (b[0] * (b[4] * b[8] - b[5] * b[7]) +
9753 b[1] * (b[5] * b[6] - b[3] * b[8]) +
9754 b[2] * (b[3] * b[7] - b[4] * b[6]));
9755 }
9756 break;
9757
9758 default:
9759 mfem_error("DetOfLinComb(...)");
9760 }
9761}
9762
9763// compute the real roots of
9764// z(0)+z(1)*x+...+z(d)*x^d = 0, d=2,3;
9765// the roots are returned in x, sorted in increasing order;
9766// it is assumed that x is at least of size d;
9767// return the number of roots counting multiplicity;
9768// return -1 if all z(i) are 0.
9769int FindRoots(const Vector &z, Vector &x)
9770{
9771 int d = z.Size()-1;
9772 if (d > 3 || d < 0)
9773 {
9774 mfem_error("FindRoots(...)");
9775 }
9776
9777 while (z(d) == 0.0)
9778 {
9779 if (d == 0)
9780 {
9781 return (-1);
9782 }
9783 d--;
9784 }
9785 switch (d)
9786 {
9787 case 0:
9788 {
9789 return 0;
9790 }
9791
9792 case 1:
9793 {
9794 x(0) = -z(0)/z(1);
9795 return 1;
9796 }
9797
9798 case 2:
9799 {
9800 real_t a = z(2), b = z(1), c = z(0);
9801 real_t D = b*b-4*a*c;
9802 if (D < 0.0)
9803 {
9804 return 0;
9805 }
9806 if (D == 0.0)
9807 {
9808 x(0) = x(1) = -0.5 * b / a;
9809 return 2; // root with multiplicity 2
9810 }
9811 if (b == 0.0)
9812 {
9813 x(0) = -(x(1) = fabs(0.5 * sqrt(D) / a));
9814 return 2;
9815 }
9816 else
9817 {
9818 real_t t;
9819 if (b > 0.0)
9820 {
9821 t = -0.5 * (b + sqrt(D));
9822 }
9823 else
9824 {
9825 t = -0.5 * (b - sqrt(D));
9826 }
9827 x(0) = t / a;
9828 x(1) = c / t;
9829 if (x(0) > x(1))
9830 {
9831 Swap<real_t>(x(0), x(1));
9832 }
9833 return 2;
9834 }
9835 }
9836
9837 case 3:
9838 {
9839 real_t a = z(2)/z(3), b = z(1)/z(3), c = z(0)/z(3);
9840
9841 // find the real roots of x^3 + a x^2 + b x + c = 0
9842 real_t Q = (a * a - 3 * b) / 9;
9843 real_t R = (2 * a * a * a - 9 * a * b + 27 * c) / 54;
9844 real_t Q3 = Q * Q * Q;
9845 real_t R2 = R * R;
9846
9847 if (R2 == Q3)
9848 {
9849 if (Q == 0)
9850 {
9851 x(0) = x(1) = x(2) = - a / 3;
9852 }
9853 else
9854 {
9855 real_t sqrtQ = sqrt(Q);
9856
9857 if (R > 0)
9858 {
9859 x(0) = -2 * sqrtQ - a / 3;
9860 x(1) = x(2) = sqrtQ - a / 3;
9861 }
9862 else
9863 {
9864 x(0) = x(1) = - sqrtQ - a / 3;
9865 x(2) = 2 * sqrtQ - a / 3;
9866 }
9867 }
9868 return 3;
9869 }
9870 else if (R2 < Q3)
9871 {
9872 real_t theta = acos(R / sqrt(Q3));
9873 real_t A = -2 * sqrt(Q);
9874 real_t x0, x1, x2;
9875 x0 = A * cos(theta / 3) - a / 3;
9876 x1 = A * cos((theta + 2.0 * M_PI) / 3) - a / 3;
9877 x2 = A * cos((theta - 2.0 * M_PI) / 3) - a / 3;
9878
9879 /* Sort x0, x1, x2 */
9880 if (x0 > x1)
9881 {
9882 Swap<real_t>(x0, x1);
9883 }
9884 if (x1 > x2)
9885 {
9886 Swap<real_t>(x1, x2);
9887 if (x0 > x1)
9888 {
9889 Swap<real_t>(x0, x1);
9890 }
9891 }
9892 x(0) = x0;
9893 x(1) = x1;
9894 x(2) = x2;
9895 return 3;
9896 }
9897 else
9898 {
9899 real_t A;
9900 if (R >= 0.0)
9901 {
9902 A = -pow(sqrt(R2 - Q3) + R, 1.0/3.0);
9903 }
9904 else
9905 {
9906 A = pow(sqrt(R2 - Q3) - R, 1.0/3.0);
9907 }
9908 x(0) = A + Q / A - a / 3;
9909 return 1;
9910 }
9911 }
9912 }
9913 return 0;
9914}
9915
9916void FindTMax(Vector &c, Vector &x, real_t &tmax,
9917 const real_t factor, const int Dim)
9918{
9919 const real_t c0 = c(0);
9920 c(0) = c0 * (1.0 - pow(factor, -Dim));
9921 int nr = FindRoots(c, x);
9922 for (int j = 0; j < nr; j++)
9923 {
9924 if (x(j) > tmax)
9925 {
9926 break;
9927 }
9928 if (x(j) >= 0.0)
9929 {
9930 tmax = x(j);
9931 break;
9932 }
9933 }
9934 c(0) = c0 * (1.0 - pow(factor, Dim));
9935 nr = FindRoots(c, x);
9936 for (int j = 0; j < nr; j++)
9937 {
9938 if (x(j) > tmax)
9939 {
9940 break;
9941 }
9942 if (x(j) >= 0.0)
9943 {
9944 tmax = x(j);
9945 break;
9946 }
9947 }
9948}
9949
9950void Mesh::CheckDisplacements(const Vector &displacements, real_t &tmax)
9951{
9952 int nvs = vertices.Size();
9953 DenseMatrix P, V, DS, PDS(spaceDim), VDS(spaceDim);
9954 Vector c(spaceDim+1), x(spaceDim);
9955 const real_t factor = 2.0;
9956
9957 // check for tangling assuming constant speed
9958 if (tmax < 1.0)
9959 {
9960 tmax = 1.0;
9961 }
9962 for (int i = 0; i < NumOfElements; i++)
9963 {
9964 Element *el = elements[i];
9965 int nv = el->GetNVertices();
9966 int *v = el->GetVertices();
9967 P.SetSize(spaceDim, nv);
9968 V.SetSize(spaceDim, nv);
9969 for (int j = 0; j < spaceDim; j++)
9970 for (int k = 0; k < nv; k++)
9971 {
9972 P(j, k) = vertices[v[k]](j);
9973 V(j, k) = displacements(v[k]+j*nvs);
9974 }
9975 DS.SetSize(nv, spaceDim);
9976 const FiniteElement *fe =
9978 // check if det(P.DShape+t*V.DShape) > 0 for all x and 0<=t<=1
9979 switch (el->GetType())
9980 {
9981 case Element::TRIANGLE:
9983 {
9984 // DS is constant
9986 Mult(P, DS, PDS);
9987 Mult(V, DS, VDS);
9988 DetOfLinComb(PDS, VDS, c);
9989 if (c(0) <= 0.0)
9990 {
9991 tmax = 0.0;
9992 }
9993 else
9994 {
9995 FindTMax(c, x, tmax, factor, Dim);
9996 }
9997 }
9998 break;
9999
10001 {
10002 const IntegrationRule &ir = fe->GetNodes();
10003 for (int j = 0; j < nv; j++)
10004 {
10005 fe->CalcDShape(ir.IntPoint(j), DS);
10006 Mult(P, DS, PDS);
10007 Mult(V, DS, VDS);
10008 DetOfLinComb(PDS, VDS, c);
10009 if (c(0) <= 0.0)
10010 {
10011 tmax = 0.0;
10012 }
10013 else
10014 {
10015 FindTMax(c, x, tmax, factor, Dim);
10016 }
10017 }
10018 }
10019 break;
10020
10021 default:
10022 mfem_error("Mesh::CheckDisplacements(...)");
10023 }
10024 }
10025}
10026
10027void Mesh::MoveVertices(const Vector &displacements)
10028{
10029 for (int i = 0, nv = vertices.Size(); i < nv; i++)
10030 for (int j = 0; j < spaceDim; j++)
10031 {
10032 vertices[i](j) += displacements(j*nv+i);
10033 }
10034}
10035
10036void Mesh::GetVertices(Vector &vert_coord) const
10037{
10038 int nv = vertices.Size();
10039 vert_coord.SetSize(nv*spaceDim);
10040 for (int i = 0; i < nv; i++)
10041 for (int j = 0; j < spaceDim; j++)
10042 {
10043 vert_coord(j*nv+i) = vertices[i](j);
10044 }
10045}
10046
10047void Mesh::SetVertices(const Vector &vert_coord)
10048{
10049 MFEM_VERIFY(vert_coord.Size() == spaceDim * NumOfVertices, "");
10050 vertices.SetSize(NumOfVertices);
10051 for (int i = 0, nv = vertices.Size(); i < nv; i++)
10052 for (int j = 0; j < spaceDim; j++)
10053 {
10054 vertices[i](j) = vert_coord(j*nv+i);
10055 }
10056}
10057
10058void Mesh::GetNode(int i, real_t *coord) const
10059{
10060 if (Nodes)
10061 {
10063 for (int j = 0; j < spaceDim; j++)
10064 {
10065 coord[j] = AsConst(*Nodes)(fes->DofToVDof(i, j));
10066 }
10067 }
10068 else
10069 {
10070 for (int j = 0; j < spaceDim; j++)
10071 {
10072 coord[j] = vertices[i](j);
10073 }
10074 }
10075}
10076
10077void Mesh::SetNode(int i, const real_t *coord)
10078{
10079 if (Nodes)
10080 {
10082 for (int j = 0; j < spaceDim; j++)
10083 {
10084 (*Nodes)(fes->DofToVDof(i, j)) = coord[j];
10085 }
10086 }
10087 else
10088 {
10089 for (int j = 0; j < spaceDim; j++)
10090 {
10091 vertices[i](j) = coord[j];
10092 }
10093
10094 }
10095}
10096
10097void Mesh::MoveNodes(const Vector &displacements)
10098{
10099 if (Nodes)
10100 {
10101 (*Nodes) += displacements;
10102 }
10103 else
10104 {
10105 MoveVertices(displacements);
10106 }
10107
10108 // Invalidate the old geometric factors
10109 NodesUpdated();
10110}
10111
10112void Mesh::GetNodes(Vector &node_coord) const
10113{
10114 if (Nodes)
10115 {
10116 node_coord = (*Nodes);
10117 }
10118 else
10119 {
10120 GetVertices(node_coord);
10121 }
10122}
10123
10124void Mesh::SetNodes(const Vector &node_coord)
10125{
10126 if (Nodes)
10127 {
10128 (*Nodes) = node_coord;
10129 }
10130 else
10131 {
10132 SetVertices(node_coord);
10133 }
10134
10135 // Invalidate the old geometric factors
10136 NodesUpdated();
10137}
10138
10139void Mesh::NewNodes(GridFunction &nodes, bool make_owner)
10140{
10141 if (own_nodes) { delete Nodes; }
10142 Nodes = &nodes;
10143 spaceDim = Nodes->FESpace()->GetVDim();
10144 own_nodes = (int)make_owner;
10145
10146 if (NURBSext != nodes.FESpace()->GetNURBSext())
10147 {
10148 delete NURBSext;
10149 NURBSext = nodes.FESpace()->StealNURBSext();
10150 }
10151
10152 if (ncmesh)
10153 {
10155 }
10156
10157 // Invalidate the old geometric factors
10158 NodesUpdated();
10159}
10160
10161void Mesh::SwapNodes(GridFunction *&nodes, int &own_nodes_)
10162{
10163 // If this is a nonconforming mesh without nodes, ncmesh->coordinates will
10164 // be non-empty; so if the 'nodes' argument is not NULL, we will create an
10165 // inconsistent state where the Mesh has nodes and ncmesh->coordinates is not
10166 // empty. This was creating an issue for Mesh::Print() since both the
10167 // "coordinates" and "nodes" sections were written, leading to crashes during
10168 // loading. This issue is now fixed in Mesh::Printer() by temporarily
10169 // swapping ncmesh->coordinates with an empty array when the Mesh has nodes.
10170
10172 mfem::Swap<int>(own_nodes, own_nodes_);
10173 // TODO:
10174 // if (nodes)
10175 // nodes->FESpace()->MakeNURBSextOwner();
10176 // NURBSext = (Nodes) ? Nodes->FESpace()->StealNURBSext() : NULL;
10177
10178 // Invalidate the old geometric factors
10179 NodesUpdated();
10180}
10181
10182void Mesh::AverageVertices(const int *indexes, int n, int result)
10183{
10184 int j, k;
10185
10186 for (k = 0; k < spaceDim; k++)
10187 {
10188 vertices[result](k) = vertices[indexes[0]](k);
10189 }
10190
10191 for (j = 1; j < n; j++)
10192 for (k = 0; k < spaceDim; k++)
10193 {
10194 vertices[result](k) += vertices[indexes[j]](k);
10195 }
10196
10197 for (k = 0; k < spaceDim; k++)
10198 {
10199 vertices[result](k) *= (1.0 / n);
10200 }
10201}
10202
10204{
10205 if (Nodes)
10206 {
10207 Nodes->FESpace()->Update();
10208 Nodes->Update();
10209
10210 // update vertex coordinates for compatibility (e.g., GetVertex())
10212
10213 // Invalidate the old geometric factors
10214 NodesUpdated();
10215 }
10216}
10217
10218void Mesh::UniformRefinement2D_base(bool update_nodes)
10219{
10220 ResetLazyData();
10221
10222 if (el_to_edge == NULL)
10223 {
10224 el_to_edge = new Table;
10226 }
10227
10228 int quad_counter = 0;
10229 for (int i = 0; i < NumOfElements; i++)
10230 {
10231 if (elements[i]->GetType() == Element::QUADRILATERAL)
10232 {
10233 quad_counter++;
10234 }
10235 }
10236
10237 const int oedge = NumOfVertices;
10238 const int oelem = oedge + NumOfEdges;
10239
10240 Array<Element*> new_elements;
10241 Array<Element*> new_boundary;
10242
10243 vertices.SetSize(oelem + quad_counter);
10244 new_elements.SetSize(4 * NumOfElements);
10245 quad_counter = 0;
10246
10247 for (int i = 0, j = 0; i < NumOfElements; i++)
10248 {
10249 const Element::Type el_type = elements[i]->GetType();
10250 const int attr = elements[i]->GetAttribute();
10251 int *v = elements[i]->GetVertices();
10252 const int *e = el_to_edge->GetRow(i);
10253 int vv[2];
10254
10255 if (el_type == Element::TRIANGLE)
10256 {
10257 for (int ei = 0; ei < 3; ei++)
10258 {
10259 for (int k = 0; k < 2; k++)
10260 {
10261 vv[k] = v[tri_t::Edges[ei][k]];
10262 }
10263 AverageVertices(vv, 2, oedge+e[ei]);
10264 }
10265
10266 new_elements[j++] =
10267 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
10268 new_elements[j++] =
10269 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
10270 new_elements[j++] =
10271 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
10272 new_elements[j++] =
10273 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
10274 }
10275 else if (el_type == Element::QUADRILATERAL)
10276 {
10277 const int qe = quad_counter;
10278 quad_counter++;
10279 AverageVertices(v, 4, oelem+qe);
10280
10281 for (int ei = 0; ei < 4; ei++)
10282 {
10283 for (int k = 0; k < 2; k++)
10284 {
10285 vv[k] = v[quad_t::Edges[ei][k]];
10286 }
10287 AverageVertices(vv, 2, oedge+e[ei]);
10288 }
10289
10290 new_elements[j++] =
10291 new Quadrilateral(v[0], oedge+e[0], oelem+qe, oedge+e[3], attr);
10292 new_elements[j++] =
10293 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oelem+qe, attr);
10294 new_elements[j++] =
10295 new Quadrilateral(oelem+qe, oedge+e[1], v[2], oedge+e[2], attr);
10296 new_elements[j++] =
10297 new Quadrilateral(oedge+e[3], oelem+qe, oedge+e[2], v[3], attr);
10298 }
10299 else
10300 {
10301 MFEM_ABORT("unknown element type: " << el_type);
10302 }
10304 }
10305 mfem::Swap(elements, new_elements);
10306
10307 // refine boundary elements
10308 new_boundary.SetSize(2 * NumOfBdrElements);
10309 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
10310 {
10311 const int attr = boundary[i]->GetAttribute();
10312 int *v = boundary[i]->GetVertices();
10313
10314 new_boundary[j++] = new Segment(v[0], oedge+be_to_face[i], attr);
10315 new_boundary[j++] = new Segment(oedge+be_to_face[i], v[1], attr);
10316
10318 }
10319 mfem::Swap(boundary, new_boundary);
10320
10321 static const real_t A = 0.0, B = 0.5, C = 1.0;
10322 static real_t tri_children[2*3*4] =
10323 {
10324 A,A, B,A, A,B,
10325 B,B, A,B, B,A,
10326 B,A, C,A, B,B,
10327 A,B, B,B, A,C
10328 };
10329 static real_t quad_children[2*4*4] =
10330 {
10331 A,A, B,A, B,B, A,B, // lower-left
10332 B,A, C,A, C,B, B,B, // lower-right
10333 B,B, C,B, C,C, B,C, // upper-right
10334 A,B, B,B, B,C, A,C // upper-left
10335 };
10336
10338 .UseExternalData(tri_children, 2, 3, 4);
10340 .UseExternalData(quad_children, 2, 4, 4);
10341 CoarseFineTr.embeddings.SetSize(elements.Size());
10342
10343 for (int i = 0; i < elements.Size(); i++)
10344 {
10346 emb.parent = i / 4;
10347 emb.matrix = i % 4;
10348 }
10349
10350 NumOfVertices = vertices.Size();
10353 NumOfFaces = 0;
10354
10356 GenerateFaces();
10357
10359 sequence++;
10360
10361 if (update_nodes) { UpdateNodes(); }
10362
10363#ifdef MFEM_DEBUG
10364 if (!Nodes || update_nodes)
10365 {
10367 }
10369#endif
10370}
10371
10372static inline real_t sqr(const real_t &x)
10373{
10374 return x*x;
10375}
10376
10378 bool update_nodes)
10379{
10380 ResetLazyData();
10381
10382 if (el_to_edge == NULL)
10383 {
10384 el_to_edge = new Table;
10386 }
10387
10388 if (el_to_face == NULL)
10389 {
10391 }
10392
10393 Array<int> f2qf_loc;
10394 Array<int> &f2qf = f2qf_ptr ? *f2qf_ptr : f2qf_loc;
10395 f2qf.SetSize(0);
10396
10397 int NumOfQuadFaces = 0;
10399 {
10401 {
10402 f2qf.SetSize(faces.Size());
10403 for (int i = 0; i < faces.Size(); i++)
10404 {
10405 if (faces[i]->GetType() == Element::QUADRILATERAL)
10406 {
10407 f2qf[i] = NumOfQuadFaces;
10408 NumOfQuadFaces++;
10409 }
10410 }
10411 }
10412 else
10413 {
10414 NumOfQuadFaces = faces.Size();
10415 }
10416 }
10417
10418 int hex_counter = 0;
10420 {
10421 for (int i = 0; i < elements.Size(); i++)
10422 {
10423 if (elements[i]->GetType() == Element::HEXAHEDRON)
10424 {
10425 hex_counter++;
10426 }
10427 }
10428 }
10429
10430 int pyr_counter = 0;
10432 {
10433 for (int i = 0; i < elements.Size(); i++)
10434 {
10435 if (elements[i]->GetType() == Element::PYRAMID)
10436 {
10437 pyr_counter++;
10438 }
10439 }
10440 }
10441
10442 // Map from edge-index to vertex-index, needed for ReorientTetMesh() for
10443 // parallel meshes.
10444 // Note: with the removal of ReorientTetMesh() this may no longer
10445 // be needed. Unfortunately, it's hard to be sure.
10446 Array<int> e2v;
10448 {
10449 e2v.SetSize(NumOfEdges);
10450
10451 DSTable *v_to_v_ptr = v_to_v_p;
10452 if (!v_to_v_p)
10453 {
10454 v_to_v_ptr = new DSTable(NumOfVertices);
10455 GetVertexToVertexTable(*v_to_v_ptr);
10456 }
10457
10458 Array<Pair<int,int> > J_v2v(NumOfEdges); // (second vertex id, edge id)
10459 J_v2v.SetSize(0);
10460 for (int i = 0; i < NumOfVertices; i++)
10461 {
10462 Pair<int,int> *row_start = J_v2v.end();
10463 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
10464 {
10465 J_v2v.Append(Pair<int,int>(it.Column(), it.Index()));
10466 }
10467 std::sort(row_start, J_v2v.end());
10468 }
10469
10470 for (int i = 0; i < J_v2v.Size(); i++)
10471 {
10472 e2v[J_v2v[i].two] = i;
10473 }
10474
10475 if (!v_to_v_p)
10476 {
10477 delete v_to_v_ptr;
10478 }
10479 else
10480 {
10481 for (int i = 0; i < NumOfVertices; i++)
10482 {
10483 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
10484 {
10485 it.SetIndex(e2v[it.Index()]);
10486 }
10487 }
10488 }
10489 }
10490
10491 // Offsets for new vertices from edges, faces (quads only), and elements
10492 // (hexes only); each of these entities generates one new vertex.
10493 const int oedge = NumOfVertices;
10494 const int oface = oedge + NumOfEdges;
10495 const int oelem = oface + NumOfQuadFaces;
10496
10497 Array<Element*> new_elements;
10498 Array<Element*> new_boundary;
10499
10500 vertices.SetSize(oelem + hex_counter);
10501 new_elements.SetSize(8 * NumOfElements + 2 * pyr_counter);
10502 CoarseFineTr.embeddings.SetSize(new_elements.Size());
10503
10504 hex_counter = 0;
10505 for (int i = 0, j = 0; i < NumOfElements; i++)
10506 {
10507 const Element::Type el_type = elements[i]->GetType();
10508 const int attr = elements[i]->GetAttribute();
10509 int *v = elements[i]->GetVertices();
10510 const int *e = el_to_edge->GetRow(i);
10511 int vv[4], ev[12];
10512
10513 if (e2v.Size())
10514 {
10515 const int ne = el_to_edge->RowSize(i);
10516 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
10517 e = ev;
10518 }
10519
10520 switch (el_type)
10521 {
10523 {
10524 for (int ei = 0; ei < 6; ei++)
10525 {
10526 for (int k = 0; k < 2; k++)
10527 {
10528 vv[k] = v[tet_t::Edges[ei][k]];
10529 }
10530 AverageVertices(vv, 2, oedge+e[ei]);
10531 }
10532
10533 // Algorithm for choosing refinement type:
10534 // 0: smallest octahedron diagonal
10535 // 1: best aspect ratio
10536 const int rt_algo = 1;
10537 // Refinement type:
10538 // 0: (v0,v1)-(v2,v3), 1: (v0,v2)-(v1,v3), 2: (v0,v3)-(v1,v2)
10539 // 0: e0-e5, 1: e1-e4, 2: e2-e3
10540 int rt;
10543 const DenseMatrix &J = T->Jacobian();
10544 if (rt_algo == 0)
10545 {
10546 // smallest octahedron diagonal
10547 real_t len_sqr, min_len;
10548
10549 min_len = sqr(J(0,0)-J(0,1)-J(0,2)) +
10550 sqr(J(1,0)-J(1,1)-J(1,2)) +
10551 sqr(J(2,0)-J(2,1)-J(2,2));
10552 rt = 0;
10553
10554 len_sqr = sqr(J(0,1)-J(0,0)-J(0,2)) +
10555 sqr(J(1,1)-J(1,0)-J(1,2)) +
10556 sqr(J(2,1)-J(2,0)-J(2,2));
10557 if (len_sqr < min_len) { min_len = len_sqr; rt = 1; }
10558
10559 len_sqr = sqr(J(0,2)-J(0,0)-J(0,1)) +
10560 sqr(J(1,2)-J(1,0)-J(1,1)) +
10561 sqr(J(2,2)-J(2,0)-J(2,1));
10562 if (len_sqr < min_len) { rt = 2; }
10563 }
10564 else
10565 {
10566 // best aspect ratio
10567 real_t Em_data[18], Js_data[9], Jp_data[9];
10568 DenseMatrix Em(Em_data, 3, 6);
10569 DenseMatrix Js(Js_data, 3, 3), Jp(Jp_data, 3, 3);
10570 real_t ar1, ar2, kappa, kappa_min;
10571
10572 for (int s = 0; s < 3; s++)
10573 {
10574 for (int t = 0; t < 3; t++)
10575 {
10576 Em(t,s) = 0.5*J(t,s);
10577 }
10578 }
10579 for (int t = 0; t < 3; t++)
10580 {
10581 Em(t,3) = 0.5*(J(t,0)+J(t,1));
10582 Em(t,4) = 0.5*(J(t,0)+J(t,2));
10583 Em(t,5) = 0.5*(J(t,1)+J(t,2));
10584 }
10585
10586 // rt = 0; Em: {0,5,1,2}, {0,5,2,4}
10587 for (int t = 0; t < 3; t++)
10588 {
10589 Js(t,0) = Em(t,5)-Em(t,0);
10590 Js(t,1) = Em(t,1)-Em(t,0);
10591 Js(t,2) = Em(t,2)-Em(t,0);
10592 }
10594 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10595 for (int t = 0; t < 3; t++)
10596 {
10597 Js(t,0) = Em(t,5)-Em(t,0);
10598 Js(t,1) = Em(t,2)-Em(t,0);
10599 Js(t,2) = Em(t,4)-Em(t,0);
10600 }
10602 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10603 kappa_min = std::max(ar1, ar2);
10604 rt = 0;
10605
10606 // rt = 1; Em: {1,0,4,2}, {1,2,4,5}
10607 for (int t = 0; t < 3; t++)
10608 {
10609 Js(t,0) = Em(t,0)-Em(t,1);
10610 Js(t,1) = Em(t,4)-Em(t,1);
10611 Js(t,2) = Em(t,2)-Em(t,1);
10612 }
10614 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10615 for (int t = 0; t < 3; t++)
10616 {
10617 Js(t,0) = Em(t,2)-Em(t,1);
10618 Js(t,1) = Em(t,4)-Em(t,1);
10619 Js(t,2) = Em(t,5)-Em(t,1);
10620 }
10622 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10623 kappa = std::max(ar1, ar2);
10624 if (kappa < kappa_min) { kappa_min = kappa; rt = 1; }
10625
10626 // rt = 2; Em: {2,0,1,3}, {2,1,5,3}
10627 for (int t = 0; t < 3; t++)
10628 {
10629 Js(t,0) = Em(t,0)-Em(t,2);
10630 Js(t,1) = Em(t,1)-Em(t,2);
10631 Js(t,2) = Em(t,3)-Em(t,2);
10632 }
10634 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10635 for (int t = 0; t < 3; t++)
10636 {
10637 Js(t,0) = Em(t,1)-Em(t,2);
10638 Js(t,1) = Em(t,5)-Em(t,2);
10639 Js(t,2) = Em(t,3)-Em(t,2);
10640 }
10642 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10643 kappa = std::max(ar1, ar2);
10644 if (kappa < kappa_min) { rt = 2; }
10645 }
10646
10647 static const int mv_all[3][4][4] =
10648 {
10649 { {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1} }, // rt = 0
10650 { {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0} }, // rt = 1
10651 { {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3} } // rt = 2
10652 };
10653 const int (&mv)[4][4] = mv_all[rt];
10654
10655#ifndef MFEM_USE_MEMALLOC
10656 new_elements[j+0] =
10657 new Tetrahedron(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
10658 new_elements[j+1] =
10659 new Tetrahedron(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
10660 new_elements[j+2] =
10661 new Tetrahedron(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
10662 new_elements[j+3] =
10663 new Tetrahedron(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
10664
10665 for (int k = 0; k < 4; k++)
10666 {
10667 new_elements[j+4+k] =
10668 new Tetrahedron(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
10669 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
10670 }
10671#else
10672 Tetrahedron *tet;
10673 new_elements[j+0] = tet = TetMemory.Alloc();
10674 tet->Init(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
10675
10676 new_elements[j+1] = tet = TetMemory.Alloc();
10677 tet->Init(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
10678
10679 new_elements[j+2] = tet = TetMemory.Alloc();
10680 tet->Init(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
10681
10682 new_elements[j+3] = tet = TetMemory.Alloc();
10683 tet->Init(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
10684
10685 for (int k = 0; k < 4; k++)
10686 {
10687 new_elements[j+4+k] = tet = TetMemory.Alloc();
10688 tet->Init(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
10689 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
10690 }
10691#endif
10692 for (int k = 0; k < 4; k++)
10693 {
10694 CoarseFineTr.embeddings[j+k].parent = i;
10695 CoarseFineTr.embeddings[j+k].matrix = k;
10696 }
10697 for (int k = 0; k < 4; k++)
10698 {
10699 CoarseFineTr.embeddings[j+4+k].parent = i;
10700 CoarseFineTr.embeddings[j+4+k].matrix = 4*(rt+1)+k;
10701 }
10702
10703 j += 8;
10704 }
10705 break;
10706
10707 case Element::WEDGE:
10708 {
10709 const int *f = el_to_face->GetRow(i);
10710
10711 for (int fi = 2; fi < 5; fi++)
10712 {
10713 for (int k = 0; k < 4; k++)
10714 {
10715 vv[k] = v[pri_t::FaceVert[fi][k]];
10716 }
10717 AverageVertices(vv, 4, oface + f2qf[f[fi]]);
10718 }
10719
10720 for (int ei = 0; ei < 9; ei++)
10721 {
10722 for (int k = 0; k < 2; k++)
10723 {
10724 vv[k] = v[pri_t::Edges[ei][k]];
10725 }
10726 AverageVertices(vv, 2, oedge+e[ei]);
10727 }
10728
10729 const int qf2 = f2qf[f[2]];
10730 const int qf3 = f2qf[f[3]];
10731 const int qf4 = f2qf[f[4]];
10732
10733 new_elements[j++] =
10734 new Wedge(v[0], oedge+e[0], oedge+e[2],
10735 oedge+e[6], oface+qf2, oface+qf4, attr);
10736
10737 new_elements[j++] =
10738 new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
10739 oface+qf3, oface+qf4, oface+qf2, attr);
10740
10741 new_elements[j++] =
10742 new Wedge(oedge+e[0], v[1], oedge+e[1],
10743 oface+qf2, oedge+e[7], oface+qf3, attr);
10744
10745 new_elements[j++] =
10746 new Wedge(oedge+e[2], oedge+e[1], v[2],
10747 oface+qf4, oface+qf3, oedge+e[8], attr);
10748
10749 new_elements[j++] =
10750 new Wedge(oedge+e[6], oface+qf2, oface+qf4,
10751 v[3], oedge+e[3], oedge+e[5], attr);
10752
10753 new_elements[j++] =
10754 new Wedge(oface+qf3, oface+qf4, oface+qf2,
10755 oedge+e[4], oedge+e[5], oedge+e[3], attr);
10756
10757 new_elements[j++] =
10758 new Wedge(oface+qf2, oedge+e[7], oface+qf3,
10759 oedge+e[3], v[4], oedge+e[4], attr);
10760
10761 new_elements[j++] =
10762 new Wedge(oface+qf4, oface+qf3, oedge+e[8],
10763 oedge+e[5], oedge+e[4], v[5], attr);
10764 }
10765 break;
10766
10767 case Element::PYRAMID:
10768 {
10769 const int *f = el_to_face->GetRow(i);
10770 // pyr_counter++;
10771
10772 for (int fi = 0; fi < 1; fi++)
10773 {
10774 for (int k = 0; k < 4; k++)
10775 {
10776 vv[k] = v[pyr_t::FaceVert[fi][k]];
10777 }
10778 AverageVertices(vv, 4, oface + f2qf[f[fi]]);
10779 }
10780
10781 for (int ei = 0; ei < 8; ei++)
10782 {
10783 for (int k = 0; k < 2; k++)
10784 {
10785 vv[k] = v[pyr_t::Edges[ei][k]];
10786 }
10787 AverageVertices(vv, 2, oedge+e[ei]);
10788 }
10789
10790 const int qf0 = f2qf[f[0]];
10791
10792 new_elements[j++] =
10793 new Pyramid(v[0], oedge+e[0], oface+qf0,
10794 oedge+e[3], oedge+e[4], attr);
10795
10796 new_elements[j++] =
10797 new Pyramid(oedge+e[0], v[1], oedge+e[1],
10798 oface+qf0, oedge+e[5], attr);
10799
10800 new_elements[j++] =
10801 new Pyramid(oface+qf0, oedge+e[1], v[2],
10802 oedge+e[2], oedge+e[6], attr);
10803
10804 new_elements[j++] =
10805 new Pyramid(oedge+e[3], oface+qf0, oedge+e[2],
10806 v[3], oedge+e[7], attr);
10807
10808 new_elements[j++] =
10809 new Pyramid(oedge+e[4], oedge+e[5], oedge+e[6],
10810 oedge+e[7], v[4], attr);
10811
10812 new_elements[j++] =
10813 new Pyramid(oedge+e[7], oedge+e[6], oedge+e[5],
10814 oedge+e[4], oface+qf0, attr);
10815
10816#ifndef MFEM_USE_MEMALLOC
10817 new_elements[j++] =
10818 new Tetrahedron(oedge+e[0], oedge+e[4], oedge+e[5],
10819 oface+qf0, attr);
10820
10821 new_elements[j++] =
10822 new Tetrahedron(oedge+e[1], oedge+e[5], oedge+e[6],
10823 oface+qf0, attr);
10824
10825 new_elements[j++] =
10826 new Tetrahedron(oedge+e[2], oedge+e[6], oedge+e[7],
10827 oface+qf0, attr);
10828
10829 new_elements[j++] =
10830 new Tetrahedron(oedge+e[3], oedge+e[7], oedge+e[4],
10831 oface+qf0, attr);
10832#else
10833 Tetrahedron *tet;
10834 new_elements[j++] = tet = TetMemory.Alloc();
10835 tet->Init(oedge+e[0], oedge+e[4], oedge+e[5],
10836 oface+qf0, attr);
10837
10838 new_elements[j++] = tet = TetMemory.Alloc();
10839 tet->Init(oedge+e[1], oedge+e[5], oedge+e[6],
10840 oface+qf0, attr);
10841
10842 new_elements[j++] = tet = TetMemory.Alloc();
10843 tet->Init(oedge+e[2], oedge+e[6], oedge+e[7],
10844 oface+qf0, attr);
10845
10846 new_elements[j++] = tet = TetMemory.Alloc();
10847 tet->Init(oedge+e[3], oedge+e[7], oedge+e[4],
10848 oface+qf0, attr);
10849#endif
10850 // Tetrahedral elements may be new to this mesh so ensure that
10851 // the relevant flags are switched on
10853 meshgen |= 1;
10854 }
10855 break;
10856
10858 {
10859 const int *f = el_to_face->GetRow(i);
10860 const int he = hex_counter;
10861 hex_counter++;
10862
10863 const int *qf;
10864 int qf_data[6];
10865 if (f2qf.Size() == 0)
10866 {
10867 qf = f;
10868 }
10869 else
10870 {
10871 for (int k = 0; k < 6; k++) { qf_data[k] = f2qf[f[k]]; }
10872 qf = qf_data;
10873 }
10874
10875 AverageVertices(v, 8, oelem+he);
10876
10877 for (int fi = 0; fi < 6; fi++)
10878 {
10879 for (int k = 0; k < 4; k++)
10880 {
10881 vv[k] = v[hex_t::FaceVert[fi][k]];
10882 }
10883 AverageVertices(vv, 4, oface + qf[fi]);
10884 }
10885
10886 for (int ei = 0; ei < 12; ei++)
10887 {
10888 for (int k = 0; k < 2; k++)
10889 {
10890 vv[k] = v[hex_t::Edges[ei][k]];
10891 }
10892 AverageVertices(vv, 2, oedge+e[ei]);
10893 }
10894
10895 new_elements[j++] =
10896 new Hexahedron(v[0], oedge+e[0], oface+qf[0],
10897 oedge+e[3], oedge+e[8], oface+qf[1],
10898 oelem+he, oface+qf[4], attr);
10899 new_elements[j++] =
10900 new Hexahedron(oedge+e[0], v[1], oedge+e[1],
10901 oface+qf[0], oface+qf[1], oedge+e[9],
10902 oface+qf[2], oelem+he, attr);
10903 new_elements[j++] =
10904 new Hexahedron(oface+qf[0], oedge+e[1], v[2],
10905 oedge+e[2], oelem+he, oface+qf[2],
10906 oedge+e[10], oface+qf[3], attr);
10907 new_elements[j++] =
10908 new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
10909 v[3], oface+qf[4], oelem+he,
10910 oface+qf[3], oedge+e[11], attr);
10911 new_elements[j++] =
10912 new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
10913 oface+qf[4], v[4], oedge+e[4],
10914 oface+qf[5], oedge+e[7], attr);
10915 new_elements[j++] =
10916 new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
10917 oelem+he, oedge+e[4], v[5],
10918 oedge+e[5], oface+qf[5], attr);
10919 new_elements[j++] =
10920 new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
10921 oface+qf[3], oface+qf[5], oedge+e[5],
10922 v[6], oedge+e[6], attr);
10923 new_elements[j++] =
10924 new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
10925 oedge+e[11], oedge+e[7], oface+qf[5],
10926 oedge+e[6], v[7], attr);
10927 }
10928 break;
10929
10930 default:
10931 MFEM_ABORT("Unknown 3D element type \"" << el_type << "\"");
10932 break;
10933 }
10935 }
10936 mfem::Swap(elements, new_elements);
10937
10938 // refine boundary elements
10939 new_boundary.SetSize(4 * NumOfBdrElements);
10940 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
10941 {
10942 const Element::Type bdr_el_type = boundary[i]->GetType();
10943 const int attr = boundary[i]->GetAttribute();
10944 int *v = boundary[i]->GetVertices();
10945 const int *e = bel_to_edge->GetRow(i);
10946 int ev[4];
10947
10948 if (e2v.Size())
10949 {
10950 const int ne = bel_to_edge->RowSize(i);
10951 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
10952 e = ev;
10953 }
10954
10955 if (bdr_el_type == Element::TRIANGLE)
10956 {
10957 new_boundary[j++] =
10958 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
10959 new_boundary[j++] =
10960 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
10961 new_boundary[j++] =
10962 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
10963 new_boundary[j++] =
10964 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
10965 }
10966 else if (bdr_el_type == Element::QUADRILATERAL)
10967 {
10968 const int qf =
10969 (f2qf.Size() == 0) ? be_to_face[i] : f2qf[be_to_face[i]];
10970
10971 new_boundary[j++] =
10972 new Quadrilateral(v[0], oedge+e[0], oface+qf, oedge+e[3], attr);
10973 new_boundary[j++] =
10974 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oface+qf, attr);
10975 new_boundary[j++] =
10976 new Quadrilateral(oface+qf, oedge+e[1], v[2], oedge+e[2], attr);
10977 new_boundary[j++] =
10978 new Quadrilateral(oedge+e[3], oface+qf, oedge+e[2], v[3], attr);
10979 }
10980 else
10981 {
10982 MFEM_ABORT("boundary Element is not a triangle or a quad!");
10983 }
10985 }
10986 mfem::Swap(boundary, new_boundary);
10987
10988 static const real_t A = 0.0, B = 0.5, C = 1.0, D = -1.0;
10989 static real_t tet_children[3*4*16] =
10990 {
10991 A,A,A, B,A,A, A,B,A, A,A,B,
10992 B,A,A, C,A,A, B,B,A, B,A,B,
10993 A,B,A, B,B,A, A,C,A, A,B,B,
10994 A,A,B, B,A,B, A,B,B, A,A,C,
10995 // edge coordinates:
10996 // 0 -> B,A,A 1 -> A,B,A 2 -> A,A,B
10997 // 3 -> B,B,A 4 -> B,A,B 5 -> A,B,B
10998 // rt = 0: {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1}
10999 B,A,A, A,B,B, A,B,A, A,A,B,
11000 B,A,A, A,B,B, A,A,B, B,A,B,
11001 B,A,A, A,B,B, B,A,B, B,B,A,
11002 B,A,A, A,B,B, B,B,A, A,B,A,
11003 // rt = 1: {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0}
11004 A,B,A, B,A,A, B,A,B, A,A,B,
11005 A,B,A, A,A,B, B,A,B, A,B,B,
11006 A,B,A, A,B,B, B,A,B, B,B,A,
11007 A,B,A, B,B,A, B,A,B, B,A,A,
11008 // rt = 2: {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3}
11009 A,A,B, B,A,A, A,B,A, B,B,A,
11010 A,A,B, A,B,A, A,B,B, B,B,A,
11011 A,A,B, A,B,B, B,A,B, B,B,A,
11012 A,A,B, B,A,B, B,A,A, B,B,A
11013 };
11014 static real_t pyr_children[3*5*10] =
11015 {
11016 A,A,A, B,A,A, B,B,A, A,B,A, A,A,B,
11017 B,A,A, C,A,A, C,B,A, B,B,A, B,A,B,
11018 B,B,A, C,B,A, C,C,A, B,C,A, B,B,B,
11019 A,B,A, B,B,A, B,C,A, A,C,A, A,B,B,
11020 A,A,B, B,A,B, B,B,B, A,B,B, A,A,C,
11021 A,B,B, B,B,B, B,A,B, A,A,B, B,B,A,
11022 B,A,A, A,A,B, B,A,B, B,B,A, D,D,D,
11023 C,B,A, B,A,B, B,B,B, B,B,A, D,D,D,
11024 B,C,A, B,B,B, A,B,B, B,B,A, D,D,D,
11025 A,B,A, A,B,B, A,A,B, B,B,A, D,D,D
11026 };
11027 static real_t pri_children[3*6*8] =
11028 {
11029 A,A,A, B,A,A, A,B,A, A,A,B, B,A,B, A,B,B,
11030 B,B,A, A,B,A, B,A,A, B,B,B, A,B,B, B,A,B,
11031 B,A,A, C,A,A, B,B,A, B,A,B, C,A,B, B,B,B,
11032 A,B,A, B,B,A, A,C,A, A,B,B, B,B,B, A,C,B,
11033 A,A,B, B,A,B, A,B,B, A,A,C, B,A,C, A,B,C,
11034 B,B,B, A,B,B, B,A,B, B,B,C, A,B,C, B,A,C,
11035 B,A,B, C,A,B, B,B,B, B,A,C, C,A,C, B,B,C,
11036 A,B,B, B,B,B, A,C,B, A,B,C, B,B,C, A,C,C
11037 };
11038 static real_t hex_children[3*8*8] =
11039 {
11040 A,A,A, B,A,A, B,B,A, A,B,A, A,A,B, B,A,B, B,B,B, A,B,B,
11041 B,A,A, C,A,A, C,B,A, B,B,A, B,A,B, C,A,B, C,B,B, B,B,B,
11042 B,B,A, C,B,A, C,C,A, B,C,A, B,B,B, C,B,B, C,C,B, B,C,B,
11043 A,B,A, B,B,A, B,C,A, A,C,A, A,B,B, B,B,B, B,C,B, A,C,B,
11044 A,A,B, B,A,B, B,B,B, A,B,B, A,A,C, B,A,C, B,B,C, A,B,C,
11045 B,A,B, C,A,B, C,B,B, B,B,B, B,A,C, C,A,C, C,B,C, B,B,C,
11046 B,B,B, C,B,B, C,C,B, B,C,B, B,B,C, C,B,C, C,C,C, B,C,C,
11047 A,B,B, B,B,B, B,C,B, A,C,B, A,B,C, B,B,C, B,C,C, A,C,C
11048 };
11049
11051 .UseExternalData(tet_children, 3, 4, 16);
11053 .UseExternalData(pyr_children, 3, 5, 10);
11055 .UseExternalData(pri_children, 3, 6, 8);
11057 .UseExternalData(hex_children, 3, 8, 8);
11058
11059 for (int i = 0; i < elements.Size(); i++)
11060 {
11061 // tetrahedron elements are handled above:
11062 if (elements[i]->GetType() == Element::TETRAHEDRON) { continue; }
11063
11065 emb.parent = i / 8;
11066 emb.matrix = i % 8;
11067 }
11068
11069 NumOfVertices = vertices.Size();
11070 NumOfElements = 8 * NumOfElements + 2 * pyr_counter;
11072
11074 GenerateFaces();
11075
11076#ifdef MFEM_DEBUG
11078#endif
11079
11081
11083 sequence++;
11084
11085 if (update_nodes) { UpdateNodes(); }
11086}
11087
11088void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
11089{
11090 int i, j, ind, nedges;
11091 Array<int> v;
11092
11093 ResetLazyData();
11094
11095 if (ncmesh)
11096 {
11097 MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
11098 }
11099
11101
11102 if (Dim == 1) // --------------------------------------------------------
11103 {
11104 int cne = NumOfElements, cnv = NumOfVertices;
11105 NumOfVertices += marked_el.Size();
11106 NumOfElements += marked_el.Size();
11107 vertices.SetSize(NumOfVertices);
11108 elements.SetSize(NumOfElements);
11110
11111 for (j = 0; j < marked_el.Size(); j++)
11112 {
11113 i = marked_el[j];
11114 Segment *c_seg = (Segment *)elements[i];
11115 int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
11116 int new_v = cnv + j, new_e = cne + j;
11117 AverageVertices(vert, 2, new_v);
11118 elements[new_e] = new Segment(new_v, vert[1], attr);
11119 vert[1] = new_v;
11120
11123 }
11124
11125 static real_t seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
11127 UseExternalData(seg_children, 1, 2, 3);
11128
11129 GenerateFaces();
11130
11131 } // end of 'if (Dim == 1)'
11132 else if (Dim == 2) // ---------------------------------------------------
11133 {
11134 // 1. Get table of vertex to vertex connections.
11135 DSTable v_to_v(NumOfVertices);
11136 GetVertexToVertexTable(v_to_v);
11137
11138 // 2. Get edge to element connections in arrays edge1 and edge2
11139 nedges = v_to_v.NumberOfEntries();
11140 int *edge1 = new int[nedges];
11141 int *edge2 = new int[nedges];
11142 int *middle = new int[nedges];
11143
11144 for (i = 0; i < nedges; i++)
11145 {
11146 edge1[i] = edge2[i] = middle[i] = -1;
11147 }
11148
11149 for (i = 0; i < NumOfElements; i++)
11150 {
11151 elements[i]->GetVertices(v);
11152 for (j = 1; j < v.Size(); j++)
11153 {
11154 ind = v_to_v(v[j-1], v[j]);
11155 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
11156 }
11157 ind = v_to_v(v[0], v[v.Size()-1]);
11158 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
11159 }
11160
11161 // 3. Do the red refinement.
11162 for (i = 0; i < marked_el.Size(); i++)
11163 {
11164 RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
11165 }
11166
11167 // 4. Do the green refinement (to get conforming mesh).
11168 int need_refinement;
11169 do
11170 {
11171 need_refinement = 0;
11172 for (i = 0; i < nedges; i++)
11173 {
11174 if (middle[i] != -1 && edge1[i] != -1)
11175 {
11176 need_refinement = 1;
11177 GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
11178 }
11179 }
11180 }
11181 while (need_refinement == 1);
11182
11183 // 5. Update the boundary elements.
11184 int v1[2], v2[2], bisect, temp;
11185 temp = NumOfBdrElements;
11186 for (i = 0; i < temp; i++)
11187 {
11188 boundary[i]->GetVertices(v);
11189 bisect = v_to_v(v[0], v[1]);
11190 if (middle[bisect] != -1) // the element was refined (needs updating)
11191 {
11192 if (boundary[i]->GetType() == Element::SEGMENT)
11193 {
11194 v1[0] = v[0]; v1[1] = middle[bisect];
11195 v2[0] = middle[bisect]; v2[1] = v[1];
11196
11197 boundary[i]->SetVertices(v1);
11199 }
11200 else
11201 mfem_error("Only bisection of segment is implemented"
11202 " for bdr elem.");
11203 }
11204 }
11205 NumOfBdrElements = boundary.Size();
11206
11207 // 6. Free the allocated memory.
11208 delete [] edge1;
11209 delete [] edge2;
11210 delete [] middle;
11211
11212 if (el_to_edge != NULL)
11213 {
11215 GenerateFaces();
11216 }
11217
11218 }
11219 else if (Dim == 3) // ---------------------------------------------------
11220 {
11221 // 1. Hash table of vertex to vertex connections corresponding to refined
11222 // edges.
11223 HashTable<Hashed2> v_to_v;
11224
11225 MFEM_VERIFY(GetNE() == 0 ||
11226 ((Tetrahedron*)elements[0])->GetRefinementFlag() != 0,
11227 "tetrahedral mesh is not marked for refinement:"
11228 " call Finalize(true)");
11229
11230 // 2. Do the red refinement.
11231 int ii;
11232 switch (type)
11233 {
11234 case 1:
11235 for (i = 0; i < marked_el.Size(); i++)
11236 {
11237 Bisection(marked_el[i], v_to_v);
11238 }
11239 break;
11240 case 2:
11241 for (i = 0; i < marked_el.Size(); i++)
11242 {
11243 Bisection(marked_el[i], v_to_v);
11244
11245 Bisection(NumOfElements - 1, v_to_v);
11246 Bisection(marked_el[i], v_to_v);
11247 }
11248 break;
11249 case 3:
11250 for (i = 0; i < marked_el.Size(); i++)
11251 {
11252 Bisection(marked_el[i], v_to_v);
11253
11254 ii = NumOfElements - 1;
11255 Bisection(ii, v_to_v);
11256 Bisection(NumOfElements - 1, v_to_v);
11257 Bisection(ii, v_to_v);
11258
11259 Bisection(marked_el[i], v_to_v);
11260 Bisection(NumOfElements-1, v_to_v);
11261 Bisection(marked_el[i], v_to_v);
11262 }
11263 break;
11264 }
11265
11266 // 3. Do the green refinement (to get conforming mesh).
11267 int need_refinement;
11268 // int need_refinement, onoe, max_gen = 0;
11269 do
11270 {
11271 // int redges[2], type, flag;
11272 need_refinement = 0;
11273 // onoe = NumOfElements;
11274 // for (i = 0; i < onoe; i++)
11275 for (i = 0; i < NumOfElements; i++)
11276 {
11277 // ((Tetrahedron *)elements[i])->
11278 // ParseRefinementFlag(redges, type, flag);
11279 // if (flag > max_gen) max_gen = flag;
11280 if (elements[i]->NeedRefinement(v_to_v))
11281 {
11282 need_refinement = 1;
11283 Bisection(i, v_to_v);
11284 }
11285 }
11286 }
11287 while (need_refinement == 1);
11288
11289 // mfem::out << "Maximum generation: " << max_gen << endl;
11290
11291 // 4. Update the boundary elements.
11292 do
11293 {
11294 need_refinement = 0;
11295 for (i = 0; i < NumOfBdrElements; i++)
11296 if (boundary[i]->NeedRefinement(v_to_v))
11297 {
11298 need_refinement = 1;
11299 BdrBisection(i, v_to_v);
11300 }
11301 }
11302 while (need_refinement == 1);
11303
11304 NumOfVertices = vertices.Size();
11305 NumOfBdrElements = boundary.Size();
11306
11307 // 5. Update element-to-edge and element-to-face relations.
11308 if (el_to_edge != NULL)
11309 {
11311 }
11312 if (el_to_face != NULL)
11313 {
11315 GenerateFaces();
11316 }
11317
11318 } // end 'if (Dim == 3)'
11319
11321 sequence++;
11322
11323 UpdateNodes();
11324
11325#ifdef MFEM_DEBUG
11327#endif
11328}
11329
11331 int nc_limit)
11332{
11333 MFEM_VERIFY(!NURBSext, "Nonconforming refinement of NURBS meshes is "
11334 "not supported. Project the NURBS to Nodes first.");
11335
11336 ResetLazyData();
11337
11338 if (!ncmesh)
11339 {
11340 // start tracking refinement hierarchy
11341 ncmesh = new NCMesh(this);
11342 }
11343
11344 if (!refinements.Size())
11345 {
11347 return;
11348 }
11349
11350 // do the refinements
11352 ncmesh->Refine(refinements);
11353
11354 if (nc_limit > 0)
11355 {
11356 ncmesh->LimitNCLevel(nc_limit);
11357 }
11358
11359 // create a second mesh containing the finest elements from 'ncmesh'
11360 Mesh* mesh2 = new Mesh(*ncmesh);
11361 ncmesh->OnMeshUpdated(mesh2);
11362
11363 // now swap the meshes, the second mesh will become the old coarse mesh
11364 // and this mesh will be the new fine mesh
11365 Swap(*mesh2, false);
11366 delete mesh2;
11367
11369
11371 sequence++;
11372
11373 UpdateNodes();
11374}
11375
11377 const int *fine, int nfine, int op)
11378{
11379 real_t error = (op == 3) ? std::pow(elem_error[fine[0]],
11380 2.0) : elem_error[fine[0]];
11381
11382 for (int i = 1; i < nfine; i++)
11383 {
11384 MFEM_VERIFY(fine[i] < elem_error.Size(), "");
11385
11386 real_t err_fine = elem_error[fine[i]];
11387 switch (op)
11388 {
11389 case 0: error = std::min(error, err_fine); break;
11390 case 1: error += err_fine; break;
11391 case 2: error = std::max(error, err_fine); break;
11392 case 3: error += std::pow(err_fine, 2.0); break;
11393 default: MFEM_ABORT("Invalid operation.");
11394 }
11395 }
11396 return (op == 3) ? std::sqrt(error) : error;
11397}
11398
11400 real_t threshold, int nc_limit, int op)
11401{
11402 MFEM_VERIFY(ncmesh, "Only supported for non-conforming meshes.");
11403 MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
11404 "Project the NURBS to Nodes first.");
11405
11406 ResetLazyData();
11407
11408 const Table &dt = ncmesh->GetDerefinementTable();
11409
11410 Array<int> level_ok;
11411 if (nc_limit > 0)
11412 {
11413 ncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
11414 }
11415
11416 Array<int> derefs;
11417 for (int i = 0; i < dt.Size(); i++)
11418 {
11419 if (nc_limit > 0 && !level_ok[i]) { continue; }
11420
11421 real_t error =
11422 AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
11423
11424 if (error < threshold) { derefs.Append(i); }
11425 }
11426
11427 if (!derefs.Size()) { return false; }
11428
11429 ncmesh->Derefine(derefs);
11430
11431 Mesh* mesh2 = new Mesh(*ncmesh);
11432 ncmesh->OnMeshUpdated(mesh2);
11433
11434 Swap(*mesh2, false);
11435 delete mesh2;
11436
11438
11440 sequence++;
11441
11442 UpdateNodes();
11443
11444 return true;
11445}
11446
11447bool Mesh::DerefineByError(Array<real_t> &elem_error, real_t threshold,
11448 int nc_limit, int op)
11449{
11450 // NOTE: the error array is not const because it will be expanded in parallel
11451 // by ghost element errors
11452 if (Nonconforming())
11453 {
11454 return NonconformingDerefinement(elem_error, threshold, nc_limit, op);
11455 }
11456 else
11457 {
11458 MFEM_ABORT("Derefinement is currently supported for non-conforming "
11459 "meshes only.");
11460 return false;
11461 }
11462}
11463
11464bool Mesh::DerefineByError(const Vector &elem_error, real_t threshold,
11465 int nc_limit, int op)
11466{
11467 Array<real_t> tmp(elem_error.Size());
11468 for (int i = 0; i < tmp.Size(); i++)
11469 {
11470 tmp[i] = elem_error(i);
11471 }
11472 return DerefineByError(tmp, threshold, nc_limit, op);
11473}
11474
11475
11476void Mesh::InitFromNCMesh(const NCMesh &ncmesh_)
11477{
11478 Dim = ncmesh_.Dimension();
11479 spaceDim = ncmesh_.SpaceDimension();
11480
11481 DeleteTables();
11482
11483 ncmesh_.GetMeshComponents(*this);
11484
11485 NumOfVertices = vertices.Size();
11486 NumOfElements = elements.Size();
11487 NumOfBdrElements = boundary.Size();
11488
11489 SetMeshGen(); // set the mesh type: 'meshgen', ...
11490
11491 NumOfEdges = NumOfFaces = 0;
11493
11494 if (Dim > 1)
11495 {
11496 el_to_edge = new Table;
11498 }
11499 if (Dim > 2)
11500 {
11502 }
11503 GenerateFaces();
11504#ifdef MFEM_DEBUG
11506#endif
11507
11508 // NOTE: ncmesh->OnMeshUpdated() and GenerateNCFaceInfo() should be called
11509 // outside after this method.
11510}
11511
11512Mesh::Mesh(const NCMesh &ncmesh_)
11513 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
11514{
11515 Init();
11516 InitTables();
11517 InitFromNCMesh(ncmesh_);
11518 SetAttributes();
11519}
11520
11521void Mesh::Swap(Mesh& other, bool non_geometry)
11522{
11523 mfem::Swap(Dim, other.Dim);
11525
11531
11532 mfem::Swap(meshgen, other.meshgen);
11534
11538 mfem::Swap(faces, other.faces);
11543
11553
11558
11561
11562#ifdef MFEM_USE_MEMALLOC
11563 TetMemory.Swap(other.TetMemory);
11564#endif
11565
11566 if (non_geometry)
11567 {
11569 mfem::Swap(ncmesh, other.ncmesh);
11570
11571 mfem::Swap(Nodes, other.Nodes);
11572 if (Nodes) { Nodes->FESpace()->UpdateMeshPointer(this); }
11573 if (other.Nodes) { other.Nodes->FESpace()->UpdateMeshPointer(&other); }
11575
11577
11581 }
11582
11583 // copy attribute caches
11586
11587 mfem::Swap(face_indices[0], other.face_indices[0]);
11588 mfem::Swap(face_indices[1], other.face_indices[1]);
11589 inv_face_indices[0].swap(other.inv_face_indices[0]);
11590 inv_face_indices[1].swap(other.inv_face_indices[1]);
11591}
11592
11593void Mesh::GetElementData(const Array<Element*> &elem_array, int geom,
11594 Array<int> &elem_vtx, Array<int> &attr) const
11595{
11596 // protected method
11597 const int nv = Geometry::NumVerts[geom];
11598 int num_elems = 0;
11599 for (int i = 0; i < elem_array.Size(); i++)
11600 {
11601 if (elem_array[i]->GetGeometryType() == geom)
11602 {
11603 num_elems++;
11604 }
11605 }
11606 elem_vtx.SetSize(nv*num_elems);
11607 attr.SetSize(num_elems);
11608 elem_vtx.SetSize(0);
11609 attr.SetSize(0);
11610 for (int i = 0; i < elem_array.Size(); i++)
11611 {
11612 Element *el = elem_array[i];
11613 if (el->GetGeometryType() != geom) { continue; }
11614
11615 Array<int> loc_vtx(el->GetVertices(), nv);
11616 elem_vtx.Append(loc_vtx);
11617 attr.Append(el->GetAttribute());
11618 }
11619}
11620
11621static Array<int>& AllElements(Array<int> &list, int nelem)
11622{
11623 list.SetSize(nelem);
11624 for (int i = 0; i < nelem; i++) { list[i] = i; }
11625 return list;
11626}
11627
11628void Mesh::UniformRefinement(int ref_algo)
11629{
11630 Array<int> list;
11631
11632 if (NURBSext)
11633 {
11635 }
11636 else if (ncmesh)
11637 {
11638 GeneralRefinement(AllElements(list, GetNE()));
11639 }
11640 else if (ref_algo == 1 && meshgen == 1 && Dim == 3)
11641 {
11642 // algorithm "B" for an all-tet mesh
11643 LocalRefinement(AllElements(list, GetNE()));
11644 }
11645 else
11646 {
11647 switch (Dim)
11648 {
11649 case 1: LocalRefinement(AllElements(list, GetNE())); break;
11650 case 2: UniformRefinement2D(); break;
11651 case 3: UniformRefinement3D(); break;
11652 default: MFEM_ABORT("internal error");
11653 }
11654 }
11655}
11656
11658{
11659 if (NURBSext && cf > 1)
11660 {
11662 Array<int> initialCoarsening; // Initial coarsening factors
11663 NURBSext->GetCoarseningFactors(initialCoarsening);
11664
11665 // If refinement formulas are nested, then initial coarsening is skipped.
11666 bool noInitialCoarsening = true;
11667 for (auto f : initialCoarsening)
11668 {
11669 noInitialCoarsening = (noInitialCoarsening && f == 1);
11670 }
11671
11672 if (noInitialCoarsening)
11673 {
11674 NURBSext->Coarsen(cf, tol);
11675 }
11676 else
11677 {
11678 // Perform an initial full coarsening, and then refine. This is
11679 // necessary only for non-nested refinement formulas.
11680 NURBSext->Coarsen(initialCoarsening, tol);
11681
11682 // FiniteElementSpace::Update is not supported
11684 sequence++;
11685
11686 UpdateNURBS();
11687
11688 // Prepare for refinement by factors.
11690
11691 Array<int> rf(initialCoarsening);
11692 bool divisible = true;
11693 for (int i=0; i<rf.Size(); ++i)
11694 {
11695 rf[i] /= cf;
11696 divisible = divisible && cf * rf[i] == initialCoarsening[i];
11697 }
11698
11699 MFEM_VERIFY(divisible, "Invalid coarsening");
11700
11701 // Refine from the fully coarsened mesh to the mesh coarsened by the
11702 // factor cf.
11704 }
11705
11706 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
11707 sequence++;
11708
11709 UpdateNURBS();
11710 }
11711}
11712
11714 int nonconforming, int nc_limit)
11715{
11716 if (ncmesh)
11717 {
11718 nonconforming = 1;
11719 }
11720 else if (Dim == 1 || (Dim == 3 && (meshgen & 1)))
11721 {
11722 nonconforming = 0;
11723 }
11724 else if (nonconforming < 0)
11725 {
11726 // determine if nonconforming refinement is suitable
11727 if ((meshgen & 2) || (meshgen & 4) || (meshgen & 8))
11728 {
11729 nonconforming = 1; // tensor product elements and wedges
11730 }
11731 else
11732 {
11733 nonconforming = 0; // simplices
11734 }
11735 }
11736
11737 if (nonconforming)
11738 {
11739 // non-conforming refinement (hanging nodes)
11740 NonconformingRefinement(refinements, nc_limit);
11741 }
11742 else
11743 {
11744 Array<int> el_to_refine(refinements.Size());
11745 for (int i = 0; i < refinements.Size(); i++)
11746 {
11747 el_to_refine[i] = refinements[i].index;
11748 }
11749
11750 // infer 'type' of local refinement from first element's 'ref_type'
11751 int type, rt = (refinements.Size() ? refinements[0].GetType() : 7);
11752 if (rt == 1 || rt == 2 || rt == 4)
11753 {
11754 type = 1; // bisection
11755 }
11756 else if (rt == 3 || rt == 5 || rt == 6)
11757 {
11758 type = 2; // quadrisection
11759 }
11760 else
11761 {
11762 type = 3; // octasection
11763 }
11764
11765 // red-green refinement and bisection, no hanging nodes
11766 LocalRefinement(el_to_refine, type);
11767 }
11768}
11769
11770void Mesh::GeneralRefinement(const Array<int> &el_to_refine, int nonconforming,
11771 int nc_limit)
11772{
11773 Array<Refinement> refinements(el_to_refine.Size());
11774 for (int i = 0; i < el_to_refine.Size(); i++)
11775 {
11776 refinements[i] = Refinement(el_to_refine[i]);
11777 }
11778 GeneralRefinement(refinements, nonconforming, nc_limit);
11779}
11780
11781void Mesh::EnsureNCMesh(bool simplices_nonconforming)
11782{
11783 MFEM_VERIFY(!NURBSext, "Cannot convert a NURBS mesh to an NC mesh. "
11784 "Please project the NURBS to Nodes first, with SetCurvature().");
11785
11786#ifdef MFEM_USE_MPI
11787 MFEM_VERIFY(ncmesh != NULL || dynamic_cast<const ParMesh*>(this) == NULL,
11788 "Sorry, converting a conforming ParMesh to an NC mesh is "
11789 "not possible.");
11790#endif
11791
11792 if (!ncmesh)
11793 {
11794 if ((meshgen & 0x2) /* quads/hexes */ ||
11795 (meshgen & 0x4) /* wedges */ ||
11796 (simplices_nonconforming && (meshgen & 0x1)) /* simplices */)
11797 {
11798 ncmesh = new NCMesh(this);
11799 ncmesh->OnMeshUpdated(this);
11801 }
11802 }
11803}
11804
11805void Mesh::RandomRefinement(real_t prob, bool aniso, int nonconforming,
11806 int nc_limit)
11807{
11808 Array<Refinement> refs;
11809 for (int i = 0; i < GetNE(); i++)
11810 {
11811 if ((real_t) rand() / real_t(RAND_MAX) < prob)
11812 {
11813 int type = 7;
11814 if (aniso)
11815 {
11816 type = (Dim == 3) ? (rand() % 7 + 1) : (rand() % 3 + 1);
11817 }
11818 refs.Append(Refinement(i, type));
11819 }
11820 }
11821 GeneralRefinement(refs, nonconforming, nc_limit);
11822}
11823
11824void Mesh::RefineAtVertex(const Vertex& vert, real_t eps, int nonconforming)
11825{
11826 Array<int> v;
11827 Array<Refinement> refs;
11828 for (int i = 0; i < GetNE(); i++)
11829 {
11830 GetElementVertices(i, v);
11831 bool refine = false;
11832 for (int j = 0; j < v.Size(); j++)
11833 {
11834 real_t dist = 0.0;
11835 for (int l = 0; l < spaceDim; l++)
11836 {
11837 real_t d = vert(l) - vertices[v[j]](l);
11838 dist += d*d;
11839 }
11840 if (dist <= eps*eps) { refine = true; break; }
11841 }
11842 if (refine)
11843 {
11844 refs.Append(Refinement(i));
11845 }
11846 }
11847 GeneralRefinement(refs, nonconforming);
11848}
11849
11850bool Mesh::RefineByError(const Array<real_t> &elem_error, real_t threshold,
11851 int nonconforming, int nc_limit)
11852{
11853 MFEM_VERIFY(elem_error.Size() == GetNE(), "");
11854 Array<Refinement> refs;
11855 for (int i = 0; i < GetNE(); i++)
11856 {
11857 if (elem_error[i] > threshold)
11858 {
11859 refs.Append(Refinement(i));
11860 }
11861 }
11862 if (ReduceInt(refs.Size()))
11863 {
11864 GeneralRefinement(refs, nonconforming, nc_limit);
11865 return true;
11866 }
11867 return false;
11868}
11869
11870bool Mesh::RefineByError(const Vector &elem_error, real_t threshold,
11871 int nonconforming, int nc_limit)
11872{
11873 Array<real_t> tmp(const_cast<real_t*>(elem_error.GetData()),
11874 elem_error.Size());
11875 return RefineByError(tmp, threshold, nonconforming, nc_limit);
11876}
11877
11878
11879void Mesh::Bisection(int i, const DSTable &v_to_v,
11880 int *edge1, int *edge2, int *middle)
11881{
11882 int *vert;
11883 int v[2][4], v_new, bisect, t;
11884 Element *el = elements[i];
11885 Vertex V;
11886
11887 t = el->GetType();
11888 if (t == Element::TRIANGLE)
11889 {
11890 Triangle *tri = (Triangle *) el;
11891
11892 vert = tri->GetVertices();
11893
11894 // 1. Get the index for the new vertex in v_new.
11895 bisect = v_to_v(vert[0], vert[1]);
11896 MFEM_ASSERT(bisect >= 0, "");
11897
11898 if (middle[bisect] == -1)
11899 {
11900 v_new = NumOfVertices++;
11901 for (int d = 0; d < spaceDim; d++)
11902 {
11903 V(d) = 0.5 * (vertices[vert[0]](d) + vertices[vert[1]](d));
11904 }
11905 vertices.Append(V);
11906
11907 // Put the element that may need refinement (because of this
11908 // bisection) in edge1, or -1 if no more refinement is needed.
11909 if (edge1[bisect] == i)
11910 {
11911 edge1[bisect] = edge2[bisect];
11912 }
11913
11914 middle[bisect] = v_new;
11915 }
11916 else
11917 {
11918 v_new = middle[bisect];
11919
11920 // This edge will require no more refinement.
11921 edge1[bisect] = -1;
11922 }
11923
11924 // 2. Set the node indices for the new elements in v[0] and v[1] so that
11925 // the edge marked for refinement is between the first two nodes.
11926 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
11927 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
11928
11929 tri->SetVertices(v[0]); // changes vert[0..2] !!!
11930
11931 Triangle* tri_new = new Triangle(v[1], tri->GetAttribute());
11932 elements.Append(tri_new);
11933
11934 int tr = tri->GetTransform();
11935 tri_new->ResetTransform(tr);
11936
11937 // record the sequence of refinements
11938 tri->PushTransform(4);
11939 tri_new->PushTransform(5);
11940
11941 int coarse = FindCoarseElement(i);
11942 CoarseFineTr.embeddings[i].parent = coarse;
11944
11945 // 3. edge1 and edge2 may have to be changed for the second triangle.
11946 if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
11947 {
11948 bisect = v_to_v(v[1][0], v[1][1]);
11949 MFEM_ASSERT(bisect >= 0, "");
11950
11951 if (edge1[bisect] == i)
11952 {
11953 edge1[bisect] = NumOfElements;
11954 }
11955 else if (edge2[bisect] == i)
11956 {
11957 edge2[bisect] = NumOfElements;
11958 }
11959 }
11960 NumOfElements++;
11961 }
11962 else
11963 {
11964 MFEM_ABORT("Bisection for now works only for triangles.");
11965 }
11966}
11967
11969{
11970 int *vert;
11971 int v[2][4], v_new, bisect, t;
11972 Element *el = elements[i];
11973 Vertex V;
11974
11975 t = el->GetType();
11976 if (t == Element::TETRAHEDRON)
11977 {
11978 Tetrahedron *tet = (Tetrahedron *) el;
11979
11980 MFEM_VERIFY(tet->GetRefinementFlag() != 0,
11981 "TETRAHEDRON element is not marked for refinement.");
11982
11983 vert = tet->GetVertices();
11984
11985 // 1. Get the index for the new vertex in v_new.
11986 bisect = v_to_v.FindId(vert[0], vert[1]);
11987 if (bisect == -1)
11988 {
11989 v_new = NumOfVertices + v_to_v.GetId(vert[0],vert[1]);
11990 for (int j = 0; j < 3; j++)
11991 {
11992 V(j) = 0.5 * (vertices[vert[0]](j) + vertices[vert[1]](j));
11993 }
11994 vertices.Append(V);
11995 }
11996 else
11997 {
11998 v_new = NumOfVertices + bisect;
11999 }
12000
12001 // 2. Set the node indices for the new elements in v[2][4] so that
12002 // the edge marked for refinement is between the first two nodes.
12003 int type, old_redges[2], flag;
12004 tet->ParseRefinementFlag(old_redges, type, flag);
12005
12006 int new_type, new_redges[2][2];
12007 v[0][3] = v_new;
12008 v[1][3] = v_new;
12009 new_redges[0][0] = 2;
12010 new_redges[0][1] = 1;
12011 new_redges[1][0] = 2;
12012 new_redges[1][1] = 1;
12013 int tr1 = -1, tr2 = -1;
12014 switch (old_redges[0])
12015 {
12016 case 2:
12017 v[0][0] = vert[0]; v[0][1] = vert[2]; v[0][2] = vert[3];
12018 if (type == Tetrahedron::TYPE_PF) { new_redges[0][1] = 4; }
12019 tr1 = 0;
12020 break;
12021 case 3:
12022 v[0][0] = vert[3]; v[0][1] = vert[0]; v[0][2] = vert[2];
12023 tr1 = 2;
12024 break;
12025 case 5:
12026 v[0][0] = vert[2]; v[0][1] = vert[3]; v[0][2] = vert[0];
12027 tr1 = 4;
12028 }
12029 switch (old_redges[1])
12030 {
12031 case 1:
12032 v[1][0] = vert[2]; v[1][1] = vert[1]; v[1][2] = vert[3];
12033 if (type == Tetrahedron::TYPE_PF) { new_redges[1][0] = 3; }
12034 tr2 = 1;
12035 break;
12036 case 4:
12037 v[1][0] = vert[1]; v[1][1] = vert[3]; v[1][2] = vert[2];
12038 tr2 = 3;
12039 break;
12040 case 5:
12041 v[1][0] = vert[3]; v[1][1] = vert[2]; v[1][2] = vert[1];
12042 tr2 = 5;
12043 }
12044
12045 int attr = tet->GetAttribute();
12046 tet->SetVertices(v[0]);
12047
12048#ifdef MFEM_USE_MEMALLOC
12049 Tetrahedron *tet2 = TetMemory.Alloc();
12050 tet2->SetVertices(v[1]);
12051 tet2->SetAttribute(attr);
12052#else
12053 Tetrahedron *tet2 = new Tetrahedron(v[1], attr);
12054#endif
12055 tet2->ResetTransform(tet->GetTransform());
12056 elements.Append(tet2);
12057
12058 // record the sequence of refinements
12059 tet->PushTransform(tr1);
12060 tet2->PushTransform(tr2);
12061
12062 int coarse = FindCoarseElement(i);
12063 CoarseFineTr.embeddings[i].parent = coarse;
12065
12066 // 3. Set the bisection flag
12067 switch (type)
12068 {
12070 new_type = Tetrahedron::TYPE_PF; break;
12072 new_type = Tetrahedron::TYPE_A; break;
12073 default:
12074 new_type = Tetrahedron::TYPE_PU;
12075 }
12076
12077 tet->CreateRefinementFlag(new_redges[0], new_type, flag+1);
12078 tet2->CreateRefinementFlag(new_redges[1], new_type, flag+1);
12079
12080 NumOfElements++;
12081 }
12082 else
12083 {
12084 MFEM_ABORT("Bisection with HashTable for now works only for tetrahedra.");
12085 }
12086}
12087
12088void Mesh::BdrBisection(int i, const HashTable<Hashed2> &v_to_v)
12089{
12090 int *vert;
12091 int v[2][3], v_new, bisect, t;
12092 Element *bdr_el = boundary[i];
12093
12094 t = bdr_el->GetType();
12095 if (t == Element::TRIANGLE)
12096 {
12097 Triangle *tri = (Triangle *) bdr_el;
12098
12099 vert = tri->GetVertices();
12100
12101 // 1. Get the index for the new vertex in v_new.
12102 bisect = v_to_v.FindId(vert[0], vert[1]);
12103 MFEM_ASSERT(bisect >= 0, "");
12104 v_new = NumOfVertices + bisect;
12105 MFEM_ASSERT(v_new != -1, "");
12106
12107 // 2. Set the node indices for the new elements in v[0] and v[1] so that
12108 // the edge marked for refinement is between the first two nodes.
12109 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
12110 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
12111
12112 tri->SetVertices(v[0]);
12113
12114 boundary.Append(new Triangle(v[1], tri->GetAttribute()));
12115
12117 }
12118 else
12119 {
12120 MFEM_ABORT("Bisection of boundary elements with HashTable works only for"
12121 " triangles!");
12122 }
12123}
12124
12125void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
12126 int *edge1, int *edge2, int *middle)
12127{
12128 Array<int> v;
12129 int j, v1[3], v2[3], v3[3], v4[3], v_new[3], bisect[3];
12130 Vertex V;
12131
12132 if (elements[i]->GetType() == Element::TRIANGLE)
12133 {
12134 Triangle *tri0 = (Triangle*) elements[i];
12135 tri0->GetVertices(v);
12136
12137 // 1. Get the indices for the new vertices in array v_new
12138 bisect[0] = v_to_v(v[0],v[1]);
12139 bisect[1] = v_to_v(v[1],v[2]);
12140 bisect[2] = v_to_v(v[0],v[2]);
12141 MFEM_ASSERT(bisect[0] >= 0 && bisect[1] >= 0 && bisect[2] >= 0, "");
12142
12143 for (j = 0; j < 3; j++) // for the 3 edges fix v_new
12144 {
12145 if (middle[bisect[j]] == -1)
12146 {
12147 v_new[j] = NumOfVertices++;
12148 for (int d = 0; d < spaceDim; d++)
12149 {
12150 V(d) = (vertices[v[j]](d) + vertices[v[(j+1)%3]](d))/2.;
12151 }
12152 vertices.Append(V);
12153
12154 // Put the element that may need refinement (because of this
12155 // bisection) in edge1, or -1 if no more refinement is needed.
12156 if (edge1[bisect[j]] == i)
12157 {
12158 edge1[bisect[j]] = edge2[bisect[j]];
12159 }
12160
12161 middle[bisect[j]] = v_new[j];
12162 }
12163 else
12164 {
12165 v_new[j] = middle[bisect[j]];
12166
12167 // This edge will require no more refinement.
12168 edge1[bisect[j]] = -1;
12169 }
12170 }
12171
12172 // 2. Set the node indices for the new elements in v1, v2, v3 & v4 so that
12173 // the edges marked for refinement be between the first two nodes.
12174 v1[0] = v[0]; v1[1] = v_new[0]; v1[2] = v_new[2];
12175 v2[0] = v_new[0]; v2[1] = v[1]; v2[2] = v_new[1];
12176 v3[0] = v_new[2]; v3[1] = v_new[1]; v3[2] = v[2];
12177 v4[0] = v_new[1]; v4[1] = v_new[2]; v4[2] = v_new[0];
12178
12179 Triangle* tri1 = new Triangle(v1, tri0->GetAttribute());
12180 Triangle* tri2 = new Triangle(v2, tri0->GetAttribute());
12181 Triangle* tri3 = new Triangle(v3, tri0->GetAttribute());
12182
12183 elements.Append(tri1);
12184 elements.Append(tri2);
12185 elements.Append(tri3);
12186
12187 tri0->SetVertices(v4);
12188
12189 // record the sequence of refinements
12190 unsigned code = tri0->GetTransform();
12191 tri1->ResetTransform(code);
12192 tri2->ResetTransform(code);
12193 tri3->ResetTransform(code);
12194
12195 tri0->PushTransform(3);
12196 tri1->PushTransform(0);
12197 tri2->PushTransform(1);
12198 tri3->PushTransform(2);
12199
12200 // set parent indices
12201 int coarse = FindCoarseElement(i);
12206
12207 NumOfElements += 3;
12208 }
12209 else
12210 {
12211 MFEM_ABORT("Uniform refinement for now works only for triangles.");
12212 }
12213}
12214
12216{
12217 // initialize CoarseFineTr
12220 for (int i = 0; i < NumOfElements; i++)
12221 {
12222 elements[i]->ResetTransform(0);
12224 }
12225}
12226
12228{
12229 int coarse;
12230 while ((coarse = CoarseFineTr.embeddings[i].parent) != i)
12231 {
12232 i = coarse;
12233 }
12234 return coarse;
12235}
12236
12238{
12239 MFEM_VERIFY(GetLastOperation() == Mesh::REFINE, "");
12240
12241 if (ncmesh)
12242 {
12244 }
12245
12246 Mesh::GeometryList elem_geoms(*this);
12247 for (int i = 0; i < elem_geoms.Size(); i++)
12248 {
12249 const Geometry::Type geom = elem_geoms[i];
12250 if (CoarseFineTr.point_matrices[geom].SizeK()) { continue; }
12251
12252 if (geom == Geometry::TRIANGLE ||
12253 geom == Geometry::TETRAHEDRON)
12254 {
12255 std::map<unsigned, int> mat_no;
12256 mat_no[0] = 1; // identity
12257
12258 // assign matrix indices to element transformations
12259 for (int j = 0; j < elements.Size(); j++)
12260 {
12261 int index = 0;
12262 unsigned code = elements[j]->GetTransform();
12263 if (code)
12264 {
12265 int &matrix = mat_no[code];
12266 if (!matrix) { matrix = static_cast<int>(mat_no.size()); }
12267 index = matrix-1;
12268 }
12269 CoarseFineTr.embeddings[j].matrix = index;
12270 }
12271
12273 pmats.SetSize(Dim, Dim+1, static_cast<int>((mat_no.size())));
12274
12275 // calculate the point matrices used
12276 std::map<unsigned, int>::iterator it;
12277 for (it = mat_no.begin(); it != mat_no.end(); ++it)
12278 {
12279 if (geom == Geometry::TRIANGLE)
12280 {
12281 Triangle::GetPointMatrix(it->first, pmats(it->second-1));
12282 }
12283 else
12284 {
12285 Tetrahedron::GetPointMatrix(it->first, pmats(it->second-1));
12286 }
12287 }
12288 }
12289 else
12290 {
12291 MFEM_ABORT("Don't know how to construct CoarseFineTransformations for"
12292 " geom = " << geom);
12293 }
12294 }
12295
12296 // NOTE: quads and hexes already have trivial transformations ready
12297 return CoarseFineTr;
12298}
12299
12300void Mesh::PrintXG(std::ostream &os) const
12301{
12302 MFEM_ASSERT(Dim==spaceDim, "2D Manifold meshes not supported");
12303 int i, j;
12304 Array<int> v;
12305
12306 if (Dim == 2)
12307 {
12308 // Print the type of the mesh.
12309 if (Nodes == NULL)
12310 {
12311 os << "areamesh2\n\n";
12312 }
12313 else
12314 {
12315 os << "curved_areamesh2\n\n";
12316 }
12317
12318 // Print the boundary elements.
12319 os << NumOfBdrElements << '\n';
12320 for (i = 0; i < NumOfBdrElements; i++)
12321 {
12322 boundary[i]->GetVertices(v);
12323
12324 os << boundary[i]->GetAttribute();
12325 for (j = 0; j < v.Size(); j++)
12326 {
12327 os << ' ' << v[j] + 1;
12328 }
12329 os << '\n';
12330 }
12331
12332 // Print the elements.
12333 os << NumOfElements << '\n';
12334 for (i = 0; i < NumOfElements; i++)
12335 {
12336 elements[i]->GetVertices(v);
12337
12338 os << elements[i]->GetAttribute() << ' ' << v.Size();
12339 for (j = 0; j < v.Size(); j++)
12340 {
12341 os << ' ' << v[j] + 1;
12342 }
12343 os << '\n';
12344 }
12345
12346 if (Nodes == NULL)
12347 {
12348 // Print the vertices.
12349 os << NumOfVertices << '\n';
12350 for (i = 0; i < NumOfVertices; i++)
12351 {
12352 os << vertices[i](0);
12353 for (j = 1; j < Dim; j++)
12354 {
12355 os << ' ' << vertices[i](j);
12356 }
12357 os << '\n';
12358 }
12359 }
12360 else
12361 {
12362 os << NumOfVertices << '\n';
12363 Nodes->Save(os);
12364 }
12365 }
12366 else // ===== Dim != 2 =====
12367 {
12368 if (Nodes)
12369 {
12370 mfem_error("Mesh::PrintXG(...) : Curved mesh in 3D");
12371 }
12372
12373 if (meshgen == 1)
12374 {
12375 int nv;
12376 const int *ind;
12377
12378 os << "NETGEN_Neutral_Format\n";
12379 // print the vertices
12380 os << NumOfVertices << '\n';
12381 for (i = 0; i < NumOfVertices; i++)
12382 {
12383 for (j = 0; j < Dim; j++)
12384 {
12385 os << ' ' << vertices[i](j);
12386 }
12387 os << '\n';
12388 }
12389
12390 // print the elements
12391 os << NumOfElements << '\n';
12392 for (i = 0; i < NumOfElements; i++)
12393 {
12394 nv = elements[i]->GetNVertices();
12395 ind = elements[i]->GetVertices();
12396 os << elements[i]->GetAttribute();
12397 for (j = 0; j < nv; j++)
12398 {
12399 os << ' ' << ind[j]+1;
12400 }
12401 os << '\n';
12402 }
12403
12404 // print the boundary information.
12405 os << NumOfBdrElements << '\n';
12406 for (i = 0; i < NumOfBdrElements; i++)
12407 {
12408 nv = boundary[i]->GetNVertices();
12409 ind = boundary[i]->GetVertices();
12410 os << boundary[i]->GetAttribute();
12411 for (j = 0; j < nv; j++)
12412 {
12413 os << ' ' << ind[j]+1;
12414 }
12415 os << '\n';
12416 }
12417 }
12418 else if (meshgen == 2) // TrueGrid
12419 {
12420 int nv;
12421 const int *ind;
12422
12423 os << "TrueGrid\n"
12424 << "1 " << NumOfVertices << " " << NumOfElements
12425 << " 0 0 0 0 0 0 0\n"
12426 << "0 0 0 1 0 0 0 0 0 0 0\n"
12427 << "0 0 " << NumOfBdrElements << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
12428 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
12429 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
12430
12431 for (i = 0; i < NumOfVertices; i++)
12432 os << i+1 << " 0.0 " << vertices[i](0) << ' ' << vertices[i](1)
12433 << ' ' << vertices[i](2) << " 0.0\n";
12434
12435 for (i = 0; i < NumOfElements; i++)
12436 {
12437 nv = elements[i]->GetNVertices();
12438 ind = elements[i]->GetVertices();
12439 os << i+1 << ' ' << elements[i]->GetAttribute();
12440 for (j = 0; j < nv; j++)
12441 {
12442 os << ' ' << ind[j]+1;
12443 }
12444 os << '\n';
12445 }
12446
12447 for (i = 0; i < NumOfBdrElements; i++)
12448 {
12449 nv = boundary[i]->GetNVertices();
12450 ind = boundary[i]->GetVertices();
12451 os << boundary[i]->GetAttribute();
12452 for (j = 0; j < nv; j++)
12453 {
12454 os << ' ' << ind[j]+1;
12455 }
12456 os << " 1.0 1.0 1.0 1.0\n";
12457 }
12458 }
12459 }
12460
12461 os << flush;
12462}
12463
12464void Mesh::Printer(std::ostream &os, std::string section_delimiter,
12465 const std::string &comments) const
12466{
12467 int i, j;
12468
12469 if (NURBSext)
12470 {
12471 // general format
12472 NURBSext->Print(os, comments);
12473 os << '\n';
12474 Nodes->Save(os);
12475
12477 // patch-wise format
12478 // NURBSext->ConvertToPatches(*Nodes);
12479 // NURBSext->Print(os);
12480
12481 return;
12482 }
12483
12484 if (Nonconforming())
12485 {
12486 // Workaround for inconsistent Mesh state where the Mesh has nodes and
12487 // ncmesh->coordinates is not empty. Such state can be created with the
12488 // method Mesh::SwapNodes(), see the comment at the beginning of its
12489 // implementation.
12490 Array<real_t> coords_save;
12491 if (Nodes) { mfem::Swap(coords_save, ncmesh->coordinates); }
12492
12493 // nonconforming mesh format
12494 ncmesh->Print(os, comments);
12495
12496 if (Nodes)
12497 {
12498 mfem::Swap(coords_save, ncmesh->coordinates);
12499
12500 os << "\n# mesh curvature GridFunction";
12501 os << "\nnodes\n";
12502 Nodes->Save(os);
12503 }
12504
12505 os << "\nmfem_mesh_end" << endl;
12506 return;
12507 }
12508
12509 // serial/parallel conforming mesh format
12510 const bool set_names = attribute_sets.SetsExist() ||
12512 os << (!set_names && section_delimiter.empty()
12513 ? "MFEM mesh v1.0\n" :
12514 (!set_names ? "MFEM mesh v1.2\n" : "MFEM mesh v1.3\n"));
12515
12516 if (set_names && section_delimiter.empty())
12517 {
12518 section_delimiter = "mfem_mesh_end";
12519 }
12520
12521 // optional
12522 if (!comments.empty()) { os << '\n' << comments << '\n'; }
12523
12524 os <<
12525 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
12526 "# POINT = 0\n"
12527 "# SEGMENT = 1\n"
12528 "# TRIANGLE = 2\n"
12529 "# SQUARE = 3\n"
12530 "# TETRAHEDRON = 4\n"
12531 "# CUBE = 5\n"
12532 "# PRISM = 6\n"
12533 "# PYRAMID = 7\n"
12534 "#\n";
12535
12536 os << "\ndimension\n" << Dim;
12537
12538 os << "\n\nelements\n" << NumOfElements << '\n';
12539 for (i = 0; i < NumOfElements; i++)
12540 {
12541 PrintElement(elements[i], os);
12542 }
12543
12544 if (set_names)
12545 {
12546 os << "\nattribute_sets\n";
12548 }
12549
12550 os << "\nboundary\n" << NumOfBdrElements << '\n';
12551 for (i = 0; i < NumOfBdrElements; i++)
12552 {
12553 PrintElement(boundary[i], os);
12554 }
12555
12556 if (set_names)
12557 {
12558 os << "\nbdr_attribute_sets\n";
12560 }
12561
12562 os << "\nvertices\n" << NumOfVertices << '\n';
12563 if (Nodes == NULL)
12564 {
12565 os << spaceDim << '\n';
12566 for (i = 0; i < NumOfVertices; i++)
12567 {
12568 os << vertices[i](0);
12569 for (j = 1; j < spaceDim; j++)
12570 {
12571 os << ' ' << vertices[i](j);
12572 }
12573 os << '\n';
12574 }
12575 os.flush();
12576 }
12577 else
12578 {
12579 os << "\nnodes\n";
12580 Nodes->Save(os);
12581 }
12582
12583 if (!section_delimiter.empty())
12584 {
12585 os << '\n'
12586 << section_delimiter << endl; // only with formats v1.2 and above
12587 }
12588}
12589
12590void Mesh::PrintTopo(std::ostream &os, const Array<int> &e_to_k,
12591 const int version, const std::string &comments) const
12592{
12593 MFEM_VERIFY(version == 10 || version == 11, "Invalid NURBS mesh version");
12594
12595 int i;
12596 Array<int> vert;
12597
12598 os << "MFEM NURBS mesh v" << int(version / 10) << "." << version % 10 << "\n";
12599
12600 // optional
12601 if (!comments.empty()) { os << '\n' << comments << '\n'; }
12602
12603 os <<
12604 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
12605 "# SEGMENT = 1\n"
12606 "# SQUARE = 3\n"
12607 "# CUBE = 5\n"
12608 "#\n";
12609
12610 os << "\ndimension\n" << Dim
12611 << "\n\nelements\n" << NumOfElements << '\n';
12612 for (i = 0; i < NumOfElements; i++)
12613 {
12614 PrintElement(elements[i], os);
12615 }
12616
12617 os << "\nboundary\n" << NumOfBdrElements << '\n';
12618 for (i = 0; i < NumOfBdrElements; i++)
12619 {
12620 PrintElement(boundary[i], os);
12621 }
12622
12623 PrintTopoEdges(os, e_to_k);
12624}
12625
12626void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
12627 bool vmap) const
12628{
12629 Array<int> vert;
12630
12631 // In 1D patch-topology NURBS meshes, knotvector orientation is stored in the
12632 // file's `edges` section, but the topological 1D mesh has NumOfEdges == 0
12633 // (its "faces" are vertices). When a valid edge->knotvector map is provided,
12634 // print a pseudo-edge list derived from the 1D elements so external tools
12635 // (e.g. VisIt) can consume the mapping.
12636 if (Dim == 1 && NumOfEdges == 0 && e_to_k.Size() == NumOfElements)
12637 {
12638 const int ne = NumOfElements;
12639 os << "\nedges\n" << ne << '\n';
12640 for (int i = 0; i < ne; i++)
12641 {
12642 const int *v = elements[i]->GetVertices();
12643 int v0 = v[0], v1 = v[1];
12644
12645 int ki = e_to_k[i];
12646 const bool flip = (ki < 0); // desired output vertex order: descending
12647 if (flip) { ki = -1 - ki; } // print the unsigned knotvector index
12648
12649 // Encode the sign of e_to_k in the vertex ordering, consistent with
12650 // Mesh::LoadPatchTopo(): v0 > v1 => negative sign.
12651 if ((v0 > v1) != flip) { std::swap(v0, v1); }
12652
12653 os << ki << ' ' << v0 << ' ' << v1 << '\n';
12654 }
12655
12656 if (!vmap)
12657 {
12658 os << "\nvertices\n" << NumOfVertices << '\n';
12659 }
12660 return;
12661 }
12662
12663 os << "\nedges\n" << NumOfEdges << '\n';
12664 for (int i = 0; i < NumOfEdges; i++)
12665 {
12666 edge_vertex->GetRow(i, vert);
12667 const int ki = UnsignIndex(e_to_k[i]);
12668
12669 if (vmap)
12670 {
12671 for (int j=0; j<2; ++j)
12672 {
12673 vert[j] = ncmesh->vertex_nodeId[vert[j]];
12674 }
12675
12676 if (e_to_k[i] < 0)
12677 {
12678 // Swap the entries of vert
12679 const int s = vert[0];
12680 vert[0] = vert[1];
12681 vert[1] = s;
12682 }
12683 }
12684
12685 os << ki << ' ' << vert[0] << ' ' << vert[1] << '\n';
12686 }
12687
12688 if (!vmap)
12689 {
12690 os << "\nvertices\n" << NumOfVertices << '\n';
12691 }
12692}
12693
12694void Mesh::Save(const std::string &fname, int precision) const
12695{
12696 ofstream ofs(fname);
12697 ofs.precision(precision);
12698 Print(ofs);
12699}
12700
12701#ifdef MFEM_USE_ADIOS2
12703{
12704 os.Print(*this);
12705}
12706#endif
12707
12708void Mesh::PrintVTK(std::ostream &os)
12709{
12710 os <<
12711 "# vtk DataFile Version 3.0\n"
12712 "Generated by MFEM\n"
12713 "ASCII\n"
12714 "DATASET UNSTRUCTURED_GRID\n";
12715
12716 if (Nodes == NULL)
12717 {
12718 os << "POINTS " << NumOfVertices << " double\n";
12719 for (int i = 0; i < NumOfVertices; i++)
12720 {
12721 os << vertices[i](0);
12722 int j;
12723 for (j = 1; j < spaceDim; j++)
12724 {
12725 os << ' ' << vertices[i](j);
12726 }
12727 for ( ; j < 3; j++)
12728 {
12729 os << ' ' << 0.0;
12730 }
12731 os << '\n';
12732 }
12733 }
12734 else
12735 {
12736 Array<int> vdofs(3);
12737 os << "POINTS " << Nodes->FESpace()->GetNDofs() << " double\n";
12738 for (int i = 0; i < Nodes->FESpace()->GetNDofs(); i++)
12739 {
12740 vdofs.SetSize(1);
12741 vdofs[0] = i;
12742 Nodes->FESpace()->DofsToVDofs(vdofs);
12743 os << (*Nodes)(vdofs[0]);
12744 int j;
12745 for (j = 1; j < spaceDim; j++)
12746 {
12747 os << ' ' << (*Nodes)(vdofs[j]);
12748 }
12749 for ( ; j < 3; j++)
12750 {
12751 os << ' ' << 0.0;
12752 }
12753 os << '\n';
12754 }
12755 }
12756
12757 int order = -1;
12758 if (Nodes == NULL)
12759 {
12760 int size = 0;
12761 for (int i = 0; i < NumOfElements; i++)
12762 {
12763 size += elements[i]->GetNVertices() + 1;
12764 }
12765 os << "CELLS " << NumOfElements << ' ' << size << '\n';
12766 for (int i = 0; i < NumOfElements; i++)
12767 {
12768 const int *v = elements[i]->GetVertices();
12769 const int nv = elements[i]->GetNVertices();
12770 os << nv;
12771 Geometry::Type geom = elements[i]->GetGeometryType();
12772 const int *perm = VTKGeometry::VertexPermutation[geom];
12773 for (int j = 0; j < nv; j++)
12774 {
12775 os << ' ' << v[perm ? perm[j] : j];
12776 }
12777 os << '\n';
12778 }
12779 order = 1;
12780 }
12781 else
12782 {
12783 Array<int> dofs;
12784 int size = 0;
12785 for (int i = 0; i < NumOfElements; i++)
12786 {
12787 Nodes->FESpace()->GetElementDofs(i, dofs);
12788 MFEM_ASSERT(Dim != 0 || dofs.Size() == 1,
12789 "Point meshes should have a single dof per element");
12790 size += dofs.Size() + 1;
12791 }
12792 os << "CELLS " << NumOfElements << ' ' << size << '\n';
12793 const char *fec_name = Nodes->FESpace()->FEColl()->Name();
12794
12795 if (!strcmp(fec_name, "Linear") ||
12796 !strcmp(fec_name, "H1_0D_P1") ||
12797 !strcmp(fec_name, "H1_1D_P1") ||
12798 !strcmp(fec_name, "H1_2D_P1") ||
12799 !strcmp(fec_name, "H1_3D_P1"))
12800 {
12801 order = 1;
12802 }
12803 else if (!strcmp(fec_name, "Quadratic") ||
12804 !strcmp(fec_name, "H1_1D_P2") ||
12805 !strcmp(fec_name, "H1_2D_P2") ||
12806 !strcmp(fec_name, "H1_3D_P2"))
12807 {
12808 order = 2;
12809 }
12810 if (order == -1)
12811 {
12812 mfem::err << "Mesh::PrintVTK : can not save '"
12813 << fec_name << "' elements!" << endl;
12814 mfem_error();
12815 }
12816 for (int i = 0; i < NumOfElements; i++)
12817 {
12818 Nodes->FESpace()->GetElementDofs(i, dofs);
12819 os << dofs.Size();
12820 if (order == 1)
12821 {
12822 for (int j = 0; j < dofs.Size(); j++)
12823 {
12824 os << ' ' << dofs[j];
12825 }
12826 }
12827 else if (order == 2)
12828 {
12829 const int *vtk_mfem;
12830 switch (elements[i]->GetGeometryType())
12831 {
12832 case Geometry::SEGMENT:
12833 case Geometry::TRIANGLE:
12834 case Geometry::SQUARE:
12835 vtk_mfem = vtk_quadratic_hex; break; // identity map
12837 vtk_mfem = vtk_quadratic_tet; break;
12838 case Geometry::PRISM:
12839 vtk_mfem = vtk_quadratic_wedge; break;
12840 case Geometry::CUBE:
12841 default:
12842 vtk_mfem = vtk_quadratic_hex; break;
12843 }
12844 for (int j = 0; j < dofs.Size(); j++)
12845 {
12846 os << ' ' << dofs[vtk_mfem[j]];
12847 }
12848 }
12849 os << '\n';
12850 }
12851 }
12852
12853 os << "CELL_TYPES " << NumOfElements << '\n';
12854 for (int i = 0; i < NumOfElements; i++)
12855 {
12856 int vtk_cell_type = 5;
12858 if (order == 1) { vtk_cell_type = VTKGeometry::Map[geom]; }
12859 else if (order == 2) { vtk_cell_type = VTKGeometry::QuadraticMap[geom]; }
12860 os << vtk_cell_type << '\n';
12861 }
12862
12863 // write attributes
12864 os << "CELL_DATA " << NumOfElements << '\n'
12865 << "SCALARS material int\n"
12866 << "LOOKUP_TABLE default\n";
12867 for (int i = 0; i < NumOfElements; i++)
12868 {
12869 os << elements[i]->GetAttribute() << '\n';
12870 }
12871 os.flush();
12872}
12873
12874void Mesh::PrintVTU(std::string fname,
12875 VTKFormat format,
12876 bool high_order_output,
12877 int compression_level,
12878 bool bdr_elements)
12879{
12880 int ref = (high_order_output && Nodes)
12881 ? Nodes->FESpace()->GetMaxElementOrder() : 1;
12882
12883 fname = fname + ".vtu";
12884 std::fstream os(fname.c_str(),std::ios::out);
12885 os << "<VTKFile type=\"UnstructuredGrid\" version=\"2.2\"";
12886 if (compression_level != 0)
12887 {
12888 os << " compressor=\"vtkZLibDataCompressor\"";
12889 }
12890 os << " byte_order=\"" << VTKByteOrder() << "\">\n";
12891 os << "<UnstructuredGrid>\n";
12892 PrintVTU(os, ref, format, high_order_output, compression_level, bdr_elements);
12893 os << "</Piece>\n"; // need to close the piece open in the PrintVTU method
12894 os << "</UnstructuredGrid>\n";
12895 os << "</VTKFile>" << std::endl;
12896
12897 os.close();
12898}
12899
12900void Mesh::PrintBdrVTU(std::string fname,
12901 VTKFormat format,
12902 bool high_order_output,
12903 int compression_level)
12904{
12905 PrintVTU(fname, format, high_order_output, compression_level, true);
12906}
12907
12908void Mesh::PrintVTU(std::ostream &os, int ref, VTKFormat format,
12909 bool high_order_output, int compression_level,
12910 bool bdr_elements)
12911{
12912 RefinedGeometry *RefG;
12913 DenseMatrix pmat;
12914
12915 const char *fmt_str = (format == VTKFormat::ASCII) ? "ascii" : "binary";
12916 const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
12917 std::vector<char> buf;
12918
12919 auto get_geom = [&](int i)
12920 {
12921 if (bdr_elements) { return GetBdrElementGeometry(i); }
12922 else { return GetElementBaseGeometry(i); }
12923 };
12924
12925 int ne = bdr_elements ? GetNBE() : GetNE();
12926 // count the number of points and cells
12927 int np = 0, nc_ref = 0;
12928 for (int i = 0; i < ne; i++)
12929 {
12930 Geometry::Type geom = get_geom(i);
12931 int nv = Geometries.GetVertices(geom)->GetNPoints();
12932 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12933 np += RefG->RefPts.GetNPoints();
12934 nc_ref += RefG->RefGeoms.Size() / nv;
12935 }
12936
12937 os << "<Piece NumberOfPoints=\"" << np << "\" NumberOfCells=\""
12938 << (high_order_output ? ne : nc_ref) << "\">\n";
12939
12940 // print out the points
12941 os << "<Points>\n";
12942 os << "<DataArray type=\"" << type_str
12943 << "\" NumberOfComponents=\"3\" format=\"" << fmt_str << "\">\n";
12944 for (int i = 0; i < ne; i++)
12945 {
12946 RefG = GlobGeometryRefiner.Refine(get_geom(i), ref, 1);
12947
12948 if (bdr_elements)
12949 {
12951 }
12952 else
12953 {
12954 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
12955 }
12956
12957 for (int j = 0; j < pmat.Width(); j++)
12958 {
12959 WriteBinaryOrASCII(os, buf, pmat(0,j), " ", format);
12960 if (pmat.Height() > 1)
12961 {
12962 WriteBinaryOrASCII(os, buf, pmat(1,j), " ", format);
12963 }
12964 else
12965 {
12966 WriteBinaryOrASCII(os, buf, 0.0, " ", format);
12967 }
12968 if (pmat.Height() > 2)
12969 {
12970 WriteBinaryOrASCII(os, buf, pmat(2,j), "", format);
12971 }
12972 else
12973 {
12974 WriteBinaryOrASCII(os, buf, 0.0, "", format);
12975 }
12976 if (format == VTKFormat::ASCII) { os << '\n'; }
12977 }
12978 }
12979 if (format != VTKFormat::ASCII)
12980 {
12981 WriteBase64WithSizeAndClear(os, buf, compression_level);
12982 }
12983 os << "</DataArray>" << std::endl;
12984 os << "</Points>" << std::endl;
12985
12986 os << "<Cells>" << std::endl;
12987 os << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\""
12988 << fmt_str << "\">" << std::endl;
12989 // connectivity
12990 std::vector<int> offset;
12991
12992 np = 0;
12993 if (high_order_output)
12994 {
12995 Array<int> local_connectivity;
12996 for (int iel = 0; iel < ne; iel++)
12997 {
12998 Geometry::Type geom = get_geom(iel);
12999 CreateVTKElementConnectivity(local_connectivity, geom, ref);
13000 int nnodes = local_connectivity.Size();
13001 for (int i=0; i<nnodes; ++i)
13002 {
13003 WriteBinaryOrASCII(os, buf, np+local_connectivity[i], " ",
13004 format);
13005 }
13006 if (format == VTKFormat::ASCII) { os << '\n'; }
13007 np += nnodes;
13008 offset.push_back(np);
13009 }
13010 }
13011 else
13012 {
13013 int coff = 0;
13014 for (int i = 0; i < ne; i++)
13015 {
13016 Geometry::Type geom = get_geom(i);
13017 int nv = Geometries.GetVertices(geom)->GetNPoints();
13018 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13019 Array<int> &RG = RefG->RefGeoms;
13020 for (int j = 0; j < RG.Size(); )
13021 {
13022 coff = coff+nv;
13023 offset.push_back(coff);
13024 const int *p = VTKGeometry::VertexPermutation[geom];
13025 for (int k = 0; k < nv; k++, j++)
13026 {
13027 WriteBinaryOrASCII(os, buf, np + RG[p ? (j - k + p[k]) : j], " ",
13028 format);
13029 }
13030 if (format == VTKFormat::ASCII) { os << '\n'; }
13031 }
13032 np += RefG->RefPts.GetNPoints();
13033 }
13034 }
13035 if (format != VTKFormat::ASCII)
13036 {
13037 WriteBase64WithSizeAndClear(os, buf, compression_level);
13038 }
13039 os << "</DataArray>" << std::endl;
13040
13041 os << "<DataArray type=\"Int32\" Name=\"offsets\" format=\""
13042 << fmt_str << "\">" << std::endl;
13043 // offsets
13044 for (size_t ii=0; ii<offset.size(); ii++)
13045 {
13046 WriteBinaryOrASCII(os, buf, offset[ii], "\n", format);
13047 }
13048 if (format != VTKFormat::ASCII)
13049 {
13050 WriteBase64WithSizeAndClear(os, buf, compression_level);
13051 }
13052 os << "</DataArray>" << std::endl;
13053 os << "<DataArray type=\"UInt8\" Name=\"types\" format=\""
13054 << fmt_str << "\">" << std::endl;
13055 // cell types
13056 const int *vtk_geom_map =
13057 high_order_output ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
13058 for (int i = 0; i < ne; i++)
13059 {
13060 Geometry::Type geom = get_geom(i);
13061 uint8_t vtk_cell_type = 5;
13062
13063 vtk_cell_type = vtk_geom_map[geom];
13064
13065 if (high_order_output)
13066 {
13067 WriteBinaryOrASCII(os, buf, vtk_cell_type, "\n", format);
13068 }
13069 else
13070 {
13071 int nv = Geometries.GetVertices(geom)->GetNPoints();
13072 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13073 Array<int> &RG = RefG->RefGeoms;
13074 for (int j = 0; j < RG.Size(); j += nv)
13075 {
13076 WriteBinaryOrASCII(os, buf, vtk_cell_type, "\n", format);
13077 }
13078 }
13079 }
13080 if (format != VTKFormat::ASCII)
13081 {
13082 WriteBase64WithSizeAndClear(os, buf, compression_level);
13083 }
13084 os << "</DataArray>" << std::endl;
13085 os << "</Cells>" << std::endl;
13086
13087 os << "<CellData Scalars=\"attribute\">" << std::endl;
13088 os << "<DataArray type=\"Int32\" Name=\"attribute\" format=\""
13089 << fmt_str << "\">" << std::endl;
13090 for (int i = 0; i < ne; i++)
13091 {
13092 int attr = bdr_elements ? GetBdrAttribute(i) : GetAttribute(i);
13093 if (high_order_output)
13094 {
13095 WriteBinaryOrASCII(os, buf, attr, "\n", format);
13096 }
13097 else
13098 {
13099 Geometry::Type geom = get_geom(i);
13100 int nv = Geometries.GetVertices(geom)->GetNPoints();
13101 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13102 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
13103 {
13104 WriteBinaryOrASCII(os, buf, attr, "\n", format);
13105 }
13106 }
13107 }
13108 if (format != VTKFormat::ASCII)
13109 {
13110 WriteBase64WithSizeAndClear(os, buf, compression_level);
13111 }
13112 os << "</DataArray>" << std::endl;
13113 os << "</CellData>" << std::endl;
13114}
13115
13116
13117void Mesh::PrintVTK(std::ostream &os, int ref, int field_data)
13118{
13119 int np, nc, size;
13120 RefinedGeometry *RefG;
13121 DenseMatrix pmat;
13122
13123 os <<
13124 "# vtk DataFile Version 3.0\n"
13125 "Generated by MFEM\n"
13126 "ASCII\n"
13127 "DATASET UNSTRUCTURED_GRID\n";
13128
13129 // additional dataset information
13130 if (field_data)
13131 {
13132 os << "FIELD FieldData 1\n"
13133 << "MaterialIds " << 1 << " " << attributes.Size() << " int\n";
13134 for (int i = 0; i < attributes.Size(); i++)
13135 {
13136 os << ' ' << attributes[i];
13137 }
13138 os << '\n';
13139 }
13140
13141 // count the points, cells, size
13142 np = nc = size = 0;
13143 for (int i = 0; i < GetNE(); i++)
13144 {
13146 int nv = Geometries.GetVertices(geom)->GetNPoints();
13147 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13148 np += RefG->RefPts.GetNPoints();
13149 nc += RefG->RefGeoms.Size() / nv;
13150 size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
13151 }
13152 os << "POINTS " << np << " double\n";
13153 // write the points
13154 for (int i = 0; i < GetNE(); i++)
13155 {
13157 GetElementBaseGeometry(i), ref, 1);
13158
13159 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
13160
13161 for (int j = 0; j < pmat.Width(); j++)
13162 {
13163 os << pmat(0, j) << ' ';
13164 if (pmat.Height() > 1)
13165 {
13166 os << pmat(1, j) << ' ';
13167 if (pmat.Height() > 2)
13168 {
13169 os << pmat(2, j);
13170 }
13171 else
13172 {
13173 os << 0.0;
13174 }
13175 }
13176 else
13177 {
13178 os << 0.0 << ' ' << 0.0;
13179 }
13180 os << '\n';
13181 }
13182 }
13183
13184 // write the cells
13185 os << "CELLS " << nc << ' ' << size << '\n';
13186 np = 0;
13187 for (int i = 0; i < GetNE(); i++)
13188 {
13190 int nv = Geometries.GetVertices(geom)->GetNPoints();
13191 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13192 Array<int> &RG = RefG->RefGeoms;
13193
13194 for (int j = 0; j < RG.Size(); )
13195 {
13196 os << nv;
13197 for (int k = 0; k < nv; k++, j++)
13198 {
13199 os << ' ' << np + RG[j];
13200 }
13201 os << '\n';
13202 }
13203 np += RefG->RefPts.GetNPoints();
13204 }
13205 os << "CELL_TYPES " << nc << '\n';
13206 for (int i = 0; i < GetNE(); i++)
13207 {
13209 int nv = Geometries.GetVertices(geom)->GetNPoints();
13210 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13211 Array<int> &RG = RefG->RefGeoms;
13212 int vtk_cell_type = VTKGeometry::Map[geom];
13213
13214 for (int j = 0; j < RG.Size(); j += nv)
13215 {
13216 os << vtk_cell_type << '\n';
13217 }
13218 }
13219 // write attributes (materials)
13220 os << "CELL_DATA " << nc << '\n'
13221 << "SCALARS material int\n"
13222 << "LOOKUP_TABLE default\n";
13223 for (int i = 0; i < GetNE(); i++)
13224 {
13226 int nv = Geometries.GetVertices(geom)->GetNPoints();
13227 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13228 int attr = GetAttribute(i);
13229 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
13230 {
13231 os << attr << '\n';
13232 }
13233 }
13234
13235 if (Dim > 1)
13236 {
13237 Array<int> coloring;
13238 srand((unsigned)time(0));
13239 real_t a = rand_real();
13240 int el0 = (int)floor(a * GetNE());
13241 GetElementColoring(coloring, el0);
13242 os << "SCALARS element_coloring int\n"
13243 << "LOOKUP_TABLE default\n";
13244 for (int i = 0; i < GetNE(); i++)
13245 {
13247 int nv = Geometries.GetVertices(geom)->GetNPoints();
13248 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
13249 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
13250 {
13251 os << coloring[i] + 1 << '\n';
13252 }
13253 }
13254 }
13255
13256 // prepare to write data
13257 os << "POINT_DATA " << np << '\n' << flush;
13258}
13259
13260#ifdef MFEM_USE_HDF5
13261
13262void Mesh::SaveVTKHDF(const std::string &fname, bool high_order)
13263{
13264#ifdef MFEM_USE_MPI
13265 if (ParMesh *pmesh = dynamic_cast<ParMesh*>(this))
13266 {
13267#ifdef MFEM_PARALLEL_HDF5
13268 VTKHDF vtkhdf(fname, pmesh->GetComm());
13269 vtkhdf.SaveMesh(*this, high_order);
13270 return;
13271#else
13272 MFEM_ABORT("Requires HDF5 library with parallel support enabled");
13273#endif
13274 }
13275#endif
13276 VTKHDF vtkhdf(fname);
13277 vtkhdf.SaveMesh(*this, high_order);
13278}
13279
13280#endif
13281
13283{
13284 int delete_el_to_el = (el_to_el) ? (0) : (1);
13285 const Table &el_el = ElementToElementTable();
13286 int num_el = GetNE(), stack_p, stack_top_p, max_num_col;
13287 Array<int> el_stack(num_el);
13288
13289 const int *i_el_el = el_el.GetI();
13290 const int *j_el_el = el_el.GetJ();
13291
13292 colors.SetSize(num_el);
13293 colors = -2;
13294 max_num_col = 1;
13295 stack_p = stack_top_p = 0;
13296 for (int el = el0; stack_top_p < num_el; el=(el+1)%num_el)
13297 {
13298 if (colors[el] != -2)
13299 {
13300 continue;
13301 }
13302
13303 colors[el] = -1;
13304 el_stack[stack_top_p++] = el;
13305
13306 for ( ; stack_p < stack_top_p; stack_p++)
13307 {
13308 int i = el_stack[stack_p];
13309 int num_nb = i_el_el[i+1] - i_el_el[i];
13310 if (max_num_col < num_nb + 1)
13311 {
13312 max_num_col = num_nb + 1;
13313 }
13314 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
13315 {
13316 int k = j_el_el[j];
13317 if (colors[k] == -2)
13318 {
13319 colors[k] = -1;
13320 el_stack[stack_top_p++] = k;
13321 }
13322 }
13323 }
13324 }
13325
13326 Array<int> col_marker(max_num_col);
13327
13328 for (stack_p = 0; stack_p < stack_top_p; stack_p++)
13329 {
13330 int i = el_stack[stack_p], col;
13331 col_marker = 0;
13332 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
13333 {
13334 col = colors[j_el_el[j]];
13335 if (col != -1)
13336 {
13337 col_marker[col] = 1;
13338 }
13339 }
13340
13341 for (col = 0; col < max_num_col; col++)
13342 if (col_marker[col] == 0)
13343 {
13344 break;
13345 }
13346
13347 colors[i] = col;
13348 }
13349
13350 if (delete_el_to_el)
13351 {
13352 delete el_to_el;
13353 el_to_el = NULL;
13354 }
13355}
13356
13357void Mesh::PrintWithPartitioning(int *partitioning, std::ostream &os,
13358 int elem_attr) const
13359{
13360 if (Dim != 3 && Dim != 2) { return; }
13361
13362 int i, j, k, l, nv, nbe, *v;
13363
13364 os << "MFEM mesh v1.0\n";
13365
13366 // optional
13367 os <<
13368 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
13369 "# POINT = 0\n"
13370 "# SEGMENT = 1\n"
13371 "# TRIANGLE = 2\n"
13372 "# SQUARE = 3\n"
13373 "# TETRAHEDRON = 4\n"
13374 "# CUBE = 5\n"
13375 "# PRISM = 6\n"
13376 "#\n";
13377
13378 os << "\ndimension\n" << Dim
13379 << "\n\nelements\n" << NumOfElements << '\n';
13380 for (i = 0; i < NumOfElements; i++)
13381 {
13382 os << int((elem_attr) ? partitioning[i]+1 : elements[i]->GetAttribute())
13383 << ' ' << elements[i]->GetGeometryType();
13384 nv = elements[i]->GetNVertices();
13385 v = elements[i]->GetVertices();
13386 for (j = 0; j < nv; j++)
13387 {
13388 os << ' ' << v[j];
13389 }
13390 os << '\n';
13391 }
13392 nbe = 0;
13393 for (i = 0; i < faces_info.Size(); i++)
13394 {
13395 if ((l = faces_info[i].Elem2No) >= 0)
13396 {
13397 k = partitioning[faces_info[i].Elem1No];
13398 l = partitioning[l];
13399 if (k != l)
13400 {
13401 nbe++;
13402 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
13403 {
13404 nbe++;
13405 }
13406 }
13407 }
13408 else
13409 {
13410 nbe++;
13411 }
13412 }
13413 os << "\nboundary\n" << nbe << '\n';
13414 for (i = 0; i < faces_info.Size(); i++)
13415 {
13416 if ((l = faces_info[i].Elem2No) >= 0)
13417 {
13418 k = partitioning[faces_info[i].Elem1No];
13419 l = partitioning[l];
13420 if (k != l)
13421 {
13422 nv = faces[i]->GetNVertices();
13423 v = faces[i]->GetVertices();
13424 os << k+1 << ' ' << faces[i]->GetGeometryType();
13425 for (j = 0; j < nv; j++)
13426 {
13427 os << ' ' << v[j];
13428 }
13429 os << '\n';
13430 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
13431 {
13432 os << l+1 << ' ' << faces[i]->GetGeometryType();
13433 for (j = nv-1; j >= 0; j--)
13434 {
13435 os << ' ' << v[j];
13436 }
13437 os << '\n';
13438 }
13439 }
13440 }
13441 else
13442 {
13443 k = partitioning[faces_info[i].Elem1No];
13444 nv = faces[i]->GetNVertices();
13445 v = faces[i]->GetVertices();
13446 os << k+1 << ' ' << faces[i]->GetGeometryType();
13447 for (j = 0; j < nv; j++)
13448 {
13449 os << ' ' << v[j];
13450 }
13451 os << '\n';
13452 }
13453 }
13454 os << "\nvertices\n" << NumOfVertices << '\n';
13455 if (Nodes == NULL)
13456 {
13457 os << spaceDim << '\n';
13458 for (i = 0; i < NumOfVertices; i++)
13459 {
13460 os << vertices[i](0);
13461 for (j = 1; j < spaceDim; j++)
13462 {
13463 os << ' ' << vertices[i](j);
13464 }
13465 os << '\n';
13466 }
13467 os.flush();
13468 }
13469 else
13470 {
13471 os << "\nnodes\n";
13472 Nodes->Save(os);
13473 }
13474}
13475
13477 std::ostream &os,
13478 int interior_faces)
13479{
13480 MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported\n");
13481 if (Dim != 3 && Dim != 2) { return; }
13482
13483 int *vcount = new int[NumOfVertices];
13484 for (int i = 0; i < NumOfVertices; i++)
13485 {
13486 vcount[i] = 0;
13487 }
13488 for (int i = 0; i < NumOfElements; i++)
13489 {
13490 int nv = elements[i]->GetNVertices();
13491 const int *ind = elements[i]->GetVertices();
13492 for (int j = 0; j < nv; j++)
13493 {
13494 vcount[ind[j]]++;
13495 }
13496 }
13497
13498 int *voff = new int[NumOfVertices+1];
13499 voff[0] = 0;
13500 for (int i = 1; i <= NumOfVertices; i++)
13501 {
13502 voff[i] = vcount[i-1] + voff[i-1];
13503 }
13504
13505 int **vown = new int*[NumOfVertices];
13506 for (int i = 0; i < NumOfVertices; i++)
13507 {
13508 vown[i] = new int[vcount[i]];
13509 }
13510
13511 // 2D
13512 if (Dim == 2)
13513 {
13514 Table edge_el;
13515 Transpose(ElementToEdgeTable(), edge_el);
13516
13517 // Fake printing of the elements.
13518 for (int i = 0; i < NumOfElements; i++)
13519 {
13520 int nv = elements[i]->GetNVertices();
13521 const int *ind = elements[i]->GetVertices();
13522 for (int j = 0; j < nv; j++)
13523 {
13524 vcount[ind[j]]--;
13525 vown[ind[j]][vcount[ind[j]]] = i;
13526 }
13527 }
13528
13529 for (int i = 0; i < NumOfVertices; i++)
13530 {
13531 vcount[i] = voff[i+1] - voff[i];
13532 }
13533
13534 int nbe = 0;
13535 for (int i = 0; i < edge_el.Size(); i++)
13536 {
13537 const int *el = edge_el.GetRow(i);
13538 if (edge_el.RowSize(i) > 1)
13539 {
13540 int k = partitioning[el[0]];
13541 int l = partitioning[el[1]];
13542 if (interior_faces || k != l)
13543 {
13544 nbe += 2;
13545 }
13546 }
13547 else
13548 {
13549 nbe++;
13550 }
13551 }
13552
13553 // Print the type of the mesh and the boundary elements.
13554 os << "areamesh2\n\n" << nbe << '\n';
13555
13556 for (int i = 0; i < edge_el.Size(); i++)
13557 {
13558 const int *el = edge_el.GetRow(i);
13559 if (edge_el.RowSize(i) > 1)
13560 {
13561 int k = partitioning[el[0]];
13562 int l = partitioning[el[1]];
13563 if (interior_faces || k != l)
13564 {
13565 Array<int> ev;
13566 GetEdgeVertices(i,ev);
13567 os << k+1; // attribute
13568 for (int j = 0; j < 2; j++)
13569 for (int s = 0; s < vcount[ev[j]]; s++)
13570 if (vown[ev[j]][s] == el[0])
13571 {
13572 os << ' ' << voff[ev[j]]+s+1;
13573 }
13574 os << '\n';
13575 os << l+1; // attribute
13576 for (int j = 1; j >= 0; j--)
13577 for (int s = 0; s < vcount[ev[j]]; s++)
13578 if (vown[ev[j]][s] == el[1])
13579 {
13580 os << ' ' << voff[ev[j]]+s+1;
13581 }
13582 os << '\n';
13583 }
13584 }
13585 else
13586 {
13587 int k = partitioning[el[0]];
13588 Array<int> ev;
13589 GetEdgeVertices(i,ev);
13590 os << k+1; // attribute
13591 for (int j = 0; j < 2; j++)
13592 for (int s = 0; s < vcount[ev[j]]; s++)
13593 if (vown[ev[j]][s] == el[0])
13594 {
13595 os << ' ' << voff[ev[j]]+s+1;
13596 }
13597 os << '\n';
13598 }
13599 }
13600
13601 // Print the elements.
13602 os << NumOfElements << '\n';
13603 for (int i = 0; i < NumOfElements; i++)
13604 {
13605 int nv = elements[i]->GetNVertices();
13606 const int *ind = elements[i]->GetVertices();
13607 os << partitioning[i]+1 << ' '; // use subdomain number as attribute
13608 os << nv << ' ';
13609 for (int j = 0; j < nv; j++)
13610 {
13611 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13612 vown[ind[j]][vcount[ind[j]]] = i;
13613 }
13614 os << '\n';
13615 }
13616
13617 for (int i = 0; i < NumOfVertices; i++)
13618 {
13619 vcount[i] = voff[i+1] - voff[i];
13620 }
13621
13622 // Print the vertices.
13623 os << voff[NumOfVertices] << '\n';
13624 for (int i = 0; i < NumOfVertices; i++)
13625 for (int k = 0; k < vcount[i]; k++)
13626 {
13627 for (int j = 0; j < Dim; j++)
13628 {
13629 os << vertices[i](j) << ' ';
13630 }
13631 os << '\n';
13632 }
13633 }
13634 // Dim is 3
13635 else if (meshgen == 1)
13636 {
13637 os << "NETGEN_Neutral_Format\n";
13638 // print the vertices
13639 os << voff[NumOfVertices] << '\n';
13640 for (int i = 0; i < NumOfVertices; i++)
13641 for (int k = 0; k < vcount[i]; k++)
13642 {
13643 for (int j = 0; j < Dim; j++)
13644 {
13645 os << ' ' << vertices[i](j);
13646 }
13647 os << '\n';
13648 }
13649
13650 // print the elements
13651 os << NumOfElements << '\n';
13652 for (int i = 0; i < NumOfElements; i++)
13653 {
13654 int nv = elements[i]->GetNVertices();
13655 const int *ind = elements[i]->GetVertices();
13656 os << partitioning[i]+1; // use subdomain number as attribute
13657 for (int j = 0; j < nv; j++)
13658 {
13659 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13660 vown[ind[j]][vcount[ind[j]]] = i;
13661 }
13662 os << '\n';
13663 }
13664
13665 for (int i = 0; i < NumOfVertices; i++)
13666 {
13667 vcount[i] = voff[i+1] - voff[i];
13668 }
13669
13670 // print the boundary information.
13671 int nbe = 0;
13672 for (int i = 0; i < NumOfFaces; i++)
13673 {
13674 int l = faces_info[i].Elem2No;
13675 if (l >= 0)
13676 {
13677 int k = partitioning[faces_info[i].Elem1No];
13678 l = partitioning[l];
13679 if (interior_faces || k != l)
13680 {
13681 nbe += 2;
13682 }
13683 }
13684 else
13685 {
13686 nbe++;
13687 }
13688 }
13689
13690 os << nbe << '\n';
13691 for (int i = 0; i < NumOfFaces; i++)
13692 {
13693 int l = faces_info[i].Elem2No;
13694 if (l >= 0)
13695 {
13696 int k = partitioning[faces_info[i].Elem1No];
13697 l = partitioning[l];
13698 if (interior_faces || k != l)
13699 {
13700 int nv = faces[i]->GetNVertices();
13701 const int *ind = faces[i]->GetVertices();
13702 os << k+1; // attribute
13703 for (int j = 0; j < nv; j++)
13704 for (int s = 0; s < vcount[ind[j]]; s++)
13705 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13706 {
13707 os << ' ' << voff[ind[j]]+s+1;
13708 }
13709 os << '\n';
13710 os << l+1; // attribute
13711 for (int j = nv-1; j >= 0; j--)
13712 for (int s = 0; s < vcount[ind[j]]; s++)
13713 if (vown[ind[j]][s] == faces_info[i].Elem2No)
13714 {
13715 os << ' ' << voff[ind[j]]+s+1;
13716 }
13717 os << '\n';
13718 }
13719 }
13720 else
13721 {
13722 int k = partitioning[faces_info[i].Elem1No];
13723 int nv = faces[i]->GetNVertices();
13724 const int *ind = faces[i]->GetVertices();
13725 os << k+1; // attribute
13726 for (int j = 0; j < nv; j++)
13727 for (int s = 0; s < vcount[ind[j]]; s++)
13728 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13729 {
13730 os << ' ' << voff[ind[j]]+s+1;
13731 }
13732 os << '\n';
13733 }
13734 }
13735 }
13736 // Dim is 3
13737 else if (meshgen == 2) // TrueGrid
13738 {
13739 // count the number of the boundary elements.
13740 int nbe = 0;
13741 for (int i = 0; i < NumOfFaces; i++)
13742 {
13743 int l = faces_info[i].Elem2No;
13744 if (l >= 0)
13745 {
13746 int k = partitioning[faces_info[i].Elem1No];
13747 l = partitioning[l];
13748 if (interior_faces || k != l)
13749 {
13750 nbe += 2;
13751 }
13752 }
13753 else
13754 {
13755 nbe++;
13756 }
13757 }
13758
13759 os << "TrueGrid\n"
13760 << "1 " << voff[NumOfVertices] << " " << NumOfElements
13761 << " 0 0 0 0 0 0 0\n"
13762 << "0 0 0 1 0 0 0 0 0 0 0\n"
13763 << "0 0 " << nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
13764 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
13765 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
13766
13767 for (int i = 0; i < NumOfVertices; i++)
13768 for (int k = 0; k < vcount[i]; k++)
13769 os << voff[i]+k << " 0.0 " << vertices[i](0) << ' '
13770 << vertices[i](1) << ' ' << vertices[i](2) << " 0.0\n";
13771
13772 for (int i = 0; i < NumOfElements; i++)
13773 {
13774 int nv = elements[i]->GetNVertices();
13775 const int *ind = elements[i]->GetVertices();
13776 os << i+1 << ' ' << partitioning[i]+1; // partitioning as attribute
13777 for (int j = 0; j < nv; j++)
13778 {
13779 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13780 vown[ind[j]][vcount[ind[j]]] = i;
13781 }
13782 os << '\n';
13783 }
13784
13785 for (int i = 0; i < NumOfVertices; i++)
13786 {
13787 vcount[i] = voff[i+1] - voff[i];
13788 }
13789
13790 // boundary elements
13791 for (int i = 0; i < NumOfFaces; i++)
13792 {
13793 int l = faces_info[i].Elem2No;
13794 if (l >= 0)
13795 {
13796 int k = partitioning[faces_info[i].Elem1No];
13797 l = partitioning[l];
13798 if (interior_faces || k != l)
13799 {
13800 int nv = faces[i]->GetNVertices();
13801 const int *ind = faces[i]->GetVertices();
13802 os << k+1; // attribute
13803 for (int j = 0; j < nv; j++)
13804 for (int s = 0; s < vcount[ind[j]]; s++)
13805 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13806 {
13807 os << ' ' << voff[ind[j]]+s+1;
13808 }
13809 os << " 1.0 1.0 1.0 1.0\n";
13810 os << l+1; // attribute
13811 for (int j = nv-1; j >= 0; j--)
13812 for (int s = 0; s < vcount[ind[j]]; s++)
13813 if (vown[ind[j]][s] == faces_info[i].Elem2No)
13814 {
13815 os << ' ' << voff[ind[j]]+s+1;
13816 }
13817 os << " 1.0 1.0 1.0 1.0\n";
13818 }
13819 }
13820 else
13821 {
13822 int k = partitioning[faces_info[i].Elem1No];
13823 int nv = faces[i]->GetNVertices();
13824 const int *ind = faces[i]->GetVertices();
13825 os << k+1; // attribute
13826 for (int j = 0; j < nv; j++)
13827 for (int s = 0; s < vcount[ind[j]]; s++)
13828 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13829 {
13830 os << ' ' << voff[ind[j]]+s+1;
13831 }
13832 os << " 1.0 1.0 1.0 1.0\n";
13833 }
13834 }
13835 }
13836
13837 os << flush;
13838
13839 for (int i = 0; i < NumOfVertices; i++)
13840 {
13841 delete [] vown[i];
13842 }
13843
13844 delete [] vcount;
13845 delete [] voff;
13846 delete [] vown;
13847}
13848
13849void Mesh::PrintSurfaces(const Table & Aface_face, std::ostream &os) const
13850{
13851 int i, j;
13852
13853 if (NURBSext)
13854 {
13855 mfem_error("Mesh::PrintSurfaces"
13856 " NURBS mesh is not supported!");
13857 return;
13858 }
13859
13860 os << "MFEM mesh v1.0\n";
13861
13862 // optional
13863 os <<
13864 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
13865 "# POINT = 0\n"
13866 "# SEGMENT = 1\n"
13867 "# TRIANGLE = 2\n"
13868 "# SQUARE = 3\n"
13869 "# TETRAHEDRON = 4\n"
13870 "# CUBE = 5\n"
13871 "# PRISM = 6\n"
13872 "#\n";
13873
13874 os << "\ndimension\n" << Dim
13875 << "\n\nelements\n" << NumOfElements << '\n';
13876 for (i = 0; i < NumOfElements; i++)
13877 {
13878 PrintElement(elements[i], os);
13879 }
13880
13881 os << "\nboundary\n" << Aface_face.Size_of_connections() << '\n';
13882 const int * const i_AF_f = Aface_face.GetI();
13883 const int * const j_AF_f = Aface_face.GetJ();
13884
13885 for (int iAF=0; iAF < Aface_face.Size(); ++iAF)
13886 for (const int * iface = j_AF_f + i_AF_f[iAF];
13887 iface < j_AF_f + i_AF_f[iAF+1];
13888 ++iface)
13889 {
13890 os << iAF+1 << ' ';
13891 PrintElementWithoutAttr(faces[*iface],os);
13892 }
13893
13894 os << "\nvertices\n" << NumOfVertices << '\n';
13895 if (Nodes == NULL)
13896 {
13897 os << spaceDim << '\n';
13898 for (i = 0; i < NumOfVertices; i++)
13899 {
13900 os << vertices[i](0);
13901 for (j = 1; j < spaceDim; j++)
13902 {
13903 os << ' ' << vertices[i](j);
13904 }
13905 os << '\n';
13906 }
13907 os.flush();
13908 }
13909 else
13910 {
13911 os << "\nnodes\n";
13912 Nodes->Save(os);
13913 }
13914}
13915
13917{
13918 int i,j,k;
13919 Array<int> vert;
13920 DenseMatrix pointmat;
13921 int na = attributes.Size();
13922 real_t *cg = new real_t[na*spaceDim];
13923 int *nbea = new int[na];
13924
13925 int *vn = new int[NumOfVertices];
13926 for (i = 0; i < NumOfVertices; i++)
13927 {
13928 vn[i] = 0;
13929 }
13930 for (i = 0; i < na; i++)
13931 {
13932 for (j = 0; j < spaceDim; j++)
13933 {
13934 cg[i*spaceDim+j] = 0.0;
13935 }
13936 nbea[i] = 0;
13937 }
13938
13939 for (i = 0; i < NumOfElements; i++)
13940 {
13941 GetElementVertices(i, vert);
13942 for (k = 0; k < vert.Size(); k++)
13943 {
13944 vn[vert[k]] = 1;
13945 }
13946 }
13947
13948 for (i = 0; i < NumOfElements; i++)
13949 {
13950 int bea = GetAttribute(i)-1;
13951 GetPointMatrix(i, pointmat);
13952 GetElementVertices(i, vert);
13953
13954 for (k = 0; k < vert.Size(); k++)
13955 if (vn[vert[k]] == 1)
13956 {
13957 nbea[bea]++;
13958 for (j = 0; j < spaceDim; j++)
13959 {
13960 cg[bea*spaceDim+j] += pointmat(j,k);
13961 }
13962 vn[vert[k]] = 2;
13963 }
13964 }
13965
13966 for (i = 0; i < NumOfElements; i++)
13967 {
13968 int bea = GetAttribute(i)-1;
13969 GetElementVertices (i, vert);
13970
13971 for (k = 0; k < vert.Size(); k++)
13972 if (vn[vert[k]])
13973 {
13974 for (j = 0; j < spaceDim; j++)
13975 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
13976 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
13977 vn[vert[k]] = 0;
13978 }
13979 }
13980
13981 delete [] cg;
13982 delete [] nbea;
13983 delete [] vn;
13984}
13985
13987{
13988 int i,j,k;
13989 Array<int> vert;
13990 DenseMatrix pointmat;
13991 int na = NumOfElements;
13992 real_t *cg = new real_t[na*spaceDim];
13993 int *nbea = new int[na];
13994
13995 int *vn = new int[NumOfVertices];
13996 for (i = 0; i < NumOfVertices; i++)
13997 {
13998 vn[i] = 0;
13999 }
14000 for (i = 0; i < na; i++)
14001 {
14002 for (j = 0; j < spaceDim; j++)
14003 {
14004 cg[i*spaceDim+j] = 0.0;
14005 }
14006 nbea[i] = 0;
14007 }
14008
14009 for (i = 0; i < NumOfElements; i++)
14010 {
14011 GetElementVertices(i, vert);
14012 for (k = 0; k < vert.Size(); k++)
14013 {
14014 vn[vert[k]] = 1;
14015 }
14016 }
14017
14018 for (i = 0; i < NumOfElements; i++)
14019 {
14020 int bea = i;
14021 GetPointMatrix(i, pointmat);
14022 GetElementVertices(i, vert);
14023
14024 for (k = 0; k < vert.Size(); k++)
14025 if (vn[vert[k]] == 1)
14026 {
14027 nbea[bea]++;
14028 for (j = 0; j < spaceDim; j++)
14029 {
14030 cg[bea*spaceDim+j] += pointmat(j,k);
14031 }
14032 vn[vert[k]] = 2;
14033 }
14034 }
14035
14036 for (i = 0; i < NumOfElements; i++)
14037 {
14038 int bea = i;
14039 GetElementVertices(i, vert);
14040
14041 for (k = 0; k < vert.Size(); k++)
14042 if (vn[vert[k]])
14043 {
14044 for (j = 0; j < spaceDim; j++)
14045 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
14046 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
14047 vn[vert[k]] = 0;
14048 }
14049 }
14050
14051 delete [] cg;
14052 delete [] nbea;
14053 delete [] vn;
14054}
14055
14056void Mesh::Transform(std::function<void(const Vector &, Vector&)> f)
14057{
14058 // TODO: support for different new spaceDim.
14059 if (Nodes == NULL)
14060 {
14061 Vector vold(spaceDim), vnew(NULL, spaceDim);
14062 for (int i = 0; i < vertices.Size(); i++)
14063 {
14064 for (int j = 0; j < spaceDim; j++)
14065 {
14066 vold(j) = vertices[i](j);
14067 }
14068 vnew.SetData(vertices[i]());
14069 f(vold, vnew);
14070 }
14071 }
14072 else
14073 {
14074 GridFunction xnew(Nodes->FESpace());
14076 xnew.ProjectCoefficient(f_pert);
14077 *Nodes = xnew;
14078 }
14079 NodesUpdated();
14080}
14081
14083{
14084 MFEM_VERIFY(spaceDim == deformation.GetVDim(),
14085 "incompatible vector dimensions");
14086 if (Nodes == NULL)
14087 {
14090 GridFunction xnew(&fes);
14091 xnew.ProjectCoefficient(deformation);
14092 for (int i = 0; i < NumOfVertices; i++)
14093 for (int d = 0; d < spaceDim; d++)
14094 {
14095 vertices[i](d) = xnew(d + spaceDim*i);
14096 }
14097 }
14098 else
14099 {
14100 GridFunction xnew(Nodes->FESpace());
14101 xnew.ProjectCoefficient(deformation);
14102 *Nodes = xnew;
14103 }
14104 NodesUpdated();
14105}
14106
14108{
14109 if (NURBSext || ncmesh) { return; }
14110
14111 Array<int> v2v(GetNV());
14112 v2v = -1;
14113 for (int i = 0; i < GetNE(); i++)
14114 {
14115 Element *el = GetElement(i);
14116 int nv = el->GetNVertices();
14117 int *v = el->GetVertices();
14118 for (int j = 0; j < nv; j++)
14119 {
14120 v2v[v[j]] = 0;
14121 }
14122 }
14123 for (int i = 0; i < GetNBE(); i++)
14124 {
14125 Element *el = GetBdrElement(i);
14126 int *v = el->GetVertices();
14127 int nv = el->GetNVertices();
14128 for (int j = 0; j < nv; j++)
14129 {
14130 v2v[v[j]] = 0;
14131 }
14132 }
14133 int num_vert = 0;
14134 for (int i = 0; i < v2v.Size(); i++)
14135 {
14136 if (v2v[i] == 0)
14137 {
14138 vertices[num_vert] = vertices[i];
14139 v2v[i] = num_vert++;
14140 }
14141 }
14142
14143 if (num_vert == v2v.Size()) { return; }
14144
14145 Vector nodes_by_element;
14146 Array<int> vdofs;
14147 if (Nodes)
14148 {
14149 int s = 0;
14150 for (int i = 0; i < GetNE(); i++)
14151 {
14152 Nodes->FESpace()->GetElementVDofs(i, vdofs);
14153 s += vdofs.Size();
14154 }
14155 nodes_by_element.SetSize(s);
14156 s = 0;
14157 for (int i = 0; i < GetNE(); i++)
14158 {
14159 Nodes->FESpace()->GetElementVDofs(i, vdofs);
14160 Nodes->GetSubVector(vdofs, &nodes_by_element(s));
14161 s += vdofs.Size();
14162 }
14163 }
14164 vertices.SetSize(num_vert);
14165 NumOfVertices = num_vert;
14166 for (int i = 0; i < GetNE(); i++)
14167 {
14168 Element *el = GetElement(i);
14169 int *v = el->GetVertices();
14170 int nv = el->GetNVertices();
14171 for (int j = 0; j < nv; j++)
14172 {
14173 v[j] = v2v[v[j]];
14174 }
14175 }
14176 for (int i = 0; i < GetNBE(); i++)
14177 {
14178 Element *el = GetBdrElement(i);
14179 int *v = el->GetVertices();
14180 int nv = el->GetNVertices();
14181 for (int j = 0; j < nv; j++)
14182 {
14183 v[j] = v2v[v[j]];
14184 }
14185 }
14186 DeleteTables();
14187 if (Dim > 1)
14188 {
14189 // generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
14190 el_to_edge = new Table;
14192 }
14193 if (Dim > 2)
14194 {
14195 // generate el_to_face, be_to_face
14197 }
14198 // Update faces and faces_info
14199 GenerateFaces();
14200 if (Nodes)
14201 {
14202 Nodes->FESpace()->Update();
14203 Nodes->Update();
14204 int s = 0;
14205 for (int i = 0; i < GetNE(); i++)
14206 {
14207 Nodes->FESpace()->GetElementVDofs(i, vdofs);
14208 Nodes->SetSubVector(vdofs, &nodes_by_element(s));
14209 s += vdofs.Size();
14210 }
14211 }
14212}
14213
14215{
14216 if (NURBSext || ncmesh) { return; }
14217
14218 int num_bdr_elem = 0;
14219 int new_bel_to_edge_nnz = 0;
14220 for (int i = 0; i < GetNBE(); i++)
14221 {
14223 {
14225 }
14226 else
14227 {
14228 num_bdr_elem++;
14229 if (Dim == 3)
14230 {
14231 new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
14232 }
14233 }
14234 }
14235
14236 if (num_bdr_elem == GetNBE()) { return; }
14237
14238 Array<Element *> new_boundary(num_bdr_elem);
14239 Array<int> new_be_to_face;
14240 Table *new_bel_to_edge = NULL;
14241 new_boundary.SetSize(0);
14242 new_be_to_face.Reserve(num_bdr_elem);
14243 if (Dim == 3)
14244 {
14245 new_bel_to_edge = new Table;
14246 new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
14247 }
14248 for (int i = 0; i < GetNBE(); i++)
14249 {
14251 {
14252 new_boundary.Append(boundary[i]);
14253 int row = new_be_to_face.Size();
14254 new_be_to_face.Append(be_to_face[i]);
14255 if (Dim == 3)
14256 {
14257 int *e = bel_to_edge->GetRow(i);
14258 int ne = bel_to_edge->RowSize(i);
14259 int *new_e = new_bel_to_edge->GetRow(row);
14260 for (int j = 0; j < ne; j++)
14261 {
14262 new_e[j] = e[j];
14263 }
14264 new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
14265 }
14266 }
14267 }
14268
14269 NumOfBdrElements = new_boundary.Size();
14270 mfem::Swap(boundary, new_boundary);
14271
14272 mfem::Swap(be_to_face, new_be_to_face);
14273
14274 if (Dim == 3)
14275 {
14276 delete bel_to_edge;
14277 bel_to_edge = new_bel_to_edge;
14278 }
14279
14280 Array<int> attribs(num_bdr_elem);
14281 for (int i = 0; i < attribs.Size(); i++)
14282 {
14283 attribs[i] = GetBdrAttribute(i);
14284 }
14285 attribs.Sort();
14286 attribs.Unique();
14288 attribs.Copy(bdr_attributes);
14289}
14290
14292{
14293#ifdef MFEM_USE_MEMALLOC
14294 if (E)
14295 {
14296 if (E->GetType() == Element::TETRAHEDRON)
14297 {
14298 TetMemory.Free((Tetrahedron*) E);
14299 }
14300 else
14301 {
14302 delete E;
14303 }
14304 }
14305#else
14306 delete E;
14307#endif
14308}
14309
14310std::ostream &operator<<(std::ostream &os, const Mesh &mesh)
14311{
14312 mesh.Print(os);
14313 return os;
14314}
14315
14316int Mesh::FindPoints(DenseMatrix &point_mat, Array<int>& elem_ids,
14317 Array<IntegrationPoint>& ips, bool warn,
14319{
14320 const int npts = point_mat.Width();
14321 if (!npts) { return 0; }
14322 MFEM_VERIFY(point_mat.Height() == spaceDim,"Invalid points matrix");
14323 elem_ids.SetSize(npts);
14324 ips.SetSize(npts);
14325 elem_ids = -1;
14326 if (!GetNE()) { return 0; }
14327
14328 real_t *data = point_mat.GetData();
14329 InverseElementTransformation *inv_tr = inv_trans;
14330 inv_tr = inv_tr ? inv_tr : new InverseElementTransformation;
14331
14332 // For each point in 'point_mat', find the element whose center is closest.
14333 Vector min_dist(npts);
14334 Array<int> e_idx(npts);
14335 min_dist = std::numeric_limits<real_t>::max();
14336 e_idx = -1;
14337
14338 Vector pt(spaceDim);
14339 for (int i = 0; i < GetNE(); i++)
14340 {
14341 GetElementTransformation(i)->Transform(
14343 for (int k = 0; k < npts; k++)
14344 {
14345 real_t dist = pt.DistanceTo(data+k*spaceDim);
14346 if (dist < min_dist(k))
14347 {
14348 min_dist(k) = dist;
14349 e_idx[k] = i;
14350 }
14351 }
14352 }
14353
14354 // Checks if the points lie in the closest element
14355 int pts_found = 0;
14356 pt.NewDataAndSize(NULL, spaceDim);
14357 for (int k = 0; k < npts; k++)
14358 {
14359 pt.SetData(data+k*spaceDim);
14360 inv_tr->SetTransformation(*GetElementTransformation(e_idx[k]));
14361 int res = inv_tr->Transform(pt, ips[k]);
14363 {
14364 elem_ids[k] = e_idx[k];
14365 pts_found++;
14366 }
14367 }
14368 if (pts_found != npts)
14369 {
14370 Array<int> elvertices;
14371 Table *vtoel = GetVertexToElementTable();
14372 for (int k = 0; k < npts; k++)
14373 {
14374 if (elem_ids[k] != -1) { continue; }
14375 // Try all vertex-neighbors of element e_idx[k]
14376 pt.SetData(data+k*spaceDim);
14377 GetElementVertices(e_idx[k], elvertices);
14378 for (int v = 0; v < elvertices.Size(); v++)
14379 {
14380 int vv = elvertices[v];
14381 int ne = vtoel->RowSize(vv);
14382 const int* els = vtoel->GetRow(vv);
14383 for (int e = 0; e < ne; e++)
14384 {
14385 if (els[e] == e_idx[k]) { continue; }
14387 int res = inv_tr->Transform(pt, ips[k]);
14389 {
14390 elem_ids[k] = els[e];
14391 pts_found++;
14392 goto next_point;
14393 }
14394 }
14395 }
14396 // Try neighbors for non-conforming meshes
14397 if (ncmesh)
14398 {
14399 Array<int> neigh;
14400 int le = ncmesh->leaf_elements[e_idx[k]];
14401 ncmesh->FindNeighbors(le,neigh);
14402 for (int e = 0; e < neigh.Size(); e++)
14403 {
14404 int nn = neigh[e];
14405 if (ncmesh->IsGhost(ncmesh->elements[nn])) { continue; }
14406 int el = ncmesh->elements[nn].index;
14408 int res = inv_tr->Transform(pt, ips[k]);
14410 {
14411 elem_ids[k] = el;
14412 pts_found++;
14413 goto next_point;
14414 }
14415 }
14416 }
14417 next_point: ;
14418 }
14419 delete vtoel;
14420 }
14421 if (inv_trans == NULL) { delete inv_tr; }
14422
14423 if (warn && pts_found != npts)
14424 {
14425 MFEM_WARNING((npts-pts_found) << " points were not found");
14426 }
14427 return pts_found;
14428}
14429
14431 real_t &volume,
14432 Vector &aspr,
14433 Vector &skew,
14434 Vector &ori) const
14435{
14436 J.HostRead();
14437 aspr.HostWrite();
14438 skew.HostWrite();
14439 ori.HostWrite();
14440 MFEM_VERIFY(Dim == 2 || Dim == 3, "Only 2D/3D meshes supported right now.");
14441 MFEM_VERIFY(Dim == spaceDim, "Surface meshes not currently supported.");
14442 if (Dim == 2)
14443 {
14444 aspr.SetSize(1);
14445 skew.SetSize(1);
14446 ori.SetSize(1);
14447 Vector col1, col2;
14448 J.GetColumn(0, col1);
14449 J.GetColumn(1, col2);
14450
14451 // Area/Volume
14452 volume = J.Det();
14453
14454 // Aspect-ratio
14455 aspr(0) = col2.Norml2()/col1.Norml2();
14456
14457 // Skewness
14458 skew(0) = std::atan2(J.Det(), col1 * col2);
14459
14460 // Orientation
14461 ori(0) = std::atan2(J(1,0), J(0,0));
14462 }
14463 else if (Dim == 3)
14464 {
14465 aspr.SetSize(4);
14466 skew.SetSize(3);
14467 ori.SetSize(4);
14468 Vector col1, col2, col3;
14469 J.GetColumn(0, col1);
14470 J.GetColumn(1, col2);
14471 J.GetColumn(2, col3);
14472 real_t len1 = col1.Norml2(),
14473 len2 = col2.Norml2(),
14474 len3 = col3.Norml2();
14475
14476 Vector col1unit = col1,
14477 col2unit = col2,
14478 col3unit = col3;
14479 col1unit *= 1.0/len1;
14480 col2unit *= 1.0/len2;
14481 col3unit *= 1.0/len3;
14482
14483 // Area/Volume
14484 volume = J.Det();
14485
14486 // Aspect-ratio - non-dimensional
14487 aspr(0) = len1/std::sqrt(len2*len3),
14488 aspr(1) = len2/std::sqrt(len1*len3);
14489
14490 // Aspect-ratio - dimensional - needed for TMOP
14491 aspr(2) = std::sqrt(len1/(len2*len3)),
14492 aspr(3) = std::sqrt(len2/(len1*len3));
14493
14494 // Skewness
14495 Vector crosscol12, crosscol13;
14496 col1.cross3D(col2, crosscol12);
14497 col1.cross3D(col3, crosscol13);
14498 skew(0) = std::acos(col1unit*col2unit);
14499 skew(1) = std::acos(col1unit*col3unit);
14500 skew(2) = std::atan(len1*volume/(crosscol12*crosscol13));
14501
14502 // Orientation
14503 // First we define the rotation matrix
14504 DenseMatrix rot(Dim);
14505 // First column
14506 for (int d=0; d<Dim; d++) { rot(d, 0) = col1unit(d); }
14507 // Second column
14508 Vector rot2 = col2unit;
14509 Vector rot1 = col1unit;
14510 rot1 *= col1unit*col2unit;
14511 rot2 -= rot1;
14512 col1unit.cross3D(col2unit, rot1);
14513 rot2 /= rot1.Norml2();
14514 for (int d=0; d < Dim; d++) { rot(d, 1) = rot2(d); }
14515 // Third column
14516 rot1 /= rot1.Norml2();
14517 for (int d=0; d < Dim; d++) { rot(d, 2) = rot1(d); }
14518 real_t delta = sqrt(pow(rot(2,1)-rot(1,2), 2.0) +
14519 pow(rot(0,2)-rot(2,0), 2.0) +
14520 pow(rot(1,0)-rot(0,1), 2.0));
14521 ori = 0.0;
14522 if (delta == 0.0) // Matrix is symmetric. Check if it is Identity.
14523 {
14524 DenseMatrix Iden(Dim);
14525 for (int d = 0; d < Dim; d++) { Iden(d, d) = 1.0; };
14526 Iden -= rot;
14527 if (Iden.FNorm2() != 0)
14528 {
14529 // TODO: Handling of these cases.
14530 rot.Print();
14531 MFEM_ABORT("Invalid rotation matrix. Contact TMOP Developers.");
14532 }
14533 }
14534 else
14535 {
14536 ori(0) = (1./delta)*(rot(2,1)-rot(1,2));
14537 ori(1) = (1./delta)*(rot(0,2)-rot(2,0));
14538 ori(2) = (1./delta)*(rot(1,0)-rot(0,1));
14539 ori(3) = std::acos(0.5*(rot.Trace()-1.0));
14540 }
14541 }
14542}
14543
14544
14546 int dim_, const Array<int> (&entity_to_vertex_)[Geometry::NumGeom])
14547 : dim(dim_),
14548 entity_to_vertex(entity_to_vertex_)
14549{
14550 int geom_offset = 0;
14551 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
14552 {
14553 geom_offsets[g] = geom_offset;
14554 geom_offset += entity_to_vertex[g].Size()/Geometry::NumVerts[g];
14555 }
14556 geom_offsets[Geometry::DimStart[dim+1]] = geom_offset;
14557 num_entities = geom_offset;
14558}
14559
14561{
14562 // Find the 'geom' that corresponds to 'bytype_entity_id'
14563 int geom = Geometry::DimStart[dim];
14564 while (geom_offsets[geom+1] <= bytype_entity_id) { geom++; }
14565 MFEM_ASSERT(geom < Geometry::NumGeom, "internal error");
14566 MFEM_ASSERT(Geometry::Dimension[geom] == dim, "internal error");
14567 const int nv = Geometry::NumVerts[geom];
14568 const int geom_elem_id = bytype_entity_id - geom_offsets[geom];
14569 const int *v = &entity_to_vertex[geom][nv*geom_elem_id];
14570 return { geom, nv, v };
14571}
14572
14573void MeshPart::Print(std::ostream &os) const
14574{
14575 os << "MFEM mesh v1.2\n";
14576
14577 // optional
14578 os <<
14579 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
14580 "# POINT = 0\n"
14581 "# SEGMENT = 1\n"
14582 "# TRIANGLE = 2\n"
14583 "# SQUARE = 3\n"
14584 "# TETRAHEDRON = 4\n"
14585 "# CUBE = 5\n"
14586 "# PRISM = 6\n"
14587 "# PYRAMID = 7\n"
14588 "#\n";
14589
14590 const int dim = dimension;
14591 os << "\ndimension\n" << dim;
14592
14593 os << "\n\nelements\n" << num_elements << '\n';
14594 {
14595 const bool have_element_map = (element_map.Size() == num_elements);
14596 MFEM_ASSERT(have_element_map || element_map.Size() == 0,
14597 "invalid MeshPart state");
14598 EntityHelper elem_helper(dim, entity_to_vertex);
14599 MFEM_ASSERT(elem_helper.num_entities == num_elements,
14600 "invalid MeshPart state");
14601 for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
14602 {
14603 const int bytype_elem_id = have_element_map ?
14604 element_map[nat_elem_id] : nat_elem_id;
14605 const Entity ent = elem_helper.FindEntity(bytype_elem_id);
14606 // Print the element
14607 os << attributes[nat_elem_id] << ' ' << ent.geom;
14608 for (int i = 0; i < ent.num_verts; i++)
14609 {
14610 os << ' ' << ent.verts[i];
14611 }
14612 os << '\n';
14613 }
14614 }
14615
14616 os << "\nboundary\n" << num_bdr_elements << '\n';
14617 {
14618 const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
14619 MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
14620 "invalid MeshPart state");
14621 EntityHelper bdr_helper(dim-1, entity_to_vertex);
14622 MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
14623 "invalid MeshPart state");
14624 for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
14625 {
14626 const int bytype_bdr_id = have_boundary_map ?
14627 boundary_map[nat_bdr_id] : nat_bdr_id;
14628 const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
14629 // Print the boundary element
14630 os << bdr_attributes[nat_bdr_id] << ' ' << ent.geom;
14631 for (int i = 0; i < ent.num_verts; i++)
14632 {
14633 os << ' ' << ent.verts[i];
14634 }
14635 os << '\n';
14636 }
14637 }
14638
14639 os << "\nvertices\n" << num_vertices << '\n';
14640 if (!nodes)
14641 {
14642 const int sdim = space_dimension;
14643 os << sdim << '\n';
14644 for (int i = 0; i < num_vertices; i++)
14645 {
14646 os << vertex_coordinates[i*sdim];
14647 for (int d = 1; d < sdim; d++)
14648 {
14649 os << ' ' << vertex_coordinates[i*sdim+d];
14650 }
14651 os << '\n';
14652 }
14653 }
14654 else
14655 {
14656 os << "\nnodes\n";
14657 nodes->Save(os);
14658 }
14659
14660 os << "\nmfem_serial_mesh_end\n";
14661
14662 // Start: GroupTopology::Save
14663 const int num_groups = my_groups.Size();
14664 os << "\ncommunication_groups\n";
14665 os << "number_of_groups " << num_groups << "\n\n";
14666
14667 os << "# number of entities in each group, followed by ranks in group\n";
14668 for (int group_id = 0; group_id < num_groups; ++group_id)
14669 {
14670 const int group_size = my_groups.RowSize(group_id);
14671 const int *group_ptr = my_groups.GetRow(group_id);
14672 os << group_size;
14673 for (int group_member_index = 0; group_member_index < group_size;
14674 ++group_member_index)
14675 {
14676 os << ' ' << group_ptr[group_member_index];
14677 }
14678 os << '\n';
14679 }
14680 // End: GroupTopology::Save
14681
14686
14687 MFEM_VERIFY(g2v.RowSize(0) == 0, "internal erroor");
14688 os << "\ntotal_shared_vertices " << g2v.Size_of_connections() << '\n';
14689 if (dimension >= 2)
14690 {
14691 MFEM_VERIFY(g2ev.RowSize(0) == 0, "internal erroor");
14692 os << "total_shared_edges " << g2ev.Size_of_connections()/2 << '\n';
14693 }
14694 if (dimension >= 3)
14695 {
14696 MFEM_VERIFY(g2tv.RowSize(0) == 0, "internal erroor");
14697 MFEM_VERIFY(g2qv.RowSize(0) == 0, "internal erroor");
14698 const int total_shared_faces =
14699 g2tv.Size_of_connections()/3 + g2qv.Size_of_connections()/4;
14700 os << "total_shared_faces " << total_shared_faces << '\n';
14701 }
14702 os << "\n# group 0 has no shared entities\n";
14703 for (int gr = 1; gr < num_groups; gr++)
14704 {
14705 {
14706 const int nv = g2v.RowSize(gr);
14707 const int *sv = g2v.GetRow(gr);
14708 os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
14709 for (int i = 0; i < nv; i++)
14710 {
14711 os << sv[i] << '\n';
14712 }
14713 }
14714 if (dimension >= 2)
14715 {
14716 const int ne = g2ev.RowSize(gr)/2;
14717 const int *se = g2ev.GetRow(gr);
14718 os << "\nshared_edges " << ne << '\n';
14719 for (int i = 0; i < ne; i++)
14720 {
14721 const int *v = se + 2*i;
14722 os << v[0] << ' ' << v[1] << '\n';
14723 }
14724 }
14725 if (dimension >= 3)
14726 {
14727 const int nt = g2tv.RowSize(gr)/3;
14728 const int *st = g2tv.GetRow(gr);
14729 const int nq = g2qv.RowSize(gr)/4;
14730 const int *sq = g2qv.GetRow(gr);
14731 os << "\nshared_faces " << nt+nq << '\n';
14732 for (int i = 0; i < nt; i++)
14733 {
14734 os << Geometry::TRIANGLE;
14735 const int *v = st + 3*i;
14736 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
14737 os << '\n';
14738 }
14739 for (int i = 0; i < nq; i++)
14740 {
14741 os << Geometry::SQUARE;
14742 const int *v = sq + 4*i;
14743 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
14744 os << '\n';
14745 }
14746 }
14747 }
14748
14749 // Write out section end tag for mesh.
14750 os << "\nmfem_mesh_end" << endl;
14751}
14752
14754{
14755 if (mesh) { return *mesh; }
14756
14757 mesh.reset(new Mesh(dimension,
14762
14763 // Add elements
14764 {
14765 const bool have_element_map = (element_map.Size() == num_elements);
14766 MFEM_ASSERT(have_element_map || element_map.Size() == 0,
14767 "invalid MeshPart state");
14769 MFEM_ASSERT(elem_helper.num_entities == num_elements,
14770 "invalid MeshPart state");
14771 const bool have_tet_refine_flags = (tet_refine_flags.Size() > 0);
14772 for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
14773 {
14774 const int bytype_elem_id = have_element_map ?
14775 element_map[nat_elem_id] : nat_elem_id;
14776 const Entity ent = elem_helper.FindEntity(bytype_elem_id);
14777 Element *el = mesh->NewElement(ent.geom);
14778 el->SetVertices(ent.verts);
14779 el->SetAttribute(attributes[nat_elem_id]);
14780 if (ent.geom == Geometry::TETRAHEDRON && have_tet_refine_flags)
14781 {
14782 constexpr int geom_tet = Geometry::TETRAHEDRON;
14783 const int tet_id = (ent.verts - entity_to_vertex[geom_tet])/4;
14784 const int ref_flag = tet_refine_flags[tet_id];
14785 static_cast<Tetrahedron*>(el)->SetRefinementFlag(ref_flag);
14786 }
14787 mesh->AddElement(el);
14788 }
14789 }
14790
14791 // Add boundary elements
14792 {
14793 const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
14794 MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
14795 "invalid MeshPart state");
14797 MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
14798 "invalid MeshPart state");
14799 for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
14800 {
14801 const int bytype_bdr_id = have_boundary_map ?
14802 boundary_map[nat_bdr_id] : nat_bdr_id;
14803 const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
14804 Element *bdr = mesh->NewElement(ent.geom);
14805 bdr->SetVertices(ent.verts);
14806 bdr->SetAttribute(bdr_attributes[nat_bdr_id]);
14807 mesh->AddBdrElement(bdr);
14808 }
14809 }
14810
14811 // Add vertices
14813 {
14814 MFEM_ASSERT(!nodes, "invalid MeshPart state");
14815 for (int vert_id = 0; vert_id < num_vertices; vert_id++)
14816 {
14817 mesh->AddVertex(vertex_coordinates + space_dimension*vert_id);
14818 }
14819 }
14820 else
14821 {
14822 MFEM_ASSERT(vertex_coordinates.Size() == 0, "invalid MeshPart state");
14823 for (int vert_id = 0; vert_id < num_vertices; vert_id++)
14824 {
14825 mesh->AddVertex(0., 0., 0.);
14826 }
14827 // 'mesh.Nodes' cannot be set here -- they can be set later, if needed
14828 }
14829
14830 mesh->FinalizeTopology(/* generate_bdr: */ false);
14831
14832 return *mesh;
14833}
14834
14835
14837 int num_parts_,
14838 const int *partitioning_,
14839 int part_method)
14840 : mesh(mesh_)
14841{
14842 if (partitioning_)
14843 {
14844 partitioning.MakeRef(const_cast<int *>(partitioning_), mesh.GetNE(),
14845 false);
14846 }
14847 else
14848 {
14849 // Mesh::GeneratePartitioning always uses new[] to allocate the,
14850 // partitioning, so we need to tell the memory manager to free it with
14851 // delete[] (even if a different host memory type has been selected).
14852 constexpr MemoryType mt = MemoryType::HOST;
14853 partitioning.MakeRef(mesh.GeneratePartitioning(num_parts_, part_method),
14854 mesh.GetNE(), mt, true);
14855 }
14856
14858 // Note: the element ids in each row of 'part_to_element' are sorted.
14859
14860 const int dim = mesh.Dimension();
14861 if (dim >= 2)
14862 {
14864 }
14865
14866 Array<int> boundary_to_part(mesh.GetNBE());
14867 // Same logic as in ParMesh::BuildLocalBoundary
14868 if (dim >= 3)
14869 {
14870 for (int i = 0; i < boundary_to_part.Size(); i++)
14871 {
14872 int face, o, el1, el2;
14873 mesh.GetBdrElementFace(i, &face, &o);
14874 mesh.GetFaceElements(face, &el1, &el2);
14875 boundary_to_part[i] =
14876 partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2];
14877 }
14878 }
14879 else if (dim == 2)
14880 {
14881 for (int i = 0; i < boundary_to_part.Size(); i++)
14882 {
14883 int edge = mesh.GetBdrElementFaceIndex(i);
14884 int el1 = edge_to_element.GetRow(edge)[0];
14885 boundary_to_part[i] = partitioning[el1];
14886 }
14887 }
14888 else if (dim == 1)
14889 {
14890 for (int i = 0; i < boundary_to_part.Size(); i++)
14891 {
14892 int vert = mesh.GetBdrElementFaceIndex(i);
14893 int el1, el2;
14894 mesh.GetFaceElements(vert, &el1, &el2);
14895 boundary_to_part[i] = partitioning[el1];
14896 }
14897 }
14898 Transpose(boundary_to_part, part_to_boundary, num_parts_);
14899 // Note: the boundary element ids in each row of 'part_to_boundary' are
14900 // sorted.
14901 boundary_to_part.DeleteAll();
14902
14903 Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
14904 vertex_to_element.Swap(*vert_element);
14905 delete vert_element;
14906}
14907
14908void MeshPartitioner::ExtractPart(int part_id, MeshPart &mesh_part) const
14909{
14910 const int num_parts = part_to_element.Size();
14911
14912 MFEM_VERIFY(0 <= part_id && part_id < num_parts,
14913 "invalid part_id = " << part_id
14914 << ", num_parts = " << num_parts);
14915
14916 const int dim = mesh.Dimension();
14917 const int sdim = mesh.SpaceDimension();
14918 const int num_elems = part_to_element.RowSize(part_id);
14919 const int *elem_list = part_to_element.GetRow(part_id); // sorted
14920 const int num_bdr_elems = part_to_boundary.RowSize(part_id);
14921 const int *bdr_elem_list = part_to_boundary.GetRow(part_id); // sorted
14922
14923 // Initialize 'mesh_part'
14924 mesh_part.dimension = dim;
14925 mesh_part.space_dimension = sdim;
14926 mesh_part.num_vertices = 0;
14927 mesh_part.num_elements = num_elems;
14928 mesh_part.num_bdr_elements = num_bdr_elems;
14929 for (int g = 0; g < Geometry::NumGeom; g++)
14930 {
14931 mesh_part.entity_to_vertex[g].SetSize(0); // can reuse Array allocation
14932 }
14933 mesh_part.tet_refine_flags.SetSize(0);
14934 mesh_part.element_map.SetSize(0); // 0 or 'num_elements', if needed
14935 mesh_part.boundary_map.SetSize(0); // 0 or 'num_bdr_elements', if needed
14936 mesh_part.attributes.SetSize(num_elems);
14937 mesh_part.bdr_attributes.SetSize(num_bdr_elems);
14938 mesh_part.vertex_coordinates.SetSize(0);
14939
14940 mesh_part.num_parts = num_parts;
14941 mesh_part.my_part_id = part_id;
14942 mesh_part.my_groups.Clear();
14943 for (int g = 0; g < Geometry::NumGeom; g++)
14944 {
14945 mesh_part.group_shared_entity_to_vertex[g].Clear();
14946 }
14947 mesh_part.nodes.reset(nullptr);
14948 mesh_part.nodal_fes.reset(nullptr);
14949 mesh_part.mesh.reset(nullptr);
14950
14951 // Initialize:
14952 // - 'mesh_part.entity_to_vertex' for the elements (boundary elements are
14953 // set later); vertex ids are global at this point - they will be mapped to
14954 // local ids later
14955 // - 'mesh_part.attributes'
14956 // - 'mesh_part.tet_refine_flags' if needed
14957 int geom_marker = 0, num_geom = 0;
14958 for (int i = 0; i < num_elems; i++)
14959 {
14960 const Element *elem = mesh.GetElement(elem_list[i]);
14961 const int geom = elem->GetGeometryType();
14962 const int nv = Geometry::NumVerts[geom];
14963 const int *v = elem->GetVertices();
14964 MFEM_VERIFY(numeric_limits<int>::max() - nv >=
14965 mesh_part.entity_to_vertex[geom].Size(),
14966 "overflow in 'entity_to_vertex[geom]', geom: "
14967 << Geometry::Name[geom]);
14968 mesh_part.entity_to_vertex[geom].Append(v, nv);
14969 mesh_part.attributes[i] = elem->GetAttribute();
14970 if (geom == Geometry::TETRAHEDRON)
14971 {
14972 // Create 'mesh_part.tet_refine_flags' but only if we find at least one
14973 // non-zero flag in a tetrahedron.
14974 const Tetrahedron *tet = static_cast<const Tetrahedron*>(elem);
14975 const int ref_flag = tet->GetRefinementFlag();
14976 if (mesh_part.tet_refine_flags.Size() == 0)
14977 {
14978 if (ref_flag)
14979 {
14980 // This is the first time we encounter non-zero 'ref_flag'
14981 const int num_tets = mesh_part.entity_to_vertex[geom].Size()/nv;
14982 mesh_part.tet_refine_flags.SetSize(num_tets, 0);
14983 mesh_part.tet_refine_flags.Last() = ref_flag;
14984 }
14985 }
14986 else
14987 {
14988 mesh_part.tet_refine_flags.Append(ref_flag);
14989 }
14990 }
14991 if ((geom_marker & (1 << geom)) == 0)
14992 {
14993 geom_marker |= (1 << geom);
14994 num_geom++;
14995 }
14996 }
14997 MFEM_ASSERT(mesh_part.tet_refine_flags.Size() == 0 ||
14998 mesh_part.tet_refine_flags.Size() ==
15000 "internal error");
15001 // Initialize 'mesh_part.element_map' if needed
15002 if (num_geom > 1)
15003 {
15004 int offsets[Geometry::NumGeom];
15005 int offset = 0;
15006 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
15007 {
15008 offsets[g] = offset;
15009 offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
15010 }
15011 mesh_part.element_map.SetSize(num_elems);
15012 for (int i = 0; i < num_elems; i++)
15013 {
15014 const int geom = mesh.GetElementGeometry(elem_list[i]);
15015 mesh_part.element_map[i] = offsets[geom]++;
15016 }
15017 }
15018
15019 // Initialize:
15020 // - 'mesh_part.entity_to_vertex' for the boundary elements; vertex ids are
15021 // global at this point - they will be mapped to local ids later
15022 // - 'mesh_part.bdr_attributes'
15023 geom_marker = 0; num_geom = 0;
15024 for (int i = 0; i < num_bdr_elems; i++)
15025 {
15026 const Element *bdr_elem = mesh.GetBdrElement(bdr_elem_list[i]);
15027 const int geom = bdr_elem->GetGeometryType();
15028 const int nv = Geometry::NumVerts[geom];
15029 const int *v = bdr_elem->GetVertices();
15030 MFEM_VERIFY(numeric_limits<int>::max() - nv >=
15031 mesh_part.entity_to_vertex[geom].Size(),
15032 "overflow in 'entity_to_vertex[geom]', geom: "
15033 << Geometry::Name[geom]);
15034 mesh_part.entity_to_vertex[geom].Append(v, nv);
15035 mesh_part.bdr_attributes[i] = bdr_elem->GetAttribute();
15036 if ((geom_marker & (1 << geom)) == 0)
15037 {
15038 geom_marker |= (1 << geom);
15039 num_geom++;
15040 }
15041 }
15042 // Initialize 'mesh_part.boundary_map' if needed
15043 if (num_geom > 1)
15044 {
15045 int offsets[Geometry::NumGeom];
15046 int offset = 0;
15047 for (int g = Geometry::DimStart[dim-1]; g < Geometry::DimStart[dim]; g++)
15048 {
15049 offsets[g] = offset;
15050 offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
15051 }
15052 mesh_part.boundary_map.SetSize(num_bdr_elems);
15053 for (int i = 0; i < num_bdr_elems; i++)
15054 {
15055 const int geom = mesh.GetBdrElementGeometry(bdr_elem_list[i]);
15056 mesh_part.boundary_map[i] = offsets[geom]++;
15057 }
15058 }
15059
15060 // Create the vertex id map, 'vertex_loc_to_glob', which maps local ids to
15061 // global ones; the map is sorted, preserving the global ordering.
15062 Array<int> vertex_loc_to_glob;
15063 {
15064 std::unordered_set<int> vertex_set;
15065 for (int i = 0; i < num_elems; i++)
15066 {
15067 const Element *elem = mesh.GetElement(elem_list[i]);
15068 const int geom = elem->GetGeometryType();
15069 const int nv = Geometry::NumVerts[geom];
15070 const int *v = elem->GetVertices();
15071 vertex_set.insert(v, v + nv);
15072 }
15073 vertex_loc_to_glob.SetSize(static_cast<int>(vertex_set.size()));
15074 std::copy(vertex_set.begin(), vertex_set.end(), // src
15075 vertex_loc_to_glob.begin()); // dest
15076 }
15077 vertex_loc_to_glob.Sort();
15078
15079 // Initialize 'mesh_part.num_vertices'
15080 mesh_part.num_vertices = vertex_loc_to_glob.Size();
15081
15082 // Update the vertex ids in the arrays 'mesh_part.entity_to_vertex' from
15083 // global to local.
15084 for (int g = 0; g < Geometry::NumGeom; g++)
15085 {
15086 Array<int> &vert_array = mesh_part.entity_to_vertex[g];
15087 for (int i = 0; i < vert_array.Size(); i++)
15088 {
15089 const int glob_id = vert_array[i];
15090 const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
15091 MFEM_ASSERT(loc_id >= 0, "internal error: global vertex id not found");
15092 vert_array[i] = loc_id;
15093 }
15094 }
15095
15096 // Initialize one of 'mesh_part.vertex_coordinates' or 'mesh_part.nodes'
15097 if (!mesh.GetNodes())
15098 {
15099 MFEM_VERIFY(numeric_limits<int>::max()/sdim >= vertex_loc_to_glob.Size(),
15100 "overflow in 'vertex_coordinates', num_vertices = "
15101 << vertex_loc_to_glob.Size() << ", sdim = " << sdim);
15102 mesh_part.vertex_coordinates.SetSize(sdim*vertex_loc_to_glob.Size());
15103 for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
15104 {
15105 const real_t *coord = mesh.GetVertex(vertex_loc_to_glob[i]);
15106 for (int d = 0; d < sdim; d++)
15107 {
15108 mesh_part.vertex_coordinates[i*sdim+d] = coord[d];
15109 }
15110 }
15111 }
15112 else
15113 {
15114 const GridFunction &glob_nodes = *mesh.GetNodes();
15115 mesh_part.nodal_fes = ExtractFESpace(mesh_part, *glob_nodes.FESpace());
15116 // Initialized 'mesh_part.mesh'.
15117 // Note: the nodes of 'mesh_part.mesh' are not set.
15118
15119 mesh_part.nodes = ExtractGridFunction(mesh_part, glob_nodes,
15120 *mesh_part.nodal_fes);
15121
15122 // Attach the 'mesh_part.nodes' to the 'mesh_part.mesh'.
15123 mesh_part.mesh->NewNodes(*mesh_part.nodes, /* make_owner: */ false);
15124 // Note: the vertices of 'mesh_part.mesh' are not set.
15125 }
15126
15127 // Begin constructing the "neighbor" groups, i.e. the groups that contain
15128 // 'part_id'.
15129 ListOfIntegerSets groups;
15130 {
15131 // the first group is the local one
15132 IntegerSet group;
15133 group.Recreate(1, &part_id);
15134 groups.Insert(group);
15135 }
15136
15137 // 'shared_faces' : shared face id -> (global_face_id, group_id)
15138 // Note: 'shared_faces' will be sorted by 'global_face_id'.
15139 Array<Pair<int,int>> shared_faces;
15140
15141 // Add "neighbor" groups defined by faces
15142 // Construct 'shared_faces'.
15143 if (dim >= 3)
15144 {
15145 std::unordered_set<int> face_set;
15146 // Construct 'face_set'
15147 const Table &elem_to_face = mesh.ElementToFaceTable();
15148 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
15149 {
15150 const int glob_elem_id = elem_list[loc_elem_id];
15151 const int nfaces = elem_to_face.RowSize(glob_elem_id);
15152 const int *faces = elem_to_face.GetRow(glob_elem_id);
15153 face_set.insert(faces, faces + nfaces);
15154 }
15155 // Construct 'shared_faces'; add "neighbor" groups defined by faces.
15156 IntegerSet group;
15157 for (int glob_face_id : face_set)
15158 {
15159 int el[2];
15160 mesh.GetFaceElements(glob_face_id, &el[0], &el[1]);
15161 if (el[1] < 0) { continue; }
15162 el[0] = partitioning[el[0]];
15163 el[1] = partitioning[el[1]];
15164 MFEM_ASSERT(el[0] == part_id || el[1] == part_id, "internal error");
15165 if (el[0] != part_id || el[1] != part_id)
15166 {
15167 group.Recreate(2, el);
15168 const int group_id = groups.Insert(group);
15169 shared_faces.Append(Pair<int,int>(glob_face_id, group_id));
15170 }
15171 }
15172 shared_faces.Sort(); // sort the shared faces by 'glob_face_id'
15173 }
15174
15175 // 'shared_edges' : shared edge id -> (global_edge_id, group_id)
15176 // Note: 'shared_edges' will be sorted by 'global_edge_id'.
15177 Array<Pair<int,int>> shared_edges;
15178
15179 // Add "neighbor" groups defined by edges.
15180 // Construct 'shared_edges'.
15181 if (dim >= 2)
15182 {
15183 std::unordered_set<int> edge_set;
15184 // Construct 'edge_set'
15185 const Table &elem_to_edge = mesh.ElementToEdgeTable();
15186 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
15187 {
15188 const int glob_elem_id = elem_list[loc_elem_id];
15189 const int nedges = elem_to_edge.RowSize(glob_elem_id);
15190 const int *edges = elem_to_edge.GetRow(glob_elem_id);
15191 edge_set.insert(edges, edges + nedges);
15192 }
15193 // Construct 'shared_edges'; add "neighbor" groups defined by edges.
15194 IntegerSet group;
15195 for (int glob_edge_id : edge_set)
15196 {
15197 const int nelem = edge_to_element.RowSize(glob_edge_id);
15198 const int *elem = edge_to_element.GetRow(glob_edge_id);
15199 Array<int> &gr = group; // reference to the 'group' internal Array
15200 gr.SetSize(nelem);
15201 for (int j = 0; j < nelem; j++)
15202 {
15203 gr[j] = partitioning[elem[j]];
15204 }
15205 gr.Sort();
15206 gr.Unique();
15207 MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
15208 if (group.Size() > 1)
15209 {
15210 const int group_id = groups.Insert(group);
15211 shared_edges.Append(Pair<int,int>(glob_edge_id, group_id));
15212 }
15213 }
15214 shared_edges.Sort(); // sort the shared edges by 'glob_edge_id'
15215 }
15216
15217 // 'shared_verts' : shared vertex id -> (global_vertex_id, group_id)
15218 // Note: 'shared_verts' will be sorted by 'global_vertex_id'.
15219 Array<Pair<int,int>> shared_verts;
15220
15221 // Add "neighbor" groups defined by vertices.
15222 // Construct 'shared_verts'.
15223 {
15224 IntegerSet group;
15225 for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
15226 {
15227 // 'vertex_to_element' maps global vertex ids to global element ids
15228 const int glob_vertex_id = vertex_loc_to_glob[i];
15229 const int nelem = vertex_to_element.RowSize(glob_vertex_id);
15230 const int *elem = vertex_to_element.GetRow(glob_vertex_id);
15231 Array<int> &gr = group; // reference to the 'group' internal Array
15232 gr.SetSize(nelem);
15233 for (int j = 0; j < nelem; j++)
15234 {
15235 gr[j] = partitioning[elem[j]];
15236 }
15237 gr.Sort();
15238 gr.Unique();
15239 MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
15240 if (group.Size() > 1)
15241 {
15242 const int group_id = groups.Insert(group);
15243 shared_verts.Append(Pair<int,int>(glob_vertex_id, group_id));
15244 }
15245 }
15246 }
15247
15248 // Done constructing the "neighbor" groups in 'groups'.
15249 const int num_groups = groups.Size();
15250
15251 // Define 'mesh_part.my_groups'
15252 groups.AsTable(mesh_part.my_groups);
15253
15254 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::POINT]'
15255 Table &group__shared_vertex_to_vertex =
15257 group__shared_vertex_to_vertex.MakeI(num_groups);
15258 for (int sv = 0; sv < shared_verts.Size(); sv++)
15259 {
15260 const int group_id = shared_verts[sv].two;
15261 group__shared_vertex_to_vertex.AddAColumnInRow(group_id);
15262 }
15263 group__shared_vertex_to_vertex.MakeJ();
15264 for (int sv = 0; sv < shared_verts.Size(); sv++)
15265 {
15266 const int glob_vertex_id = shared_verts[sv].one;
15267 const int group_id = shared_verts[sv].two;
15268 const int loc_vertex_id = vertex_loc_to_glob.FindSorted(glob_vertex_id);
15269 MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
15270 group__shared_vertex_to_vertex.AddConnection(group_id, loc_vertex_id);
15271 }
15272 group__shared_vertex_to_vertex.ShiftUpI();
15273
15274 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::SEGMENT]'
15275 if (dim >= 2)
15276 {
15277 Table &group__shared_edge_to_vertex =
15279 group__shared_edge_to_vertex.MakeI(num_groups);
15280 for (int se = 0; se < shared_edges.Size(); se++)
15281 {
15282 const int group_id = shared_edges[se].two;
15283 group__shared_edge_to_vertex.AddColumnsInRow(group_id, 2);
15284 }
15285 group__shared_edge_to_vertex.MakeJ();
15286 const Table &edge_to_vertex = *mesh.GetEdgeVertexTable();
15287 for (int se = 0; se < shared_edges.Size(); se++)
15288 {
15289 const int glob_edge_id = shared_edges[se].one;
15290 const int group_id = shared_edges[se].two;
15291 const int *v = edge_to_vertex.GetRow(glob_edge_id);
15292 for (int i = 0; i < 2; i++)
15293 {
15294 const int loc_vertex_id = vertex_loc_to_glob.FindSorted(v[i]);
15295 MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
15296 group__shared_edge_to_vertex.AddConnection(group_id, loc_vertex_id);
15297 }
15298 }
15299 group__shared_edge_to_vertex.ShiftUpI();
15300 }
15301
15302 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::TRIANGLE]'
15303 // and 'mesh_part.group_shared_entity_to_vertex[Geometry::SQUARE]'.
15304 if (dim >= 3)
15305 {
15306 Table &group__shared_tria_to_vertex =
15308 Table &group__shared_quad_to_vertex =
15310 Array<int> vertex_ids;
15311 group__shared_tria_to_vertex.MakeI(num_groups);
15312 group__shared_quad_to_vertex.MakeI(num_groups);
15313 for (int sf = 0; sf < shared_faces.Size(); sf++)
15314 {
15315 const int glob_face_id = shared_faces[sf].one;
15316 const int group_id = shared_faces[sf].two;
15317 const int geom = mesh.GetFaceGeometry(glob_face_id);
15318 mesh_part.group_shared_entity_to_vertex[geom].
15319 AddColumnsInRow(group_id, Geometry::NumVerts[geom]);
15320 }
15321 group__shared_tria_to_vertex.MakeJ();
15322 group__shared_quad_to_vertex.MakeJ();
15323 for (int sf = 0; sf < shared_faces.Size(); sf++)
15324 {
15325 const int glob_face_id = shared_faces[sf].one;
15326 const int group_id = shared_faces[sf].two;
15327 const int geom = mesh.GetFaceGeometry(glob_face_id);
15328 mesh.GetFaceVertices(glob_face_id, vertex_ids);
15329 // Rotate shared triangles that have an adjacent tetrahedron with a
15330 // nonzero refinement flag.
15331 // See also ParMesh::BuildSharedFaceElems.
15332 if (geom == Geometry::TRIANGLE)
15333 {
15334 int glob_el_id[2];
15335 mesh.GetFaceElements(glob_face_id, &glob_el_id[0], &glob_el_id[1]);
15336 int side = 0;
15337 const Element *el = mesh.GetElement(glob_el_id[0]);
15338 const Tetrahedron *tet = nullptr;
15340 {
15341 tet = static_cast<const Tetrahedron*>(el);
15342 }
15343 else
15344 {
15345 side = 1;
15346 el = mesh.GetElement(glob_el_id[1]);
15348 {
15349 tet = static_cast<const Tetrahedron*>(el);
15350 }
15351 }
15352 if (tet && tet->GetRefinementFlag())
15353 {
15354 // mark the shared face for refinement by reorienting
15355 // it according to the refinement flag in the tetrahedron
15356 // to which this shared face belongs to.
15357 int info[2];
15358 mesh.GetFaceInfos(glob_face_id, &info[0], &info[1]);
15359 tet->GetMarkedFace(info[side]/64, &vertex_ids[0]);
15360 }
15361 }
15362 for (int i = 0; i < vertex_ids.Size(); i++)
15363 {
15364 const int glob_id = vertex_ids[i];
15365 const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
15366 MFEM_ASSERT(loc_id >= 0, "internal error");
15367 vertex_ids[i] = loc_id;
15368 }
15369 mesh_part.group_shared_entity_to_vertex[geom].
15370 AddConnections(group_id, vertex_ids, vertex_ids.Size());
15371 }
15372 group__shared_tria_to_vertex.ShiftUpI();
15373 group__shared_quad_to_vertex.ShiftUpI();
15374 }
15375}
15376
15377std::unique_ptr<FiniteElementSpace>
15379 const FiniteElementSpace &global_fespace) const
15380{
15381 mesh_part.GetMesh(); // initialize 'mesh_part.mesh'
15382 // Note: the nodes of 'mesh_part.mesh' are not set by GetMesh() unless they
15383 // were already constructed, e.g. by ExtractPart().
15384
15385 return std::unique_ptr<FiniteElementSpace>(
15386 new FiniteElementSpace(mesh_part.mesh.get(),
15387 global_fespace.FEColl(),
15388 global_fespace.GetVDim(),
15389 global_fespace.GetOrdering()));
15390}
15391
15392std::unique_ptr<GridFunction>
15394 const GridFunction &global_gf,
15395 FiniteElementSpace &local_fespace) const
15396{
15397 std::unique_ptr<GridFunction> local_gf(new GridFunction(&local_fespace));
15398
15399 // Transfer data from 'global_gf' to 'local_gf'.
15400 Array<int> gvdofs, lvdofs;
15401 Vector loc_vals;
15402 const int part_id = mesh_part.my_part_id;
15403 const int num_elems = part_to_element.RowSize(part_id);
15404 const int *elem_list = part_to_element.GetRow(part_id); // sorted
15405 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
15406 {
15407 const int glob_elem_id = elem_list[loc_elem_id];
15408 DofTransformation glob_dt, local_dt;
15409 global_gf.FESpace()->GetElementVDofs(glob_elem_id, gvdofs, glob_dt);
15410 global_gf.GetSubVector(gvdofs, loc_vals);
15411 glob_dt.InvTransformPrimal(loc_vals);
15412 local_fespace.GetElementVDofs(loc_elem_id, lvdofs, local_dt);
15413 local_dt.TransformPrimal(loc_vals);
15414 local_gf->SetSubVector(lvdofs, loc_vals);
15415 }
15416 return local_gf;
15417}
15418
15419
15421 int flags, MemoryType d_mt)
15422{
15423 this->mesh = mesh;
15424 IntRule = &ir;
15425 computed_factors = flags;
15426
15427 MFEM_ASSERT(mesh->GetNumGeometries(mesh->Dimension()) <= 1,
15428 "mixed meshes are not supported!");
15429 MFEM_ASSERT(mesh->GetNodes(), "meshes without nodes are not supported!");
15430
15431 Compute(*mesh->GetNodes(), d_mt);
15432}
15433
15435 const IntegrationRule &ir,
15436 int flags, MemoryType d_mt)
15437{
15438 this->mesh = nodes.FESpace()->GetMesh();
15439 IntRule = &ir;
15440 computed_factors = flags;
15441
15442 Compute(nodes, d_mt);
15443}
15444
15445void GeometricFactors::Compute(const GridFunction &nodes,
15446 MemoryType d_mt)
15447{
15448
15449 const FiniteElementSpace *fespace = nodes.FESpace();
15450 const FiniteElement *fe = fespace->GetTypicalFE();
15451 const int dim = fe->GetDim();
15452 const int vdim = fespace->GetVDim();
15453 const int NE = fespace->GetNE();
15454 const int ND = fe->GetDof();
15455 const int NQ = IntRule->GetNPoints();
15456
15457 unsigned eval_flags = 0;
15458 MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
15459 Device::GetDeviceMemoryType();
15461 {
15462 X.SetSize(vdim*NQ*NE, my_d_mt); // NQ x SDIM x NE
15463 eval_flags |= QuadratureInterpolator::VALUES;
15464 }
15466 {
15467 J.SetSize(dim*vdim*NQ*NE, my_d_mt); // NQ x SDIM x DIM x NE
15469 }
15471 {
15472 detJ.SetSize(NQ*NE, my_d_mt); // NQ x NE
15474 }
15475
15476 const QuadratureInterpolator *qi = fespace->GetQuadratureInterpolator(*IntRule);
15477 // All X, J, and detJ use this layout:
15479
15480 const bool use_tensor_products = UsesTensorBasis(*fespace);
15481
15482 qi->DisableTensorProducts(!use_tensor_products);
15483 const ElementDofOrdering e_ordering = use_tensor_products ?
15486 const Operator *elem_restr = fespace->GetElementRestriction(e_ordering);
15487
15488 if (elem_restr) // Always true as of 2021-04-27
15489 {
15490 Vector Enodes(vdim*ND*NE, my_d_mt);
15491 elem_restr->Mult(nodes, Enodes);
15492 qi->Mult(Enodes, eval_flags, X, J, detJ);
15493 }
15494 else
15495 {
15496 qi->Mult(nodes, eval_flags, X, J, detJ);
15497 }
15498}
15499
15501 const IntegrationRule &ir,
15502 int flags, FaceType type,
15503 MemoryType d_mt)
15504 : type(type)
15505{
15506 this->mesh = mesh;
15507 IntRule = &ir;
15508 computed_factors = flags;
15509
15510 const GridFunction *nodes = mesh->GetNodes();
15511 const FiniteElementSpace *fespace = nodes->FESpace();
15512 const int vdim = fespace->GetVDim();
15513 const int NF = fespace->GetNFbyType(type);
15514 const int NQ = ir.GetNPoints();
15515
15516 const FaceRestriction *face_restr = fespace->GetFaceRestriction(
15518 type,
15520
15521
15522 MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
15524
15525 Vector Fnodes(face_restr->Height(), my_d_mt);
15526 face_restr->Mult(*nodes, Fnodes);
15527
15528 unsigned eval_flags = 0;
15529
15531 {
15532 X.SetSize(vdim*NQ*NF, my_d_mt);
15534 }
15536 {
15537 J.SetSize(vdim*(mesh->Dimension() - 1)*NQ*NF, my_d_mt);
15539 }
15541 {
15542 detJ.SetSize(NQ*NF, my_d_mt);
15544 }
15546 {
15547 normal.SetSize(vdim*NQ*NF, my_d_mt);
15549 }
15550
15551 const FaceQuadratureInterpolator *qi =
15552 fespace->GetFaceQuadratureInterpolator(ir, type);
15553 // All face data vectors assume layout byNODES.
15555 const bool use_tensor_products = UsesTensorBasis(*fespace);
15556 qi->DisableTensorProducts(!use_tensor_products);
15557
15558 qi->Mult(Fnodes, eval_flags, X, J, detJ, normal);
15559}
15560
15562 const real_t s_)
15563 : VectorCoefficient(dim), n(n_), s(s_), tip(p, dim-1)
15564{
15565}
15566
15568 const IntegrationPoint &ip)
15569{
15570 V.SetSize(vdim);
15571 T.Transform(ip, tip);
15572 V(0) = p[0];
15573 if (vdim == 2)
15574 {
15575 V(1) = s * ((ip.y + layer) / n);
15576 }
15577 else
15578 {
15579 V(1) = p[1];
15580 V(2) = s * ((ip.z + layer) / n);
15581 }
15582}
15583
15584
15585Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy, const bool closed)
15586{
15587 if (mesh->Dimension() != 1)
15588 {
15589 mfem::err << "Extrude1D : Not a 1D mesh!" << endl;
15590 mfem_error();
15591 }
15592
15593 int nvy = (closed) ? (ny) : (ny + 1);
15594 int nvt = mesh->GetNV() * nvy;
15595
15596 Mesh *mesh2d;
15597
15598 if (closed)
15599 {
15600 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny, mesh->GetNBE()*ny);
15601 }
15602 else
15603 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny,
15604 mesh->GetNBE()*ny+2*mesh->GetNE());
15605
15606 // vertices
15607 real_t vc[2];
15608 for (int i = 0; i < mesh->GetNV(); i++)
15609 {
15610 vc[0] = mesh->GetVertex(i)[0];
15611 for (int j = 0; j < nvy; j++)
15612 {
15613 vc[1] = sy * (real_t(j) / ny);
15614 mesh2d->AddVertex(vc);
15615 }
15616 }
15617 // elements
15618 Array<int> vert;
15619 for (int i = 0; i < mesh->GetNE(); i++)
15620 {
15621 const Element *elem = mesh->GetElement(i);
15622 elem->GetVertices(vert);
15623 const int attr = elem->GetAttribute();
15624 for (int j = 0; j < ny; j++)
15625 {
15626 int qv[4];
15627 qv[0] = vert[0] * nvy + j;
15628 qv[1] = vert[1] * nvy + j;
15629 qv[2] = vert[1] * nvy + (j + 1) % nvy;
15630 qv[3] = vert[0] * nvy + (j + 1) % nvy;
15631
15632 mesh2d->AddQuad(qv, attr);
15633 }
15634 }
15635 // 2D boundary from the 1D boundary
15636 for (int i = 0; i < mesh->GetNBE(); i++)
15637 {
15638 const Element *elem = mesh->GetBdrElement(i);
15639 elem->GetVertices(vert);
15640 const int attr = elem->GetAttribute();
15641 for (int j = 0; j < ny; j++)
15642 {
15643 int sv[2];
15644 sv[0] = vert[0] * nvy + j;
15645 sv[1] = vert[0] * nvy + (j + 1) % nvy;
15646
15647 if (attr%2)
15648 {
15649 Swap<int>(sv[0], sv[1]);
15650 }
15651
15652 mesh2d->AddBdrSegment(sv, attr);
15653 }
15654 }
15655
15656 if (!closed)
15657 {
15658 // 2D boundary from the 1D elements (bottom + top)
15659 int nba = (mesh->bdr_attributes.Size() > 0 ?
15660 mesh->bdr_attributes.Max() : 0);
15661 for (int i = 0; i < mesh->GetNE(); i++)
15662 {
15663 const Element *elem = mesh->GetElement(i);
15664 elem->GetVertices(vert);
15665 const int attr = nba + elem->GetAttribute();
15666 int sv[2];
15667 sv[0] = vert[0] * nvy;
15668 sv[1] = vert[1] * nvy;
15669
15670 mesh2d->AddBdrSegment(sv, attr);
15671
15672 sv[0] = vert[1] * nvy + ny;
15673 sv[1] = vert[0] * nvy + ny;
15674
15675 mesh2d->AddBdrSegment(sv, attr);
15676 }
15677 }
15678
15679 mesh2d->FinalizeQuadMesh(1, 0, false);
15680
15681 GridFunction *nodes = mesh->GetNodes();
15682 if (nodes)
15683 {
15684 // duplicate the fec of the 1D mesh so that it can be deleted safely
15685 // along with its nodes, fes and fec
15686 FiniteElementCollection *fec2d = NULL;
15687 FiniteElementSpace *fes2d;
15688 const char *name = nodes->FESpace()->FEColl()->Name();
15689 string cname = name;
15690 if (cname == "Linear")
15691 {
15692 fec2d = new LinearFECollection;
15693 }
15694 else if (cname == "Quadratic")
15695 {
15696 fec2d = new QuadraticFECollection;
15697 }
15698 else if (cname == "Cubic")
15699 {
15700 fec2d = new CubicFECollection;
15701 }
15702 else if (!strncmp(name, "H1_", 3))
15703 {
15704 fec2d = new H1_FECollection(atoi(name + 7), 2);
15705 }
15706 else if (!strncmp(name, "L2_T", 4))
15707 {
15708 fec2d = new L2_FECollection(atoi(name + 10), 2, atoi(name + 4));
15709 }
15710 else if (!strncmp(name, "L2_", 3))
15711 {
15712 fec2d = new L2_FECollection(atoi(name + 7), 2);
15713 }
15714 else
15715 {
15716 delete mesh2d;
15717 mfem::err << "Extrude1D : The mesh uses unknown FE collection : "
15718 << cname << endl;
15719 mfem_error();
15720 }
15721 fes2d = new FiniteElementSpace(mesh2d, fec2d, 2);
15722 mesh2d->SetNodalFESpace(fes2d);
15723 GridFunction *nodes2d = mesh2d->GetNodes();
15724 nodes2d->MakeOwner(fec2d);
15725
15726 NodeExtrudeCoefficient ecoeff(2, ny, sy);
15727 Vector lnodes;
15728 Array<int> vdofs2d;
15729 for (int i = 0; i < mesh->GetNE(); i++)
15730 {
15732 for (int j = ny-1; j >= 0; j--)
15733 {
15734 fes2d->GetElementVDofs(i*ny+j, vdofs2d);
15735 lnodes.SetSize(vdofs2d.Size());
15736 ecoeff.SetLayer(j);
15737 fes2d->GetFE(i*ny+j)->Project(ecoeff, T, lnodes);
15738 nodes2d->SetSubVector(vdofs2d, lnodes);
15739 }
15740 }
15741 }
15742 return mesh2d;
15743}
15744
15745Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz)
15746{
15747 if (mesh->Dimension() != 2)
15748 {
15749 mfem::err << "Extrude2D : Not a 2D mesh!" << endl;
15750 mfem_error();
15751 }
15752
15753 int nvz = nz + 1;
15754 int nvt = mesh->GetNV() * nvz;
15755
15756 Mesh *mesh3d = new Mesh(3, nvt, mesh->GetNE()*nz,
15757 mesh->GetNBE()*nz+2*mesh->GetNE());
15758
15759 bool wdgMesh = false;
15760 bool hexMesh = false;
15761
15762 // vertices
15763 real_t vc[3];
15764 for (int i = 0; i < mesh->GetNV(); i++)
15765 {
15766 vc[0] = mesh->GetVertex(i)[0];
15767 vc[1] = mesh->GetVertex(i)[1];
15768 for (int j = 0; j < nvz; j++)
15769 {
15770 vc[2] = sz * (real_t(j) / nz);
15771 mesh3d->AddVertex(vc);
15772 }
15773 }
15774 // elements
15775 Array<int> vert;
15776 for (int i = 0; i < mesh->GetNE(); i++)
15777 {
15778 const Element *elem = mesh->GetElement(i);
15779 elem->GetVertices(vert);
15780 const int attr = elem->GetAttribute();
15781 Geometry::Type geom = elem->GetGeometryType();
15782 switch (geom)
15783 {
15784 case Geometry::TRIANGLE:
15785 wdgMesh = true;
15786 for (int j = 0; j < nz; j++)
15787 {
15788 int pv[6];
15789 pv[0] = vert[0] * nvz + j;
15790 pv[1] = vert[1] * nvz + j;
15791 pv[2] = vert[2] * nvz + j;
15792 pv[3] = vert[0] * nvz + (j + 1) % nvz;
15793 pv[4] = vert[1] * nvz + (j + 1) % nvz;
15794 pv[5] = vert[2] * nvz + (j + 1) % nvz;
15795
15796 mesh3d->AddWedge(pv, attr);
15797 }
15798 break;
15799 case Geometry::SQUARE:
15800 hexMesh = true;
15801 for (int j = 0; j < nz; j++)
15802 {
15803 int hv[8];
15804 hv[0] = vert[0] * nvz + j;
15805 hv[1] = vert[1] * nvz + j;
15806 hv[2] = vert[2] * nvz + j;
15807 hv[3] = vert[3] * nvz + j;
15808 hv[4] = vert[0] * nvz + (j + 1) % nvz;
15809 hv[5] = vert[1] * nvz + (j + 1) % nvz;
15810 hv[6] = vert[2] * nvz + (j + 1) % nvz;
15811 hv[7] = vert[3] * nvz + (j + 1) % nvz;
15812
15813 mesh3d->AddHex(hv, attr);
15814 }
15815 break;
15816 default:
15817 mfem::err << "Extrude2D : Invalid 2D element type \'"
15818 << geom << "\'" << endl;
15819 mfem_error();
15820 break;
15821 }
15822 }
15823 // 3D boundary from the 2D boundary
15824 for (int i = 0; i < mesh->GetNBE(); i++)
15825 {
15826 const Element *elem = mesh->GetBdrElement(i);
15827 elem->GetVertices(vert);
15828 const int attr = elem->GetAttribute();
15829 for (int j = 0; j < nz; j++)
15830 {
15831 int qv[4];
15832 qv[0] = vert[0] * nvz + j;
15833 qv[1] = vert[1] * nvz + j;
15834 qv[2] = vert[1] * nvz + (j + 1) % nvz;
15835 qv[3] = vert[0] * nvz + (j + 1) % nvz;
15836
15837 mesh3d->AddBdrQuad(qv, attr);
15838 }
15839 }
15840
15841 // 3D boundary from the 2D elements (bottom + top)
15842 int nba = (mesh->bdr_attributes.Size() > 0 ?
15843 mesh->bdr_attributes.Max() : 0);
15844 for (int i = 0; i < mesh->GetNE(); i++)
15845 {
15846 const Element *elem = mesh->GetElement(i);
15847 elem->GetVertices(vert);
15848 const int attr = nba + elem->GetAttribute();
15849 Geometry::Type geom = elem->GetGeometryType();
15850 switch (geom)
15851 {
15852 case Geometry::TRIANGLE:
15853 {
15854 int tv[3];
15855 tv[0] = vert[0] * nvz;
15856 tv[1] = vert[2] * nvz;
15857 tv[2] = vert[1] * nvz;
15858
15859 mesh3d->AddBdrTriangle(tv, attr);
15860
15861 tv[0] = vert[0] * nvz + nz;
15862 tv[1] = vert[1] * nvz + nz;
15863 tv[2] = vert[2] * nvz + nz;
15864
15865 mesh3d->AddBdrTriangle(tv, attr);
15866 }
15867 break;
15868 case Geometry::SQUARE:
15869 {
15870 int qv[4];
15871 qv[0] = vert[0] * nvz;
15872 qv[1] = vert[3] * nvz;
15873 qv[2] = vert[2] * nvz;
15874 qv[3] = vert[1] * nvz;
15875
15876 mesh3d->AddBdrQuad(qv, attr);
15877
15878 qv[0] = vert[0] * nvz + nz;
15879 qv[1] = vert[1] * nvz + nz;
15880 qv[2] = vert[2] * nvz + nz;
15881 qv[3] = vert[3] * nvz + nz;
15882
15883 mesh3d->AddBdrQuad(qv, attr);
15884 }
15885 break;
15886 default:
15887 mfem::err << "Extrude2D : Invalid 2D element type \'"
15888 << geom << "\'" << endl;
15889 mfem_error();
15890 break;
15891 }
15892 }
15893
15894 if ( hexMesh && wdgMesh )
15895 {
15896 mesh3d->FinalizeMesh(0, false);
15897 }
15898 else if ( hexMesh )
15899 {
15900 mesh3d->FinalizeHexMesh(1, 0, false);
15901 }
15902 else if ( wdgMesh )
15903 {
15904 mesh3d->FinalizeWedgeMesh(1, 0, false);
15905 }
15906
15907 GridFunction *nodes = mesh->GetNodes();
15908 if (nodes)
15909 {
15910 // duplicate the fec of the 2D mesh so that it can be deleted safely
15911 // along with its nodes, fes and fec
15912 FiniteElementCollection *fec3d = NULL;
15913 FiniteElementSpace *fes3d;
15914 const char *name = nodes->FESpace()->FEColl()->Name();
15915 string cname = name;
15916 if (cname == "Linear")
15917 {
15918 fec3d = new LinearFECollection;
15919 }
15920 else if (cname == "Quadratic")
15921 {
15922 fec3d = new QuadraticFECollection;
15923 }
15924 else if (cname == "Cubic")
15925 {
15926 fec3d = new CubicFECollection;
15927 }
15928 else if (!strncmp(name, "H1_", 3))
15929 {
15930 fec3d = new H1_FECollection(atoi(name + 7), 3);
15931 }
15932 else if (!strncmp(name, "L2_T", 4))
15933 {
15934 fec3d = new L2_FECollection(atoi(name + 10), 3, atoi(name + 4));
15935 }
15936 else if (!strncmp(name, "L2_", 3))
15937 {
15938 fec3d = new L2_FECollection(atoi(name + 7), 3);
15939 }
15940 else
15941 {
15942 delete mesh3d;
15943 mfem::err << "Extrude3D : The mesh uses unknown FE collection : "
15944 << cname << endl;
15945 mfem_error();
15946 }
15947 fes3d = new FiniteElementSpace(mesh3d, fec3d, 3);
15948 mesh3d->SetNodalFESpace(fes3d);
15949 GridFunction *nodes3d = mesh3d->GetNodes();
15950 nodes3d->MakeOwner(fec3d);
15951
15952 NodeExtrudeCoefficient ecoeff(3, nz, sz);
15953 Vector lnodes;
15954 Array<int> vdofs3d;
15955 for (int i = 0; i < mesh->GetNE(); i++)
15956 {
15958 for (int j = nz-1; j >= 0; j--)
15959 {
15960 fes3d->GetElementVDofs(i*nz+j, vdofs3d);
15961 lnodes.SetSize(vdofs3d.Size());
15962 ecoeff.SetLayer(j);
15963 fes3d->GetFE(i*nz+j)->Project(ecoeff, T, lnodes);
15964 nodes3d->SetSubVector(vdofs3d, lnodes);
15965 }
15966 }
15967 }
15968 return mesh3d;
15969}
15970
15971Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
15972 int &par_ref, Array<int> &partitioning)
15973{
15974 MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
15975
15976 // Closest int divisor to the cubit root, going down.
15977 auto factor3 = [](int N)
15978 {
15979 for (int i = static_cast<int>(round(cbrt(N))); i > 0; i--)
15980 { if (N % i == 0) { return i; } }
15981 return 1;
15982 };
15983
15984 // Closest int divisor to the square root, going down.
15985 auto factor2 = [](int N)
15986 {
15987 for (int i = static_cast<int>(round(sqrt(N))); i > 0; i--)
15988 { if (N % i == 0) { return i; } }
15989 return 1;
15990 };
15991
15992 par_ref = 0;
15993 const int ref_factor = (dim == 2) ? 4 : 8;
15994
15995 // Elements per task before performing parallel refinements.
15996 // This will be used to form the serial mesh.
15997 int el0 = elem_per_mpi;
15998 while (el0 % ref_factor == 0)
15999 {
16000 el0 /= ref_factor;
16001 par_ref++;
16002 }
16003
16004 // In the serial mesh we have:
16005 // The number of MPI blocks is mpi_cnt = mp_x.mpy_y.mpy_z.
16006 // The size of each MPI block is el0 = el0_x.el0_y.el0_z.
16007 int mpi_x, mpi_y, mpi_z;
16008 int el0_x, el0_y, el0_z;
16009 if (dim == 2)
16010 {
16011 mpi_x = factor2(mpi_cnt);
16012 mpi_y = mpi_cnt / mpi_x;
16013
16014 // Switch order for better balance.
16015 el0_y = factor2(el0);
16016 el0_x = el0 / el0_y;
16017 }
16018 else
16019 {
16020 mpi_x = factor3(mpi_cnt);
16021 mpi_y = factor2(mpi_cnt / mpi_x);
16022 mpi_z = mpi_cnt / mpi_x / mpi_y;
16023
16024 // Switch order for better balance.
16025 el0_z = factor3(el0);
16026 el0_y = factor2(el0 / el0_z);
16027 el0_x = el0 / el0_y / el0_z;
16028 }
16029
16030 if (print && dim == 2)
16031 {
16032 int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
16033 elem_par_y = mpi_y * el0_y * pow(2, par_ref);
16034
16035 mfem::out << "--- Mesh generation: \n";
16036 mfem::out << "Par mesh: " << elem_par_x << " x " << elem_par_y
16037 << " (" << elem_par_x * elem_par_y << " elements)\n"
16038 << "Elem / task: "
16039 << el0_x * pow(2, par_ref) << " x "
16040 << el0_y * pow(2, par_ref)
16041 << " (" << el0_x * pow(2, 2*par_ref) * el0_y << " elements)\n"
16042 << "MPI blocks: " << mpi_x << " x " << mpi_y
16043 << " (" << mpi_x * mpi_y << " mpi tasks)\n" << "-\n"
16044 << "Serial mesh: "
16045 << mpi_x * el0_x << " x " << mpi_y * el0_y
16046 << " (" << mpi_x * el0_x * mpi_y * el0_y << " elements)\n"
16047 << "Elem / task: " << el0_x << " x " << el0_y << std::endl
16048 << "Par refine: " << par_ref << std::endl;
16049 mfem::out << "--- \n";
16050 }
16051
16052 if (print && dim == 3)
16053 {
16054 int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
16055 elem_par_y = mpi_y * el0_y * pow(2, par_ref),
16056 elem_par_z = mpi_z * el0_z * pow(2, par_ref);
16057
16058 mfem::out << "--- Mesh generation: \n";
16059 mfem::out << "Par mesh: "
16060 << elem_par_x << " x " << elem_par_y << " x " << elem_par_z
16061 << " (" << elem_par_x*elem_par_y*elem_par_z << " elements)\n"
16062 << "Elem / task: "
16063 << el0_x * pow(2, par_ref) << " x "
16064 << el0_y * pow(2, par_ref) << " x "
16065 << el0_z * pow(2, par_ref)
16066 << " (" << el0_x*pow(2, 3*par_ref)*el0_y*el0_z << " elements)\n"
16067 << "MPI blocks: " << mpi_x << " x " << mpi_y << " x " << mpi_z
16068 << " (" << mpi_x * mpi_y * mpi_z << " mpi tasks)\n" << "-\n"
16069 << "Serial mesh: "
16070 << mpi_x*el0_x << " x " << mpi_y*el0_y << " x " << mpi_z*el0_z
16071 << " (" << mpi_x*el0_x*mpi_y*el0_y*mpi_z*el0_z << " elements)\n"
16072 << "Elem / task: "
16073 << el0_x << " x " << el0_y << " x " << el0_z << std::endl
16074 << "Par refine: " << par_ref << std::endl;
16075 mfem::out << "--- \n";
16076 }
16077
16078 Mesh mesh;
16079 int nxyz[3];
16080 if (dim == 2)
16081 {
16082 mesh = Mesh::MakeCartesian2D(mpi_x * el0_x,
16083 mpi_y * el0_y, Element::QUADRILATERAL, true);
16084 nxyz[0] = mpi_x; nxyz[1] = mpi_y;
16085 }
16086 else
16087 {
16088 mesh = Mesh::MakeCartesian3D(mpi_x * el0_x,
16089 mpi_y * el0_y,
16090 mpi_z * el0_z, Element::HEXAHEDRON, true);
16091 nxyz[0] = mpi_x; nxyz[1] = mpi_y; nxyz[2] = mpi_z;
16092 }
16093
16094 const int NE = mesh.GetNE();
16095 partitioning.SetSize(NE);
16096 std::unique_ptr<int[]> p_raw(mesh.CartesianPartitioning(nxyz));
16097 std::copy(p_raw.get(), p_raw.get() + NE, partitioning.GetData());
16098
16099 return mesh;
16100}
16101
16103{
16104 if (NURBSext)
16105 {
16106 // NURBS meshes are always conforming (element-wise). NURBS patch
16107 // conformity is indicated by NURBSExtension::NonconformingPatches.
16108 return true;
16109 }
16110 else
16111 {
16112 return ncmesh == NULL;
16113 }
16114}
16115
16116#ifdef MFEM_DEBUG
16117void Mesh::DebugDump(std::ostream &os) const
16118{
16119 // dump vertices and edges (NCMesh "nodes")
16120 os << NumOfVertices + NumOfEdges << "\n";
16121 for (int i = 0; i < NumOfVertices; i++)
16122 {
16123 const real_t *v = GetVertex(i);
16124 os << i << " " << v[0] << " " << v[1] << " " << v[2]
16125 << " 0 0 " << i << " -1 0\n";
16126 }
16127
16128 Array<int> ev;
16129 for (int i = 0; i < NumOfEdges; i++)
16130 {
16131 GetEdgeVertices(i, ev);
16132 real_t mid[3] = {0, 0, 0};
16133 for (int j = 0; j < 2; j++)
16134 {
16135 for (int k = 0; k < spaceDim; k++)
16136 {
16137 mid[k] += GetVertex(ev[j])[k];
16138 }
16139 }
16140 os << NumOfVertices+i << " "
16141 << mid[0]/2 << " " << mid[1]/2 << " " << mid[2]/2 << " "
16142 << ev[0] << " " << ev[1] << " -1 " << i << " 0\n";
16143 }
16144
16145 // dump elements
16146 os << NumOfElements << "\n";
16147 for (int i = 0; i < NumOfElements; i++)
16148 {
16149 const Element* e = elements[i];
16150 os << e->GetNVertices() << " ";
16151 for (int j = 0; j < e->GetNVertices(); j++)
16152 {
16153 os << e->GetVertices()[j] << " ";
16154 }
16155 os << e->GetAttribute() << " 0 " << i << "\n";
16156 }
16157
16158 // dump faces
16159 os << "0\n";
16160}
16161#endif
16162
16163}
Float cost() const
Definition gecko.cpp:857
void order(Functional *functional, uint iterations=1, uint window=2, uint period=2, uint seed=0, Progress *progress=0)
Definition gecko.cpp:1232
Node::Index insert_node(Float length=1)
Definition gecko.cpp:657
Arc::Index insert_arc(Node::Index i, Node::Index j, Float w=1, Float b=1)
Definition gecko.cpp:679
uint rank(Node::Index i) const
Definition gecko.hpp:672
uint Index
Definition gecko.hpp:595
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
Definition array.hpp:1010
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:341
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
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.cpp:86
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void PartialSum()
Fill the entries of the array with the cumulative sum of the entries.
Definition array.cpp:104
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 Find(const T &el) const
Return the first index where 'el' is found; return -1 if not found.
Definition array.hpp:1000
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 Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:349
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:430
T * end()
STL-like end. Returns pointer after the last element of the array.
Definition array.hpp:398
T * begin()
STL-like begin. Returns pointer to the first element of the array.
Definition array.hpp:395
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:422
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.
bool AttributeSetExists(const std::string &name) const
Return true if the named attribute set is present.
ArraysByName< int > attr_sets
Named sets of attributes.
Array< int > GetAttributeSetMarker(const std::string &set_name) const
Return a marker array corresponding to a named attribute set.
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.
static int GetQuadrature1D(int b_type)
Get the corresponding Quadrature1D constant, when that makes sense; otherwise return Quadrature1D::In...
Definition fe_base.hpp:65
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
Piecewise-(bi)cubic continuous finite elements.
Definition fe_coll.hpp:991
int NumberOfEntries() const
Definition table.hpp:266
int NumberOfRows() const
Definition table.hpp:265
int Push(int a, int b)
Definition table.hpp:267
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void MultTranspose(const real_t *x, real_t *y) const
Multiply a vector with the transpose matrix.
Definition densemat.cpp:158
const real_t * HostRead() const
Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
Definition densemat.hpp:509
void GetColumnReference(int c, Vector &col)
Definition densemat.hpp:340
real_t * Data() const
Returns the matrix data array. Warning: this method casts away constness.
Definition densemat.hpp:131
real_t * GetData() const
Returns the matrix data array. Warning: this method casts away constness.
Definition densemat.hpp:135
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 Trace() const
Trace of a square matrix.
Definition densemat.cpp:409
real_t CalcSingularvalue(const int i) const
Return the i-th singular value (decreasing order) of NxN matrix, N=1,2,3.
void Print(std::ostream &out=mfem::out, int width_=4) const override
Prints matrix to stream out.
void GetColumn(int c, Vector &col) const
real_t FNorm2() const
Compute the square of the Frobenius norm of the matrix.
Definition densemat.hpp:297
real_t Det() const
Definition densemat.cpp:496
Rank 3 tensor (array of matrices)
void SetSize(int i, int j, int k, MemoryType mt_=MemoryType::PRESERVE)
void UseExternalData(real_t *ext_data, int i, int j, int k)
int SizeK() const
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Definition device.hpp:129
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition device.hpp:298
void InvTransformPrimal(real_t *v) const
Definition doftrans.cpp:47
void TransformPrimal(real_t *v) const
Definition doftrans.cpp:17
const Mesh * mesh
The Mesh object containing the element.
Definition eltrans.hpp:97
Geometry::Type GetGeometryType() const
Return the Geometry::Type of the reference element.
Definition eltrans.hpp:175
real_t Weight()
Return the weight of the Jacobian matrix of the transformation at the currently set IntegrationPoint....
Definition eltrans.hpp:144
virtual int OrderJ() const =0
Return the order of the elements of the Jacobian of the transformation.
const DenseMatrix & Jacobian()
Return the Jacobian matrix of the transformation at the currently set IntegrationPoint,...
Definition eltrans.hpp:132
void SetIntPoint(const IntegrationPoint *ip)
Set the integration point ip that weights and Jacobians will be evaluated at.
Definition eltrans.hpp:106
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
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
static Type TypeFromGeometry(const Geometry::Type geom)
Return the Element::Type associated with the given Geometry::Type.
Definition element.cpp:17
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
Structure for storing face geometric factors: coordinates, Jacobians, determinants of the Jacobians,...
Definition mesh.hpp:3173
Vector normal
Normals at all quadrature points.
Definition mesh.hpp:3219
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3206
const IntegrationRule * IntRule
Definition mesh.hpp:3176
Vector X
Mapped (physical) coordinates of all quadrature points.
Definition mesh.hpp:3197
FaceGeometricFactors(const Mesh *mesh, const IntegrationRule &ir, int flags, FaceType type, MemoryType d_mt=MemoryType::DEFAULT)
Definition mesh.cpp:15500
Vector detJ
Determinants of the Jacobians at all quadrature points.
Definition mesh.hpp:3212
A class that performs interpolation from a face E-vector to quadrature point values and/or derivative...
@ DERIVATIVES
Evaluate the derivatives at quadrature points.
@ DETERMINANTS
Assuming the derivative at quadrature points form a matrix, this flag can be used to compute and stor...
@ VALUES
Evaluate the values at quadrature points.
void DisableTensorProducts(bool disable=true) const
Disable the use of tensor product evaluations, for tensor-product elements, e.g. quads and hexes.
void SetOutputLayout(QVectorLayout layout) const
Set the desired output Q-vector layout. The default value is QVectorLayout::byNODES.
void Mult(const Vector &e_vec, unsigned eval_flags, Vector &q_val, Vector &q_der, Vector &q_det, Vector &q_nor) const
Interpolate the E-vector e_vec to quadrature points.
Base class for operators that extracts Face degrees of freedom.
void Mult(const Vector &x, Vector &y) const override=0
Extract the face degrees of freedom from x into y.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
virtual const int * DofOrderForOrientation(Geometry::Type GeomType, int Or) const =0
Returns an array, say p, that maps a local permuted index i to a local base index: base_i = p[i].
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 DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:232
void GetEdgeInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified edge.
Definition fespace.cpp:3841
const FiniteElement * GetBE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th boundary fac...
Definition fespace.cpp:3906
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
Definition fespace.cpp:3538
void GetEdgeVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
Definition fespace.cpp:332
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3786
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition fespace.hpp:821
virtual void UpdateMeshPointer(Mesh *new_mesh)
Definition fespace.cpp:4363
const QuadratureInterpolator * GetQuadratureInterpolator(const IntegrationRule &ir) const
Return a QuadratureInterpolator that interpolates E-vectors to quadrature point values and/or derivat...
Definition fespace.cpp:1588
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 GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1476
const FiniteElement * GetEdgeElement(int i, int variant=0) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th edge in the ...
Definition fespace.cpp:3984
void GetFaceVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
Definition fespace.cpp:326
virtual void Update(bool want_transform=true)
Reflect changes in the mesh: update number of DOFs, etc. Also, calculate GridFunction transformation ...
Definition fespace.cpp:4192
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
void SetRelaxedHpConformity(bool relaxed=true)
Definition fespace.hpp:1613
int GetNFDofs() const
Number of all scalar face-interior dofs.
Definition fespace.hpp:861
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Definition fespace.cpp:195
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3949
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
Definition fespace.cpp:170
int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type.
Definition fespace.hpp:884
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'. The returned indices are offsets in...
Definition fespace.cpp:3643
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
const FaceQuadratureInterpolator * GetFaceQuadratureInterpolator(const IntegrationRule &ir, FaceType type) const
Return a FaceQuadratureInterpolator that interpolates E-vectors to quadrature point values and/or der...
Definition fespace.cpp:1627
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1587
void GetFaceInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified face.
Definition fespace.cpp:3817
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:669
void GetElementInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified element.
Definition fespace.cpp:3796
DofTransformation * GetBdrElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for i'th boundary element. The returned indices are offsets int...
Definition fespace.cpp:314
int DofToVDof(int dof, int vd, int ndofs=-1) const
Compute a single vdof corresponding to the index dof and the vector index vd.
Definition fespace.cpp:268
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType, L2FaceValues mul=L2FaceValues::DoubleValued) const
Return an Operator that converts L-vectors to E-vectors on each face.
Definition fespace.cpp:1509
Abstract class for all finite elements.
Definition fe_base.hpp:294
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:381
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:476
virtual void CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const =0
Evaluate the gradients of all shape functions of a scalar finite element in reference space at the gi...
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
Definition fe_base.hpp:407
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
Structure for storing mesh geometric factors: coordinates, Jacobians, and determinants of the Jacobia...
Definition mesh.hpp:3119
Vector X
Mapped (physical) coordinates of all quadrature points.
Definition mesh.hpp:3149
const Mesh * mesh
Definition mesh.hpp:3125
const IntegrationRule * IntRule
Definition mesh.hpp:3126
Vector detJ
Determinants of the Jacobians at all quadrature points.
Definition mesh.hpp:3164
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3158
GeometricFactors(const Mesh *mesh, const IntegrationRule &ir, int flags, MemoryType d_mt=MemoryType::DEFAULT)
Definition mesh.cpp:15420
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
Definition geom.cpp:1136
static const int NumGeom
Definition geom.hpp:46
static const int Dimension[NumGeom]
Definition geom.hpp:51
const IntegrationPoint & GetCenter(int GeomType) const
Return the center of the given Geometry::Type, GeomType.
Definition geom.hpp:75
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
void JacToPerfJac(int GeomType, const DenseMatrix &J, DenseMatrix &PJ) const
Definition geom.cpp:909
static int GetInverseOrientation(Type geom_type, int orientation)
Return the inverse of the given orientation for the specified geometry type.
Definition geom.cpp:264
static const int DimStart[MaxDim+2]
Definition geom.hpp:52
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
virtual void Update()
Transform by the Space UpdateMatrix (e.g., on Mesh change).
Definition gridfunc.cpp:169
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
FiniteElementSpace * FESpace()
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
Definition gridfunc.hpp:166
virtual void ProjectCoefficient(Coefficient &coeff, ProjectType type=ProjectType::DEFAULT)
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
void GetNodalValues(int i, Array< real_t > &nval, int vdim=1) const
Returns the values at the vertices of element i for the 1-based dimension vdim.
Definition gridfunc.cpp:377
void GetVectorValues(int i, const IntegrationRule &ir, DenseMatrix &vals, DenseMatrix &tr) const
Definition gridfunc.cpp:687
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
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
Data type hexahedron element.
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 integration point with weight.
Definition intrules.hpp:35
void Get(real_t *p, const int dim) const
Definition intrules.hpp:82
void Set3(const real_t x1, const real_t x2, const real_t x3)
Definition intrules.hpp:57
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
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:258
const IntegrationRule & Get(int GeomType, int Order)
Returns an integration rule for given GeomType and Order.
The inverse transformation of a given ElementTransformation.
Definition eltrans.hpp:200
virtual int Transform(const Vector &pt, IntegrationPoint &ip)
Given a point, pt, in physical space, find its reference coordinates, ip.
Definition eltrans.cpp:338
@ Inside
The point is inside the element.
Definition eltrans.hpp:244
void SetTransformation(ElementTransformation &Trans)
Set a new forward ElementTransformation, Trans.
Definition eltrans.hpp:320
A standard isoparametric element transformation.
Definition eltrans.hpp:629
void SetPointMat(const DenseMatrix &pm)
Set the underlying point matrix describing the transformation.
Definition eltrans.hpp:668
void SetFE(const FiniteElement *FE)
Set the element that will be used to compute the transformations.
Definition eltrans.hpp:648
void SetIdentityTransformation(Geometry::Type GeomType)
Set the FiniteElement Geometry for the reference elements being used.
Definition eltrans.cpp:417
const DenseMatrix & GetPointMat() const
Return the stored point matrix.
Definition eltrans.hpp:671
void Transform(const IntegrationPoint &, Vector &) override
Transform integration point from reference coordinates to physical coordinates and store them in the ...
Definition eltrans.cpp:532
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
int GetBasisType() const
Definition fe_coll.hpp:414
Piecewise-(bi/tri)linear continuous finite elements.
Definition fe_coll.hpp:911
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
void AsTable(Table &t) const
Write the list of sets into table 't'.
Definition sets.cpp:81
int Size() const
Return the number of integer sets in the list.
Definition sets.hpp:58
Class containing a minimal description of a part (a subset of the elements) of a Mesh and its connect...
Definition mesh.hpp:2808
Array< real_t > vertex_coordinates
Definition mesh.hpp:2908
int dimension
Reference space dimension of the elements.
Definition mesh.hpp:2825
int num_vertices
Number of vertices.
Definition mesh.hpp:2831
Table group_shared_entity_to_vertex[Geometry::NumGeom]
Definition mesh.hpp:2971
Array< int > entity_to_vertex[Geometry::NumGeom]
Definition mesh.hpp:2860
std::unique_ptr< Mesh > mesh
Definition mesh.hpp:2915
Array< int > boundary_map
Optional re-ordering for the boundary elements, similar to 'element_map'.
Definition mesh.hpp:2884
std::unique_ptr< FiniteElementSpace > nodal_fes
Definition mesh.hpp:2921
int num_parts
Total number of MeshParts.
Definition mesh.hpp:2935
Array< int > tet_refine_flags
Store the refinement flags for tetraheral elements. If all tets have zero refinement flags then this ...
Definition mesh.hpp:2864
int space_dimension
Dimension of the physical space into which the MeshPart is embedded.
Definition mesh.hpp:2828
int num_bdr_elements
Number of boundary elements with reference space dimension equal to 'dimension'-1.
Definition mesh.hpp:2838
int num_elements
Number of elements with reference space dimension equal to 'dimension'.
Definition mesh.hpp:2834
Mesh & GetMesh()
Construct a serial Mesh object from the MeshPart.
Definition mesh.cpp:14753
int my_part_id
Index of the part described by this MeshPart: 0 <= 'my_part_id' < 'num_parts'.
Definition mesh.hpp:2939
Array< int > element_map
Definition mesh.hpp:2881
Array< int > attributes
Definition mesh.hpp:2890
Table my_groups
Definition mesh.hpp:2952
std::unique_ptr< GridFunction > nodes
Definition mesh.hpp:2929
void Print(std::ostream &os) const
Write the MeshPart to a stream using the parallel format "MFEM mesh v1.2".
Definition mesh.cpp:14573
Array< int > bdr_attributes
Definition mesh.hpp:2897
Array< int > partitioning
Definition mesh.hpp:3039
std::unique_ptr< FiniteElementSpace > ExtractFESpace(MeshPart &mesh_part, const FiniteElementSpace &global_fespace) const
Construct a local version of the given FiniteElementSpace global_fespace corresponding to the given m...
Definition mesh.cpp:15378
MeshPartitioner(Mesh &mesh_, int num_parts_, const int *partitioning_=nullptr, int part_method=1)
Construct a MeshPartitioner.
Definition mesh.cpp:14836
std::unique_ptr< GridFunction > ExtractGridFunction(const MeshPart &mesh_part, const GridFunction &global_gf, FiniteElementSpace &local_fespace) const
Construct a local version of the given GridFunction, global_gf, corresponding to the given mesh_part....
Definition mesh.cpp:15393
void ExtractPart(int part_id, MeshPart &mesh_part) const
Construct a MeshPart corresponding to the given part_id.
Definition mesh.cpp:14908
List of mesh geometries stored as Array<Geometry::Type>.
Definition mesh.hpp:1603
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 GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:8109
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 NURBSCoarsening(int cf=2, real_t tol=1.0e-12)
Definition mesh.cpp:11657
void SetVerticesFromNodes(const GridFunction *nodes)
Helper to set vertex coordinates given a high-order curvature function.
Definition mesh.cpp:7246
int GetPatchBdrAttribute(int i) const
Return the attribute of patch boundary element i, for a NURBS mesh.
Definition mesh.cpp:3529
int GetElementToEdgeTable(Table &)
Definition mesh.cpp:8551
int meshgen
Definition mesh.hpp:93
void LoadNonconformingPatchTopo(std::istream &input, Array< int > &edge_to_ukv)
Read NURBS patch/macro-element mesh (MFEM NURBS NC-patch mesh format)
Definition mesh.cpp:7070
void GetGeometries(int dim, Array< Geometry::Type > &el_geoms) const
Return all element geometries of the given dimension present in the mesh.
Definition mesh.cpp:8025
void SetVertices(const Vector &vert_coord)
Definition mesh.cpp:10047
Element * NewElement(int geom)
Definition mesh.cpp:4978
Operation GetLastOperation() const
Return type of last modification of the mesh.
Definition mesh.hpp:2553
IsoparametricTransformation Transformation2
Definition mesh.hpp:260
Table * GetEdgeFaceTable() const
Definition mesh.cpp:8176
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1396
void MarkForRefinement()
Definition mesh.cpp:3109
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:8369
void InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
Begin construction of a mesh.
Definition mesh.cpp:2056
Table * GetVertexToBdrElementTable()
Definition mesh.cpp:8251
static void PrintElement(const Element *el, std::ostream &os)
Definition mesh.cpp:5044
Array< FaceInfo > faces_info
Definition mesh.hpp:242
int EulerNumber() const
Equals 1 + num_holes - num_loops.
Definition mesh.hpp:1320
CoarseFineTransformations CoarseFineTr
Definition mesh.hpp:267
void GetElementJacobian(int i, DenseMatrix &J, const IntegrationPoint *ip=NULL)
Definition mesh.cpp:66
int AddSegment(int v1, int v2, int attr=1)
Adds a segment to the mesh given by 2 vertices v1 and v2.
Definition mesh.cpp:2136
int AddBdrElement(Element *elem)
Definition mesh.cpp:2449
void GetElementColoring(Array< int > &colors, int el0=0)
Definition mesh.cpp:13282
virtual FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31)
Definition mesh.cpp:1179
void FinalizeMesh(int refine=0, bool fix_orientation=true)
Finalize the construction of any type of Mesh.
Definition mesh.cpp:3654
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:309
static void PrintElementWithoutAttr(const Element *el, std::ostream &os)
Definition mesh.cpp:5020
MemAlloc< Tetrahedron, 1024 > TetMemory
Definition mesh.hpp:282
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
void ReadTrueGridMesh(std::istream &input)
static const int vtk_quadratic_tet[10]
Definition mesh.hpp:275
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
Definition mesh.cpp:1638
friend class ParNCMesh
Definition mesh.hpp:74
virtual void GetExteriorFaceMarker(Array< int > &face_marker) const
Populate a marker array identifying exterior faces.
Definition mesh.cpp:1733
IsoparametricTransformation EdgeTransformation
Definition mesh.hpp:262
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Return FiniteElement for reference element of the specified type.
Definition mesh.cpp:340
int AddBdrQuad(int v1, int v2, int v3, int v4, int attr=1)
Definition mesh.cpp:2496
int * CartesianPartitioning(int nxyz[])
Definition mesh.cpp:9188
Array< int > FindFaceNeighbors(const int elem) const
Returns the sorted, unique indices of elements sharing a face with element elem, including elem.
Definition mesh.cpp:8341
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
void GetLocalSegToQuadTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:794
virtual long long ReduceInt(int value) const
Utility function: sum integers from all processors (Allreduce).
Definition mesh.hpp:2771
const Array< int > & GetFaceIndices(FaceType ftype) const
Map from boundary or interior face indices to mesh face indices.
Definition mesh.cpp:1078
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
std::unordered_map< int, int > inv_face_indices[2]
cache for FaceIndices(ftype)
Definition mesh.hpp:290
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
virtual void UnmarkNamedBoundaries(const std::string &set_name, Array< int > &bdr_marker) const
Unmark boundary attributes in the named set.
Definition mesh.cpp:1797
void ReadNetgen3DMesh(std::istream &input)
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition mesh.hpp:1626
Array< int > face_indices[2]
cache for FaceIndices(ftype)
Definition mesh.hpp:288
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1651
void GeneralRefinement(const Array< Refinement > &refinements, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11713
Array< int > bdr_face_attrs_cache
internal cache for boundary element attributes
Definition mesh.hpp:117
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1548
Geometry::Type GetBdrElementGeometry(int i) const
Definition mesh.hpp:1560
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 AddTri(const int *vi, int attr=1)
Adds a triangle to the mesh given by 3 vertices vi.
Definition mesh.hpp:1027
static Mesh MakeCartesian1D(int n, real_t sx=1.0)
Creates 1D mesh, divided into n equal intervals.
Definition mesh.cpp:4768
int GetAttribute(int i) const
Return the attribute of element i.
Definition mesh.hpp:1497
void NodesUpdated()
This function should be called after the mesh node coordinates have been updated externally,...
Definition mesh.hpp:2342
void EnsureNodes()
Make sure that the mesh has valid nodes, i.e. its geometry is described by a vector finite element gr...
Definition mesh.cpp:7159
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
int AddQuad(int v1, int v2, int v3, int v4, int attr=1)
Adds a quadrilateral to the mesh given by 4 vertices v1 through v4.
Definition mesh.cpp:2164
long nodes_sequence
Counter for geometric factor invalidation.
Definition mesh.hpp:103
virtual void Load(std::istream &input, int generate_edges=0, int refine=1, bool fix_orientation=true)
Definition mesh.hpp:823
void ComputeFaceInfo(FaceType ftype) const
compute face_indices[ftype] and inv_face_indices[type]
Definition mesh.cpp:1057
IsoparametricTransformation FaceTransformation
Definition mesh.hpp:262
Array< NCFaceInfo > nc_faces_info
Definition mesh.hpp:243
friend class NCMesh
Definition mesh.hpp:68
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
int AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr=1)
Adds a wedge to the mesh given by 6 vertices v1 through v6.
Definition mesh.cpp:2199
Array< int > GetFaceToBdrElMap() const
Definition mesh.cpp:1692
static Mesh MakeCartesian2DWith4TrisPerQuad(int nx, int ny, real_t sx=1.0, real_t sy=1.0)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny*4 triangles.
Definition mesh.cpp:4805
void ReadInlineMesh(std::istream &input, bool generate_edges=false)
void SetPatchAttribute(int i, int attr)
Set the attribute of patch i, for a NURBS mesh.
Definition mesh.cpp:3500
void FinalizeTetMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a tetrahedral Mesh.
Definition mesh.cpp:3548
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 AddBdrQuadAsTriangles(const int *vi, int attr=1)
Definition mesh.cpp:2510
int AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr=1)
Adds a pyramid to the mesh given by 5 vertices v1 through v5.
Definition mesh.cpp:2213
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition mesh.cpp:5103
const Table & ElementToElementTable()
Definition mesh.cpp:8590
void ScaleElements(real_t sf)
Definition mesh.cpp:13986
void GenerateNCFaceInfo()
Definition mesh.cpp:8875
void ReadLineMesh(std::istream &input)
Table * edge_face
Definition mesh.hpp:257
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 CheckPartitioning(int *partitioning_)
Definition mesh.cpp:9632
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition mesh.cpp:3255
Geometry::Type GetTypicalElementGeometry() const
If the local mesh is not empty, return GetElementGeometry(0); otherwise, return a typical Geometry pr...
Definition mesh.cpp:1705
void GetLocalPtToSegTransformation(IsoparametricTransformation &, int i) const
Used in GetFaceElementTransformations (...)
Definition mesh.cpp:759
void CorrectPatchTopoOrientations(Array< int > &edge_to_ukv) const
Set signs to ensure knotvectors are pointed in the same direction.
Definition mesh.cpp:6823
bool Nonconforming() const
Definition mesh.hpp:2539
int GetBdrAttribute(int i) const
Return the attribute of boundary element i.
Definition mesh.hpp:1503
virtual void MarkExternalBoundaries(Array< int > &bdr_marker, bool excl=true) const
Mark boundary attributes of external boundaries.
Definition mesh.cpp:1818
void PrintCharacteristics(Vector *Vh=NULL, Vector *Vk=NULL, std::ostream &os=mfem::out)
Compute and print mesh characteristics such as number of vertices, number of elements,...
Definition mesh.cpp:255
void UpdateNURBS()
Definition mesh.cpp:6568
static int ComposeQuadOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:7634
int AddTriangle(int v1, int v2, int v3, int attr=1)
Adds a triangle to the mesh given by 3 vertices v1 through v3.
Definition mesh.cpp:2150
void GenerateFaces()
Definition mesh.cpp:8768
static const int vtk_quadratic_wedge[18]
Definition mesh.hpp:277
int EulerNumber2D() const
Equals 1 - num_holes.
Definition mesh.hpp:1323
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:315
void AddBdrElements(Array< Element * > &bdr_elems, const Array< int > &be_to_face)
Add an array of boundary elements to the mesh, along with map from the elements to their faces.
Definition mesh.cpp:2456
void Destroy()
Definition mesh.cpp:1959
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
void Make1D(int n, real_t sx=1.0)
Definition mesh.cpp:4566
friend class Tetrahedron
Definition mesh.hpp:281
void DeleteTables()
Definition mesh.hpp:340
void RefineNURBSFromFile(std::string ref_file)
Definition mesh.cpp:6375
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1447
int AddBdrPoint(int v, int attr=1)
Definition mesh.cpp:2525
void FinalizeWedgeMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a wedge Mesh.
Definition mesh.cpp:3589
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7497
void PrintTopo(std::ostream &os, const Array< int > &e_to_k, const int version, const std::string &comment="") const
Write the beginning of a NURBS mesh to os, specifying the NURBS patch topology. Optional file comment...
Definition mesh.cpp:12590
static Mesh MakeSimplicial(const Mesh &orig_mesh)
Definition mesh.cpp:5727
void SetPatchBdrAttribute(int i, int attr)
Set the attribute of patch boundary element i, for a NURBS mesh.
Definition mesh.cpp:3517
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1399
int AddVertexAtMeanCenter(const int *vi, const int nverts, int dim=3)
Definition mesh.cpp:2119
static int GetTetOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7665
std::unique_ptr< GridFunction > GetJacobianDeterminantGF() const
Create a GridFunction representing the Jacobian determinant.
Definition mesh.cpp:7285
real_t GetGeckoElementOrdering(Array< int > &ordering, int iterations=4, int window=4, int period=2, int seed=0, bool verbose=false, real_t time_limit=0)
Definition mesh.cpp:2672
static int EncodeFaceInfo(int local_face_index, int orientation)
Given local_face_index and orientation, return the corresponding encoded "face info int".
Definition mesh.hpp:2200
bool FaceIsTrueInterior(int FaceNo) const
Definition mesh.hpp:618
long GetSequence() const
Definition mesh.hpp:2559
const CoarseFineTransformations & GetRefinementTransforms() const
Definition mesh.cpp:12237
void Make2D5QuadsFromQuad(int nx, int ny, real_t sx, real_t sy)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny*5 quadrilaterals.
Definition mesh.cpp:4197
bool IsMixedMesh() const
Returns true if the mesh is a mixed mesh, false otherwise.
Definition mesh.cpp:8038
const Array< int > & GetElementAttributes() const
Returns the attributes for all elements in this mesh. The i'th entry of the array is the attribute of...
Definition mesh.cpp:1040
void GetLocalQuadToWdgTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:911
ElementTransformation * GetFaceTransformation(int FaceNo)
Returns a pointer to the transformation defining the given face element.
Definition mesh.cpp:610
void SetAttribute(int i, int attr)
Set the attribute of element i.
Definition mesh.cpp:8433
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 DestroyTables()
Definition mesh.cpp:1913
void RefineNURBS(bool usingKVF, real_t tol, const Array< int > &rf, const std::string &kvf)
Refine the NURBS mesh with default refinement factors in rf for each dimension.
Definition mesh.cpp:6498
static Mesh MakeCartesian2DWith5QuadsPerQuad(int nx, int ny, real_t sx=1.0, real_t sy=1.0)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny*5 quadrilaterals.
Definition mesh.cpp:4814
const FaceGeometricFactors * GetFaceGeometricFactors(const IntegrationRule &ir, const int flags, FaceType type, MemoryType d_mt=MemoryType::DEFAULT)
Return the mesh geometric factors for the faces corresponding to the given integration rule.
Definition mesh.cpp:978
void PrintWithPartitioning(int *partitioning, std::ostream &os, int elem_attr=0) const
Prints the mesh with boundary elements given by the boundary of the subdomains, so that the boundary ...
Definition mesh.cpp:13357
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:835
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition mesh.cpp:3189
void ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo, const std::string &xml_prefix="")
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2079
virtual void LocalRefinement(const Array< int > &marked_el, int type=3)
This function is not public anymore. Use GeneralRefinement instead.
Definition mesh.cpp:11088
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
void Make3D(int nx, int ny, int nz, Element::Type type, real_t sx, real_t sy, real_t sz, bool sfc_ordering)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
Definition mesh.cpp:3841
virtual void Save(const std::string &fname, int precision=16) const
Definition mesh.cpp:12694
int AddTet(int v1, int v2, int v3, int v4, int attr=1)
Adds a tetrahedron to the mesh given by 4 vertices v1 through v4.
Definition mesh.cpp:2178
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:142
void GetBdrPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:8473
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
ElementTransformation * GetTypicalElementTransformation()
If the local mesh is not empty return GetElementTransformation(0); otherwise, return the identity tra...
Definition mesh.cpp:394
Table * el_to_face
Definition mesh.hpp:246
void RandomRefinement(real_t prob, bool aniso=false, int nonconforming=-1, int nc_limit=0)
Refine each element with given probability. Uses GeneralRefinement.
Definition mesh.cpp:11805
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1462
void CheckDisplacements(const Vector &displacements, real_t &tmax)
Definition mesh.cpp:9950
friend class NURBSExtension
Definition mesh.hpp:69
void AddTriangleFaceElement(int lf, int gf, int el, int v0, int v1, int v2)
Definition mesh.cpp:8713
void AddHexAs24TetsWithPoints(int *vi, std::map< std::array< int, 4 >, int > &hex_face_verts, int attr=1)
Adds 24 tetrahedrons to the mesh by splitting a hexahedron.
Definition mesh.cpp:2381
void GetNode(int i, real_t *coord) const
Definition mesh.cpp:10058
void ReorderElements(const Array< int > &ordering, bool reorder_vertices=true)
Definition mesh.cpp:2891
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
void PrintElementsWithPartitioning(int *partitioning, std::ostream &os, int interior_faces=0)
Definition mesh.cpp:13476
Mesh & operator=(Mesh &&mesh)
Move assignment operator.
Definition mesh.cpp:4752
void Transform(std::function< void(const Vector &, Vector &)> f)
Definition mesh.cpp:14056
long sequence
Definition mesh.hpp:100
static int InvertQuadOrientation(int ori)
Definition mesh.cpp:7659
Array< FaceGeometricFactors * > face_geom_factors
Definition mesh.hpp:320
void GetLocalTriToPyrTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:864
Table * bel_to_edge
Definition mesh.hpp:250
static Mesh MakeRefined(Mesh &orig_mesh, int ref_factor, int ref_type)
Create a refined (by any factor) version of orig_mesh.
Definition mesh.cpp:4823
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
Element * ReadElementWithoutAttr(std::istream &input)
Definition mesh.cpp:5002
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1, int pyr_type=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:7211
int AddBdrSegment(int v1, int v2, int attr=1)
Definition mesh.cpp:2468
bool DerefineByError(Array< real_t > &elem_error, real_t threshold, int nc_limit=0, int op=1)
Definition mesh.cpp:11447
void FinalizeHexMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a hexahedral Mesh.
Definition mesh.cpp:3624
int AddElement(Element *elem)
Definition mesh.cpp:2442
static int DecodeFaceInfoLocalIndex(int info)
Given a "face info int", return the local face index.
Definition mesh.hpp:2196
Table * el_to_edge
Definition mesh.hpp:245
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1368
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition mesh.cpp:7313
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
void RefineAtVertex(const Vertex &vert, real_t eps=0.0, int nonconforming=-1)
Refine elements sharing the specified vertex. Uses GeneralRefinement.
Definition mesh.cpp:11824
void GetBdrElementEdges(int i, Array< int > &edges, Array< int > &cor) const
Return the indices and the orientations of all edges of bdr element i.
Definition mesh.cpp:8077
static int InvertTriOrientation(int ori)
Definition mesh.cpp:7580
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition mesh.cpp:8996
void FinalizeQuadMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a quadrilateral Mesh.
Definition mesh.cpp:2610
void SaveVTKHDF(const std::string &fname, bool high_order=true)
Save the Mesh in VTKHDF format.
Definition mesh.cpp:13262
void Make3D24TetsFromHex(int nx, int ny, int nz, real_t sx, real_t sy, real_t sz)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz*24 tetrahedrons.
Definition mesh.cpp:4271
virtual bool NonconformingDerefinement(Array< real_t > &elem_error, real_t threshold, int nc_limit=0, int op=1)
NC version of GeneralDerefinement.
Definition mesh.cpp:11399
void AddVertexParents(int i, int p1, int p2)
Mark vertex i as nonconforming, with parent vertices p1 and p2.
Definition mesh.cpp:2103
MFEM_DEPRECATED void GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const
Deprecated.
Definition mesh.cpp:8410
int GetPatchAttribute(int i) const
Return the attribute of patch i, for a NURBS mesh.
Definition mesh.cpp:3511
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
bool FaceIsInterior(int FaceNo) const
Return true if the given face is interior.
Definition mesh.hpp:1576
ElementTransformation * GetBdrElementTransformation(int i)
Returns a pointer to the transformation defining the i-th boundary element.
Definition mesh.cpp:533
IsoparametricTransformation Transformation
Definition mesh.hpp:260
void Init()
Definition mesh.cpp:1881
void Make2D4TrisFromQuad(int nx, int ny, real_t sx, real_t sy)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny*4 triangles.
Definition mesh.cpp:4124
void GetLocalTriToWdgTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:838
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 GetNURBSPatches(Array< NURBSPatch * > &patches)
Definition mesh.cpp:3535
const GeometricFactors * GetGeometricFactors(const IntegrationRule &ir, const int flags, MemoryType d_mt=MemoryType::DEFAULT)
Return the mesh geometric factors corresponding to the given integration rule.
Definition mesh.cpp:958
static Mesh MakeCartesian3D(int nx, int ny, int nz, Element::Type type, real_t sx=1.0, real_t sy=1.0, real_t sz=1.0, bool sfc_ordering=true)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
Definition mesh.cpp:4786
Table * edge_vertex
Definition mesh.hpp:258
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
void ReadCubit(const std::string &filename, int &curved, int &read_gf)
Load a mesh from a Genesis file.
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 SetNode(int i, const real_t *coord)
Definition mesh.cpp:10077
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:10112
int NumOfVertices
Definition mesh.hpp:84
const Array< int > & GetBdrFaceAttributes() const
Returns the attributes for all boundary elements in this mesh.
Definition mesh.cpp:1000
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:312
virtual void UniformRefinement3D()
Refine a mixed 3D mesh uniformly.
Definition mesh.hpp:481
static int ComposeTriOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:7557
Array< int > be_to_face
Definition mesh.hpp:248
void PrintBdrVTU(std::string fname, VTKFormat format=VTKFormat::ASCII, bool high_order_output=false, int compression_level=0)
Definition mesh.cpp:12900
const std::unordered_map< int, int > & GetInvFaceIndices(FaceType ftype) const
Inverse of the map FaceIndices(ftype)
Definition mesh.cpp:1088
void AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
Definition mesh.cpp:8676
FaceElementTransformations * GetBdrFaceTransformations(int BdrElemNo)
Builds the transformation defining the given boundary face.
Definition mesh.cpp:1298
int AddBdrTriangle(int v1, int v2, int v3, int attr=1)
Definition mesh.cpp:2482
void GetGeometricParametersFromJacobian(const DenseMatrix &J, real_t &volume, Vector &aspr, Vector &skew, Vector &ori) const
Computes geometric parameters associated with a Jacobian matrix in 2D/3D. These parameters are (1) Ar...
Definition mesh.cpp:14430
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1387
static int DecodeFaceInfoOrientation(int info)
Given a "face info int", return the face orientation.
Definition mesh.hpp:2193
void GetHilbertElementOrdering(Array< int > &ordering)
Definition mesh.cpp:2839
void GetEdgeToUniqueKnotvector(Array< int > &edge_to_ukv, Array< int > &ukv_to_rpkv) const
Definition mesh.cpp:6708
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:8139
void AddQuadAs5QuadsWithPoints(int *vi, int attr=1)
Adds 5 quadrilaterals to the mesh by splitting a quadrilateral given by 4 vertices vi.
Definition mesh.cpp:2321
void ReadVTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo)
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
virtual void UniformRefinement2D()
Refine a mixed 2D mesh uniformly.
Definition mesh.hpp:471
GridFunction * Nodes
Definition mesh.hpp:272
static Mesh MakePeriodic(const Mesh &orig_mesh, const std::vector< int > &v2v)
Create a periodic mesh by identifying vertices of orig_mesh.
Definition mesh.cpp:6205
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 GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
For the given boundary element, bdr_el, return its adjacent element and its info, i....
Definition mesh.cpp:8388
Array< int > elem_attrs_cache
internal cache for element attributes
Definition mesh.hpp:115
Table * el_to_el
Definition mesh.hpp:247
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
Table * face_edge
Definition mesh.hpp:256
real_t GetElementVolume(int i)
Definition mesh.cpp:125
static Mesh LoadFromFile(const std::string &filename, int generate_edges=0, int refine=1, bool fix_orientation=true)
Definition mesh.cpp:4758
int NumOfElements
Definition mesh.hpp:84
static const int vtk_quadratic_hex[27]
Definition mesh.hpp:278
void Swap(Mesh &other, bool non_geometry)
Definition mesh.cpp:11521
Array< Triple< int, int, int > > tmp_vertex_parents
Definition mesh.hpp:286
virtual void GenerateBoundaryElements()
Definition mesh.cpp:2532
static void GetElementArrayEdgeTable(const Array< Element * > &elem_array, const DSTable &v_to_v, Table &el_to_edge)
Definition mesh.cpp:8504
std::vector< int > CreatePeriodicVertexMapping(const std::vector< Vector > &translations, real_t tol=1e-8) const
Creates a mapping v2v from the vertex indices of the mesh such that coincident vertices under the giv...
Definition mesh.cpp:6239
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition mesh.cpp:11879
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1640
void ResetLazyData()
Definition mesh.cpp:1996
void UniformRefinement2D_base(bool update_nodes=true)
Definition mesh.cpp:10218
IsoparametricTransformation BdrTransformation
Definition mesh.hpp:261
void PrintTopoEdges(std::ostream &out, const Array< int > &e_to_k, bool vmap=false) const
Write the patch topology edges of a NURBS mesh (see PrintTopo()).
Definition mesh.cpp:12626
void GetLocalSegToTriTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:774
void DestroyPointers()
Definition mesh.cpp:1933
void InitTables()
Definition mesh.cpp:1900
void DegreeElevate(int rel_degree, int degree=16)
Definition mesh.cpp:6551
Table * face_to_elem
Definition mesh.hpp:255
int NumOfFaces
Definition mesh.hpp:85
int spaceDim
Definition mesh.hpp:82
int FindCoarseElement(int i)
Definition mesh.cpp:12227
void FinalizeTriMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a triangular Mesh.
Definition mesh.cpp:2581
void GetElementCenter(int i, Vector &center)
Definition mesh.cpp:81
void AddHexAsPyramids(const int *vi, int attr=1)
Adds 6 pyramids to the mesh by splitting a hexahedron given by 8 vertices vi.
Definition mesh.cpp:2280
static void PrintElementsByGeometry(int dim, const Array< int > &num_elems_by_geom, std::ostream &os)
Auxiliary method used by PrintCharacteristics().
Definition mesh.cpp:241
int AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int attr=1)
Adds a hexahedron to the mesh given by 8 vertices v1 through v8.
Definition mesh.cpp:2227
static IntegrationPoint TransformBdrElementToFace(Geometry::Type geom, int o, const IntegrationPoint &ip)
For the vertex (1D), edge (2D), or face (3D) of a boundary element with the orientation o,...
Definition mesh.cpp:7909
void SetEmpty()
Definition mesh.cpp:1907
virtual void SetNodalFESpace(FiniteElementSpace *nfes)
Definition mesh.cpp:7153
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1393
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition mesh.cpp:3766
void AddQuadFaceElement(int lf, int gf, int el, int v0, int v1, int v2, int v3)
Definition mesh.cpp:8741
void ReadMFEMMesh(std::istream &input, int version, int &curved)
void KnotRemove(Array< Vector * > &kv)
Definition mesh.cpp:6456
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
void GetEdgeTransformation(int i, IsoparametricTransformation *EdTr) const
Builds the transformation defining the i-th edge element in EdTr. EdTr must be allocated in advance a...
Definition mesh.cpp:616
int own_nodes
Definition mesh.hpp:273
void GetElementData(const Array< Element * > &elem_array, int geom, Array< int > &elem_vtx, Array< int > &attr) const
Definition mesh.cpp:11593
void PrintSurfaces(const Table &Aface_face, std::ostream &os) const
Print set of disjoint surfaces:
Definition mesh.cpp:13849
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
virtual void MarkTetMeshForRefinement(const DSTable &v_to_v)
Definition mesh.cpp:3166
void GetPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:8455
FaceElementTransformations * GetInteriorFaceTransformations(int FaceNo)
See GetFaceElementTransformations().
Definition mesh.cpp:1278
void GetLocalTriToTetTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:814
virtual void PrintXG(std::ostream &os=mfem::out) const
Print the mesh to the given stream using Netgen/Truegrid format.
Definition mesh.cpp:12300
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
virtual void NURBSUniformRefinement(int rf=2, real_t tol=1.0e-12)
Refine NURBS mesh, with an optional refinement factor, generally anisotropic.
Definition mesh.cpp:6483
int mesh_geoms
Definition mesh.hpp:95
void DebugDump(std::ostream &os) const
Output an NCMesh-compatible debug dump.
Definition mesh.cpp:16117
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition mesh.hpp:2389
bool RefineByError(const Array< real_t > &elem_error, real_t threshold, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11850
static Mesh MakeCartesian3DWith24TetsPerHex(int nx, int ny, int nz, real_t sx=1.0, real_t sy=1.0, real_t sz=1.0)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz*24 tetrahedrons.
Definition mesh.cpp:4796
STable3D * GetFacesTable()
Definition mesh.cpp:8933
Table * GetFaceEdgeTable() const
Definition mesh.cpp:8148
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:11781
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
Definition mesh.hpp:1348
virtual MFEM_DEPRECATED void ReorientTetMesh()
Definition mesh.cpp:9126
void PrintVTK(std::ostream &os)
Definition mesh.cpp:12708
virtual void MarkNamedBoundaries(const std::string &set_name, Array< int > &bdr_marker) const
Mark boundary attributes in the named set.
Definition mesh.cpp:1860
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf, bool spacing=false, bool nc=false)
void MoveNodes(const Vector &displacements)
Definition mesh.cpp:10097
Array< GeometricFactors * > geom_factors
Optional geometric factors.
Definition mesh.hpp:319
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition mesh.cpp:5050
int nbBoundaryFaces
Definition mesh.hpp:90
static Mesh MakeCartesian2D(int nx, int ny, Element::Type type, bool generate_edges=false, real_t sx=1.0, real_t sy=1.0, bool sfc_ordering=true)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4776
void GetElementEdges(int i, Array< int > &edges, Array< int > &cor) const
Return the indices and the orientations of all edges of element i.
Definition mesh.cpp:8044
void Make2D(int nx, int ny, Element::Type type, real_t sx, real_t sy, bool generate_edges, bool sfc_ordering)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4388
void AddHexAsTets(const int *vi, int attr=1)
Adds 6 tetrahedrons to the mesh by splitting a hexahedron given by 8 vertices vi.
Definition mesh.cpp:2243
FaceElementTransformations FaceElemTr
Definition mesh.hpp:263
void SetNodes(const Vector &node_coord)
Updates the vertex/node locations. Invokes NodesUpdated().
Definition mesh.cpp:10124
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:8014
std::unique_ptr< L2_SegmentElement > EdgeTransfElement
Definition mesh.hpp:264
void FinalizeCheck()
Definition mesh.cpp:2567
void GetLocalQuadToPyrTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:935
void AddHexAsWedges(const int *vi, int attr=1)
Adds 2 wedges to the mesh by splitting a hexahedron given by 8 vertices vi.
Definition mesh.cpp:2262
Element * ReadElement(std::istream &input)
Definition mesh.cpp:5032
virtual bool HasBoundaryElements() const
Checks if the mesh has boundary elements.
Definition mesh.hpp:1344
void ScaleSubdomains(real_t sf)
Definition mesh.cpp:13916
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:8526
void GetLocalQuadToHexTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:889
int NumOfEdges
Definition mesh.hpp:85
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:12125
Geometry::Type GetTypicalFaceGeometry() const
If the local mesh is not empty, return GetFaceGeometry(0); otherwise return a typical face geometry p...
Definition mesh.cpp:1671
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1569
virtual void UnmarkInternalBoundaries(Array< int > &bdr_marker, bool excl=true) const
Unmark boundary attributes of internal boundaries.
Definition mesh.cpp:1752
Operation last_operation
Definition mesh.hpp:334
void ReadGmshMesh(std::istream &input)
Definition gmsh.cpp:1311
void SwapNodes(GridFunction *&nodes, int &own_nodes_)
Swap the internal node GridFunction pointer and ownership flag members with the given ones.
Definition mesh.cpp:10161
virtual void RefineNURBSWithKVFactors(int rf, const std::string &kvf)
Definition mesh.cpp:6478
void LoadPatchTopo(std::istream &input, Array< int > &edge_to_ukv)
Read NURBS patch/macro-element mesh.
Definition mesh.cpp:6631
void SetBdrAttribute(int i, int attr)
Set the attribute of boundary element i.
Definition mesh.hpp:1506
void ChangeVertexDataOwnership(real_t *vertices, int len_vertices, bool zerocopy=false)
Set the internal Vertex array to point to the given vertices array without assuming ownership of the ...
Definition mesh.cpp:4867
int nbInteriorFaces
Definition mesh.hpp:90
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
Table * GetEdgeVertexTable() const
Definition mesh.cpp:8192
int * GeneratePartitioning(int nparts, int part_method=1)
Definition mesh.cpp:9232
Table * GetFaceToElementTable() const
Definition mesh.cpp:8284
void MarkTriMeshForRefinement()
Definition mesh.cpp:3126
void ReadNetgen2DMesh(std::istream &input, int &curved)
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
void AddPointFaceElement(int lf, int gf, int el)
Used in GenerateFaces()
Definition mesh.cpp:8644
void RemoveInternalBoundaries()
Definition mesh.cpp:14214
virtual void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0)
This function is not public anymore. Use GeneralRefinement instead.
Definition mesh.cpp:11330
void MoveVertices(const Vector &displacements)
Definition mesh.cpp:10027
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1429
void DeleteGeometricFactors()
Destroy all GeometricFactors stored by the Mesh.
Definition mesh.cpp:1097
const Table & ElementToFaceTable() const
Definition mesh.cpp:8626
void AddQuadAs4TrisWithPoints(int *vi, int attr=1)
Adds 4 triangles to the mesh by splitting a quadrilateral given by 4 vertices vi.
Definition mesh.cpp:2299
void RemoveUnusedVertices()
Remove unused vertices and rebuild mesh connectivity.
Definition mesh.cpp:14107
void KnotInsert(Array< KnotVector * > &kv)
Definition mesh.cpp:6412
A class for non-conforming AMR. The class is not used directly by the user, rather it is an extension...
Definition ncmesh.hpp:190
void OnMeshUpdated(Mesh *mesh)
Definition ncmesh.cpp:2887
void FindNeighbors(int elem, Array< int > &neighbors, const Array< int > *search_set=NULL)
Definition ncmesh.cpp:4322
void MakeTopologyOnly()
Definition ncmesh.hpp:584
void GetMeshComponents(Mesh &mesh) const
Fill Mesh::{vertices,elements,boundary} for the current finest level.
Definition ncmesh.cpp:2760
const CoarseFineTransformations & GetRefinementTransforms() const
Definition ncmesh.cpp:5204
int Dimension() const
Return the dimension of the NCMesh.
Definition ncmesh.hpp:214
void Print(std::ostream &out, const std::string &comments="", bool nurbs=false) const
Definition ncmesh.cpp:6349
BlockArray< Element > elements
Definition ncmesh.hpp:688
static void GridSfcOrdering3D(int width, int height, int depth, Array< int > &coords)
Definition ncmesh.cpp:5607
Array< int > leaf_elements
finest elements, in Mesh ordering (+ ghosts)
Definition ncmesh.hpp:787
virtual void LimitNCLevel(int max_nc_level)
Definition ncmesh.cpp:6090
Array< int > vertex_nodeId
vertex-index to node-id map, see UpdateVertices
Definition ncmesh.hpp:789
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition ncmesh.hpp:369
virtual void Derefine(const Array< int > &derefs)
Definition ncmesh.cpp:2309
Array< real_t > coordinates
Definition ncmesh.hpp:769
bool IsGhost(const Element &el) const
Return true if the Element el is a ghost element.
Definition ncmesh.hpp:851
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:376
int spaceDim
dimensions of the elements and the vertex coordinates
Definition ncmesh.hpp:588
void MarkCoarseLevel()
Definition ncmesh.cpp:5156
virtual void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level)
Definition ncmesh.cpp:2280
const Table & GetDerefinementTable()
Definition ncmesh.cpp:2265
void SetAttribute(int i, int attr)
Set the attribute of leaf element i, which is a Mesh element index.
Definition ncmesh.hpp:524
int SpaceDimension() const
Return the space dimension of the NCMesh.
Definition ncmesh.hpp:216
virtual void Refine(const Array< Refinement > &refinements)
Definition ncmesh.cpp:1947
static void GridSfcOrdering2D(int width, int height, Array< int > &coords)
Definition ncmesh.cpp:5592
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
Definition nurbs.hpp:575
int GetNBE() const
Return the number of active boundary elements.
Definition nurbs.hpp:962
void GetPatches(Array< NURBSPatch * > &patches)
Definition nurbs.cpp:5670
void GetCoarseningFactors(Array< int > &f) const
Definition nurbs.cpp:5291
void SetPatchAttribute(int i, int attr)
Set the attribute for patch i, which is set to all elements in the patch.
Definition nurbs.hpp:1012
const Array< int > & GetPatchElements(int patch)
Return the array of indices of all elements in patch patch.
Definition nurbs.cpp:5710
void UniformRefinement(int rf=2)
Refine with optional refinement factor rf. Uniform means refinement is done everywhere by the same fa...
Definition nurbs.cpp:5223
void Print(std::ostream &os, const std::string &comments="") const
Writes all patch data to the stream os.
Definition nurbs.cpp:3196
virtual void ReadCoarsePatchCP(std::istream &input)
Read the control points for coarse patches.
Definition nurbs.cpp:5768
void SetPatchBdrAttribute(int i, int attr)
Set the attribute for patch boundary element i to attr, which is set to all boundary elements in the ...
Definition nurbs.hpp:1020
int GetPatchSpaceDimension() const
Return the physical dimension of the NURBS geometry.
Definition nurbs.cpp:5680
void Coarsen(int cf=2, real_t tol=1.0e-12)
Coarsen with optional coarsening factor cf.
Definition nurbs.cpp:5284
int GetPatchAttribute(int i) const
Get the attribute for patch i, which is set to all elements in the patch.
Definition nurbs.hpp:1016
void SetCoordsFromPatches(Vector &Nodes, int vdim)
Set FE coordinates in Nodes, using data from patches, with physical vector dimension vdim,...
Definition nurbs.cpp:5044
void GetElementTopo(Array< Element * > &elements) const
Generate the active mesh elements and return them in elements.
Definition nurbs.cpp:4267
const Array< int > & GetPatchBdrElements(int patch)
Return the array of indices of all boundary elements in patch patch.
Definition nurbs.cpp:5717
int GetNKV() const
Return the number of KnotVectors.
Definition nurbs.hpp:949
void GetVertexLocalToGlobal(Array< int > &lvert_vert)
Get the local to global vertex index map lvert_vert.
Definition nurbs.cpp:4961
bool HavePatches() const
Return true if at least 1 patch is defined, false otherwise.
Definition nurbs.hpp:995
void GetBdrElementTopo(Array< Element * > &boundary) const
Generate the active mesh boundary elements and return them in boundary.
Definition nurbs.cpp:4396
bool NonconformingPatches() const
Return true if the patch topology mesh is nonconforming.
Definition nurbs.hpp:1128
virtual void RefineWithKVFactors(int rf, const std::string &kvf_filename, bool coarsened)
Definition nurbs.cpp:5801
void KnotRemove(Array< Vector * > &kv, real_t tol=1.0e-12)
Definition nurbs.cpp:5412
void GetElementLocalToGlobal(Array< int > &lelem_elem)
Get the local to global element index map lelem_elem.
Definition nurbs.cpp:4971
virtual void PrintCoarsePatches(std::ostream &os)
Print control points for coarse patches.
Definition nurbs.cpp:5773
void FullyCoarsen()
Fully coarsen all structured patches, for non-nested refinement of a mesh with a nonconforming patch ...
Definition nurbs.cpp:5251
void KnotInsert(Array< KnotVector * > &kv)
Insert knots from kv into all KnotVectors in all patches. The size of kv should be the same as knotVe...
Definition nurbs.cpp:5328
int GetOrder() const
If all KnotVector orders are identical, return that number. Otherwise, return NURBSFECollection::Vari...
Definition nurbs.hpp:946
int GetNV() const
Return the local number of active vertices.
Definition nurbs.hpp:954
void ConvertToPatches(const Vector &Nodes)
Define patches in IKJ (B-net) format, using FE coordinates in Nodes.
Definition nurbs.cpp:5025
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:926
void SetKnotsFromPatches()
Set KnotVectors from patches and construct mesh and space data.
Definition nurbs.cpp:5052
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:958
int GetPatchBdrAttribute(int i) const
Get the attribute for boundary patch element i, which is set to all boundary elements in the patch.
Definition nurbs.hpp:1025
void DegreeElevate(int rel_degree, int degree=16)
Call DegreeElevate for all KnotVectors of all patches. For each KnotVector, the new degree is max(old...
Definition nurbs.cpp:5168
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:749
Class for standard nodal finite elements.
Definition fe_base.hpp:798
Class used to extrude the nodes of a mesh.
Definition mesh.hpp:3225
void SetLayer(const int l)
Definition mesh.hpp:3232
NodeExtrudeCoefficient(const int dim, const int n_, const real_t s_)
Definition mesh.cpp:15561
void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) override
Evaluate the vector coefficient in the element described by T at the point ip, storing the result in ...
Definition mesh.cpp:15567
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
Type
Ordering methods:
Definition ordering.hpp:17
A pair of objects.
Class for parallel meshes.
Definition pmesh.hpp:35
MPI_Comm GetComm() const
Definition pmesh.hpp:403
int GetMyRank() const
Definition pmesh.hpp:405
Parallel version of NURBSExtension.
Definition nurbs.hpp:1148
Data type point element.
Definition point.hpp:23
Data type Pyramid element.
Definition pyramid.hpp:23
Piecewise-(bi)quadratic continuous finite elements.
Definition fe_coll.hpp:939
static int CheckClosed(int type)
If the Quadrature1D type is not closed return Invalid; otherwise return type.
@ VALUES
Evaluate the values at quadrature points.
@ DERIVATIVES
Evaluate the derivatives at quadrature points.
@ DETERMINANTS
Assuming the derivative at quadrature points form a matrix, this flag can be used to compute and stor...
void SetOutputLayout(QVectorLayout layout) const
Set the desired output Q-vector layout. The default value is QVectorLayout::byNODES.
Data type quadrilateral element.
IntegrationRule RefPts
Definition geom.hpp:321
Array< int > RefGeoms
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 NumberOfElements()
Return the number of elements added to the table.
Definition stable3d.hpp:70
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
Timing object.
Definition tic_toc.hpp:36
void Start()
Start the stopwatch. The elapsed time is not cleared.
Definition tic_toc.cpp:411
double UserTime()
Return the number of user seconds elapsed since the stopwatch was started.
Definition tic_toc.cpp:437
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
int Push(int i, int j)
Establish connection between element i and element j in the table.
Definition table.cpp:263
void AddConnection(int r, int c)
Definition table.hpp:89
void Finalize()
Finalize the table initialization.
Definition table.cpp:287
void MakeI(int nrows)
Definition table.cpp:130
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.
void Init(int ind1, int ind2, int ind3, int ind4, int attr=1, int ref_flag=0)
Initialize the vertex indices and the attribute of a Tetrahedron.
void PushTransform(int tr) override
Add 'tr' to the current chain of coarse-fine transformations.
void ParseRefinementFlag(int refinement_edges[2], int &type, int &flag) const
int GetRefinementFlag() const
void SetVertices(const Array< int > &v) override
Set the indices defining the vertices.
void ResetTransform(int tr) override
Set current coarse-fine transformation number.
unsigned GetTransform() const override
Return current coarse-fine transformation.
static void GetPointMatrix(unsigned transform, DenseMatrix &pm)
Calculate point matrix corresponding to a chain of transformations.
void GetMarkedFace(const int face, int *fv) const
void CreateRefinementFlag(int refinement_edges[2], int type, int flag=0)
void GetVertices(Array< int > &v) const override
Get the indices defining the vertices.
Data type triangle element.
Definition triangle.hpp:24
void SetVertices(const Array< int > &v) override
Set the indices defining the vertices.
Definition triangle.cpp:194
static void GetPointMatrix(unsigned transform, DenseMatrix &pm)
Calculate point matrix corresponding to a chain of transformations.
Definition triangle.cpp:126
void PushTransform(int tr) override
Add 'tr' to the current chain of coarse-fine transformations.
Definition triangle.hpp:62
void ResetTransform(int tr) override
Set current coarse-fine transformation number.
Definition triangle.hpp:58
unsigned GetTransform() const override
Return current coarse-fine transformation.
Definition triangle.hpp:59
void GetVertices(Array< int > &v) const override
Get the indices defining the vertices.
Definition triangle.cpp:188
A triple of objects.
Low-level class for writing VTKHDF data (for use in ParaView).
Definition vtkhdf.hpp:37
void SaveMesh(const Mesh &mesh, bool high_order=true, int ref=-1)
Save the mesh, appending as a new time step.
Definition vtkhdf.cpp:534
Base class for vector Coefficients that optionally depend on time and space.
int GetVDim()
Returns dimension of the vector.
A general vector function coefficient.
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
real_t Norml2() const
Returns the l2 norm of the vector.
Definition vector.cpp:968
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
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:532
void NewDataAndSize(real_t *d, int s)
Set the Vector data and size, deleting the old data, if owned.
Definition vector.hpp:197
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
void SetData(real_t *d)
Definition vector.hpp:184
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:676
void cross3D(const Vector &vin, Vector &vout) const
Definition vector.cpp:639
real_t DistanceTo(const real_t *p) const
Compute the Euclidean distance to another vector.
Definition vector.hpp:797
Data type for vertex.
Definition vertex.hpp:23
Data type Wedge element.
Definition wedge.hpp:23
void Print(const Mesh &mesh, const adios2stream::mode print_mode=mode::sync)
const std::string filename
Definition zstr.hpp:811
real_t kappa
Definition ex24.cpp:54
int dim
Definition ex24.cpp:53
prob_type prob
Definition ex25.cpp:156
constexpr int dimension
This example only works in 3D. Kernels for 2D are not implemented.
Definition hooke.cpp:45
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t b
Definition lissajous.cpp:42
real_t delta
Definition lissajous.cpp:43
real_t a
Definition lissajous.cpp:41
int idxtype
Definition mesh.cpp:50
void METIS_PartGraphRecursive(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *)
void METIS_PartGraphVKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *)
int idx_t
Definition mesh.cpp:49
void METIS_PartGraphKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *)
mfem::real_t real_t
unsigned int uint
Definition gecko.hpp:204
double Float
Definition gecko.hpp:208
Linear1DFiniteElement SegmentFE
Definition segment.cpp:52
std::ostream & operator<<(std::ostream &os, SparseMatrix const &mat)
PointFiniteElement PointFE
Definition point.cpp:42
TriLinear3DFiniteElement HexahedronFE
Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print, int &par_ref, Array< int > &partitioning)
Constructs the smallest possible [0,1]^dim serial mesh that can be used later to obtain a ParMesh wit...
Definition mesh.cpp:15971
void mfem_error(const char *msg)
Definition error.cpp:154
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition table.cpp:505
GeometryRefiner GlobGeometryRefiner
Definition geom.cpp:2014
int FindRoots(const Vector &z, Vector &x)
Definition mesh.cpp:9769
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
real_t rand_real()
Generate a random real_t number in the interval [0,1) using rand().
Definition vector.hpp:61
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:414
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:443
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
MFEM_EXPORT class Linear3DFiniteElement TetrahedronFE
Definition fe.cpp:36
void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
Definition mesh.cpp:9689
Mesh * Extrude1D(Mesh *mesh, const int ny, const real_t sy, const bool closed)
Extrude a 1D mesh.
Definition mesh.cpp:15585
MFEM_HOST_DEVICE int UnsignIndex(int i)
Definition globals.hpp:118
MFEM_EXPORT class LinearWedgeFiniteElement WedgeFE
Definition fe.cpp:40
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
void WriteBase64WithSizeAndClear(std::ostream &os, std::vector< char > &buf, int compression_level)
Encode in base 64 (and potentially compress) the given data, write it to the output stream (with a he...
Definition vtk.cpp:654
Mesh * Extrude2D(Mesh *mesh, const int nz, const real_t sz)
Extrude a 2D mesh.
Definition mesh.cpp:15745
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
void filter_dos(std::string &line)
Check for, and remove, a trailing '\r' from and std::string.
Definition text.hpp:45
bool UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition fespace.hpp:1644
void FindTMax(Vector &c, Vector &x, real_t &tmax, const real_t factor, const int Dim)
Definition mesh.cpp:9916
BiLinear2DFiniteElement QuadrilateralFE
void WriteBinaryOrASCII(std::ostream &os, std::vector< char > &buf, const T &val, const char *suffix, VTKFormat format)
Write either ASCII data to the stream or binary data to the buffer depending on the given format.
Definition vtk.hpp:148
MFEM_EXPORT class LinearPyramidFiniteElement PyramidFE
Definition fe.cpp:44
ComplexDenseMatrix * MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B)
Multiply the complex conjugate transpose of a matrix A with a matrix B. A^H*B.
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[].
void CreateVTKElementConnectivity(Array< int > &con, Geometry::Type geom, int ref)
Create the VTK element connectivity array for a given element geometry and refinement level.
Definition vtk.cpp:497
void FindPartitioningComponents(Table &elem_elem, const Array< int > &partitioning, Array< int > &component, Array< int > &num_comp)
Definition mesh.cpp:9561
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
@ NATIVE
Native ordering as defined by the FiniteElement.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
MFEM_EXPORT Linear2DFiniteElement TriangleFE
Definition fe.cpp:32
MPI_Comm GetGlobalMPI_Comm()
Get MFEM's "global" MPI communicator.
Definition globals.cpp:67
void XYZ_VectorFunction(const Vector &p, Vector &v)
Definition mesh.cpp:7116
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
constexpr real_t infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition vector.hpp:47
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition intrules.hpp:549
STL namespace.
real_t p(const Vector &x, real_t t)
T sq(T x)
Defines the coarse-fine transformations of all fine elements.
Definition ncmesh.hpp:90
Array< Embedding > embeddings
Fine element positions in their parents.
Definition ncmesh.hpp:92
DenseTensor point_matrices[Geometry::NumGeom]
Definition ncmesh.hpp:96
Helper struct for defining a connectivity table, see Table::MakeFromList.
Definition table.hpp:28
Defines the position of a fine element within a coarse element.
Definition ncmesh.hpp:69
unsigned geom
Definition ncmesh.hpp:77
int parent
Coarse Element index in the coarse mesh.
Definition ncmesh.hpp:71
unsigned matrix
Definition ncmesh.hpp:78
static const int FaceVert[NumFaces][MaxFaceVert]
Definition geom.hpp:259
static const int Edges[NumEdges][2]
Definition geom.hpp:255
static const int FaceVert[NumFaces][MaxFaceVert]
Definition geom.hpp:281
static const int Edges[NumEdges][2]
Definition geom.hpp:277
static const int Edges[NumEdges][2]
Definition geom.hpp:299
static const int FaceVert[NumFaces][MaxFaceVert]
Definition geom.hpp:303
static const int Orient[NumOrient][NumVert]
Definition geom.hpp:162
static const int Orient[NumOrient][NumVert]
Definition geom.hpp:216
static const int Edges[NumEdges][2]
Definition geom.hpp:205
static const int Edges[NumEdges][2]
Definition geom.hpp:229
static const int FaceVert[NumFaces][MaxFaceVert]
Definition geom.hpp:233
static const int Orient[NumOrient][NumVert]
Definition geom.hpp:242
static const int Edges[NumEdges][2]
Definition geom.hpp:175
static const int Orient[NumOrient][NumVert]
Definition geom.hpp:191
EntityHelper(int dim_, const Array< int >(&entity_to_vertex_)[Geometry::NumGeom])
Definition mesh.cpp:14545
Entity FindEntity(int bytype_entity_id)
Definition mesh.cpp:14560
entity_to_vertex_type & entity_to_vertex
Definition mesh.hpp:2816
int geom_offsets[Geometry::NumGeom+1]
Definition mesh.hpp:2814
const int * verts
Definition mesh.hpp:2810
This structure stores the low level information necessary to interpret the configuration of elements ...
Definition mesh.hpp:179
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2098
ElementLocation location
Definition mesh.hpp:2105
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition mesh.hpp:2182
bool IsOfFaceType(FaceType type) const
Return true if the face is of the same type as type.
Definition mesh.hpp:2151
struct mfem::Mesh::FaceInformation::@15 element[2]
Information about the adjacent elements.
FaceTopology topology
The face topology (boundary, conforming, or nonconforming).
Definition mesh.hpp:2100
int ncface
If the face is nonconforming, the index of the NC face. -1 otherwise.
Definition mesh.hpp:2116
ElementConformity conformity
Definition mesh.hpp:2106
const DenseMatrix * point_matrix
The point matrix for nonconforming faces.
Definition mesh.hpp:2119
FaceInfoTag tag
Detailed face information (see FaceInfoTag).
Definition mesh.hpp:2113
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:301
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:287
static const int HighOrderMap[Geometry::NUM_GEOMETRIES]
Map from MFEM's Geometry::Type to arbitrary-order Lagrange VTK geometries.
Definition vtk.hpp:83
static const int QuadraticMap[Geometry::NUM_GEOMETRIES]
Map from MFEM's Geometry::Type to legacy quadratic VTK geometries/.
Definition vtk.hpp:81
static const int * VertexPermutation[Geometry::NUM_GEOMETRIES]
Permutation from MFEM's vertex ordering to VTK's vertex ordering.
Definition vtk.hpp:76
static const int Map[Geometry::NUM_GEOMETRIES]
Map from MFEM's Geometry::Type to linear VTK geometries.
Definition vtk.hpp:79
std::array< int, NCMesh::MaxFaceNodes > nodes