MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12// 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
40// Include the METIS header, if using version 5. If using METIS 4, the needed
41// declarations are inlined below, i.e. no header is needed.
42#if defined(MFEM_USE_METIS) && defined(MFEM_USE_METIS_5)
43#include "metis.h"
44#endif
45
46// METIS 4 prototypes
47#if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
48typedef int idx_t;
49typedef int idxtype;
50extern "C" {
52 int*, int*, int*, int*, int*, idxtype*);
54 int*, int*, int*, int*, int*, idxtype*);
56 int*, int*, int*, int*, int*, idxtype*);
57}
58#endif
59
60using namespace std;
61
62namespace mfem
63{
64
66{
69 if (ip == NULL)
70 {
71 eltransf->SetIntPoint(&Geometries.GetCenter(geom));
72 }
73 else
74 {
75 eltransf->SetIntPoint(ip);
76 }
77 Geometries.JacToPerfJac(geom, eltransf->Jacobian(), J);
78}
79
80void Mesh::GetElementCenter(int i, Vector &center)
81{
82 center.SetSize(spaceDim);
83 int geom = GetElementBaseGeometry(i);
85 eltransf->Transform(Geometries.GetCenter(geom), center);
86}
87
89{
91
94 Geometries.JacToPerfJac(geom, T->Jacobian(), J);
95
96 if (type == 0)
97 {
98 return pow(fabs(J.Weight()), 1./Dim);
99 }
100 else if (type == 1)
101 {
102 return J.CalcSingularvalue(Dim-1); // h_min
103 }
104 else
105 {
106 return J.CalcSingularvalue(0); // h_max
107 }
108}
109
111{
113}
114
116{
118 Vector d_hat(Dim);
119 GetElementJacobian(i, J);
120 J.MultTranspose(dir, d_hat);
121 return sqrt((d_hat * d_hat) / (dir * dir));
122}
123
125{
128 et->OrderJ());
129 real_t volume = 0.0;
130 for (int j = 0; j < ir.GetNPoints(); j++)
131 {
132 const IntegrationPoint &ip = ir.IntPoint(j);
133 et->SetIntPoint(&ip);
134 volume += ip.weight * et->Weight();
135 }
136
137 return volume;
138}
139
140// Similar to VisualizationSceneSolution3d::FindNewBox in GLVis
141void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
142{
143 min.SetSize(spaceDim);
144 max.SetSize(spaceDim);
145
146 for (int d = 0; d < spaceDim; d++)
147 {
148 min(d) = infinity();
149 max(d) = -infinity();
150 }
151
152 if (Nodes == NULL)
153 {
154 real_t *coord;
155 for (int i = 0; i < NumOfVertices; i++)
156 {
157 coord = GetVertex(i);
158 for (int d = 0; d < spaceDim; d++)
159 {
160 if (coord[d] < min(d)) { min(d) = coord[d]; }
161 if (coord[d] > max(d)) { max(d) = coord[d]; }
162 }
163 }
164 }
165 else
166 {
167 const bool use_boundary = false; // make this a parameter?
168 int ne = use_boundary ? GetNBE() : GetNE();
169 int fn, fo;
170 DenseMatrix pointmat;
171 RefinedGeometry *RefG;
172 IntegrationRule eir;
175
176 for (int i = 0; i < ne; i++)
177 {
178 if (use_boundary)
179 {
180 GetBdrElementFace(i, &fn, &fo);
183 eir.SetSize(RefG->RefPts.GetNPoints());
184 Tr->Loc1.Transform(RefG->RefPts, eir);
185 Tr->Elem1->Transform(eir, pointmat);
186 }
187 else
188 {
191 T->Transform(RefG->RefPts, pointmat);
192 }
193 for (int j = 0; j < pointmat.Width(); j++)
194 {
195 for (int d = 0; d < pointmat.Height(); d++)
196 {
197 if (pointmat(d,j) < min(d)) { min(d) = pointmat(d,j); }
198 if (pointmat(d,j) > max(d)) { max(d) = pointmat(d,j); }
199 }
200 }
201 }
202 }
203}
204
206 real_t &kappa_min, real_t &kappa_max,
207 Vector *Vh, Vector *Vk)
208{
209 int i, dim, sdim;
210 DenseMatrix J;
211 real_t h, kappa;
212
213 dim = Dimension();
214 sdim = SpaceDimension();
215
216 if (Vh) { Vh->SetSize(NumOfElements); }
217 if (Vk) { Vk->SetSize(NumOfElements); }
218
219 h_min = kappa_min = infinity();
220 h_max = kappa_max = -h_min;
221 if (dim == 0) { if (Vh) { *Vh = 1.0; } if (Vk) {*Vk = 1.0; } return; }
222 J.SetSize(sdim, dim);
223 for (i = 0; i < NumOfElements; i++)
224 {
225 GetElementJacobian(i, J);
226 h = pow(fabs(J.Weight()), 1.0/real_t(dim));
227 kappa = (dim == sdim) ?
228 J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1) : -1.0;
229 if (Vh) { (*Vh)(i) = h; }
230 if (Vk) { (*Vk)(i) = kappa; }
231
232 if (h < h_min) { h_min = h; }
233 if (h > h_max) { h_max = h; }
234 if (kappa < kappa_min) { kappa_min = kappa; }
235 if (kappa > kappa_max) { kappa_max = kappa; }
236 }
237}
238
239// static method
241 const Array<int> &num_elems_by_geom,
242 std::ostream &os)
243{
244 for (int g = Geometry::DimStart[dim], first = 1;
245 g < Geometry::DimStart[dim+1]; g++)
246 {
247 if (!num_elems_by_geom[g]) { continue; }
248 if (!first) { os << " + "; }
249 else { first = 0; }
250 os << num_elems_by_geom[g] << ' ' << Geometry::Name[g] << "(s)";
251 }
252}
253
254void Mesh::PrintCharacteristics(Vector *Vh, Vector *Vk, std::ostream &os)
255{
256 real_t h_min, h_max, kappa_min, kappa_max;
257
258 os << "Mesh Characteristics:";
259
260 this->GetCharacteristics(h_min, h_max, kappa_min, kappa_max, Vh, Vk);
261
262 Array<int> num_elems_by_geom(Geometry::NumGeom);
263 num_elems_by_geom = 0;
264 for (int i = 0; i < GetNE(); i++)
265 {
266 num_elems_by_geom[GetElementBaseGeometry(i)]++;
267 }
268
269 os << '\n'
270 << "Dimension : " << Dimension() << '\n'
271 << "Space dimension : " << SpaceDimension();
272 if (Dim == 0)
273 {
274 os << '\n'
275 << "Number of vertices : " << GetNV() << '\n'
276 << "Number of elements : " << GetNE() << '\n'
277 << "Number of bdr elem : " << GetNBE() << '\n';
278 }
279 else if (Dim == 1)
280 {
281 os << '\n'
282 << "Number of vertices : " << GetNV() << '\n'
283 << "Number of elements : " << GetNE() << '\n'
284 << "Number of bdr elem : " << GetNBE() << '\n'
285 << "h_min : " << h_min << '\n'
286 << "h_max : " << h_max << '\n';
287 }
288 else if (Dim == 2)
289 {
290 os << '\n'
291 << "Number of vertices : " << GetNV() << '\n'
292 << "Number of edges : " << GetNEdges() << '\n'
293 << "Number of elements : " << GetNE() << " -- ";
294 PrintElementsByGeometry(2, num_elems_by_geom, os);
295 os << '\n'
296 << "Number of bdr elem : " << GetNBE() << '\n'
297 << "Euler Number : " << EulerNumber2D() << '\n'
298 << "h_min : " << h_min << '\n'
299 << "h_max : " << h_max << '\n'
300 << "kappa_min : " << kappa_min << '\n'
301 << "kappa_max : " << kappa_max << '\n';
302 }
303 else
304 {
305 Array<int> num_bdr_elems_by_geom(Geometry::NumGeom);
306 num_bdr_elems_by_geom = 0;
307 for (int i = 0; i < GetNBE(); i++)
308 {
309 num_bdr_elems_by_geom[GetBdrElementGeometry(i)]++;
310 }
311 Array<int> num_faces_by_geom(Geometry::NumGeom);
312 num_faces_by_geom = 0;
313 for (int i = 0; i < GetNFaces(); i++)
314 {
315 num_faces_by_geom[GetFaceGeometry(i)]++;
316 }
317
318 os << '\n'
319 << "Number of vertices : " << GetNV() << '\n'
320 << "Number of edges : " << GetNEdges() << '\n'
321 << "Number of faces : " << GetNFaces() << " -- ";
322 PrintElementsByGeometry(Dim-1, num_faces_by_geom, os);
323 os << '\n'
324 << "Number of elements : " << GetNE() << " -- ";
325 PrintElementsByGeometry(Dim, num_elems_by_geom, os);
326 os << '\n'
327 << "Number of bdr elem : " << GetNBE() << " -- ";
328 PrintElementsByGeometry(Dim-1, num_bdr_elems_by_geom, os);
329 os << '\n'
330 << "Euler Number : " << EulerNumber() << '\n'
331 << "h_min : " << h_min << '\n'
332 << "h_max : " << h_max << '\n'
333 << "kappa_min : " << kappa_min << '\n'
334 << "kappa_max : " << kappa_max << '\n';
335 }
336 os << '\n' << std::flush;
337}
338
340{
341 switch (ElemType)
342 {
343 case Element::POINT : return &PointFE;
344 case Element::SEGMENT : return &SegmentFE;
345 case Element::TRIANGLE : return &TriangleFE;
347 case Element::TETRAHEDRON : return &TetrahedronFE;
348 case Element::HEXAHEDRON : return &HexahedronFE;
349 case Element::WEDGE : return &WedgeFE;
350 case Element::PYRAMID : return &PyramidFE;
351 default:
352 MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
353 break;
354 }
355 MFEM_ABORT("Unknown element type");
356 return NULL;
357}
358
359
361 IsoparametricTransformation *ElTr) const
362{
363 ElTr->Attribute = GetAttribute(i);
364 ElTr->ElementNo = i;
366 ElTr->mesh = this;
367 ElTr->Reset();
368 if (Nodes == NULL)
369 {
370 GetPointMatrix(i, ElTr->GetPointMat());
372 }
373 else
374 {
375 DenseMatrix &pm = ElTr->GetPointMat();
376 Array<int> vdofs;
377 Nodes->FESpace()->GetElementVDofs(i, vdofs);
378 Nodes->HostRead();
379 const GridFunction &nodes = *Nodes;
380 int n = vdofs.Size()/spaceDim;
381 pm.SetSize(spaceDim, n);
382 for (int k = 0; k < spaceDim; k++)
383 {
384 for (int j = 0; j < n; j++)
385 {
386 pm(k,j) = nodes(vdofs[n*k+j]);
387 }
388 }
389 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
390 }
391}
392
415
421
423 IsoparametricTransformation *ElTr) const
424{
425 ElTr->Attribute = GetAttribute(i);
426 ElTr->ElementNo = i;
428 ElTr->mesh = this;
429 DenseMatrix &pm = ElTr->GetPointMat();
430 ElTr->Reset();
431 nodes.HostRead();
432 if (Nodes == NULL)
433 {
434 MFEM_ASSERT(nodes.Size() == spaceDim*GetNV(), "");
435 int nv = elements[i]->GetNVertices();
436 const int *v = elements[i]->GetVertices();
437 int n = vertices.Size();
438 pm.SetSize(spaceDim, nv);
439 for (int k = 0; k < spaceDim; k++)
440 {
441 for (int j = 0; j < nv; j++)
442 {
443 pm(k, j) = nodes(k*n+v[j]);
444 }
445 }
447 }
448 else
449 {
450 MFEM_ASSERT(nodes.Size() == Nodes->Size(), "");
451 Array<int> vdofs;
452 Nodes->FESpace()->GetElementVDofs(i, vdofs);
453 int n = vdofs.Size()/spaceDim;
454 pm.SetSize(spaceDim, n);
455 for (int k = 0; k < spaceDim; k++)
456 {
457 for (int j = 0; j < n; j++)
458 {
459 pm(k,j) = nodes(vdofs[n*k+j]);
460 }
461 }
462 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
463 }
464}
465
467 IsoparametricTransformation* ElTr) const
468{
469 ElTr->Attribute = GetBdrAttribute(i);
470 ElTr->ElementNo = i; // boundary element number
472 ElTr->mesh = this;
473 DenseMatrix &pm = ElTr->GetPointMat();
474 ElTr->Reset();
475 if (Nodes == NULL)
476 {
477 GetBdrPointMatrix(i, pm);
479 }
480 else
481 {
482 const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
483 Nodes->HostRead();
484 const GridFunction &nodes = *Nodes;
485 if (bdr_el)
486 {
487 Array<int> vdofs;
488 Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
489 int n = vdofs.Size()/spaceDim;
490 pm.SetSize(spaceDim, n);
491 for (int k = 0; k < spaceDim; k++)
492 {
493 for (int j = 0; j < n; j++)
494 {
495 int idx = vdofs[n*k+j];
496 pm(k,j) = nodes((idx<0)? -1-idx:idx);
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
671 {
672 MFEM_ABORT("Not implemented.");
673 }
674 }
675}
676
682
683
685 IsoparametricTransformation &Transf, int i) const
686{
687 const IntegrationRule *SegVert;
688 DenseMatrix &locpm = Transf.GetPointMat();
689 Transf.Reset();
690
691 Transf.SetFE(&PointFE);
693 locpm.SetSize(1, 1);
694 locpm(0, 0) = SegVert->IntPoint(i/64).x;
695 // (i/64) is the local face no. in the segment
696 // (i%64) is the orientation of the point (not used)
697}
698
700 IsoparametricTransformation &Transf, int i) const
701{
702 const int *tv, *so;
703 const IntegrationRule *TriVert;
704 DenseMatrix &locpm = Transf.GetPointMat();
705 Transf.Reset();
706
707 Transf.SetFE(&SegmentFE);
708 tv = tri_t::Edges[i/64]; // (i/64) is the local face no. in the triangle
709 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
711 locpm.SetSize(2, 2);
712 for (int j = 0; j < 2; j++)
713 {
714 locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
715 locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
716 }
717}
718
720 IsoparametricTransformation &Transf, int i) const
721{
722 const int *qv, *so;
723 const IntegrationRule *QuadVert;
724 DenseMatrix &locpm = Transf.GetPointMat();
725 Transf.Reset();
726
727 Transf.SetFE(&SegmentFE);
728 qv = quad_t::Edges[i/64]; // (i/64) is the local face no. in the quad
729 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
731 locpm.SetSize(2, 2);
732 for (int j = 0; j < 2; j++)
733 {
734 locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
735 locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
736 }
737}
738
740 IsoparametricTransformation &Transf, int i) const
741{
742 DenseMatrix &locpm = Transf.GetPointMat();
743 Transf.Reset();
744
745 Transf.SetFE(&TriangleFE);
746 // (i/64) is the local face no. in the tet
747 const int *tv = tet_t::FaceVert[i/64];
748 // (i%64) is the orientation of the tetrahedron face
749 // w.r.t. the face element
750 const int *to = tri_t::Orient[i%64];
751 const IntegrationRule *TetVert =
753 locpm.SetSize(3, 3);
754 for (int j = 0; j < 3; j++)
755 {
756 const IntegrationPoint &vert = TetVert->IntPoint(tv[to[j]]);
757 locpm(0, j) = vert.x;
758 locpm(1, j) = vert.y;
759 locpm(2, j) = vert.z;
760 }
761}
762
764 IsoparametricTransformation &Transf, int i) const
765{
766 DenseMatrix &locpm = Transf.GetPointMat();
767 Transf.Reset();
768
769 Transf.SetFE(&TriangleFE);
770 // (i/64) is the local face no. in the pri
771 MFEM_VERIFY(i < 128, "Local face index " << i/64
772 << " is not a triangular face of a wedge.");
773 const int *pv = pri_t::FaceVert[i/64];
774 // (i%64) is the orientation of the wedge face
775 // w.r.t. the face element
776 const int *to = tri_t::Orient[i%64];
777 const IntegrationRule *PriVert =
779 locpm.SetSize(3, 3);
780 for (int j = 0; j < 3; j++)
781 {
782 const IntegrationPoint &vert = PriVert->IntPoint(pv[to[j]]);
783 locpm(0, j) = vert.x;
784 locpm(1, j) = vert.y;
785 locpm(2, j) = vert.z;
786 }
787}
788
790 IsoparametricTransformation &Transf, int i) const
791{
792 DenseMatrix &locpm = Transf.GetPointMat();
793
794 Transf.SetFE(&TriangleFE);
795 // (i/64) is the local face no. in the pyr
796 MFEM_VERIFY(i >= 64, "Local face index " << i/64
797 << " is not a triangular face of a pyramid.");
798 const int *pv = pyr_t::FaceVert[i/64];
799 // (i%64) is the orientation of the pyramid face
800 // w.r.t. the face element
801 const int *to = tri_t::Orient[i%64];
802 const IntegrationRule *PyrVert =
804 locpm.SetSize(3, 3);
805 for (int j = 0; j < 3; j++)
806 {
807 const IntegrationPoint &vert = PyrVert->IntPoint(pv[to[j]]);
808 locpm(0, j) = vert.x;
809 locpm(1, j) = vert.y;
810 locpm(2, j) = vert.z;
811 }
812}
813
815 IsoparametricTransformation &Transf, int i) const
816{
817 DenseMatrix &locpm = Transf.GetPointMat();
818 Transf.Reset();
819
820 Transf.SetFE(&QuadrilateralFE);
821 // (i/64) is the local face no. in the hex
822 const int *hv = hex_t::FaceVert[i/64];
823 // (i%64) is the orientation of the quad
824 const int *qo = quad_t::Orient[i%64];
826 locpm.SetSize(3, 4);
827 for (int j = 0; j < 4; j++)
828 {
829 const IntegrationPoint &vert = HexVert->IntPoint(hv[qo[j]]);
830 locpm(0, j) = vert.x;
831 locpm(1, j) = vert.y;
832 locpm(2, j) = vert.z;
833 }
834}
835
837 IsoparametricTransformation &Transf, int i) const
838{
839 DenseMatrix &locpm = Transf.GetPointMat();
840 Transf.Reset();
841
842 Transf.SetFE(&QuadrilateralFE);
843 // (i/64) is the local face no. in the pri
844 MFEM_VERIFY(i >= 128, "Local face index " << i/64
845 << " is not a quadrilateral face of a wedge.");
846 const int *pv = pri_t::FaceVert[i/64];
847 // (i%64) is the orientation of the quad
848 const int *qo = quad_t::Orient[i%64];
850 locpm.SetSize(3, 4);
851 for (int j = 0; j < 4; j++)
852 {
853 const IntegrationPoint &vert = PriVert->IntPoint(pv[qo[j]]);
854 locpm(0, j) = vert.x;
855 locpm(1, j) = vert.y;
856 locpm(2, j) = vert.z;
857 }
858}
859
861 IsoparametricTransformation &Transf, int i) const
862{
863 DenseMatrix &locpm = Transf.GetPointMat();
864
865 Transf.SetFE(&QuadrilateralFE);
866 // (i/64) is the local face no. in the pyr
867 MFEM_VERIFY(i < 64, "Local face index " << i/64
868 << " is not a quadrilateral face of a pyramid.");
869 const int *pv = pyr_t::FaceVert[i/64];
870 // (i%64) is the orientation of the quad
871 const int *qo = quad_t::Orient[i%64];
873 locpm.SetSize(3, 4);
874 for (int j = 0; j < 4; j++)
875 {
876 const IntegrationPoint &vert = PyrVert->IntPoint(pv[qo[j]]);
877 locpm(0, j) = vert.x;
878 locpm(1, j) = vert.y;
879 locpm(2, j) = vert.z;
880 }
881}
882
884 const int flags,
885 MemoryType d_mt)
886{
887 for (int i = 0; i < geom_factors.Size(); i++)
888 {
890 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags)
891 {
892 return gf;
893 }
894 }
895
896 this->EnsureNodes();
897
898 GeometricFactors *gf = new GeometricFactors(this, ir, flags, d_mt);
899 geom_factors.Append(gf);
900 return gf;
901}
902
904 const IntegrationRule& ir,
905 const int flags, FaceType type, MemoryType d_mt)
906{
907 for (int i = 0; i < face_geom_factors.Size(); i++)
908 {
910 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags &&
911 gf->type==type)
912 {
913 return gf;
914 }
915 }
916
917 this->EnsureNodes();
918
919 FaceGeometricFactors *gf = new FaceGeometricFactors(this, ir, flags, type,
920 d_mt);
921 face_geom_factors.Append(gf);
922 return gf;
923}
924
926{
927 if (bdr_face_attrs_cache.Size() == 0)
928 {
929 std::unordered_map<int, int> f_to_be;
930 for (int i = 0; i < GetNBE(); ++i)
931 {
932 const int f = GetBdrElementFaceIndex(i);
933 f_to_be[f] = i;
934 }
935 const int nf_bdr = GetNFbyType(FaceType::Boundary);
936 // MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
938 int f_ind = 0;
939 const int nf = GetNumFaces();
940 for (int f = 0; f < nf; ++f)
941 {
942 if (!GetFaceInformation(f).IsOfFaceType(FaceType::Boundary))
943 {
944 continue;
945 }
946 int attribute = -1; // default value
947 auto iter = f_to_be.find(f);
948 if (iter != f_to_be.end())
949 {
950 const int be = iter->second;
951 attribute = GetBdrAttribute(be);
952 }
953 else
954 {
955 // If a boundary face does not correspond to the a boundary element,
956 // we assign it the default attribute of -1.
957 }
958 bdr_face_attrs_cache[f_ind] = attribute;
959 ++f_ind;
960 }
961 }
963}
964
966{
967 if (elem_attrs_cache.Size() == 0)
968 {
969 // re-compute cache
972 for (int i = 0; i < GetNE(); ++i)
973 {
975 MFEM_ASSERT(elem_attrs_cache[i] > 0,
976 "Negative attribute on element " << i);
977 }
978 }
979 return elem_attrs_cache;
980}
981
983{
984 auto &fidcs = face_indices[static_cast<int>(ftype)];
985 auto &ifidcs = inv_face_indices[static_cast<int>(ftype)];
986 fidcs.SetSize(GetNFbyType(ftype));
987 fidcs.HostWrite();
988 ifidcs.reserve(fidcs.Size());
989 int f_idx = 0;
990 for (int i = 0; i < GetNumFacesWithGhost(); ++i)
991 {
992 const FaceInformation face = GetFaceInformation(i);
993 if (face.IsNonconformingCoarse() || !face.IsOfFaceType(ftype))
994 {
995 continue;
996 }
997 fidcs[f_idx] = i;
998 ifidcs[i] = f_idx;
999 ++f_idx;
1000 }
1001}
1002
1004{
1005 if (face_indices[static_cast<int>(ftype)].Size() == 0)
1006 {
1007 ComputeFaceInfo(ftype);
1008 }
1009 return face_indices[static_cast<int>(ftype)];
1010}
1011
1012const std::unordered_map<int, int> &
1014{
1015 if (inv_face_indices[static_cast<int>(ftype)].empty())
1016 {
1017 ComputeFaceInfo(ftype);
1018 }
1019 return inv_face_indices[static_cast<int>(ftype)];
1020}
1021
1023{
1024 for (int i = 0; i < geom_factors.Size(); i++)
1025 {
1026 delete geom_factors[i];
1027 }
1028 geom_factors.SetSize(0);
1029 for (int i = 0; i < face_geom_factors.Size(); i++)
1030 {
1031 delete face_geom_factors[i];
1032 }
1033 face_geom_factors.SetSize(0);
1034
1036}
1037
1038void Mesh::GetLocalFaceTransformation(int face_type, int elem_type,
1040 int info) const
1041{
1042 switch (face_type)
1043 {
1044 case Element::POINT:
1045 GetLocalPtToSegTransformation(Transf, info);
1046 break;
1047
1048 case Element::SEGMENT:
1049 if (elem_type == Element::TRIANGLE)
1050 {
1051 GetLocalSegToTriTransformation(Transf, info);
1052 }
1053 else
1054 {
1055 MFEM_ASSERT(elem_type == Element::QUADRILATERAL, "");
1056 GetLocalSegToQuadTransformation(Transf, info);
1057 }
1058 break;
1059
1060 case Element::TRIANGLE:
1061 if (elem_type == Element::TETRAHEDRON)
1062 {
1063 GetLocalTriToTetTransformation(Transf, info);
1064 }
1065 else if (elem_type == Element::WEDGE)
1066 {
1067 GetLocalTriToWdgTransformation(Transf, info);
1068 }
1069 else if (elem_type == Element::PYRAMID)
1070 {
1071 GetLocalTriToPyrTransformation(Transf, info);
1072 }
1073 else
1074 {
1075 MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
1076 "face type " << face_type
1077 << " and element type " << elem_type << "\n");
1078 }
1079 break;
1080
1082 if (elem_type == Element::HEXAHEDRON)
1083 {
1084 GetLocalQuadToHexTransformation(Transf, info);
1085 }
1086 else if (elem_type == Element::WEDGE)
1087 {
1088 GetLocalQuadToWdgTransformation(Transf, info);
1089 }
1090 else if (elem_type == Element::PYRAMID)
1091 {
1092 GetLocalQuadToPyrTransformation(Transf, info);
1093 }
1094 else
1095 {
1096 MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
1097 "face type " << face_type
1098 << " and element type " << elem_type << "\n");
1099 }
1100 break;
1101 }
1102}
1103
1111
1116 int mask) const
1117{
1118 const FaceInfo &face_info = faces_info[FaceNo];
1119
1120 int cmask = 0;
1121 FElTr.SetConfigurationMask(cmask);
1122 FElTr.Elem1 = NULL;
1123 FElTr.Elem2 = NULL;
1124
1125 // setup the transformation for the first element
1126 FElTr.Elem1No = face_info.Elem1No;
1128 {
1129 GetElementTransformation(FElTr.Elem1No, &ElTr1);
1130 FElTr.Elem1 = &ElTr1;
1131 cmask |= 1;
1132 }
1133
1134 // setup the transformation for the second element
1135 // return NULL in the Elem2 field if there's no second element, i.e.
1136 // the face is on the "boundary"
1137 FElTr.Elem2No = face_info.Elem2No;
1139 FElTr.Elem2No >= 0)
1140 {
1141#ifdef MFEM_DEBUG
1143 { MFEM_ABORT("NURBS mesh not supported!"); }
1144#endif
1145 GetElementTransformation(FElTr.Elem2No, &ElTr2);
1146 FElTr.Elem2 = &ElTr2;
1147 cmask |= 2;
1148 }
1149
1150 // setup the face transformation
1152 {
1153 GetFaceTransformation(FaceNo, &FElTr);
1154 cmask |= 16;
1155 }
1156 else
1157 {
1158 FElTr.SetGeometryType(GetFaceGeometry(FaceNo));
1159 }
1160
1161 // setup Loc1 & Loc2
1162 int face_type = GetFaceElementType(FaceNo);
1164 {
1165 int elem_type = GetElementType(face_info.Elem1No);
1166 GetLocalFaceTransformation(face_type, elem_type,
1167 FElTr.Loc1.Transf, face_info.Elem1Inf);
1168 cmask |= 4;
1169 }
1171 FElTr.Elem2No >= 0)
1172 {
1173 int elem_type = GetElementType(face_info.Elem2No);
1174 GetLocalFaceTransformation(face_type, elem_type,
1175 FElTr.Loc2.Transf, face_info.Elem2Inf);
1176
1177 // NC meshes: prepend slave edge/face transformation to Loc2
1178 if (Nonconforming() && IsSlaveFace(face_info))
1179 {
1180 ApplyLocalSlaveTransformation(FElTr, face_info, false);
1181 }
1182 cmask |= 8;
1183 }
1184
1185 FElTr.SetConfigurationMask(cmask);
1186
1187 // This check can be useful for internal debugging, however it will fail on
1188 // periodic boundary faces, so we keep it disabled in general.
1189#if 0
1190#ifdef MFEM_DEBUG
1191 real_t dist = FElTr.CheckConsistency();
1192 if (dist >= 1e-12)
1193 {
1194 mfem::out << "\nInternal error: face id = " << FaceNo
1195 << ", dist = " << dist << '\n';
1196 FElTr.CheckConsistency(1); // print coordinates
1197 MFEM_ABORT("internal error");
1198 }
1199#endif
1200#endif
1201}
1202
1209
1213 IsoparametricTransformation &ElTr2) const
1214{
1215 if (faces_info[FaceNo].Elem2No < 0)
1216 {
1218 return;
1219 }
1220 GetFaceElementTransformations(FaceNo, FElTr, ElTr1, ElTr2);
1221}
1222
1229
1233 IsoparametricTransformation &ElTr2) const
1234{
1235 // Check if the face is interior, shared, or nonconforming.
1236 int fn = GetBdrElementFaceIndex(BdrElemNo);
1237 if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
1238 {
1240 return;
1241 }
1242 GetFaceElementTransformations(fn, FElTr, ElTr1, ElTr2, 21);
1243 FElTr.Attribute = boundary[BdrElemNo]->GetAttribute();
1244 FElTr.ElementNo = BdrElemNo;
1246 FElTr.mesh = this;
1247}
1248
1249bool Mesh::IsSlaveFace(const FaceInfo &fi) const
1250{
1251 return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
1252}
1253
1255 const FaceInfo &fi, bool is_ghost) const
1256{
1257#ifdef MFEM_THREAD_SAFE
1258 DenseMatrix composition;
1259#else
1260 static DenseMatrix composition;
1261#endif
1262 MFEM_ASSERT(fi.NCFace >= 0, "");
1263 MFEM_ASSERT(nc_faces_info[fi.NCFace].Slave, "internal error");
1264 if (!is_ghost)
1265 {
1266 // side 1 -> child side, side 2 -> parent side
1268 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1269 // In 2D, we need to flip the point matrix since it is aligned with the
1270 // parent side.
1271 if (Dim == 2)
1272 {
1273 // swap points (columns) 0 and 1
1274 std::swap(composition(0,0), composition(0,1));
1275 std::swap(composition(1,0), composition(1,1));
1276 }
1277 LT.SetPointMat(composition);
1278 }
1279 else // is_ghost == true
1280 {
1281 // side 1 -> parent side, side 2 -> child side
1283 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1284 // In 2D, there is no need to flip the point matrix since it is already
1285 // aligned with the parent side, see also ParNCMesh::GetFaceNeighbors.
1286 // In 3D the point matrix was flipped during construction in
1287 // ParNCMesh::GetFaceNeighbors and due to that it is already aligned with
1288 // the parent side.
1289 LT.SetPointMat(composition);
1290 }
1291}
1292
1294{
1295 FaceInformation face;
1296 int e1, e2;
1297 int inf1, inf2;
1298 int ncface;
1299 GetFaceElements(f, &e1, &e2);
1300 GetFaceInfos(f, &inf1, &inf2, &ncface);
1301 face.element[0].index = e1;
1303 face.element[0].orientation = inf1%64;
1304 face.element[0].local_face_id = inf1/64;
1305 face.element[1].local_face_id = inf2/64;
1306 face.ncface = ncface;
1307 face.point_matrix = nullptr;
1308 // The following figures out face.location, face.conformity,
1309 // face.element[1].index, and face.element[1].orientation.
1310 if (f < GetNumFaces()) // Non-ghost face
1311 {
1312 if (e2>=0)
1313 {
1314 if (ncface==-1)
1315 {
1321 face.element[1].index = e2;
1322 face.element[1].orientation = inf2%64;
1323 }
1324 else // ncface >= 0
1325 {
1331 face.element[1].index = e2;
1332 MFEM_ASSERT(inf2%64==0, "unexpected slave face orientation.");
1333 face.element[1].orientation = inf2%64;
1334 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1335 }
1336 }
1337 else // e2<0
1338 {
1339 if (ncface==-1)
1340 {
1341 if (inf2<0)
1342 {
1348 face.element[1].index = -1;
1349 face.element[1].orientation = -1;
1350 }
1351 else // inf2 >= 0
1352 {
1358 face.element[1].index = -1 - e2;
1359 face.element[1].orientation = inf2%64;
1360 }
1361 }
1362 else // ncface >= 0
1363 {
1364 if (inf2 < 0)
1365 {
1371 face.element[1].index = -1;
1372 face.element[1].orientation = -1;
1373 }
1374 else
1375 {
1381 face.element[1].index = -1 - e2;
1382 face.element[1].orientation = inf2%64;
1383 }
1384 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1385 }
1386 }
1387 }
1388 else // Ghost face
1389 {
1390 if (e1==-1)
1391 {
1397 face.element[1].index = -1;
1398 face.element[1].orientation = -1;
1399 }
1400 else
1401 {
1407 face.element[1].index = -1 - e2;
1408 face.element[1].orientation = inf2%64;
1409 face.point_matrix = nc_faces_info[ncface].PointMatrix;
1410 }
1411 }
1412 return face;
1413}
1414
1415Mesh::FaceInformation::operator Mesh::FaceInfo() const
1416{
1417 FaceInfo res {-1, -1, -1, -1, -1};
1418 switch (tag)
1419 {
1421 res.Elem1No = element[0].index;
1422 res.Elem2No = element[1].index;
1423 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1424 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1425 res.NCFace = ncface;
1426 break;
1428 res.Elem1No = element[0].index;
1429 res.Elem2No = element[1].index;
1430 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1431 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1432 res.NCFace = ncface;
1433 break;
1435 res.Elem1No = element[0].index;
1436 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1437 break;
1439 res.Elem1No = element[0].index;
1440 res.Elem2No = -1 - element[1].index;
1441 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1442 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1443 break;
1445 res.Elem1No = element[0].index;
1446 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1447 break;
1449 res.Elem1No = element[0].index;
1450 res.Elem2No = -1 - element[1].index;
1451 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1452 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1453 break;
1455 break;
1457 res.Elem1No = element[0].index;
1458 res.Elem2No = -1 - element[1].index;
1459 res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1460 res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1461 break;
1462 }
1463 return res;
1464}
1465
1466std::ostream &operator<<(std::ostream &os, const Mesh::FaceInformation& info)
1467{
1468 os << "face topology=";
1469 switch (info.topology)
1470 {
1472 os << "Boundary";
1473 break;
1475 os << "Conforming";
1476 break;
1478 os << "Non-conforming";
1479 break;
1481 os << "NA";
1482 break;
1483 }
1484 os << '\n';
1485 os << "element[0].location=";
1486 switch (info.element[0].location)
1487 {
1489 os << "Local";
1490 break;
1492 os << "FaceNbr";
1493 break;
1495 os << "NA";
1496 break;
1497 }
1498 os << '\n';
1499 os << "element[1].location=";
1500 switch (info.element[1].location)
1501 {
1503 os << "Local";
1504 break;
1506 os << "FaceNbr";
1507 break;
1509 os << "NA";
1510 break;
1511 }
1512 os << '\n';
1513 os << "element[0].conformity=";
1514 switch (info.element[0].conformity)
1515 {
1517 os << "Coincident";
1518 break;
1520 os << "Superset";
1521 break;
1523 os << "Subset";
1524 break;
1526 os << "NA";
1527 break;
1528 }
1529 os << '\n';
1530 os << "element[1].conformity=";
1531 switch (info.element[1].conformity)
1532 {
1534 os << "Coincident";
1535 break;
1537 os << "Superset";
1538 break;
1540 os << "Subset";
1541 break;
1543 os << "NA";
1544 break;
1545 }
1546 os << '\n';
1547 os << "element[0].index=" << info.element[0].index << '\n'
1548 << "element[1].index=" << info.element[1].index << '\n'
1549 << "element[0].local_face_id=" << info.element[0].local_face_id << '\n'
1550 << "element[1].local_face_id=" << info.element[1].local_face_id << '\n'
1551 << "element[0].orientation=" << info.element[0].orientation << '\n'
1552 << "element[1].orientation=" << info.element[1].orientation << '\n'
1553 << "ncface=" << info.ncface << std::endl;
1554 return os;
1555}
1556
1557void Mesh::GetFaceElements(int Face, int *Elem1, int *Elem2) const
1558{
1559 *Elem1 = faces_info[Face].Elem1No;
1560 *Elem2 = faces_info[Face].Elem2No;
1561}
1562
1563void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
1564{
1565 *Inf1 = faces_info[Face].Elem1Inf;
1566 *Inf2 = faces_info[Face].Elem2Inf;
1567}
1568
1569void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
1570{
1571 *Inf1 = faces_info[Face].Elem1Inf;
1572 *Inf2 = faces_info[Face].Elem2Inf;
1573 *NCFace = faces_info[Face].NCFace;
1574}
1575
1577{
1578 switch (Dim)
1579 {
1580 case 1: return Geometry::POINT;
1581 case 2: return Geometry::SEGMENT;
1582 case 3:
1583 if (Face < NumOfFaces) // local (non-ghost) face
1584 {
1585 return faces[Face]->GetGeometryType();
1586 }
1587 // ghost face
1588 const int nc_face_id = faces_info[Face].NCFace;
1589
1590 MFEM_ASSERT(nc_face_id >= 0, "parent ghost faces are not supported");
1591 return faces[nc_faces_info[nc_face_id].MasterFace]->GetGeometryType();
1592 }
1593 return Geometry::INVALID;
1594}
1595
1597{
1599 switch (elem_geom)
1600 {
1605 case Geometry::CUBE: return Geometry::SQUARE;
1608 default: return Geometry::INVALID;
1609 }
1610}
1611
1613{
1614 return (Dim == 1) ? Element::POINT : faces[Face]->GetType();
1615}
1616
1618{
1619 Array<int> face_to_be(Dim == 2 ? NumOfEdges : NumOfFaces);
1620 face_to_be = -1;
1621 for (int i = 0; i < NumOfBdrElements; i++)
1622 {
1623 face_to_be[GetBdrElementFaceIndex(i)] = i;
1624 }
1625 return face_to_be;
1626}
1627
1629{
1630 if (GetNE() > 0) { return GetElementGeometry(0); }
1631
1632 const int dim = Dimension();
1633 if (dim == 1)
1634 {
1635 return Geometry::SEGMENT;
1636 }
1638 if (dim == 2)
1639 {
1640 geom = ((meshgen & 1) ? Geometry::TRIANGLE :
1642 }
1643 else if (dim == 3)
1644 {
1645 geom = ((meshgen & 1) ? Geometry::TETRAHEDRON :
1646 ((meshgen & 2) ? Geometry::CUBE :
1647 ((meshgen & 4) ? Geometry::PRISM :
1649 }
1650 MFEM_VERIFY(geom != Geometry::INVALID,
1651 "Could not determine a typical element Geometry!");
1652 return geom;
1653}
1654
1655
1657{
1658 const int num_faces = GetNumFaces();
1659
1660 face_marker.SetSize(num_faces);
1661
1662 for (int f = 0; f < num_faces; f++)
1663 {
1664 if (FaceIsTrueInterior(f))
1665 {
1666 face_marker[f] = 0;
1667 }
1668 else
1669 {
1670 face_marker[f] = 1;
1671 }
1672 }
1673}
1674
1675void Mesh::UnmarkInternalBoundaries(Array<int> &bdr_marker, bool excl) const
1676{
1677 const int max_bdr_attr = bdr_attributes.Max();
1678
1679 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1680 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1681
1682 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
1683 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
1684
1685 // Identify attributes which contain interior faces and those which
1686 // contain exterior faces.
1687 for (int be = 0; be < boundary.Size(); be++)
1688 {
1689 const int bea = boundary[be]->GetAttribute();
1690
1691 if (bdr_marker[bea-1] != 0)
1692 {
1693 const int f = be_to_face[be];
1694
1695 if (FaceIsTrueInterior(f))
1696 {
1697 interior_bdr[bea-1] = true;
1698 }
1699 else
1700 {
1701 exterior_bdr[bea-1] = true;
1702 }
1703 }
1704 }
1705
1706 // Unmark attributes which are currently marked, contain interior faces,
1707 // and satisfy the appropriate exclusivity requirement.
1708 for (int b = 0; b < max_bdr_attr; b++)
1709 {
1710 if (bdr_marker[b] != 0 && interior_bdr[b])
1711 {
1712 if (!excl || !exterior_bdr[b])
1713 {
1714 bdr_marker[b] = 0;
1715 }
1716 }
1717 }
1718}
1719
1720void Mesh::UnmarkNamedBoundaries(const std::string &set_name,
1721 Array<int> &bdr_marker) const
1722{
1723 const int max_bdr_attr = bdr_attributes.Max();
1724
1725 MFEM_VERIFY(bdr_attribute_sets.AttributeSetExists(set_name),
1726 "Named set is not defined in this mesh!");
1727 MFEM_VERIFY(bdr_marker.Size() >= bdr_attributes.Max(),
1728 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1729
1731
1732 for (int b = 0; b < max_bdr_attr; b++)
1733 {
1734 if (set_marker[b])
1735 {
1736 bdr_marker[b] = 0;
1737 }
1738 }
1739}
1740
1741void Mesh::MarkExternalBoundaries(Array<int> &bdr_marker, bool excl) const
1742{
1743 const int max_bdr_attr = bdr_attributes.Max();
1744
1745 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1746 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1747
1748 Array<bool> interior_bdr(max_bdr_attr); interior_bdr = false;
1749 Array<bool> exterior_bdr(max_bdr_attr); exterior_bdr = false;
1750
1751 // Mark boundary attributes containing exterior faces while keeping track of
1752 // those which also contain interior faces.
1753 for (int be = 0; be < boundary.Size(); be++)
1754 {
1755 const int bea = boundary[be]->GetAttribute();
1756
1757 const int f = be_to_face[be];
1758
1759 if (FaceIsTrueInterior(f))
1760 {
1761 interior_bdr[bea-1] = true;
1762 }
1763 else
1764 {
1765 exterior_bdr[bea-1] = true;
1766 }
1767 }
1768
1769 // Mark attributes which were found to contain exterior faces and satisfy
1770 // the appropriate exclusivity requirement.
1771 for (int b = 0; b < max_bdr_attr; b++)
1772 {
1773 if (bdr_marker[b] == 0 && exterior_bdr[b])
1774 {
1775 if (!excl || !interior_bdr[b])
1776 {
1777 bdr_marker[b] = 1;
1778 }
1779 }
1780 }
1781}
1782
1783void Mesh::MarkNamedBoundaries(const std::string &set_name,
1784 Array<int> &bdr_marker) const
1785{
1786 const int max_bdr_attr = bdr_attributes.Max();
1787
1788 MFEM_VERIFY(bdr_attribute_sets.AttributeSetExists(set_name),
1789 "Named set is not defined in this mesh!");
1790 MFEM_VERIFY(bdr_marker.Size() >= max_bdr_attr,
1791 "bdr_marker must be at least bdr_attriburtes.Max() in length");
1792
1794
1795 for (int b = 0; b < max_bdr_attr; b++)
1796 {
1797 if (set_marker[b])
1798 {
1799 bdr_marker[b] = 1;
1800 }
1801 }
1802}
1803
1805{
1806 // in order of declaration:
1807 Dim = spaceDim = 0;
1808 NumOfVertices = -1;
1810 NumOfEdges = NumOfFaces = 0;
1811 nbInteriorFaces = -1;
1812 nbBoundaryFaces = -1;
1813 meshgen = mesh_geoms = 0;
1814 sequence = 0;
1815 nodes_sequence = 0;
1816 Nodes = NULL;
1817 own_nodes = 1;
1818 NURBSext = NULL;
1819 ncmesh = NULL;
1821}
1822
1824{
1825 el_to_edge =
1827 face_to_elem = NULL;
1828}
1829
1831{
1832 Init();
1833 InitTables();
1834}
1835
1837{
1838 delete el_to_edge;
1839 delete el_to_face;
1840 delete el_to_el;
1842
1843 if (Dim == 3)
1844 {
1845 delete bel_to_edge;
1846 }
1847
1848 delete face_edge;
1849 delete edge_vertex;
1850
1851 delete face_to_elem;
1852 face_to_elem = NULL;
1853}
1854
1856{
1857 if (own_nodes) { delete Nodes; }
1858
1859 delete ncmesh;
1860
1861 delete NURBSext;
1862
1863 for (int i = 0; i < NumOfElements; i++)
1864 {
1866 }
1867
1868 for (int i = 0; i < NumOfBdrElements; i++)
1869 {
1871 }
1872
1873 for (int i = 0; i < faces.Size(); i++)
1874 {
1875 FreeElement(faces[i]);
1876 }
1877
1878 DestroyTables();
1879}
1880
1882{
1884
1885 elements.DeleteAll();
1886 vertices.DeleteAll();
1887 boundary.DeleteAll();
1888 faces.DeleteAll();
1889 faces_info.DeleteAll();
1890 nc_faces_info.DeleteAll();
1892
1893 // TODO:
1894 // IsoparametricTransformations
1895 // Transformation, Transformation2, BdrTransformation, FaceTransformation,
1896 // EdgeTransformation;
1897 // FaceElementTransformations FaceElemTr;
1898
1900
1901#ifdef MFEM_USE_MEMALLOC
1902 TetMemory.Clear();
1903#endif
1904
1909
1912 // force de-allocation so after this mesh has the smallest memory footprint
1913 // possible
1914 inv_face_indices[0] = std::unordered_map<int, int>();
1915 inv_face_indices[1] = std::unordered_map<int, int>();
1916}
1917
1919{
1920 delete el_to_el; el_to_el = NULL;
1921 delete face_edge; face_edge = NULL;
1922 delete face_to_elem; face_to_elem = NULL;
1923 delete edge_vertex; edge_vertex = NULL;
1925 nbInteriorFaces = -1;
1926 nbBoundaryFaces = -1;
1927 // set size to 0 so re-computations can potentially avoid a new allocation
1930
1931 face_indices[0].SetSize(0);
1932 face_indices[1].SetSize(0);
1933 inv_face_indices[0].clear();
1934 inv_face_indices[1].clear();
1935}
1936
1937void Mesh::SetAttributes(bool elem_attrs_changed, bool bdr_face_attrs_changed)
1938{
1939 if (bdr_face_attrs_changed)
1940 {
1941 bdr_face_attrs_cache.SetSize(0); // Invalidate the cache
1942
1943 // Get sorted list of unique boundary element attributes
1944 std::set<int> attribs;
1945 for (int i = 0; i < GetNBE(); i++)
1946 {
1947 attribs.emplace(GetBdrAttribute(i));
1948 }
1949
1950 bdr_attributes.SetSize(attribs.size());
1952 std::copy(attribs.begin(), attribs.end(), bdr_attributes.begin());
1953 if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1954 {
1955 MFEM_WARNING("Non-positive attributes on the boundary!");
1956 }
1957 }
1958
1959 if (elem_attrs_changed)
1960 {
1961 // Re-compute the attributes cache
1964 // Get sorted list of unique element attributes
1965 std::set<int> attribs(elem_attrs_cache.begin(), elem_attrs_cache.end());
1966 attributes.SetSize(attribs.size());
1968 std::copy(attribs.begin(), attribs.end(), attributes.begin());
1969
1970 if (attributes.Size() > 0 && attributes[0] <= 0)
1971 {
1972 MFEM_WARNING("Non-positive attributes in the domain!");
1973 }
1974 }
1975}
1976
1977void Mesh::InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
1978{
1979 SetEmpty();
1980
1981 Dim = Dim_;
1982 spaceDim = spaceDim_;
1983
1984 NumOfVertices = 0;
1985 vertices.SetSize(NVert); // just allocate space for vertices
1986
1987 NumOfElements = 0;
1988 elements.SetSize(NElem); // just allocate space for Element *
1989
1990 NumOfBdrElements = 0;
1991 boundary.SetSize(NBdrElem); // just allocate space for Element *
1992}
1993
1994template<typename T>
1995static void CheckEnlarge(Array<T> &array, int size)
1996{
1997 if (size >= array.Size()) { array.SetSize(size + 1); }
1998}
1999
2001{
2002 CheckEnlarge(vertices, NumOfVertices);
2004 v[0] = x;
2005 v[1] = y;
2006 v[2] = z;
2007 return NumOfVertices++;
2008}
2009
2010int Mesh::AddVertex(const real_t *coords)
2011{
2012 CheckEnlarge(vertices, NumOfVertices);
2013 vertices[NumOfVertices].SetCoords(spaceDim, coords);
2014 return NumOfVertices++;
2015}
2016
2017int Mesh::AddVertex(const Vector &coords)
2018{
2019 MFEM_ASSERT(coords.Size() >= spaceDim,
2020 "invalid 'coords' size: " << coords.Size());
2021 return AddVertex(coords.GetData());
2022}
2023
2024void Mesh::AddVertexParents(int i, int p1, int p2)
2025{
2026 tmp_vertex_parents.Append(Triple<int, int, int>(i, p1, p2));
2027
2028 // if vertex coordinates are defined, make sure the hanging vertex has the
2029 // correct position
2030 if (i < vertices.Size())
2031 {
2032 real_t *vi = vertices[i](), *vp1 = vertices[p1](), *vp2 = vertices[p2]();
2033 for (int j = 0; j < 3; j++)
2034 {
2035 vi[j] = (vp1[j] + vp2[j]) * 0.5;
2036 }
2037 }
2038}
2039
2040int Mesh::AddVertexAtMeanCenter(const int *vi, int nverts, int dim)
2041{
2042 Vector vii(dim);
2043 vii = 0.0;
2044 for (int i = 0; i < nverts; i++)
2045 {
2046 real_t *vp = vertices[vi[i]]();
2047 for (int j = 0; j < dim; j++)
2048 {
2049 vii(j) += vp[j];
2050 }
2051 }
2052 vii /= nverts;
2053 AddVertex(vii);
2054 return NumOfVertices;
2055}
2056
2057int Mesh::AddSegment(int v1, int v2, int attr)
2058{
2059 CheckEnlarge(elements, NumOfElements);
2060 elements[NumOfElements] = new Segment(v1, v2, attr);
2061 return NumOfElements++;
2062}
2063
2064int Mesh::AddSegment(const int *vi, int attr)
2065{
2066 CheckEnlarge(elements, NumOfElements);
2067 elements[NumOfElements] = new Segment(vi, attr);
2068 return NumOfElements++;
2069}
2070
2071int Mesh::AddTriangle(int v1, int v2, int v3, int attr)
2072{
2073 CheckEnlarge(elements, NumOfElements);
2074 elements[NumOfElements] = new Triangle(v1, v2, v3, attr);
2075 return NumOfElements++;
2076}
2077
2078int Mesh::AddTriangle(const int *vi, int attr)
2079{
2080 CheckEnlarge(elements, NumOfElements);
2081 elements[NumOfElements] = new Triangle(vi, attr);
2082 return NumOfElements++;
2083}
2084
2085int Mesh::AddQuad(int v1, int v2, int v3, int v4, int attr)
2086{
2087 CheckEnlarge(elements, NumOfElements);
2088 elements[NumOfElements] = new Quadrilateral(v1, v2, v3, v4, attr);
2089 return NumOfElements++;
2090}
2091
2092int Mesh::AddQuad(const int *vi, int attr)
2093{
2094 CheckEnlarge(elements, NumOfElements);
2095 elements[NumOfElements] = new Quadrilateral(vi, attr);
2096 return NumOfElements++;
2097}
2098
2099int Mesh::AddTet(int v1, int v2, int v3, int v4, int attr)
2100{
2101 int vi[4] = {v1, v2, v3, v4};
2102 return AddTet(vi, attr);
2103}
2104
2105int Mesh::AddTet(const int *vi, int attr)
2106{
2107 CheckEnlarge(elements, NumOfElements);
2108#ifdef MFEM_USE_MEMALLOC
2109 Tetrahedron *tet;
2110 tet = TetMemory.Alloc();
2111 tet->SetVertices(vi);
2112 tet->SetAttribute(attr);
2113 elements[NumOfElements] = tet;
2114#else
2115 elements[NumOfElements] = new Tetrahedron(vi, attr);
2116#endif
2117 return NumOfElements++;
2118}
2119
2120int Mesh::AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr)
2121{
2122 CheckEnlarge(elements, NumOfElements);
2123 elements[NumOfElements] = new Wedge(v1, v2, v3, v4, v5, v6, attr);
2124 return NumOfElements++;
2125}
2126
2127int Mesh::AddWedge(const int *vi, int attr)
2128{
2129 CheckEnlarge(elements, NumOfElements);
2130 elements[NumOfElements] = new Wedge(vi, attr);
2131 return NumOfElements++;
2132}
2133
2134int Mesh::AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr)
2135{
2136 CheckEnlarge(elements, NumOfElements);
2137 elements[NumOfElements] = new Pyramid(v1, v2, v3, v4, v5, attr);
2138 return NumOfElements++;
2139}
2140
2141int Mesh::AddPyramid(const int *vi, int attr)
2142{
2143 CheckEnlarge(elements, NumOfElements);
2144 elements[NumOfElements] = new Pyramid(vi, attr);
2145 return NumOfElements++;
2146}
2147
2148int Mesh::AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
2149 int attr)
2150{
2151 CheckEnlarge(elements, NumOfElements);
2153 new Hexahedron(v1, v2, v3, v4, v5, v6, v7, v8, attr);
2154 return NumOfElements++;
2155}
2156
2157int Mesh::AddHex(const int *vi, int attr)
2158{
2159 CheckEnlarge(elements, NumOfElements);
2160 elements[NumOfElements] = new Hexahedron(vi, attr);
2161 return NumOfElements++;
2162}
2163
2164void Mesh::AddHexAsTets(const int *vi, int attr)
2165{
2166 static const int hex_to_tet[6][4] =
2167 {
2168 { 0, 1, 2, 6 }, { 0, 5, 1, 6 }, { 0, 4, 5, 6 },
2169 { 0, 2, 3, 6 }, { 0, 3, 7, 6 }, { 0, 7, 4, 6 }
2170 };
2171 int ti[4];
2172
2173 for (int i = 0; i < 6; i++)
2174 {
2175 for (int j = 0; j < 4; j++)
2176 {
2177 ti[j] = vi[hex_to_tet[i][j]];
2178 }
2179 AddTet(ti, attr);
2180 }
2181}
2182
2183void Mesh::AddHexAsWedges(const int *vi, int attr)
2184{
2185 static const int hex_to_wdg[2][6] =
2186 {
2187 { 0, 1, 2, 4, 5, 6 }, { 0, 2, 3, 4, 6, 7 }
2188 };
2189 int ti[6];
2190
2191 for (int i = 0; i < 2; i++)
2192 {
2193 for (int j = 0; j < 6; j++)
2194 {
2195 ti[j] = vi[hex_to_wdg[i][j]];
2196 }
2197 AddWedge(ti, attr);
2198 }
2199}
2200
2201void Mesh::AddHexAsPyramids(const int *vi, int attr)
2202{
2203 static const int hex_to_pyr[6][5] =
2204 {
2205 { 0, 1, 2, 3, 8 }, { 0, 4, 5, 1, 8 }, { 1, 5, 6, 2, 8 },
2206 { 2, 6, 7, 3, 8 }, { 3, 7, 4, 0, 8 }, { 7, 6, 5, 4, 8 }
2207 };
2208 int ti[5];
2209
2210 for (int i = 0; i < 6; i++)
2211 {
2212 for (int j = 0; j < 5; j++)
2213 {
2214 ti[j] = vi[hex_to_pyr[i][j]];
2215 }
2216 AddPyramid(ti, attr);
2217 }
2218}
2219
2220void Mesh::AddQuadAs4TrisWithPoints(int *vi, int attr)
2221{
2222 int num_faces = 4;
2223 static const int quad_to_tri[4][2] =
2224 {
2225 {0, 1}, {1, 2}, {2, 3}, {3, 0}
2226 };
2227
2228 int elem_center_index = AddVertexAtMeanCenter(vi, 4, 2) - 1;
2229
2230 int ti[3];
2231 ti[2] = elem_center_index;
2232 for (int i = 0; i < num_faces; i++)
2233 {
2234 for (int j = 0; j < 2; j++)
2235 {
2236 ti[j] = vi[quad_to_tri[i][j]];
2237 }
2238 AddTri(ti, attr);
2239 }
2240}
2241
2242void Mesh::AddQuadAs5QuadsWithPoints(int *vi, int attr)
2243{
2244 int num_faces = 4;
2245 static const int quad_faces[4][2] =
2246 {
2247 {0, 1}, {1, 2}, {2, 3}, {3, 0}
2248 };
2249
2250 Vector px(4), py(4);
2251 for (int i = 0; i < 4; i++)
2252 {
2253 real_t *vp = vertices[vi[i]]();
2254 px(i) = vp[0];
2255 py(i) = vp[1];
2256 }
2257
2258 int vnew_index[4];
2259 real_t vnew[2];
2260 real_t r = 0.25, s = 0.25;
2261 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2262 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2263 AddVertex(vnew);
2264 vnew_index[0] = NumOfVertices-1;
2265
2266 r = 0.75, s = 0.25;
2267 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2268 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2269 AddVertex(vnew);
2270 vnew_index[1] = NumOfVertices-1;
2271
2272 r = 0.75, s = 0.75;
2273 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2274 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2275 AddVertex(vnew);
2276 vnew_index[2] = NumOfVertices-1;
2277
2278 r = 0.25, s = 0.75;
2279 vnew[0] = px(0)*(1-r)*(1-s) + px(1)*(r)*(1-s) + px(2)*r*s + px(3)*(1-r)*s;
2280 vnew[1] = py(0)*(1-r)*(1-s) + py(1)*(r)*(1-s) + py(2)*r*s + py(3)*(1-r)*s;
2281 AddVertex(vnew);
2282 vnew_index[3] = NumOfVertices-1;
2283
2284 static const int quad_faces_new[4][2] =
2285 {
2286 { 1, 0}, { 2, 1}, { 3, 2}, { 0, 3}
2287 };
2288
2289 int ti[4];
2290 for (int i = 0; i < num_faces; i++)
2291 {
2292 for (int j = 0; j < 2; j++)
2293 {
2294 ti[j] = vi[quad_faces[i][j]];
2295 ti[j+2] = vnew_index[quad_faces_new[i][j]];
2296 }
2297 AddQuad(ti, attr);
2298 }
2299 AddQuad(vnew_index, attr);
2300}
2301
2303 std::map<std::array<int, 4>, int> &hex_face_verts,
2304 int attr)
2305{
2306 auto get4arraysorted = [](Array<int> v)
2307 {
2308 v.Sort();
2309 return std::array<int, 4> {v[0], v[1], v[2], v[3]};
2310 };
2311
2312 int num_faces = 6;
2313 static const int hex_to_tet[6][4] =
2314 {
2315 { 0, 1, 2, 3 }, { 1, 2, 6, 5 }, { 5, 4, 7, 6},
2316 { 0, 1, 5, 4 }, { 2, 3, 7, 6 }, { 0,3, 7, 4}
2317 };
2318
2319 int elem_center_index = AddVertexAtMeanCenter(vi, 8, 3) - 1;
2320
2321 Array<int> flist(4);
2322
2323 // local vertex indices for each of the 4 edges of the face
2324 static const int tet_face[4][2] =
2325 {
2326 {0, 1}, {1, 2}, {3, 2}, {3, 0}
2327 };
2328
2329 for (int i = 0; i < num_faces; i++)
2330 {
2331 for (int j = 0; j < 4; j++)
2332 {
2333 flist[j] = vi[hex_to_tet[i][j]];
2334 }
2335 int face_center_index;
2336
2337 auto t = get4arraysorted(flist);
2338 auto it = hex_face_verts.find(t);
2339 if (it == hex_face_verts.end())
2340 {
2341 face_center_index = AddVertexAtMeanCenter(flist.GetData(),
2342 flist.Size(), 3) - 1;
2343 hex_face_verts.insert({t, face_center_index});
2344 }
2345 else
2346 {
2347 face_center_index = it->second;
2348 }
2349 int fti[4];
2350 fti[2] = face_center_index;
2351 fti[3] = elem_center_index;
2352 for (int j = 0; j < 4; j++)
2353 {
2354 for (int k = 0; k < 2; k++)
2355 {
2356 fti[k] = flist[tet_face[j][k]];
2357 }
2358 AddTet(fti, attr);
2359 }
2360 }
2361}
2362
2364{
2365 CheckEnlarge(elements, NumOfElements);
2366 elements[NumOfElements] = elem;
2367 return NumOfElements++;
2368}
2369
2371{
2372 CheckEnlarge(boundary, NumOfBdrElements);
2373 boundary[NumOfBdrElements] = elem;
2374 return NumOfBdrElements++;
2375}
2376
2378 const Array<int> &new_be_to_face)
2379{
2380 boundary.Reserve(boundary.Size() + bdr_elems.Size());
2381 MFEM_ASSERT(bdr_elems.Size() == new_be_to_face.Size(), "wrong size");
2382 for (int i = 0; i < bdr_elems.Size(); i++)
2383 {
2384 AddBdrElement(bdr_elems[i]);
2385 }
2386 be_to_face.Append(new_be_to_face);
2387}
2388
2389int Mesh::AddBdrSegment(int v1, int v2, int attr)
2390{
2391 CheckEnlarge(boundary, NumOfBdrElements);
2392 boundary[NumOfBdrElements] = new Segment(v1, v2, attr);
2393 return NumOfBdrElements++;
2394}
2395
2396int Mesh::AddBdrSegment(const int *vi, int attr)
2397{
2398 CheckEnlarge(boundary, NumOfBdrElements);
2399 boundary[NumOfBdrElements] = new Segment(vi, attr);
2400 return NumOfBdrElements++;
2401}
2402
2403int Mesh::AddBdrTriangle(int v1, int v2, int v3, int attr)
2404{
2405 CheckEnlarge(boundary, NumOfBdrElements);
2406 boundary[NumOfBdrElements] = new Triangle(v1, v2, v3, attr);
2407 return NumOfBdrElements++;
2408}
2409
2410int Mesh::AddBdrTriangle(const int *vi, int attr)
2411{
2412 CheckEnlarge(boundary, NumOfBdrElements);
2413 boundary[NumOfBdrElements] = new Triangle(vi, attr);
2414 return NumOfBdrElements++;
2415}
2416
2417int Mesh::AddBdrQuad(int v1, int v2, int v3, int v4, int attr)
2418{
2419 CheckEnlarge(boundary, NumOfBdrElements);
2420 boundary[NumOfBdrElements] = new Quadrilateral(v1, v2, v3, v4, attr);
2421 return NumOfBdrElements++;
2422}
2423
2424int Mesh::AddBdrQuad(const int *vi, int attr)
2425{
2426 CheckEnlarge(boundary, NumOfBdrElements);
2427 boundary[NumOfBdrElements] = new Quadrilateral(vi, attr);
2428 return NumOfBdrElements++;
2429}
2430
2431void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
2432{
2433 static const int quad_to_tri[2][3] = { { 0, 1, 2 }, { 0, 2, 3 } };
2434 int ti[3];
2435
2436 for (int i = 0; i < 2; i++)
2437 {
2438 for (int j = 0; j < 3; j++)
2439 {
2440 ti[j] = vi[quad_to_tri[i][j]];
2441 }
2442 AddBdrTriangle(ti, attr);
2443 }
2444}
2445
2446int Mesh::AddBdrPoint(int v, int attr)
2447{
2448 CheckEnlarge(boundary, NumOfBdrElements);
2449 boundary[NumOfBdrElements] = new Point(&v, attr);
2450 return NumOfBdrElements++;
2451}
2452
2454{
2455 for (auto &b : boundary)
2456 {
2457 FreeElement(b);
2458 }
2459
2460 if (Dim == 3)
2461 {
2462 delete bel_to_edge;
2463 bel_to_edge = NULL;
2464 }
2465
2466 // count the 'NumOfBdrElements'
2467 NumOfBdrElements = 0;
2468 for (const auto &fi : faces_info)
2469 {
2470 if (fi.Elem2No < 0) { ++NumOfBdrElements; }
2471 }
2472
2473 // Add the boundary elements
2476 for (int i = 0, j = 0; i < faces_info.Size(); i++)
2477 {
2478 if (faces_info[i].Elem2No < 0)
2479 {
2480 boundary[j] = faces[i]->Duplicate(this);
2481 be_to_face[j++] = i;
2482 }
2483 }
2484
2485 // Note: in 3D, 'bel_to_edge' is destroyed but it's not updated.
2486}
2487
2489{
2490 MFEM_VERIFY(vertices.Size() == NumOfVertices ||
2491 vertices.Size() == 0,
2492 "incorrect number of vertices: preallocated: " << vertices.Size()
2493 << ", actually added: " << NumOfVertices);
2494 MFEM_VERIFY(elements.Size() == NumOfElements,
2495 "incorrect number of elements: preallocated: " << elements.Size()
2496 << ", actually added: " << NumOfElements);
2497 MFEM_VERIFY(boundary.Size() == NumOfBdrElements,
2498 "incorrect number of boundary elements: preallocated: "
2499 << boundary.Size() << ", actually added: " << NumOfBdrElements);
2500}
2501
2502void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
2503{
2504 FinalizeCheck();
2505 CheckElementOrientation(fix_orientation);
2506
2507 if (refine)
2508 {
2510 }
2511
2512 if (generate_edges)
2513 {
2514 el_to_edge = new Table;
2516 GenerateFaces();
2518 }
2519 else
2520 {
2521 NumOfEdges = 0;
2522 }
2523
2524 NumOfFaces = 0;
2525
2526 SetAttributes();
2527
2528 SetMeshGen();
2529}
2530
2531void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
2532 bool fix_orientation)
2533{
2534 FinalizeCheck();
2535 if (fix_orientation)
2536 {
2537 CheckElementOrientation(fix_orientation);
2538 }
2539
2540 if (generate_edges)
2541 {
2542 el_to_edge = new Table;
2544 GenerateFaces();
2546 }
2547 else
2548 {
2549 NumOfEdges = 0;
2550 }
2551
2552 NumOfFaces = 0;
2553
2554 SetAttributes();
2555
2556 SetMeshGen();
2557}
2558
2559
2560class GeckoProgress : public Gecko::Progress
2561{
2562 real_t limit;
2563 mutable StopWatch sw;
2564public:
2565 GeckoProgress(real_t limit) : limit(limit) { sw.Start(); }
2566 bool quit() const override { return limit > 0 && sw.UserTime() > limit; }
2567};
2568
2569class GeckoVerboseProgress : public GeckoProgress
2570{
2571 using Float = Gecko::Float;
2572 using Graph = Gecko::Graph;
2573 using uint = Gecko::uint;
2574public:
2575 GeckoVerboseProgress(real_t limit) : GeckoProgress(limit) {}
2576
2577 void beginorder(const Graph* graph, Float cost) const override
2578 { mfem::out << "Begin Gecko ordering, cost = " << cost << std::endl; }
2579 void endorder(const Graph* graph, Float cost) const override
2580 { mfem::out << "End ordering, cost = " << cost << std::endl; }
2581
2582 void beginiter(const Graph* graph,
2583 uint iter, uint maxiter, uint window) const override
2584 {
2585 mfem::out << "Iteration " << iter << "/" << maxiter << ", window "
2586 << window << std::flush;
2587 }
2588 void enditer(const Graph* graph, Float mincost, Float cost) const override
2589 { mfem::out << ", cost = " << cost << endl; }
2590};
2591
2592
2594 int iterations, int window,
2595 int period, int seed, bool verbose,
2596 real_t time_limit)
2597{
2598 Gecko::Graph graph;
2599 Gecko::FunctionalGeometric functional; // edge product cost
2600
2601 GeckoProgress progress(time_limit);
2602 GeckoVerboseProgress vprogress(time_limit);
2603
2604 // insert elements as nodes in the graph
2605 for (int elemid = 0; elemid < GetNE(); ++elemid)
2606 {
2607 graph.insert_node();
2608 }
2609
2610 // insert graph edges for element neighbors
2611 // NOTE: indices in Gecko are 1 based hence the +1 on insertion
2612 const Table &my_el_to_el = ElementToElementTable();
2613 for (int elemid = 0; elemid < GetNE(); ++elemid)
2614 {
2615 const int *neighid = my_el_to_el.GetRow(elemid);
2616 for (int i = 0; i < my_el_to_el.RowSize(elemid); ++i)
2617 {
2618 graph.insert_arc(elemid + 1, neighid[i] + 1);
2619 }
2620 }
2621
2622 // get the ordering from Gecko and copy it into the Array<int>
2623 graph.order(&functional, iterations, window, period, seed,
2624 verbose ? &vprogress : &progress);
2625
2626 ordering.SetSize(GetNE());
2628 for (Gecko::Node::Index gnodeid = 1; gnodeid <= NE; ++gnodeid)
2629 {
2630 ordering[gnodeid - 1] = graph.rank(gnodeid);
2631 }
2632
2633 return graph.cost();
2634}
2635
2636
2637struct HilbertCmp
2638{
2639 int coord;
2640 bool dir;
2641 const Array<real_t> &points;
2642 real_t mid;
2643
2644 HilbertCmp(int coord, bool dir, const Array<real_t> &points, real_t mid)
2645 : coord(coord), dir(dir), points(points), mid(mid) {}
2646
2647 bool operator()(int i) const
2648 {
2649 return (points[3*i + coord] < mid) != dir;
2650 }
2651};
2652
2653static void HilbertSort2D(int coord1, // major coordinate to sort points by
2654 bool dir1, // sort coord1 ascending/descending?
2655 bool dir2, // sort coord2 ascending/descending?
2656 const Array<real_t> &points, int *beg, int *end,
2657 real_t xmin, real_t ymin, real_t xmax, real_t ymax)
2658{
2659 if (end - beg <= 1) { return; }
2660
2661 real_t xmid = (xmin + xmax)*0.5;
2662 real_t ymid = (ymin + ymax)*0.5;
2663
2664 int coord2 = (coord1 + 1) % 2; // the 'other' coordinate
2665
2666 // sort (partition) points into four quadrants
2667 int *p0 = beg, *p4 = end;
2668 int *p2 = std::partition(p0, p4, HilbertCmp(coord1, dir1, points, xmid));
2669 int *p1 = std::partition(p0, p2, HilbertCmp(coord2, dir2, points, ymid));
2670 int *p3 = std::partition(p2, p4, HilbertCmp(coord2, !dir2, points, ymid));
2671
2672 if (p1 != p4)
2673 {
2674 HilbertSort2D(coord2, dir2, dir1, points, p0, p1,
2675 ymin, xmin, ymid, xmid);
2676 }
2677 if (p1 != p0 || p2 != p4)
2678 {
2679 HilbertSort2D(coord1, dir1, dir2, points, p1, p2,
2680 xmin, ymid, xmid, ymax);
2681 }
2682 if (p2 != p0 || p3 != p4)
2683 {
2684 HilbertSort2D(coord1, dir1, dir2, points, p2, p3,
2685 xmid, ymid, xmax, ymax);
2686 }
2687 if (p3 != p0)
2688 {
2689 HilbertSort2D(coord2, !dir2, !dir1, points, p3, p4,
2690 ymid, xmax, ymin, xmid);
2691 }
2692}
2693
2694static void HilbertSort3D(int coord1, bool dir1, bool dir2, bool dir3,
2695 const Array<real_t> &points, int *beg, int *end,
2696 real_t xmin, real_t ymin, real_t zmin,
2697 real_t xmax, real_t ymax, real_t zmax)
2698{
2699 if (end - beg <= 1) { return; }
2700
2701 real_t xmid = (xmin + xmax)*0.5;
2702 real_t ymid = (ymin + ymax)*0.5;
2703 real_t zmid = (zmin + zmax)*0.5;
2704
2705 int coord2 = (coord1 + 1) % 3;
2706 int coord3 = (coord1 + 2) % 3;
2707
2708 // sort (partition) points into eight octants
2709 int *p0 = beg, *p8 = end;
2710 int *p4 = std::partition(p0, p8, HilbertCmp(coord1, dir1, points, xmid));
2711 int *p2 = std::partition(p0, p4, HilbertCmp(coord2, dir2, points, ymid));
2712 int *p6 = std::partition(p4, p8, HilbertCmp(coord2, !dir2, points, ymid));
2713 int *p1 = std::partition(p0, p2, HilbertCmp(coord3, dir3, points, zmid));
2714 int *p3 = std::partition(p2, p4, HilbertCmp(coord3, !dir3, points, zmid));
2715 int *p5 = std::partition(p4, p6, HilbertCmp(coord3, dir3, points, zmid));
2716 int *p7 = std::partition(p6, p8, HilbertCmp(coord3, !dir3, points, zmid));
2717
2718 if (p1 != p8)
2719 {
2720 HilbertSort3D(coord3, dir3, dir1, dir2, points, p0, p1,
2721 zmin, xmin, ymin, zmid, xmid, ymid);
2722 }
2723 if (p1 != p0 || p2 != p8)
2724 {
2725 HilbertSort3D(coord2, dir2, dir3, dir1, points, p1, p2,
2726 ymin, zmid, xmin, ymid, zmax, xmid);
2727 }
2728 if (p2 != p0 || p3 != p8)
2729 {
2730 HilbertSort3D(coord2, dir2, dir3, dir1, points, p2, p3,
2731 ymid, zmid, xmin, ymax, zmax, xmid);
2732 }
2733 if (p3 != p0 || p4 != p8)
2734 {
2735 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p3, p4,
2736 xmin, ymax, zmid, xmid, ymid, zmin);
2737 }
2738 if (p4 != p0 || p5 != p8)
2739 {
2740 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p4, p5,
2741 xmid, ymax, zmid, xmax, ymid, zmin);
2742 }
2743 if (p5 != p0 || p6 != p8)
2744 {
2745 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p5, p6,
2746 ymax, zmid, xmax, ymid, zmax, xmid);
2747 }
2748 if (p6 != p0 || p7 != p8)
2749 {
2750 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p6, p7,
2751 ymid, zmid, xmax, ymin, zmax, xmid);
2752 }
2753 if (p7 != p0)
2754 {
2755 HilbertSort3D(coord3, !dir3, !dir1, dir2, points, p7, p8,
2756 zmid, xmax, ymin, zmin, xmid, ymid);
2757 }
2758}
2759
2761{
2762 MFEM_VERIFY(spaceDim <= 3, "");
2763
2764 Vector min, max, center;
2765 GetBoundingBox(min, max);
2766
2767 Array<int> indices(GetNE());
2768 Array<real_t> points(3*GetNE());
2769
2770 if (spaceDim < 3) { points = 0.0; }
2771
2772 // calculate element centers
2773 for (int i = 0; i < GetNE(); i++)
2774 {
2775 GetElementCenter(i, center);
2776 for (int j = 0; j < spaceDim; j++)
2777 {
2778 points[3*i + j] = center(j);
2779 }
2780 indices[i] = i;
2781 }
2782
2783 if (spaceDim == 1)
2784 {
2785 indices.Sort([&](int a, int b)
2786 { return points[3*a] < points[3*b]; });
2787 }
2788 else if (spaceDim == 2)
2789 {
2790 // recursively partition the points in 2D
2791 HilbertSort2D(0, false, false,
2792 points, indices.begin(), indices.end(),
2793 min(0), min(1), max(0), max(1));
2794 }
2795 else
2796 {
2797 // recursively partition the points in 3D
2798 HilbertSort3D(0, false, false, false,
2799 points, indices.begin(), indices.end(),
2800 min(0), min(1), min(2), max(0), max(1), max(2));
2801 }
2802
2803 // return ordering in the format required by ReorderElements
2804 ordering.SetSize(GetNE());
2805 for (int i = 0; i < GetNE(); i++)
2806 {
2807 ordering[indices[i]] = i;
2808 }
2809}
2810
2811
2812void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
2813{
2814 if (NURBSext)
2815 {
2816 MFEM_WARNING("element reordering of NURBS meshes is not supported.");
2817 return;
2818 }
2819 if (ncmesh)
2820 {
2821 MFEM_WARNING("element reordering of non-conforming meshes is not"
2822 " supported.");
2823 return;
2824 }
2825 MFEM_VERIFY(ordering.Size() == GetNE(), "invalid reordering array.")
2826
2827 // Data members that need to be updated:
2828
2829 // - elements - reorder of the pointers and the vertex ids if reordering
2830 // the vertices
2831 // - vertices - if reordering the vertices
2832 // - boundary - update the vertex ids, if reordering the vertices
2833 // - faces - regenerate
2834 // - faces_info - regenerate
2835
2836 // Deleted by DeleteTables():
2837 // - el_to_edge - rebuild in 2D and 3D only
2838 // - el_to_face - rebuild in 3D only
2839 // - bel_to_edge - rebuild in 3D only
2840 // - el_to_el - no need to rebuild
2841 // - face_edge - no need to rebuild
2842 // - edge_vertex - no need to rebuild
2843 // - geom_factors - no need to rebuild
2844
2845 // - be_to_face
2846
2847 // - Nodes
2848
2849 // Save the locations of the Nodes so we can rebuild them later
2850 Array<Vector*> old_elem_node_vals;
2851 FiniteElementSpace *nodes_fes = NULL;
2852 if (Nodes)
2853 {
2854 old_elem_node_vals.SetSize(GetNE());
2855 nodes_fes = Nodes->FESpace();
2856 Array<int> old_dofs;
2857 Vector vals;
2858 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2859 {
2860 nodes_fes->GetElementVDofs(old_elid, old_dofs);
2861 Nodes->GetSubVector(old_dofs, vals);
2862 old_elem_node_vals[old_elid] = new Vector(vals);
2863 }
2864 }
2865
2866 // Get the newly ordered elements
2867 Array<Element *> new_elements(GetNE());
2868 for (int old_elid = 0; old_elid < ordering.Size(); ++old_elid)
2869 {
2870 int new_elid = ordering[old_elid];
2871 new_elements[new_elid] = elements[old_elid];
2872 }
2873 mfem::Swap(elements, new_elements);
2874 new_elements.DeleteAll();
2875
2876 if (reorder_vertices)
2877 {
2878 // Get the new vertex ordering permutation vectors and fill the new
2879 // vertices
2880 Array<int> vertex_ordering(GetNV());
2881 vertex_ordering = -1;
2882 Array<Vertex> new_vertices(GetNV());
2883 int new_vertex_ind = 0;
2884 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2885 {
2886 int *elem_vert = elements[new_elid]->GetVertices();
2887 int nv = elements[new_elid]->GetNVertices();
2888 for (int vi = 0; vi < nv; ++vi)
2889 {
2890 int old_vertex_ind = elem_vert[vi];
2891 if (vertex_ordering[old_vertex_ind] == -1)
2892 {
2893 vertex_ordering[old_vertex_ind] = new_vertex_ind;
2894 new_vertices[new_vertex_ind] = vertices[old_vertex_ind];
2895 new_vertex_ind++;
2896 }
2897 }
2898 }
2899 mfem::Swap(vertices, new_vertices);
2900 new_vertices.DeleteAll();
2901
2902 // Replace the vertex ids in the elements with the reordered vertex
2903 // numbers
2904 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2905 {
2906 int *elem_vert = elements[new_elid]->GetVertices();
2907 int nv = elements[new_elid]->GetNVertices();
2908 for (int vi = 0; vi < nv; ++vi)
2909 {
2910 elem_vert[vi] = vertex_ordering[elem_vert[vi]];
2911 }
2912 }
2913
2914 // Replace the vertex ids in the boundary with reordered vertex numbers
2915 for (int belid = 0; belid < GetNBE(); ++belid)
2916 {
2917 int *be_vert = boundary[belid]->GetVertices();
2918 int nv = boundary[belid]->GetNVertices();
2919 for (int vi = 0; vi < nv; ++vi)
2920 {
2921 be_vert[vi] = vertex_ordering[be_vert[vi]];
2922 }
2923 }
2924 }
2925
2926 // Destroy tables that need to be rebuild
2927 DeleteTables();
2928
2929 if (Dim > 1)
2930 {
2931 // generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
2932 el_to_edge = new Table;
2934 }
2935 if (Dim > 2)
2936 {
2937 // generate el_to_face, be_to_face
2939 }
2940 // Update faces and faces_info
2941 GenerateFaces();
2942
2943 // Build the nodes from the saved locations if they were around before
2944 if (Nodes)
2945 {
2946 // To force FE space update, we need to increase 'sequence':
2947 sequence++;
2950 nodes_fes->Update(false); // want_transform = false
2951 Nodes->Update(); // just needed to update Nodes->sequence
2952 Array<int> new_dofs;
2953 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2954 {
2955 int new_elid = ordering[old_elid];
2956 nodes_fes->GetElementVDofs(new_elid, new_dofs);
2957 Nodes->SetSubVector(new_dofs, *(old_elem_node_vals[old_elid]));
2958 delete old_elem_node_vals[old_elid];
2959 }
2960 }
2961}
2962
2963
2965{
2966 if (meshgen & 1)
2967 {
2968 if (Dim == 2)
2969 {
2971 }
2972 else if (Dim == 3)
2973 {
2974 DSTable v_to_v(NumOfVertices);
2975 GetVertexToVertexTable(v_to_v);
2977 }
2978 }
2979}
2980
2982{
2983 // Mark the longest triangle edge by rotating the indices so that
2984 // vertex 0 - vertex 1 is the longest edge in the triangle.
2985 DenseMatrix pmat;
2986 for (int i = 0; i < NumOfElements; i++)
2987 {
2988 if (elements[i]->GetType() == Element::TRIANGLE)
2989 {
2990 GetPointMatrix(i, pmat);
2991 static_cast<Triangle*>(elements[i])->MarkEdge(pmat);
2992 }
2993 }
2994}
2995
2996void Mesh::GetEdgeOrdering(const DSTable &v_to_v, Array<int> &order)
2997{
2998 NumOfEdges = v_to_v.NumberOfEntries();
2999 order.SetSize(NumOfEdges);
3000 Array<Pair<real_t, int> > length_idx(NumOfEdges);
3001
3002 for (int i = 0; i < NumOfVertices; i++)
3003 {
3004 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
3005 {
3006 int j = it.Index();
3007 length_idx[j].one = GetLength(i, it.Column());
3008 length_idx[j].two = j;
3009 }
3010 }
3011
3012 // Sort by increasing edge-length.
3013 length_idx.Sort();
3014
3015 for (int i = 0; i < NumOfEdges; i++)
3016 {
3017 order[length_idx[i].two] = i;
3018 }
3019}
3020
3022{
3023 // Mark the longest tetrahedral edge by rotating the indices so that
3024 // vertex 0 - vertex 1 is the longest edge in the element.
3025 Array<int> order;
3026 GetEdgeOrdering(v_to_v, order);
3027
3028 for (int i = 0; i < NumOfElements; i++)
3029 {
3030 if (elements[i]->GetType() == Element::TETRAHEDRON)
3031 {
3032 elements[i]->MarkEdge(v_to_v, order);
3033 }
3034 }
3035 for (int i = 0; i < NumOfBdrElements; i++)
3036 {
3037 if (boundary[i]->GetType() == Element::TRIANGLE)
3038 {
3039 boundary[i]->MarkEdge(v_to_v, order);
3040 }
3041 }
3042}
3043
3044void Mesh::PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
3045{
3046 if (*old_v_to_v && *old_elem_vert)
3047 {
3048 return;
3049 }
3050
3052
3053 if (*old_v_to_v == NULL)
3054 {
3055 bool need_v_to_v = false;
3056 Array<int> dofs;
3057 for (int i = 0; i < GetNEdges(); i++)
3058 {
3059 // Since edge indices may change, we need to permute edge interior dofs
3060 // any time an edge index changes and there is at least one dof on that
3061 // edge.
3062 fes->GetEdgeInteriorDofs(i, dofs);
3063 if (dofs.Size() > 0)
3064 {
3065 need_v_to_v = true;
3066 break;
3067 }
3068 }
3069 if (need_v_to_v)
3070 {
3071 *old_v_to_v = new DSTable(NumOfVertices);
3072 GetVertexToVertexTable(*(*old_v_to_v));
3073 }
3074 }
3075 if (*old_elem_vert == NULL)
3076 {
3077 bool need_elem_vert = false;
3078 Array<int> dofs;
3079 for (int i = 0; i < GetNE(); i++)
3080 {
3081 // Since element indices do not change, we need to permute element
3082 // interior dofs only when there are at least 2 interior dofs in an
3083 // element (assuming the nodal dofs are non-directional).
3084 fes->GetElementInteriorDofs(i, dofs);
3085 if (dofs.Size() > 1)
3086 {
3087 need_elem_vert = true;
3088 break;
3089 }
3090 }
3091 if (need_elem_vert)
3092 {
3093 *old_elem_vert = new Table;
3094 (*old_elem_vert)->MakeI(GetNE());
3095 for (int i = 0; i < GetNE(); i++)
3096 {
3097 (*old_elem_vert)->AddColumnsInRow(i, elements[i]->GetNVertices());
3098 }
3099 (*old_elem_vert)->MakeJ();
3100 for (int i = 0; i < GetNE(); i++)
3101 {
3102 (*old_elem_vert)->AddConnections(i, elements[i]->GetVertices(),
3103 elements[i]->GetNVertices());
3104 }
3105 (*old_elem_vert)->ShiftUpI();
3106 }
3107 }
3108}
3109
3110void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
3111{
3113 const FiniteElementCollection *fec = fes->FEColl();
3114 Array<int> old_dofs, new_dofs;
3115
3116 // assuming that all edges have the same number of dofs
3117 if (NumOfEdges) { fes->GetEdgeInteriorDofs(0, old_dofs); }
3118 const int num_edge_dofs = old_dofs.Size();
3119
3120 // Save the original nodes
3121 Nodes->HostReadWrite(); // for "(*Nodes)() = "
3122 const Vector onodes = *Nodes;
3123
3124 // vertex dofs do not need to be moved
3125 fes->GetVertexDofs(0, old_dofs);
3126 int offset = NumOfVertices * old_dofs.Size();
3127
3128 // edge dofs:
3129 // edge enumeration may be different but edge orientation is the same
3130 if (num_edge_dofs > 0)
3131 {
3132 DSTable new_v_to_v(NumOfVertices);
3133 GetVertexToVertexTable(new_v_to_v);
3134
3135 for (int i = 0; i < NumOfVertices; i++)
3136 {
3137 for (DSTable::RowIterator it(new_v_to_v, i); !it; ++it)
3138 {
3139 const int old_i = (*old_v_to_v)(i, it.Column());
3140 const int new_i = it.Index();
3141 if (new_i == old_i) { continue; }
3142
3143 old_dofs.SetSize(num_edge_dofs);
3144 new_dofs.SetSize(num_edge_dofs);
3145 for (int j = 0; j < num_edge_dofs; j++)
3146 {
3147 old_dofs[j] = offset + old_i * num_edge_dofs + j;
3148 new_dofs[j] = offset + new_i * num_edge_dofs + j;
3149 }
3150 fes->DofsToVDofs(old_dofs);
3151 fes->DofsToVDofs(new_dofs);
3152 for (int j = 0; j < old_dofs.Size(); j++)
3153 {
3154 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3155 }
3156 }
3157 }
3158 offset += NumOfEdges * num_edge_dofs;
3159 }
3160
3161 // face dofs:
3162 // both enumeration and orientation of the faces may be different
3163 if (fes->GetNFDofs() > 0)
3164 {
3165 // generate the old face-vertex table using the unmodified 'faces'
3166 Table old_face_vertex;
3167 old_face_vertex.MakeI(NumOfFaces);
3168 for (int i = 0; i < NumOfFaces; i++)
3169 {
3170 old_face_vertex.AddColumnsInRow(i, faces[i]->GetNVertices());
3171 }
3172 old_face_vertex.MakeJ();
3173 for (int i = 0; i < NumOfFaces; i++)
3174 old_face_vertex.AddConnections(i, faces[i]->GetVertices(),
3175 faces[i]->GetNVertices());
3176 old_face_vertex.ShiftUpI();
3177
3178 // update 'el_to_face', 'be_to_face', 'faces', and 'faces_info'
3179 STable3D *faces_tbl = GetElementToFaceTable(1);
3180 GenerateFaces();
3181
3182 // compute the new face dof offsets
3183 Array<int> new_fdofs(NumOfFaces+1);
3184 new_fdofs[0] = 0;
3185 for (int i = 0; i < NumOfFaces; i++) // i = old face index
3186 {
3187 const int *old_v = old_face_vertex.GetRow(i);
3188 int new_i; // new face index
3189 switch (old_face_vertex.RowSize(i))
3190 {
3191 case 3:
3192 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
3193 break;
3194 case 4:
3195 default:
3196 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
3197 break;
3198 }
3199 fes->GetFaceInteriorDofs(i, old_dofs);
3200 new_fdofs[new_i+1] = old_dofs.Size();
3201 }
3202 new_fdofs.PartialSum();
3203
3204 // loop over the old face numbers
3205 for (int i = 0; i < NumOfFaces; i++)
3206 {
3207 const int *old_v = old_face_vertex.GetRow(i), *new_v;
3208 const int *dof_ord;
3209 int new_i, new_or;
3210 switch (old_face_vertex.RowSize(i))
3211 {
3212 case 3:
3213 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
3214 new_v = faces[new_i]->GetVertices();
3215 new_or = GetTriOrientation(old_v, new_v);
3216 dof_ord = fec->DofOrderForOrientation(Geometry::TRIANGLE, new_or);
3217 break;
3218 case 4:
3219 default:
3220 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
3221 new_v = faces[new_i]->GetVertices();
3222 new_or = GetQuadOrientation(old_v, new_v);
3223 dof_ord = fec->DofOrderForOrientation(Geometry::SQUARE, new_or);
3224 break;
3225 }
3226
3227 fes->GetFaceInteriorDofs(i, old_dofs);
3228 new_dofs.SetSize(old_dofs.Size());
3229 for (int j = 0; j < old_dofs.Size(); j++)
3230 {
3231 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
3232 const int old_j = dof_ord[j];
3233 new_dofs[old_j] = offset + new_fdofs[new_i] + j;
3234 }
3235 fes->DofsToVDofs(old_dofs);
3236 fes->DofsToVDofs(new_dofs);
3237 for (int j = 0; j < old_dofs.Size(); j++)
3238 {
3239 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3240 }
3241 }
3242
3243 offset += fes->GetNFDofs();
3244 delete faces_tbl;
3245 }
3246
3247 // element dofs:
3248 // element orientation may be different
3249 if (old_elem_vert) // have elements with 2 or more dofs
3250 {
3251 // matters when the 'fec' is
3252 // (this code is executed only for triangles/tets)
3253 // - Pk on triangles, k >= 4
3254 // - Qk on quads, k >= 3
3255 // - Pk on tets, k >= 5
3256 // - Qk on hexes, k >= 3
3257 // - DG spaces
3258 // - ...
3259
3260 // loop over all elements
3261 for (int i = 0; i < GetNE(); i++)
3262 {
3263 const int *old_v = old_elem_vert->GetRow(i);
3264 const int *new_v = elements[i]->GetVertices();
3265 const int *dof_ord;
3266 int new_or;
3267 const Geometry::Type geom = elements[i]->GetGeometryType();
3268 switch (geom)
3269 {
3270 case Geometry::SEGMENT:
3271 new_or = (old_v[0] == new_v[0]) ? +1 : -1;
3272 break;
3273 case Geometry::TRIANGLE:
3274 new_or = GetTriOrientation(old_v, new_v);
3275 break;
3276 case Geometry::SQUARE:
3277 new_or = GetQuadOrientation(old_v, new_v);
3278 break;
3280 new_or = GetTetOrientation(old_v, new_v);
3281 break;
3282 default:
3283 new_or = 0;
3284 MFEM_ABORT(Geometry::Name[geom] << " elements (" << fec->Name()
3285 << " FE collection) are not supported yet!");
3286 break;
3287 }
3288 dof_ord = fec->DofOrderForOrientation(geom, new_or);
3289 MFEM_VERIFY(dof_ord != NULL,
3290 "FE collection '" << fec->Name()
3291 << "' does not define reordering for "
3292 << Geometry::Name[geom] << " elements!");
3293 fes->GetElementInteriorDofs(i, old_dofs);
3294 new_dofs.SetSize(old_dofs.Size());
3295 for (int j = 0; j < new_dofs.Size(); j++)
3296 {
3297 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
3298 const int old_j = dof_ord[j];
3299 new_dofs[old_j] = offset + j;
3300 }
3301 offset += new_dofs.Size();
3302 fes->DofsToVDofs(old_dofs);
3303 fes->DofsToVDofs(new_dofs);
3304 for (int j = 0; j < old_dofs.Size(); j++)
3305 {
3306 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
3307 }
3308 }
3309 }
3310
3311 // Update Tables, faces, etc
3312 if (Dim > 2)
3313 {
3314 if (fes->GetNFDofs() == 0)
3315 {
3316 // needed for FE spaces that have face dofs, even if
3317 // the 'Nodes' do not have face dofs.
3319 GenerateFaces();
3320 }
3322 }
3323 if (el_to_edge)
3324 {
3325 // update 'el_to_edge', 'be_to_face' (2D), 'bel_to_edge' (3D)
3327 if (Dim == 2)
3328 {
3329 // update 'faces' and 'faces_info'
3330 GenerateFaces();
3332 }
3333 }
3334 // To force FE space update, we need to increase 'sequence':
3335 sequence++;
3338 fes->Update(false); // want_transform = false
3339 Nodes->Update(); // just needed to update Nodes->sequence
3340}
3341
3342void Mesh::SetPatchAttribute(int i, int attr)
3343{
3344 MFEM_ASSERT(NURBSext, "SetPatchAttribute is only for NURBS meshes");
3345 NURBSext->SetPatchAttribute(i, attr);
3346 const Array<int>& elems = NURBSext->GetPatchElements(i);
3347 for (auto e : elems)
3348 {
3349 SetAttribute(e, attr);
3350 }
3351}
3352
3354{
3355 MFEM_ASSERT(NURBSext, "GetPatchAttribute is only for NURBS meshes");
3356 return NURBSext->GetPatchAttribute(i);
3357}
3358
3359void Mesh::SetPatchBdrAttribute(int i, int attr)
3360{
3361 MFEM_ASSERT(NURBSext, "SetPatchBdrAttribute is only for NURBS meshes");
3363
3364 const Array<int>& bdryelems = NURBSext->GetPatchBdrElements(i);
3365 for (auto be : bdryelems)
3366 {
3367 SetBdrAttribute(be, attr);
3368 }
3369}
3370
3372{
3373 MFEM_ASSERT(NURBSext, "GetBdrPatchBdrAttribute is only for NURBS meshes");
3374 return NURBSext->GetPatchBdrAttribute(i);
3375}
3376
3378{
3379 MFEM_VERIFY(NURBSext, "Must be a NURBS mesh");
3380 // This sets the data in NURBSPatch(es) from the control points (Nodes)
3382
3383 // Deep copy patches
3384 NURBSext->GetPatches(patches);
3385
3386 // Among other things, this deletes patches in NURBSext
3387 UpdateNURBS();
3388}
3389
3390void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
3391{
3392 FinalizeCheck();
3393 CheckElementOrientation(fix_orientation);
3394
3395 if (!HasBoundaryElements())
3396 {
3398 GenerateFaces();
3400 }
3401
3402 if (refine)
3403 {
3404 DSTable v_to_v(NumOfVertices);
3405 GetVertexToVertexTable(v_to_v);
3407 }
3408
3410 GenerateFaces();
3411
3413
3414 if (generate_edges == 1)
3415 {
3416 el_to_edge = new Table;
3418 }
3419 else
3420 {
3421 el_to_edge = NULL; // Not really necessary -- InitTables was called
3422 bel_to_edge = NULL;
3423 NumOfEdges = 0;
3424 }
3425
3426 SetAttributes();
3427
3428 SetMeshGen();
3429}
3430
3431void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
3432 bool fix_orientation)
3433{
3434 FinalizeCheck();
3435 CheckElementOrientation(fix_orientation);
3436
3437 if (!HasBoundaryElements())
3438 {
3440 GenerateFaces();
3442 }
3443
3445 GenerateFaces();
3446
3448
3449 if (generate_edges == 1)
3450 {
3451 el_to_edge = new Table;
3453 }
3454 else
3455 {
3456 el_to_edge = NULL; // Not really necessary -- InitTables was called
3457 bel_to_edge = NULL;
3458 NumOfEdges = 0;
3459 }
3460
3461 SetAttributes();
3462
3463 SetMeshGen();
3464}
3465
3466void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
3467{
3468 FinalizeCheck();
3469 CheckElementOrientation(fix_orientation);
3470
3472 GenerateFaces();
3473
3474 if (!HasBoundaryElements())
3475 {
3477 }
3478
3480
3481 if (generate_edges)
3482 {
3483 el_to_edge = new Table;
3485 }
3486 else
3487 {
3488 NumOfEdges = 0;
3489 }
3490
3491 SetAttributes();
3492
3493 SetMeshGen();
3494}
3495
3496void Mesh::FinalizeMesh(int refine, bool fix_orientation)
3497{
3499 Finalize(refine, fix_orientation);
3500}
3501
3502void Mesh::FinalizeTopology(bool generate_bdr)
3503{
3504 // Requirements: the following should be defined:
3505 // 1) Dim
3506 // 2) NumOfElements, elements
3507 // 3) NumOfBdrElements, boundary
3508 // 4) NumOfVertices
3509 // Optional:
3510 // 2) ncmesh may be defined
3511 // 3) el_to_edge may be allocated (it will be re-computed)
3512
3513 FinalizeCheck();
3514 bool generate_edges = true;
3515
3516 if (spaceDim == 0) { spaceDim = Dim; }
3517 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
3518
3519 // if the user defined any hanging nodes (see AddVertexParent),
3520 // we're initializing a non-conforming mesh
3521 if (tmp_vertex_parents.Size())
3522 {
3523 MFEM_VERIFY(ncmesh == NULL, "");
3524 ncmesh = new NCMesh(this);
3525
3526 // we need to recreate the Mesh because NCMesh reorders the vertices
3527 // (see NCMesh::UpdateVertices())
3529 ncmesh->OnMeshUpdated(this);
3531
3532 SetAttributes();
3533
3534 tmp_vertex_parents.DeleteAll();
3535 return;
3536 }
3537
3538 // set the mesh type: 'meshgen', ...
3539 SetMeshGen();
3540
3541 // generate the faces
3542 if (Dim > 2)
3543 {
3545 GenerateFaces();
3546 if (!HasBoundaryElements() && generate_bdr)
3547 {
3549 GetElementToFaceTable(); // update be_to_face
3550 }
3551 }
3552 else
3553 {
3554 NumOfFaces = 0;
3555 }
3556
3557 // generate edges if requested
3558 if (Dim > 1 && generate_edges)
3559 {
3560 // el_to_edge may already be allocated (P2 VTK meshes)
3561 if (!el_to_edge) { el_to_edge = new Table; }
3563 if (Dim == 2)
3564 {
3565 GenerateFaces(); // 'Faces' in 2D refers to the edges
3566 if (!HasBoundaryElements() && generate_bdr)
3567 {
3569 }
3570 }
3571 }
3572 else
3573 {
3574 NumOfEdges = 0;
3575 }
3576
3577 if (Dim == 1)
3578 {
3579 GenerateFaces();
3580 if (!HasBoundaryElements() && generate_bdr)
3581 {
3582 // be_to_face will be set inside GenerateBoundaryElements
3584 }
3585 else
3586 {
3588 for (int i = 0; i < NumOfBdrElements; ++i)
3589 {
3590 be_to_face[i] = boundary[i]->GetVertices()[0];
3591 }
3592 }
3593 }
3594
3595 if (ncmesh)
3596 {
3597 // tell NCMesh the numbering of edges/faces
3598 ncmesh->OnMeshUpdated(this);
3599
3600 // update faces_info with NC relations
3602 }
3603
3604 // generate the arrays 'attributes' and 'bdr_attributes'
3605 SetAttributes();
3606}
3607
3608void Mesh::Finalize(bool refine, bool fix_orientation)
3609{
3610 if (NURBSext || ncmesh)
3611 {
3612 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
3613 MFEM_ASSERT(CheckBdrElementOrientation() == 0, "");
3614 return;
3615 }
3616
3617 // Requirements:
3618 // 1) FinalizeTopology() or equivalent was called
3619 // 2) if (Nodes == NULL), vertices must be defined
3620 // 3) if (Nodes != NULL), Nodes must be defined
3621
3622 const bool check_orientation = true; // for regular elements, not boundary
3623 const bool curved = (Nodes != NULL);
3624 const bool may_change_topology =
3625 ( refine && (Dim > 1 && (meshgen & 1)) ) ||
3626 ( check_orientation && fix_orientation &&
3627 (Dim == 2 || (Dim == 3 && (meshgen & 1))) );
3628
3629 DSTable *old_v_to_v = NULL;
3630 Table *old_elem_vert = NULL;
3631
3632 if (curved && may_change_topology)
3633 {
3634 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3635 }
3636
3637 if (check_orientation)
3638 {
3639 // check and optionally fix element orientation
3640 CheckElementOrientation(fix_orientation);
3641 }
3642 if (refine)
3643 {
3644 MarkForRefinement(); // may change topology!
3645 }
3646
3647 if (may_change_topology)
3648 {
3649 if (curved)
3650 {
3651 DoNodeReorder(old_v_to_v, old_elem_vert); // updates the mesh topology
3652 delete old_elem_vert;
3653 delete old_v_to_v;
3654 }
3655 else
3656 {
3657 FinalizeTopology(); // Re-computes some data unnecessarily.
3658 }
3659
3660 // TODO: maybe introduce Mesh::NODE_REORDER operation and FESpace::
3661 // NodeReorderMatrix and do Nodes->Update() instead of DoNodeReorder?
3662 }
3663
3664 // check and fix boundary element orientation
3666
3667#ifdef MFEM_DEBUG
3668 // For non-orientable surfaces/manifolds, the check below will fail, so we
3669 // only perform it when Dim == spaceDim.
3670 if (Dim >= 2 && Dim == spaceDim)
3671 {
3672 const int num_faces = GetNumFaces();
3673 for (int i = 0; i < num_faces; i++)
3674 {
3675 MFEM_VERIFY(faces_info[i].Elem2No < 0 ||
3676 faces_info[i].Elem2Inf%2 != 0, "Invalid mesh topology."
3677 " Interior face with incompatible orientations.");
3678 }
3679 }
3680#endif
3681}
3682
3683void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
3684 real_t sx, real_t sy, real_t sz, bool sfc_ordering)
3685{
3686 int x, y, z;
3687
3688 int NVert, NElem, NBdrElem;
3689
3690 NVert = (nx+1) * (ny+1) * (nz+1);
3691 NElem = nx * ny * nz;
3692 NBdrElem = 2*(nx*ny+nx*nz+ny*nz);
3693 if (type == Element::TETRAHEDRON)
3694 {
3695 NElem *= 6;
3696 NBdrElem *= 2;
3697 }
3698 else if (type == Element::WEDGE)
3699 {
3700 NElem *= 2;
3701 NBdrElem += 2*nx*ny;
3702 }
3703 else if (type == Element::PYRAMID)
3704 {
3705 NElem *= 6;
3706 NVert += nx * ny * nz;
3707 }
3708
3709 InitMesh(3, 3, NVert, NElem, NBdrElem);
3710
3711 real_t coord[3];
3712 int ind[9];
3713
3714 // Sets vertices and the corresponding coordinates
3715 for (z = 0; z <= nz; z++)
3716 {
3717 coord[2] = ((real_t) z / nz) * sz;
3718 for (y = 0; y <= ny; y++)
3719 {
3720 coord[1] = ((real_t) y / ny) * sy;
3721 for (x = 0; x <= nx; x++)
3722 {
3723 coord[0] = ((real_t) x / nx) * sx;
3724 AddVertex(coord);
3725 }
3726 }
3727 }
3728 if (type == Element::PYRAMID)
3729 {
3730 for (z = 0; z < nz; z++)
3731 {
3732 coord[2] = (((real_t) z + 0.5) / nz) * sz;
3733 for (y = 0; y < ny; y++)
3734 {
3735 coord[1] = (((real_t) y + 0.5) / ny) * sy;
3736 for (x = 0; x < nx; x++)
3737 {
3738 coord[0] = (((real_t) x + 0.5) / nx) * sx;
3739 AddVertex(coord);
3740 }
3741 }
3742 }
3743 }
3744
3745#define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
3746#define VTXP(XC, YC, ZC) ((nx+1)*(ny+1)*(nz+1)+(XC)+((YC)+(ZC)*ny)*nx)
3747
3748 // Sets elements and the corresponding indices of vertices
3749 if (sfc_ordering && type == Element::HEXAHEDRON)
3750 {
3751 Array<int> sfc;
3752 NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
3753 MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
3754
3755 for (int k = 0; k < nx*ny*nz; k++)
3756 {
3757 x = sfc[3*k + 0];
3758 y = sfc[3*k + 1];
3759 z = sfc[3*k + 2];
3760
3761 // *INDENT-OFF*
3762 ind[0] = VTX(x , y , z );
3763 ind[1] = VTX(x+1, y , z );
3764 ind[2] = VTX(x+1, y+1, z );
3765 ind[3] = VTX(x , y+1, z );
3766 ind[4] = VTX(x , y , z+1);
3767 ind[5] = VTX(x+1, y , z+1);
3768 ind[6] = VTX(x+1, y+1, z+1);
3769 ind[7] = VTX(x , y+1, z+1);
3770 // *INDENT-ON*
3771
3772 AddHex(ind, 1);
3773 }
3774 }
3775 else
3776 {
3777 for (z = 0; z < nz; z++)
3778 {
3779 for (y = 0; y < ny; y++)
3780 {
3781 for (x = 0; x < nx; x++)
3782 {
3783 // *INDENT-OFF*
3784 ind[0] = VTX(x , y , z );
3785 ind[1] = VTX(x+1, y , z );
3786 ind[2] = VTX(x+1, y+1, z );
3787 ind[3] = VTX(x , y+1, z );
3788 ind[4] = VTX(x , y , z+1);
3789 ind[5] = VTX(x+1, y , z+1);
3790 ind[6] = VTX(x+1, y+1, z+1);
3791 ind[7] = VTX( x, y+1, z+1);
3792 // *INDENT-ON*
3793 if (type == Element::TETRAHEDRON)
3794 {
3795 AddHexAsTets(ind, 1);
3796 }
3797 else if (type == Element::WEDGE)
3798 {
3799 AddHexAsWedges(ind, 1);
3800 }
3801 else if (type == Element::PYRAMID)
3802 {
3803 ind[8] = VTXP(x, y, z);
3804 AddHexAsPyramids(ind, 1);
3805 }
3806 else
3807 {
3808 AddHex(ind, 1);
3809 }
3810 }
3811 }
3812 }
3813 }
3814
3815 // Sets boundary elements and the corresponding indices of vertices
3816 // bottom, bdr. attribute 1
3817 for (y = 0; y < ny; y++)
3818 {
3819 for (x = 0; x < nx; x++)
3820 {
3821 // *INDENT-OFF*
3822 ind[0] = VTX(x , y , 0);
3823 ind[1] = VTX(x , y+1, 0);
3824 ind[2] = VTX(x+1, y+1, 0);
3825 ind[3] = VTX(x+1, y , 0);
3826 // *INDENT-ON*
3827 if (type == Element::TETRAHEDRON)
3828 {
3829 AddBdrQuadAsTriangles(ind, 1);
3830 }
3831 else if (type == Element::WEDGE)
3832 {
3833 AddBdrQuadAsTriangles(ind, 1);
3834 }
3835 else
3836 {
3837 AddBdrQuad(ind, 1);
3838 }
3839 }
3840 }
3841 // top, bdr. attribute 6
3842 for (y = 0; y < ny; y++)
3843 {
3844 for (x = 0; x < nx; x++)
3845 {
3846 // *INDENT-OFF*
3847 ind[0] = VTX(x , y , nz);
3848 ind[1] = VTX(x+1, y , nz);
3849 ind[2] = VTX(x+1, y+1, nz);
3850 ind[3] = VTX(x , y+1, nz);
3851 // *INDENT-ON*
3852 if (type == Element::TETRAHEDRON)
3853 {
3854 AddBdrQuadAsTriangles(ind, 6);
3855 }
3856 else if (type == Element::WEDGE)
3857 {
3858 AddBdrQuadAsTriangles(ind, 6);
3859 }
3860 else
3861 {
3862 AddBdrQuad(ind, 6);
3863 }
3864 }
3865 }
3866 // left, bdr. attribute 5
3867 for (z = 0; z < nz; z++)
3868 {
3869 for (y = 0; y < ny; y++)
3870 {
3871 // *INDENT-OFF*
3872 ind[0] = VTX(0 , y , z );
3873 ind[1] = VTX(0 , y , z+1);
3874 ind[2] = VTX(0 , y+1, z+1);
3875 ind[3] = VTX(0 , y+1, z );
3876 // *INDENT-ON*
3877 if (type == Element::TETRAHEDRON)
3878 {
3879 AddBdrQuadAsTriangles(ind, 5);
3880 }
3881 else
3882 {
3883 AddBdrQuad(ind, 5);
3884 }
3885 }
3886 }
3887 // right, bdr. attribute 3
3888 for (z = 0; z < nz; z++)
3889 {
3890 for (y = 0; y < ny; y++)
3891 {
3892 // *INDENT-OFF*
3893 ind[0] = VTX(nx, y , z );
3894 ind[1] = VTX(nx, y+1, z );
3895 ind[2] = VTX(nx, y+1, z+1);
3896 ind[3] = VTX(nx, y , z+1);
3897 // *INDENT-ON*
3898 if (type == Element::TETRAHEDRON)
3899 {
3900 AddBdrQuadAsTriangles(ind, 3);
3901 }
3902 else
3903 {
3904 AddBdrQuad(ind, 3);
3905 }
3906 }
3907 }
3908 // front, bdr. attribute 2
3909 for (x = 0; x < nx; x++)
3910 {
3911 for (z = 0; z < nz; z++)
3912 {
3913 // *INDENT-OFF*
3914 ind[0] = VTX(x , 0, z );
3915 ind[1] = VTX(x+1, 0, z );
3916 ind[2] = VTX(x+1, 0, z+1);
3917 ind[3] = VTX(x , 0, z+1);
3918 // *INDENT-ON*
3919 if (type == Element::TETRAHEDRON)
3920 {
3921 AddBdrQuadAsTriangles(ind, 2);
3922 }
3923 else
3924 {
3925 AddBdrQuad(ind, 2);
3926 }
3927 }
3928 }
3929 // back, bdr. attribute 4
3930 for (x = 0; x < nx; x++)
3931 {
3932 for (z = 0; z < nz; z++)
3933 {
3934 // *INDENT-OFF*
3935 ind[0] = VTX(x , ny, z );
3936 ind[1] = VTX(x , ny, z+1);
3937 ind[2] = VTX(x+1, ny, z+1);
3938 ind[3] = VTX(x+1, ny, z );
3939 // *INDENT-ON*
3940 if (type == Element::TETRAHEDRON)
3941 {
3942 AddBdrQuadAsTriangles(ind, 4);
3943 }
3944 else
3945 {
3946 AddBdrQuad(ind, 4);
3947 }
3948 }
3949 }
3950
3951#undef VTX
3952#undef VTXP
3953
3954#if 0
3955 ofstream test_stream("debug.mesh");
3956 Print(test_stream);
3957 test_stream.close();
3958#endif
3959
3961
3962 // Finalize(...) can be called after this method, if needed
3963}
3964
3965
3966void Mesh::Make2D4TrisFromQuad(int nx, int ny, real_t sx, real_t sy)
3967{
3968 SetEmpty();
3969
3970 Dim = 2;
3971 spaceDim = 2;
3972
3973 NumOfVertices = (nx+1) * (ny+1);
3974 NumOfElements = nx * ny * 4;
3975 NumOfBdrElements = (2 * nx + 2 * ny);
3976 vertices.SetSize(NumOfVertices);
3977 elements.SetSize(NumOfElements);
3978 boundary.SetSize(NumOfBdrElements);
3979 NumOfElements = 0;
3980
3981 int ind[4];
3982
3983 // Sets vertices and the corresponding coordinates
3984 int k = 0;
3985 for (real_t j = 0; j < ny+1; j++)
3986 {
3987 real_t cy = (j / ny) * sy;
3988 for (real_t i = 0; i < nx+1; i++)
3989 {
3990 real_t cx = (i / nx) * sx;
3991 vertices[k](0) = cx;
3992 vertices[k](1) = cy;
3993 k++;
3994 }
3995 }
3996
3997 for (int y = 0; y < ny; y++)
3998 {
3999 for (int x = 0; x < nx; x++)
4000 {
4001 ind[0] = x + y*(nx+1);
4002 ind[1] = x + 1 +y*(nx+1);
4003 ind[2] = x + 1 + (y+1)*(nx+1);
4004 ind[3] = x + (y+1)*(nx+1);
4006 }
4007 }
4008
4009 int m = (nx+1)*ny;
4010 for (int i = 0; i < nx; i++)
4011 {
4012 boundary[i] = new Segment(i, i+1, 1);
4013 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4014 }
4015 m = nx+1;
4016 for (int j = 0; j < ny; j++)
4017 {
4018 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4019 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4020 }
4021
4022 SetMeshGen();
4024
4025 el_to_edge = new Table;
4027 GenerateFaces();
4029
4030 NumOfFaces = 0;
4031
4032 attributes.Append(1);
4035
4037}
4038
4039void Mesh::Make2D5QuadsFromQuad(int nx, int ny,
4040 real_t sx, real_t sy)
4041{
4042 SetEmpty();
4043
4044 Dim = 2;
4045 spaceDim = 2;
4046
4047 NumOfElements = nx * ny * 5;
4048 NumOfVertices = (nx+1) * (ny+1); //it will be enlarged later on
4049 NumOfBdrElements = (2 * nx + 2 * ny);
4050 vertices.SetSize(NumOfVertices);
4051 elements.SetSize(NumOfElements);
4052 boundary.SetSize(NumOfBdrElements);
4053 NumOfElements = 0;
4054
4055 int ind[4];
4056
4057 // Sets vertices and the corresponding coordinates
4058 int k = 0;
4059 for (real_t j = 0; j < ny+1; j++)
4060 {
4061 real_t cy = (j / ny) * sy;
4062 for (real_t i = 0; i < nx+1; i++)
4063 {
4064 real_t cx = (i / nx) * sx;
4065 vertices[k](0) = cx;
4066 vertices[k](1) = cy;
4067 k++;
4068 }
4069 }
4070
4071 for (int y = 0; y < ny; y++)
4072 {
4073 for (int x = 0; x < nx; x++)
4074 {
4075 ind[0] = x + y*(nx+1);
4076 ind[1] = x + 1 +y*(nx+1);
4077 ind[2] = x + 1 + (y+1)*(nx+1);
4078 ind[3] = x + (y+1)*(nx+1);
4080 }
4081 }
4082
4083 int m = (nx+1)*ny;
4084 for (int i = 0; i < nx; i++)
4085 {
4086 boundary[i] = new Segment(i, i+1, 1);
4087 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4088 }
4089 m = nx+1;
4090 for (int j = 0; j < ny; j++)
4091 {
4092 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4093 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4094 }
4095
4096 SetMeshGen();
4098
4099 el_to_edge = new Table;
4101 GenerateFaces();
4103
4104 NumOfFaces = 0;
4105
4106 attributes.Append(1);
4109
4111}
4112
4113void Mesh::Make3D24TetsFromHex(int nx, int ny, int nz,
4114 real_t sx, real_t sy, real_t sz)
4115{
4116 const int NVert = (nx+1) * (ny+1) * (nz+1);
4117 const int NElem = nx * ny * nz * 24;
4118 const int NBdrElem = 2*(nx*ny+nx*nz+ny*nz)*4;
4119
4120 InitMesh(3, 3, NVert, NElem, NBdrElem);
4121
4122 real_t coord[3];
4123
4124 // Sets vertices and the corresponding coordinates
4125 for (real_t z = 0; z <= nz; z++)
4126 {
4127 coord[2] = ( z / nz) * sz;
4128 for (real_t y = 0; y <= ny; y++)
4129 {
4130 coord[1] = (y / ny) * sy;
4131 for (real_t x = 0; x <= nx; x++)
4132 {
4133 coord[0] = (x / nx) * sx;
4134 AddVertex(coord);
4135 }
4136 }
4137 }
4138
4139 std::map<std::array<int, 4>, int> hex_face_verts;
4140 auto VertexIndex = [nx, ny](int xc, int yc, int zc)
4141 {
4142 return xc + (yc + zc*(ny+1))*(nx+1);
4143 };
4144
4145 int ind[9];
4146 for (int z = 0; z < nz; z++)
4147 {
4148 for (int y = 0; y < ny; y++)
4149 {
4150 for (int x = 0; x < nx; x++)
4151 {
4152 // *INDENT-OFF*
4153 ind[0] = VertexIndex(x , y , z );
4154 ind[1] = VertexIndex(x+1, y , z );
4155 ind[2] = VertexIndex(x+1, y+1, z );
4156 ind[3] = VertexIndex(x , y+1, z );
4157 ind[4] = VertexIndex(x , y , z+1);
4158 ind[5] = VertexIndex(x+1, y , z+1);
4159 ind[6] = VertexIndex(x+1, y+1, z+1);
4160 ind[7] = VertexIndex( x, y+1, z+1);
4161 // *INDENT-ON*
4162
4163 AddHexAs24TetsWithPoints(ind, hex_face_verts, 1);
4164 }
4165 }
4166 }
4167
4168 hex_face_verts.clear();
4170
4171 // Done adding Tets
4172 // Now figure out elements that are on the boundary
4173 GetElementToFaceTable(false);
4174 GenerateFaces();
4175
4176 // Map to count number of tets sharing a face
4177 std::map<std::array<int, 3>, int> tet_face_count;
4178 // Map from tet face defined by three vertices to the local face number
4179 std::map<std::array<int, 3>, int> face_count_map;
4180
4181 auto get3array = [](Array<int> v)
4182 {
4183 v.Sort();
4184 return std::array<int, 3> {v[0], v[1], v[2]};
4185 };
4186
4187 Array<int> el_faces;
4188 Array<int> ori;
4189 Array<int> vertidxs;
4190 for (int i = 0; i < el_to_face->Size(); i++)
4191 {
4192 el_to_face->GetRow(i, el_faces);
4193 for (int j = 0; j < el_faces.Size(); j++)
4194 {
4195 GetFaceVertices(el_faces[j], vertidxs);
4196 auto t = get3array(vertidxs);
4197 auto it = tet_face_count.find(t);
4198 if (it == tet_face_count.end()) //edge does not already exist
4199 {
4200 tet_face_count.insert({t, 1});
4201 face_count_map.insert({t, el_faces[j]});
4202 }
4203 else
4204 {
4205 it->second++; // increase edge count value by 1.
4206 }
4207 }
4208 }
4209
4210 for (const auto &edge : tet_face_count)
4211 {
4212 if (edge.second == 1) //if this only appears once, it is a boundary edge
4213 {
4214 int facenum = (face_count_map.find(edge.first))->second;
4215 GetFaceVertices(facenum, vertidxs);
4216 AddBdrTriangle(vertidxs, 1);
4217 }
4218 }
4219
4220#if 0
4221 ofstream test_stream("debug.mesh");
4222 Print(test_stream);
4223 test_stream.close();
4224#endif
4225
4227 // Finalize(...) can be called after this method, if needed
4228}
4229
4230void Mesh::Make2D(int nx, int ny, Element::Type type,
4231 real_t sx, real_t sy,
4232 bool generate_edges, bool sfc_ordering)
4233{
4234 int i, j, k;
4235
4236 SetEmpty();
4237
4238 Dim = spaceDim = 2;
4239
4240 // Creates quadrilateral mesh
4241 if (type == Element::QUADRILATERAL)
4242 {
4243 NumOfVertices = (nx+1) * (ny+1);
4244 NumOfElements = nx * ny;
4245 NumOfBdrElements = 2 * nx + 2 * ny;
4246
4247 vertices.SetSize(NumOfVertices);
4248 elements.SetSize(NumOfElements);
4249 boundary.SetSize(NumOfBdrElements);
4250
4251 real_t cx, cy;
4252 int ind[4];
4253
4254 // Sets vertices and the corresponding coordinates
4255 k = 0;
4256 for (j = 0; j < ny+1; j++)
4257 {
4258 cy = ((real_t) j / ny) * sy;
4259 for (i = 0; i < nx+1; i++)
4260 {
4261 cx = ((real_t) i / nx) * sx;
4262 vertices[k](0) = cx;
4263 vertices[k](1) = cy;
4264 k++;
4265 }
4266 }
4267
4268 // Sets elements and the corresponding indices of vertices
4269 if (sfc_ordering)
4270 {
4271 Array<int> sfc;
4272 NCMesh::GridSfcOrdering2D(nx, ny, sfc);
4273 MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
4274
4275 for (k = 0; k < nx*ny; k++)
4276 {
4277 i = sfc[2*k + 0];
4278 j = sfc[2*k + 1];
4279 ind[0] = i + j*(nx+1);
4280 ind[1] = i + 1 +j*(nx+1);
4281 ind[2] = i + 1 + (j+1)*(nx+1);
4282 ind[3] = i + (j+1)*(nx+1);
4283 elements[k] = new Quadrilateral(ind);
4284 }
4285 }
4286 else
4287 {
4288 k = 0;
4289 for (j = 0; j < ny; j++)
4290 {
4291 for (i = 0; i < nx; i++)
4292 {
4293 ind[0] = i + j*(nx+1);
4294 ind[1] = i + 1 +j*(nx+1);
4295 ind[2] = i + 1 + (j+1)*(nx+1);
4296 ind[3] = i + (j+1)*(nx+1);
4297 elements[k] = new Quadrilateral(ind);
4298 k++;
4299 }
4300 }
4301 }
4302
4303 // Sets boundary elements and the corresponding indices of vertices
4304 int m = (nx+1)*ny;
4305 for (i = 0; i < nx; i++)
4306 {
4307 boundary[i] = new Segment(i, i+1, 1);
4308 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4309 }
4310 m = nx+1;
4311 for (j = 0; j < ny; j++)
4312 {
4313 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4314 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4315 }
4316 }
4317 // Creates triangular mesh
4318 else if (type == Element::TRIANGLE)
4319 {
4320 NumOfVertices = (nx+1) * (ny+1);
4321 NumOfElements = 2 * nx * ny;
4322 NumOfBdrElements = 2 * nx + 2 * ny;
4323
4324 vertices.SetSize(NumOfVertices);
4325 elements.SetSize(NumOfElements);
4326 boundary.SetSize(NumOfBdrElements);
4327
4328 real_t cx, cy;
4329 int ind[3];
4330
4331 // Sets vertices and the corresponding coordinates
4332 k = 0;
4333 for (j = 0; j < ny+1; j++)
4334 {
4335 cy = ((real_t) j / ny) * sy;
4336 for (i = 0; i < nx+1; i++)
4337 {
4338 cx = ((real_t) i / nx) * sx;
4339 vertices[k](0) = cx;
4340 vertices[k](1) = cy;
4341 k++;
4342 }
4343 }
4344
4345 // Sets the elements and the corresponding indices of vertices
4346 k = 0;
4347 for (j = 0; j < ny; j++)
4348 {
4349 for (i = 0; i < nx; i++)
4350 {
4351 ind[0] = i + j*(nx+1);
4352 ind[1] = i + 1 + (j+1)*(nx+1);
4353 ind[2] = i + (j+1)*(nx+1);
4354 elements[k] = new Triangle(ind);
4355 k++;
4356 ind[1] = i + 1 + j*(nx+1);
4357 ind[2] = i + 1 + (j+1)*(nx+1);
4358 elements[k] = new Triangle(ind);
4359 k++;
4360 }
4361 }
4362
4363 // Sets boundary elements and the corresponding indices of vertices
4364 int m = (nx+1)*ny;
4365 for (i = 0; i < nx; i++)
4366 {
4367 boundary[i] = new Segment(i, i+1, 1);
4368 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
4369 }
4370 m = nx+1;
4371 for (j = 0; j < ny; j++)
4372 {
4373 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
4374 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
4375 }
4376
4377 // MarkTriMeshForRefinement(); // done in Finalize(...)
4378 }
4379 else
4380 {
4381 MFEM_ABORT("Unsupported element type.");
4382 }
4383
4384 SetMeshGen();
4386
4387 if (generate_edges == 1)
4388 {
4389 el_to_edge = new Table;
4391 GenerateFaces();
4393 }
4394 else
4395 {
4396 NumOfEdges = 0;
4397 }
4398
4399 NumOfFaces = 0;
4400
4401 attributes.Append(1);
4404
4405 // Finalize(...) can be called after this method, if needed
4406}
4407
4408void Mesh::Make1D(int n, real_t sx)
4409{
4410 int j, ind[1];
4411
4412 SetEmpty();
4413
4414 Dim = 1;
4415 spaceDim = 1;
4416
4417 NumOfVertices = n + 1;
4418 NumOfElements = n;
4419 NumOfBdrElements = 2;
4420 vertices.SetSize(NumOfVertices);
4421 elements.SetSize(NumOfElements);
4422 boundary.SetSize(NumOfBdrElements);
4423
4424 // Sets vertices and the corresponding coordinates
4425 for (j = 0; j < n+1; j++)
4426 {
4427 vertices[j](0) = ((real_t) j / n) * sx;
4428 }
4429
4430 // Sets elements and the corresponding indices of vertices
4431 for (j = 0; j < n; j++)
4432 {
4433 elements[j] = new Segment(j, j+1, 1);
4434 }
4435
4436 // Sets the boundary elements
4437 ind[0] = 0;
4438 boundary[0] = new Point(ind, 1);
4439 ind[0] = n;
4440 boundary[1] = new Point(ind, 2);
4441
4442 NumOfEdges = 0;
4443 NumOfFaces = 0;
4444
4445 SetMeshGen();
4446 GenerateFaces();
4447
4448 // Set be_to_face
4450 be_to_face[0] = 0;
4451 be_to_face[1] = n;
4452
4453 attributes.Append(1);
4455}
4456
4457Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
4458 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4459{
4460 Dim = mesh.Dim;
4461 spaceDim = mesh.spaceDim;
4462
4466 NumOfEdges = mesh.NumOfEdges;
4467 NumOfFaces = mesh.NumOfFaces;
4470
4471 meshgen = mesh.meshgen;
4472 mesh_geoms = mesh.mesh_geoms;
4473
4474 // Create the new Mesh instance without a record of its refinement history
4475 sequence = 0;
4476 nodes_sequence = 0;
4478
4479 // Duplicate the elements
4480 elements.SetSize(NumOfElements);
4481 for (int i = 0; i < NumOfElements; i++)
4482 {
4483 elements[i] = mesh.elements[i]->Duplicate(this);
4484 }
4485
4486 // Copy the vertices
4487 mesh.vertices.Copy(vertices);
4488
4489 // Duplicate the boundary
4490 boundary.SetSize(NumOfBdrElements);
4491 for (int i = 0; i < NumOfBdrElements; i++)
4492 {
4493 boundary[i] = mesh.boundary[i]->Duplicate(this);
4494 }
4495
4496 // Copy the element-to-face Table, el_to_face
4497 el_to_face = (mesh.el_to_face) ? new Table(*mesh.el_to_face) : NULL;
4498
4499 // Copy the boundary-to-face Array, be_to_face.
4501
4502 // Copy the element-to-edge Table, el_to_edge
4503 el_to_edge = (mesh.el_to_edge) ? new Table(*mesh.el_to_edge) : NULL;
4504
4505 // Copy the boundary-to-edge Table, bel_to_edge (3D)
4506 bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
4507
4508 // Duplicate the faces and faces_info.
4509 faces.SetSize(mesh.faces.Size());
4510 for (int i = 0; i < faces.Size(); i++)
4511 {
4512 Element *face = mesh.faces[i]; // in 1D the faces are NULL
4513 faces[i] = (face) ? face->Duplicate(this) : NULL;
4514 }
4515 mesh.faces_info.Copy(faces_info);
4516 mesh.nc_faces_info.Copy(nc_faces_info);
4517
4518 // Do NOT copy the element-to-element Table, el_to_el
4519 el_to_el = NULL;
4520
4521 // Do NOT copy the face-to-edge Table, face_edge
4522 face_edge = NULL;
4523 face_to_elem = NULL;
4524
4525 // Copy the edge-to-vertex Table, edge_vertex
4526 edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
4527
4528 // Copy the attributes and bdr_attributes
4531
4532 // Copy attribute and bdr_attribute names
4535
4536 // Deep copy the NURBSExtension.
4537#ifdef MFEM_USE_MPI
4538 ParNURBSExtension *pNURBSext =
4539 dynamic_cast<ParNURBSExtension *>(mesh.NURBSext);
4540 if (pNURBSext)
4541 {
4542 NURBSext = new ParNURBSExtension(*pNURBSext);
4543 }
4544 else
4545#endif
4546 {
4547 NURBSext = mesh.NURBSext ? new NURBSExtension(*mesh.NURBSext) : NULL;
4548 }
4549
4550 // Deep copy the NCMesh.
4551#ifdef MFEM_USE_MPI
4552 if (dynamic_cast<const ParMesh*>(&mesh))
4553 {
4554 ncmesh = NULL; // skip; will be done in ParMesh copy ctor
4555 }
4556 else
4557#endif
4558 {
4559 ncmesh = mesh.ncmesh ? new NCMesh(*mesh.ncmesh) : NULL;
4560 }
4561
4562 // Duplicate the Nodes, including the FiniteElementCollection and the
4563 // FiniteElementSpace
4564 if (mesh.Nodes && copy_nodes)
4565 {
4566 FiniteElementSpace *fes = mesh.Nodes->FESpace();
4567 const FiniteElementCollection *fec = fes->FEColl();
4568 FiniteElementCollection *fec_copy =
4570 FiniteElementSpace *fes_copy =
4571 new FiniteElementSpace(*fes, this, fec_copy);
4572 Nodes = new GridFunction(fes_copy);
4573 Nodes->MakeOwner(fec_copy);
4574 *Nodes = *mesh.Nodes;
4575 own_nodes = 1;
4576 }
4577 else
4578 {
4579 Nodes = mesh.Nodes;
4580 own_nodes = 0;
4581 }
4582
4583 // copy attribute caches
4586}
4587
4589{
4590 Swap(mesh, true);
4591}
4592
4594{
4595 Swap(mesh, true);
4596 return *this;
4597}
4598
4599Mesh Mesh::LoadFromFile(const std::string &filename, int generate_edges,
4600 int refine, bool fix_orientation)
4601{
4602 Mesh mesh;
4603 named_ifgzstream imesh(filename);
4604 if (!imesh) { MFEM_ABORT("Mesh file not found: " << filename << '\n'); }
4605 else { mesh.Load(imesh, generate_edges, refine, fix_orientation); }
4606 return mesh;
4607}
4608
4610{
4611 Mesh mesh;
4612 mesh.Make1D(n, sx);
4613 // mesh.Finalize(); not needed in this case
4614 return mesh;
4615}
4616
4618 int nx, int ny, Element::Type type, bool generate_edges,
4619 real_t sx, real_t sy, bool sfc_ordering)
4620{
4621 Mesh mesh;
4622 mesh.Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
4623 mesh.Finalize(true); // refine = true
4624 return mesh;
4625}
4626
4628 int nx, int ny, int nz, Element::Type type,
4629 real_t sx, real_t sy, real_t sz, bool sfc_ordering)
4630{
4631 Mesh mesh;
4632 mesh.Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
4633 mesh.Finalize(true); // refine = true
4634 return mesh;
4635}
4636
4638 real_t sx, real_t sy, real_t sz)
4639{
4640 Mesh mesh;
4641 mesh.Make3D24TetsFromHex(nx, ny, nz, sx, sy, sz);
4642 mesh.Finalize(false, false);
4643 return mesh;
4644}
4645
4647 real_t sx, real_t sy)
4648{
4649 Mesh mesh;
4650 mesh.Make2D4TrisFromQuad(nx, ny, sx, sy);
4651 mesh.Finalize(false, false);
4652 return mesh;
4653}
4654
4656 real_t sx, real_t sy)
4657{
4658 Mesh mesh;
4659 mesh.Make2D5QuadsFromQuad(nx, ny, sx, sy);
4660 mesh.Finalize(false, false);
4661 return mesh;
4662}
4663
4664Mesh Mesh::MakeRefined(Mesh &orig_mesh, int ref_factor, int ref_type)
4665{
4666 Mesh mesh;
4667 Array<int> ref_factors(orig_mesh.GetNE());
4668 ref_factors = ref_factor;
4669 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
4670 return mesh;
4671}
4672
4673Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
4674 int ref_type)
4675{
4676 Mesh mesh;
4677 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
4678 return mesh;
4679}
4680
4681Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
4682 bool fix_orientation)
4683 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4684{
4685 // Initialization as in the default constructor
4686 SetEmpty();
4687
4688 named_ifgzstream imesh(filename);
4689 if (!imesh)
4690 {
4691 // Abort with an error message.
4692 MFEM_ABORT("Mesh file not found: " << filename << '\n');
4693 }
4694 else
4695 {
4696 Load(imesh, generate_edges, refine, fix_orientation);
4697 }
4698}
4699
4700Mesh::Mesh(std::istream &input, int generate_edges, int refine,
4701 bool fix_orientation)
4702 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4703{
4704 SetEmpty();
4705 Load(input, generate_edges, refine, fix_orientation);
4706}
4707
4708void Mesh::ChangeVertexDataOwnership(real_t *vertex_data, int len_vertex_data,
4709 bool zerocopy)
4710{
4711 // A dimension of 3 is now required since we use mfem::Vertex objects as PODs
4712 // and these object have a hardcoded double[3] entry
4713 MFEM_VERIFY(len_vertex_data >= NumOfVertices * 3,
4714 "Not enough vertices in external array : "
4715 "len_vertex_data = "<< len_vertex_data << ", "
4716 "NumOfVertices * 3 = " << NumOfVertices * 3);
4717 // Allow multiple calls to this method with the same vertex_data
4718 if (vertex_data == (real_t *)(vertices.GetData()))
4719 {
4720 MFEM_ASSERT(!vertices.OwnsData(), "invalid ownership");
4721 return;
4722 }
4723 if (!zerocopy)
4724 {
4725 memcpy(vertex_data, vertices.GetData(),
4726 NumOfVertices * 3 * sizeof(real_t));
4727 }
4728 // Vertex is POD double[3]
4729 vertices.MakeRef(reinterpret_cast<Vertex*>(vertex_data), NumOfVertices);
4730}
4731
4732Mesh::Mesh(real_t *vertices_, int num_vertices,
4733 int *element_indices, Geometry::Type element_type,
4734 int *element_attributes, int num_elements,
4735 int *boundary_indices, Geometry::Type boundary_type,
4736 int *boundary_attributes, int num_boundary_elements,
4737 int dimension, int space_dimension)
4738 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4739{
4740 if (space_dimension == -1)
4741 {
4742 space_dimension = dimension;
4743 }
4744
4745 InitMesh(dimension, space_dimension, /*num_vertices*/ 0, num_elements,
4746 num_boundary_elements);
4747
4748 int element_index_stride = Geometry::NumVerts[element_type];
4749 int boundary_index_stride = num_boundary_elements > 0 ?
4750 Geometry::NumVerts[boundary_type] : 0;
4751
4752 // assuming Vertex is POD
4753 vertices.MakeRef(reinterpret_cast<Vertex*>(vertices_), num_vertices);
4754 NumOfVertices = num_vertices;
4755
4756 for (int i = 0; i < num_elements; i++)
4757 {
4758 elements[i] = NewElement(element_type);
4759 elements[i]->SetVertices(element_indices + i * element_index_stride);
4760 elements[i]->SetAttribute(element_attributes[i]);
4761 }
4762 NumOfElements = num_elements;
4763
4764 for (int i = 0; i < num_boundary_elements; i++)
4765 {
4766 boundary[i] = NewElement(boundary_type);
4767 boundary[i]->SetVertices(boundary_indices + i * boundary_index_stride);
4768 boundary[i]->SetAttribute(boundary_attributes[i]);
4769 }
4770 NumOfBdrElements = num_boundary_elements;
4771
4773}
4774
4776 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
4777{
4778 SetEmpty();
4779 /// make an internal copy of the NURBSExtension
4780 NURBSext = new NURBSExtension( ext );
4781
4782 Dim = NURBSext->Dimension();
4786
4789
4790 vertices.SetSize(NumOfVertices);
4791 if (NURBSext->HavePatches())
4792 {
4794 FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
4796 Nodes = new GridFunction(fes);
4797 Nodes->MakeOwner(fec);
4799 own_nodes = 1;
4801 for (int i = 0; i < spaceDim; i++)
4802 {
4803 Vector vert_val;
4804 Nodes->GetNodalValues(vert_val, i+1);
4805 for (int j = 0; j < NumOfVertices; j++)
4806 {
4807 vertices[j](i) = vert_val(j);
4808 }
4809 }
4810 }
4811 else
4812 {
4813 MFEM_ABORT("NURBS mesh has no patches.");
4814 }
4815 FinalizeMesh();
4816}
4817
4819{
4820 switch (geom)
4821 {
4822 case Geometry::POINT: return (new Point);
4823 case Geometry::SEGMENT: return (new Segment);
4824 case Geometry::TRIANGLE: return (new Triangle);
4825 case Geometry::SQUARE: return (new Quadrilateral);
4827#ifdef MFEM_USE_MEMALLOC
4828 return TetMemory.Alloc();
4829#else
4830 return (new Tetrahedron);
4831#endif
4832 case Geometry::CUBE: return (new Hexahedron);
4833 case Geometry::PRISM: return (new Wedge);
4834 case Geometry::PYRAMID: return (new Pyramid);
4835 default:
4836 MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
4837 }
4838
4839 return NULL;
4840}
4841
4843{
4844 int geom, nv, *v;
4845 Element *el;
4846
4847 input >> geom;
4848 el = NewElement(geom);
4849 MFEM_VERIFY(el, "Unsupported element type: " << geom);
4850 nv = el->GetNVertices();
4851 v = el->GetVertices();
4852 for (int i = 0; i < nv; i++)
4853 {
4854 input >> v[i];
4855 }
4856
4857 return el;
4858}
4859
4860void Mesh::PrintElementWithoutAttr(const Element *el, std::ostream &os)
4861{
4862 os << el->GetGeometryType();
4863 const int nv = el->GetNVertices();
4864 const int *v = el->GetVertices();
4865 for (int j = 0; j < nv; j++)
4866 {
4867 os << ' ' << v[j];
4868 }
4869 os << '\n';
4870}
4871
4872Element *Mesh::ReadElement(std::istream &input)
4873{
4874 int attr;
4875 Element *el;
4876
4877 input >> attr;
4878 el = ReadElementWithoutAttr(input);
4879 el->SetAttribute(attr);
4880
4881 return el;
4882}
4883
4884void Mesh::PrintElement(const Element *el, std::ostream &os)
4885{
4886 os << el->GetAttribute() << ' ';
4888}
4889
4891{
4892 meshgen = mesh_geoms = 0;
4893 for (int i = 0; i < NumOfElements; i++)
4894 {
4895 const Element::Type type = GetElement(i)->GetType();
4896 switch (type)
4897 {
4900 case Element::TRIANGLE:
4902 case Element::SEGMENT:
4903 mesh_geoms |= (1 << Geometry::SEGMENT);
4904 case Element::POINT:
4905 mesh_geoms |= (1 << Geometry::POINT);
4906 meshgen |= 1;
4907 break;
4908
4910 mesh_geoms |= (1 << Geometry::CUBE);
4912 mesh_geoms |= (1 << Geometry::SQUARE);
4913 mesh_geoms |= (1 << Geometry::SEGMENT);
4914 mesh_geoms |= (1 << Geometry::POINT);
4915 meshgen |= 2;
4916 break;
4917
4918 case Element::WEDGE:
4919 mesh_geoms |= (1 << Geometry::PRISM);
4920 mesh_geoms |= (1 << Geometry::SQUARE);
4922 mesh_geoms |= (1 << Geometry::SEGMENT);
4923 mesh_geoms |= (1 << Geometry::POINT);
4924 meshgen |= 4;
4925 break;
4926
4927 case Element::PYRAMID:
4928 mesh_geoms |= (1 << Geometry::PYRAMID);
4929 mesh_geoms |= (1 << Geometry::SQUARE);
4931 mesh_geoms |= (1 << Geometry::SEGMENT);
4932 mesh_geoms |= (1 << Geometry::POINT);
4933 meshgen |= 8;
4934 break;
4935
4936 default:
4937 MFEM_ABORT("invalid element type: " << type);
4938 break;
4939 }
4940 }
4941}
4942
4943void Mesh::Loader(std::istream &input, int generate_edges,
4944 std::string parse_tag)
4945{
4946 int curved = 0, read_gf = 1;
4947 bool finalize_topo = true;
4948
4949 if (!input)
4950 {
4951 MFEM_ABORT("Input stream is not open");
4952 }
4953
4954 Clear();
4955
4956 string mesh_type;
4957 input >> ws;
4958 getline(input, mesh_type);
4959 filter_dos(mesh_type);
4960
4961 // MFEM's conforming mesh formats
4962 int mfem_version = 0;
4963 if (mesh_type == "MFEM mesh v1.0") { mfem_version = 10; } // serial
4964 else if (mesh_type == "MFEM mesh v1.2") { mfem_version = 12; } // parallel
4965 else if (mesh_type == "MFEM mesh v1.3") { mfem_version = 13; } // attr sets
4966
4967 // MFEM nonconforming mesh format
4968 // (NOTE: previous v1.1 is now under this branch for backward compatibility)
4969 int mfem_nc_version = 0;
4970 if (mesh_type == "MFEM NC mesh v1.0") { mfem_nc_version = 10; }
4971 else if (mesh_type == "MFEM NC mesh v1.1") { mfem_nc_version = 11; }
4972 else if (mesh_type == "MFEM mesh v1.1") { mfem_nc_version = 1 /*legacy*/; }
4973
4974 if (mfem_version)
4975 {
4976 // Formats mfem_v12 and newer have a tag indicating the end of the mesh
4977 // section in the stream. A user provided parse tag can also be provided
4978 // via the arguments. For example, if this is called from parallel mesh
4979 // object, it can indicate to read until parallel mesh section begins.
4980 if (mfem_version >= 12 && parse_tag.empty())
4981 {
4982 parse_tag = "mfem_mesh_end";
4983 }
4984 ReadMFEMMesh(input, mfem_version, curved);
4985 }
4986 else if (mfem_nc_version)
4987 {
4988 MFEM_ASSERT(ncmesh == NULL, "internal error");
4989 int is_nc = 1;
4990
4991#ifdef MFEM_USE_MPI
4992 ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
4993 if (pmesh)
4994 {
4995 MFEM_VERIFY(mfem_nc_version >= 10,
4996 "Legacy nonconforming format (MFEM mesh v1.1) cannot be "
4997 "used to load a parallel nonconforming mesh, sorry.");
4998
4999 ncmesh = new ParNCMesh(pmesh->GetComm(),
5000 input, mfem_nc_version, curved, is_nc);
5001 }
5002 else
5003#endif
5004 {
5005 ncmesh = new NCMesh(input, mfem_nc_version, curved, is_nc);
5006 }
5008
5009 if (!is_nc)
5010 {
5011 // special case for backward compatibility with MFEM <=4.2:
5012 // if the "vertex_parents" section is missing in the v1.1 format,
5013 // the mesh is treated as conforming
5014 delete ncmesh;
5015 ncmesh = NULL;
5016 }
5017 }
5018 else if (mesh_type == "linemesh") // 1D mesh
5019 {
5020 ReadLineMesh(input);
5021 }
5022 else if (mesh_type == "areamesh2" || mesh_type == "curved_areamesh2")
5023 {
5024 if (mesh_type == "curved_areamesh2")
5025 {
5026 curved = 1;
5027 }
5028 ReadNetgen2DMesh(input, curved);
5029 }
5030 else if (mesh_type == "NETGEN" || mesh_type == "NETGEN_Neutral_Format")
5031 {
5032 ReadNetgen3DMesh(input);
5033 }
5034 else if (mesh_type == "TrueGrid")
5035 {
5036 ReadTrueGridMesh(input);
5037 }
5038 else if (mesh_type.rfind("# vtk DataFile Version") == 0)
5039 {
5040 int major_vtk_version = mesh_type[mesh_type.length()-3] - '0';
5041 // int minor_vtk_version = mesh_type[mesh_type.length()-1] - '0';
5042 MFEM_VERIFY(major_vtk_version >= 2 && major_vtk_version <= 4,
5043 "Unsupported VTK format");
5044 ReadVTKMesh(input, curved, read_gf, finalize_topo);
5045 }
5046 else if (mesh_type.rfind("<VTKFile ") == 0 || mesh_type.rfind("<?xml") == 0)
5047 {
5048 ReadXML_VTKMesh(input, curved, read_gf, finalize_topo, mesh_type);
5049 }
5050 else if (mesh_type == "MFEM NURBS mesh v1.0")
5051 {
5052 ReadNURBSMesh(input, curved, read_gf);
5053 }
5054 else if (mesh_type == "MFEM NURBS NC-patch mesh v1.0")
5055 {
5056 ReadNURBSMesh(input, curved, read_gf, true, true); // Spacing is required
5057 }
5058 else if (mesh_type == "MFEM NURBS mesh v1.1")
5059 {
5060 ReadNURBSMesh(input, curved, read_gf, true);
5061 }
5062 else if (mesh_type == "MFEM INLINE mesh v1.0")
5063 {
5064 ReadInlineMesh(input, generate_edges);
5065 return; // done with inline mesh construction
5066 }
5067 else if (mesh_type == "$MeshFormat") // Gmsh
5068 {
5069 ReadGmshMesh(input, curved, read_gf);
5070 }
5071 else if
5072 ((mesh_type.size() > 2 &&
5073 mesh_type[0] == 'C' && mesh_type[1] == 'D' && mesh_type[2] == 'F') ||
5074 (mesh_type.size() > 3 &&
5075 mesh_type[1] == 'H' && mesh_type[2] == 'D' && mesh_type[3] == 'F'))
5076 {
5077 named_ifgzstream *mesh_input = dynamic_cast<named_ifgzstream *>(&input);
5078 if (mesh_input)
5079 {
5080#ifdef MFEM_USE_NETCDF
5081 ReadCubit(mesh_input->filename, curved, read_gf);
5082#else
5083 MFEM_ABORT("NetCDF support requires configuration with"
5084 " MFEM_USE_NETCDF=YES");
5085 return;
5086#endif
5087 }
5088 else
5089 {
5090 MFEM_ABORT("Can not determine Cubit mesh filename!"
5091 " Use mfem::named_ifgzstream for input.");
5092 return;
5093 }
5094 }
5095 else
5096 {
5097 MFEM_ABORT("Unknown input mesh format: " << mesh_type);
5098 return;
5099 }
5100
5101 // at this point the following should be defined:
5102 // 1) Dim
5103 // 2) NumOfElements, elements
5104 // 3) NumOfBdrElements, boundary
5105 // 4) NumOfVertices, with allocated space in vertices
5106 // 5) curved
5107 // 5a) if curved == 0, vertices must be defined
5108 // 5b) if curved != 0 and read_gf != 0,
5109 // 'input' must point to a GridFunction
5110 // 5c) if curved != 0 and read_gf == 0,
5111 // vertices and Nodes must be defined
5112 // optional:
5113 // 1) el_to_edge may be allocated (as in the case of P2 VTK meshes)
5114 // 2) ncmesh may be allocated
5115
5116 // FinalizeTopology() will:
5117 // - assume that generate_edges is true
5118 // - assume that refine is false
5119 // - does not check the orientation of regular and boundary elements
5120 if (finalize_topo)
5121 {
5122 // don't generate any boundary elements, especially in parallel
5123 bool generate_bdr = false;
5124
5125 FinalizeTopology(generate_bdr);
5126 }
5127
5128 if (curved && read_gf)
5129 {
5130 Nodes = new GridFunction(this, input);
5131
5132 own_nodes = 1;
5134 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
5135
5136 // Set vertex coordinates from the 'Nodes'
5138 }
5139
5140 // If a parse tag was supplied, keep reading the stream until the tag is
5141 // encountered.
5142 if (mfem_version >= 12)
5143 {
5144 string line;
5145 do
5146 {
5147 skip_comment_lines(input, '#');
5148 MFEM_VERIFY(input.good(), "Required mesh-end tag not found");
5149 getline(input, line);
5150 filter_dos(line);
5151 // mfem v1.2 may not have parse_tag in it, e.g. if trying to read a
5152 // serial mfem v1.2 mesh as parallel with "mfem_serial_mesh_end" as
5153 // parse_tag. That's why, regardless of parse_tag, we stop reading if
5154 // we find "mfem_mesh_end" which is required by mfem v1.2 format.
5155 if (line == "mfem_mesh_end") { break; }
5156 }
5157 while (line != parse_tag);
5158 }
5159 else if (mfem_nc_version >= 10)
5160 {
5161 string ident;
5162 skip_comment_lines(input, '#');
5163 input >> ident;
5164 MFEM_VERIFY(ident == "mfem_mesh_end",
5165 "invalid mesh: end of file tag not found");
5166 }
5167
5169 {
5170 string ident;
5171 skip_comment_lines(input, '#');
5172 // Check for the optional section "patch_cp"
5173 if (input.peek() == 'p')
5174 {
5175 input >> ident;
5176 MFEM_VERIFY(ident == "patch_cp", "Invalid mesh format");
5178 }
5179 }
5180
5181 // Finalize(...) should be called after this, if needed.
5182}
5183
5184Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
5185 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
5186{
5187 int i, j, ie, ib, iv, *v, nv;
5188 Element *el;
5189 Mesh *m;
5190
5191 SetEmpty();
5192
5193 Dim = mesh_array[0]->Dimension();
5194 spaceDim = mesh_array[0]->SpaceDimension();
5195
5196 if (mesh_array[0]->NURBSext)
5197 {
5198 // assuming the pieces form a partition of a NURBS mesh
5199 NURBSext = new NURBSExtension(mesh_array, num_pieces);
5200
5203
5205
5206 // NumOfBdrElements = NURBSext->GetNBE();
5207 // NURBSext->GetBdrElementTopo(boundary);
5208
5209 Array<int> lvert_vert, lelem_elem;
5210
5211 // Here, for visualization purposes, we copy the boundary elements from
5212 // the individual pieces which include the interior boundaries. This
5213 // creates 'boundary' array that is different from the one generated by
5214 // the NURBSExtension which, in particular, makes the boundary-dof table
5215 // invalid. This, in turn, causes GetBdrElementTransformation to not
5216 // function properly.
5217 NumOfBdrElements = 0;
5218 for (i = 0; i < num_pieces; i++)
5219 {
5220 NumOfBdrElements += mesh_array[i]->GetNBE();
5221 }
5222 boundary.SetSize(NumOfBdrElements);
5223 vertices.SetSize(NumOfVertices);
5224 ib = 0;
5225 for (i = 0; i < num_pieces; i++)
5226 {
5227 m = mesh_array[i];
5228 m->NURBSext->GetVertexLocalToGlobal(lvert_vert);
5229 m->NURBSext->GetElementLocalToGlobal(lelem_elem);
5230 // copy the element attributes
5231 for (j = 0; j < m->GetNE(); j++)
5232 {
5233 elements[lelem_elem[j]]->SetAttribute(m->GetAttribute(j));
5234 }
5235 // copy the boundary
5236 for (j = 0; j < m->GetNBE(); j++)
5237 {
5238 el = m->GetBdrElement(j)->Duplicate(this);
5239 v = el->GetVertices();
5240 nv = el->GetNVertices();
5241 for (int k = 0; k < nv; k++)
5242 {
5243 v[k] = lvert_vert[v[k]];
5244 }
5245 boundary[ib++] = el;
5246 }
5247 // copy the vertices
5248 for (j = 0; j < m->GetNV(); j++)
5249 {
5250 vertices[lvert_vert[j]].SetCoords(m->SpaceDimension(),
5251 m->GetVertex(j));
5252 }
5253 }
5254 }
5255 else // not a NURBS mesh
5256 {
5257 NumOfElements = 0;
5258 NumOfBdrElements = 0;
5259 NumOfVertices = 0;
5260 for (i = 0; i < num_pieces; i++)
5261 {
5262 m = mesh_array[i];
5263 NumOfElements += m->GetNE();
5264 NumOfBdrElements += m->GetNBE();
5265 NumOfVertices += m->GetNV();
5266 }
5267 elements.SetSize(NumOfElements);
5268 boundary.SetSize(NumOfBdrElements);
5269 vertices.SetSize(NumOfVertices);
5270 ie = ib = iv = 0;
5271 for (i = 0; i < num_pieces; i++)
5272 {
5273 m = mesh_array[i];
5274 // copy the elements
5275 for (j = 0; j < m->GetNE(); j++)
5276 {
5277 el = m->GetElement(j)->Duplicate(this);
5278 v = el->GetVertices();
5279 nv = el->GetNVertices();
5280 for (int k = 0; k < nv; k++)
5281 {
5282 v[k] += iv;
5283 }
5284 elements[ie++] = el;
5285 }
5286 // copy the boundary elements
5287 for (j = 0; j < m->GetNBE(); j++)
5288 {
5289 el = m->GetBdrElement(j)->Duplicate(this);
5290 v = el->GetVertices();
5291 nv = el->GetNVertices();
5292 for (int k = 0; k < nv; k++)
5293 {
5294 v[k] += iv;
5295 }
5296 boundary[ib++] = el;
5297 }
5298 // copy the vertices
5299 for (j = 0; j < m->GetNV(); j++)
5300 {
5301 vertices[iv++].SetCoords(m->SpaceDimension(), m->GetVertex(j));
5302 }
5303 }
5304 }
5305
5307
5308 // copy the nodes (curvilinear meshes)
5309 GridFunction *g = mesh_array[0]->GetNodes();
5310 if (g)
5311 {
5312 Array<GridFunction *> gf_array(num_pieces);
5313 for (i = 0; i < num_pieces; i++)
5314 {
5315 gf_array[i] = mesh_array[i]->GetNodes();
5316 }
5317 Nodes = new GridFunction(this, gf_array, num_pieces);
5318 own_nodes = 1;
5319 }
5320
5321#ifdef MFEM_DEBUG
5324#endif
5325}
5326
5327Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
5328 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
5329{
5330 Array<int> ref_factors(orig_mesh->GetNE());
5331 ref_factors = ref_factor;
5332 MakeRefined_(*orig_mesh, ref_factors, ref_type);
5333}
5334
5335void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> &ref_factors,
5336 int ref_type)
5337{
5338 SetEmpty();
5339 Dim = orig_mesh.Dimension();
5340 spaceDim = orig_mesh.SpaceDimension();
5341
5342 int orig_ne = orig_mesh.GetNE();
5343 MFEM_VERIFY(ref_factors.Size() == orig_ne,
5344 "Number of refinement factors must equal number of elements")
5345 MFEM_VERIFY(orig_ne == 0 ||
5346 ref_factors.Min() >= 1, "Refinement factor must be >= 1");
5347 const int q_type = BasisType::GetQuadrature1D(ref_type);
5348 MFEM_VERIFY(Quadrature1D::CheckClosed(q_type) != Quadrature1D::Invalid,
5349 "Invalid refinement type. Must use closed basis type.");
5350
5351 int min_ref = orig_ne > 0 ? ref_factors.Min() : 1;
5352 int max_ref = orig_ne > 0 ? ref_factors.Max() : 1;
5353
5354 bool var_order = (min_ref != max_ref);
5355
5356 // variable order space can only be constructed over an NC mesh
5357 if (var_order) { orig_mesh.EnsureNCMesh(true); }
5358
5359 // Construct a scalar H1 FE space of order ref_factor and use its dofs as
5360 // the indices of the new, refined vertices.
5361 H1_FECollection rfec(min_ref, Dim, ref_type);
5362 FiniteElementSpace rfes(&orig_mesh, &rfec);
5363
5364 if (var_order)
5365 {
5366 rfes.SetRelaxedHpConformity(false);
5367 for (int i = 0; i < orig_ne; i++)
5368 {
5369 rfes.SetElementOrder(i, ref_factors[i]);
5370 }
5371 rfes.Update(false);
5372 }
5373
5374 // Set the number of vertices, set the actual coordinates later
5375 NumOfVertices = rfes.GetNDofs();
5376 vertices.SetSize(NumOfVertices);
5377
5378 Array<int> rdofs;
5379 DenseMatrix phys_pts;
5380 GeometryRefiner refiner(q_type);
5381
5382 // Add refined elements and set vertex coordinates
5383 for (int el = 0; el < orig_ne; el++)
5384 {
5385 Geometry::Type geom = orig_mesh.GetElementGeometry(el);
5386 int attrib = orig_mesh.GetAttribute(el);
5387 int nvert = Geometry::NumVerts[geom];
5388 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el]);
5389
5390 rfes.GetElementDofs(el, rdofs);
5391 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
5392 const FiniteElement *rfe = rfes.GetFE(el);
5393 orig_mesh.GetElementTransformation(el)->Transform(rfe->GetNodes(),
5394 phys_pts);
5395 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[el]);
5396 for (int i = 0; i < phys_pts.Width(); i++)
5397 {
5398 vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
5399 }
5400 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5401 {
5402 Element *elem = NewElement(geom);
5403 elem->SetAttribute(attrib);
5404 int *v = elem->GetVertices();
5405 for (int k = 0; k < nvert; k++)
5406 {
5407 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5408 v[k] = rdofs[c2h_map[cid]];
5409 }
5410 AddElement(elem);
5411 }
5412 }
5413
5414 if (Dim > 2)
5415 {
5416 GetElementToFaceTable(false);
5417 GenerateFaces();
5418 }
5419
5420 // Add refined boundary elements
5421 for (int el = 0; el < orig_mesh.GetNBE(); el++)
5422 {
5423 int i, info;
5424 orig_mesh.GetBdrElementAdjacentElement(el, i, info);
5425 Geometry::Type geom = orig_mesh.GetBdrElementGeometry(el);
5426 int attrib = orig_mesh.GetBdrAttribute(el);
5427 int nvert = Geometry::NumVerts[geom];
5428 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[i]);
5429
5430 rfes.GetBdrElementDofs(el, rdofs);
5431 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
5432 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[i]);
5433 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5434 {
5435 Element *elem = NewElement(geom);
5436 elem->SetAttribute(attrib);
5437 int *v = elem->GetVertices();
5438 for (int k = 0; k < nvert; k++)
5439 {
5440 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5441 v[k] = rdofs[c2h_map[cid]];
5442 }
5443 AddBdrElement(elem);
5444 }
5445 }
5446 FinalizeTopology(false);
5447 sequence = orig_mesh.GetSequence() + 1;
5449
5450 // Set up the nodes of the new mesh (if the original mesh has nodes). The new
5451 // mesh is always straight-sided (i.e. degree 1 finite element space), but
5452 // the nodes are required for e.g. periodic meshes.
5453 if (orig_mesh.GetNodes())
5454 {
5455 bool discont = orig_mesh.GetNodalFESpace()->IsDGSpace();
5456 Ordering::Type dof_ordering = orig_mesh.GetNodalFESpace()->GetOrdering();
5457 Mesh::SetCurvature(1, discont, spaceDim, dof_ordering);
5458 FiniteElementSpace *nodal_fes = Nodes->FESpace();
5459 const FiniteElementCollection *nodal_fec = nodal_fes->FEColl();
5460 H1_FECollection vertex_fec(1, Dim);
5461 Array<int> dofs;
5462 int el_counter = 0;
5463 for (int iel = 0; iel < orig_ne; iel++)
5464 {
5465 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(iel);
5466 int nvert = Geometry::NumVerts[geom];
5467 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[iel]);
5468 rfes.GetElementDofs(iel, rdofs);
5469 const FiniteElement *rfe = rfes.GetFE(iel);
5470 orig_mesh.GetElementTransformation(iel)->Transform(rfe->GetNodes(),
5471 phys_pts);
5472 const int *node_map = NULL;
5473 const H1_FECollection *h1_fec =
5474 dynamic_cast<const H1_FECollection *>(nodal_fec);
5475 if (h1_fec != NULL) { node_map = h1_fec->GetDofMap(geom); }
5476 const int *vertex_map = vertex_fec.GetDofMap(geom);
5477 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[iel]);
5478 for (int jel = 0; jel < RG.RefGeoms.Size()/nvert; jel++)
5479 {
5480 nodal_fes->GetElementVDofs(el_counter++, dofs);
5481 for (int iv_lex=0; iv_lex<nvert; ++iv_lex)
5482 {
5483 // convert from lexicographic to vertex index
5484 int iv = vertex_map[iv_lex];
5485 // index of vertex of current element in phys_pts matrix
5486 int pt_idx = c2h_map[RG.RefGeoms[iv+nvert*jel]];
5487 // index of current vertex into DOF array
5488 int node_idx = node_map ? node_map[iv_lex] : iv_lex;
5489 for (int d=0; d<spaceDim; ++d)
5490 {
5491 (*Nodes)[dofs[node_idx + d*nvert]] = phys_pts(d,pt_idx);
5492 }
5493 }
5494 }
5495 }
5496 }
5497
5498 // Setup the data for the coarse-fine refinement transformations
5499 CoarseFineTr.embeddings.SetSize(GetNE());
5500 // First, compute total number of point matrices that we need per geometry
5501 // and the offsets into that array
5502 using GeomRef = std::pair<Geometry::Type, int>;
5503 std::map<GeomRef, int> point_matrices_offsets;
5504 int n_point_matrices[Geometry::NumGeom] = {}; // initialize to zero
5505 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
5506 {
5507 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
5508 // Have we seen this pair of (goemetry, refinement level) before?
5509 GeomRef id(geom, ref_factors[el_coarse]);
5510 if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
5511 {
5512 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el_coarse]);
5513 int nvert = Geometry::NumVerts[geom];
5514 int nref_el = RG.RefGeoms.Size()/nvert;
5515 // If not, then store the offset and add to the size required
5516 point_matrices_offsets[id] = n_point_matrices[geom];
5517 n_point_matrices[geom] += nref_el;
5518 }
5519 }
5520
5521 // Set up the sizes
5522 for (int geom = 0; geom < Geometry::NumGeom; ++geom)
5523 {
5524 int nmatrices = n_point_matrices[geom];
5525 int nvert = Geometry::NumVerts[geom];
5526 CoarseFineTr.point_matrices[geom].SetSize(Dim, nvert, nmatrices);
5527 }
5528
5529 // Compute the point matrices and embeddings
5530 int el_fine = 0;
5531 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
5532 {
5533 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
5534 int ref = ref_factors[el_coarse];
5535 int offset = point_matrices_offsets[GeomRef(geom, ref)];
5536 int nvert = Geometry::NumVerts[geom];
5537 RefinedGeometry &RG = *refiner.Refine(geom, ref);
5538 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
5539 {
5540 DenseMatrix &Pj = CoarseFineTr.point_matrices[geom](offset + j);
5541 for (int k = 0; k < nvert; k++)
5542 {
5543 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
5544 const IntegrationPoint &ip = RG.RefPts[cid];
5545 ip.Get(Pj.GetColumn(k), Dim);
5546 }
5547
5548 Embedding &emb = CoarseFineTr.embeddings[el_fine];
5549 emb.geom = geom;
5550 emb.parent = el_coarse;
5551 emb.matrix = offset + j;
5552 ++el_fine;
5553 }
5554 }
5555
5556 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
5557
5558 // The check below is disabled because is fails for parallel meshes with
5559 // interior "boundary" element that, when such "boundary" element is between
5560 // two elements on different processors.
5561 // MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
5562}
5563
5565{
5566 Mesh mesh;
5567 auto parent_elements = mesh.MakeSimplicial_(orig_mesh, NULL);
5568 if (orig_mesh.GetNodes() != nullptr)
5569 {
5570 mesh.MakeHigherOrderSimplicial_(orig_mesh, parent_elements);
5571 }
5572 return mesh;
5573}
5574
5575Array<int> Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
5576{
5577 MFEM_VERIFY(const_cast<Mesh&>(orig_mesh).CheckElementOrientation(false) == 0,
5578 "Mesh::MakeSimplicial requires a properly oriented input mesh");
5579 MFEM_VERIFY(orig_mesh.Conforming(),
5580 "Mesh::MakeSimplicial does not support non-conforming meshes.")
5581
5582 int dim = orig_mesh.Dimension();
5583 int sdim = orig_mesh.SpaceDimension();
5584
5585 if (dim == 1)
5586 {
5587 Mesh copy(orig_mesh);
5588 Swap(copy, true);
5589 Array<int> parent_elements(GetNE());
5590 std::iota(parent_elements.begin(), parent_elements.end(), 0);
5591 return parent_elements;
5592 }
5593
5594 int nv = orig_mesh.GetNV();
5595 int ne = orig_mesh.GetNE();
5596 int nbe = orig_mesh.GetNBE();
5597
5598 static int num_subdivisions[Geometry::NUM_GEOMETRIES];
5599 num_subdivisions[Geometry::POINT] = 1;
5600 num_subdivisions[Geometry::SEGMENT] = 1;
5601 num_subdivisions[Geometry::TRIANGLE] = 1;
5602 num_subdivisions[Geometry::TETRAHEDRON] = 1;
5603 num_subdivisions[Geometry::SQUARE] = 2;
5604 num_subdivisions[Geometry::PRISM] = 3;
5605 num_subdivisions[Geometry::CUBE] = 6;
5606 // NOTE: some hexes may be subdivided into only 5 tets, so this is an
5607 // estimate only. The actual number of created tets may be less, so the
5608 // elements array will need to be shrunk after mesh creation.
5609 int new_ne = 0, new_nbe = 0;
5610 for (int i=0; i<ne; ++i)
5611 {
5612 new_ne += num_subdivisions[orig_mesh.GetElementBaseGeometry(i)];
5613 }
5614 for (int i=0; i<nbe; ++i)
5615 {
5616 new_nbe += num_subdivisions[orig_mesh.GetBdrElementGeometry(i)];
5617 }
5618
5619 InitMesh(dim, sdim, nv, new_ne, new_nbe);
5620
5621 // Vertices of the new mesh are same as the original mesh
5622 NumOfVertices = nv;
5623 for (int i=0; i<nv; ++i)
5624 {
5625 vertices[i].SetCoords(sdim, orig_mesh.vertices[i]());
5626 }
5627
5628 // We need a global vertex numbering to identify which diagonals to split
5629 // (quad faces are split using the diagonal originating from the smallest
5630 // global vertex number). Use the supplied global numbering, if it is
5631 // non-NULL, otherwise use the local numbering.
5632 Array<int> vglobal_id;
5633 if (vglobal == nullptr)
5634 {
5635 vglobal_id.SetSize(nv);
5636 std::iota(vglobal_id.begin(), vglobal_id.end(), 0);
5637 vglobal = vglobal_id.GetData();
5638 }
5639
5640 // Number of vertices per element
5641 constexpr int nv_tri = 3, nv_quad = 4, nv_tet = 4, nv_prism = 6, nv_hex = 8;
5642 constexpr int quad_ntris = 2; // NTriangles per quad
5643 constexpr int prism_ntets = 3; // NTets per prism
5644 // Map verts of quad to verts of tri, in two possible configurations.
5645 // quad_trimap[i][0,2,4] is the first triangle, and quad_trimap[i][1,3,5] is
5646 // the second, for each configuration.
5647 static const int quad_trimap[2][nv_tri*quad_ntris] =
5648 {
5649 {
5650 0, 0,
5651 1, 2,
5652 2, 3
5653 },{
5654 0, 1,
5655 1, 2,
5656 3, 3
5657 }
5658 };
5659 static const int prism_rot[nv_prism*nv_prism] =
5660 {
5661 0, 1, 2, 3, 4, 5,
5662 1, 2, 0, 4, 5, 3,
5663 2, 0, 1, 5, 3, 4,
5664 3, 5, 4, 0, 2, 1,
5665 4, 3, 5, 1, 0, 2,
5666 5, 4, 3, 2, 1, 0
5667 };
5668 static const int prism_f[nv_quad] = {1, 2, 5, 4};
5669 static const int prism_tetmaps[2][nv_prism*prism_ntets] =
5670 {
5671 {
5672 0, 0, 0,
5673 1, 1, 4,
5674 2, 5, 5,
5675 5, 4, 3
5676 },{
5677 0, 0, 0,
5678 1, 4, 4,
5679 2, 2, 5,
5680 4, 5, 3
5681 }
5682 };
5683 static const int hex_rot[nv_hex*nv_hex] =
5684 {
5685 0, 1, 2, 3, 4, 5, 6, 7,
5686 1, 0, 4, 5, 2, 3, 7, 6,
5687 2, 1, 5, 6, 3, 0, 4, 7,
5688 3, 0, 1, 2, 7, 4, 5, 6,
5689 4, 0, 3, 7, 5, 1, 2, 6,
5690 5, 1, 0, 4, 6, 2, 3, 7,
5691 6, 2, 1, 5, 7, 3, 0, 4,
5692 7, 3, 2, 6, 4, 0, 1, 5
5693 };
5694 static const int hex_f0[nv_quad] = {1, 2, 6, 5};
5695 static const int hex_f1[nv_quad] = {2, 3, 7, 6};
5696 static const int hex_f2[nv_quad] = {4, 5, 6, 7};
5697 static const int num_rot[8] = {0, 1, 2, 0, 0, 2, 1, 0};
5698 static const int hex_tetmap0[nv_tet*5] =
5699 {
5700 0, 0, 0, 0, 2,
5701 1, 2, 2, 5, 7,
5702 2, 7, 3, 7, 5,
5703 5, 5, 7, 4, 6
5704 };
5705 static const int hex_tetmap1[nv_tet*6] =
5706 {
5707 0, 0, 1, 0, 0, 1,
5708 5, 1, 6, 7, 7, 7,
5709 7, 7, 7, 2, 1, 6,
5710 4, 5, 5, 3, 2, 2
5711 };
5712 static const int hex_tetmap2[nv_tet*6] =
5713 {
5714 0, 0, 0, 0, 0, 0,
5715 4, 3, 7, 1, 3, 6,
5716 5, 7, 4, 2, 6, 5,
5717 6, 6, 6, 5, 2, 2
5718 };
5719 static const int hex_tetmap3[nv_tet*6] =
5720 {
5721 0, 0, 0, 0, 1, 1,
5722 2, 3, 7, 5, 5, 6,
5723 3, 7, 4, 6, 6, 2,
5724 6, 6, 6, 4, 0, 0
5725 };
5726 static const int *hex_tetmaps[4] =
5727 {
5728 hex_tetmap0, hex_tetmap1, hex_tetmap2, hex_tetmap3
5729 };
5730
5731 auto find_min = [](const int *a, int n) { return std::min_element(a,a+n)-a; };
5732
5733 Array<int> parent_elems;
5734 for (int i=0; i<ne; ++i)
5735 {
5736 const int *v = orig_mesh.elements[i]->GetVertices();
5737 const int attrib = orig_mesh.GetAttribute(i);
5738 const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(i);
5739
5740 if (num_subdivisions[orig_geom] == 1)
5741 {
5742 // (num_subdivisions[orig_geom] == 1) implies that the element does not
5743 // need to be further split (it is either a segment, triangle, or
5744 // tetrahedron), and so it is left unchanged.
5745 Element *e = NewElement(orig_geom);
5746 e->SetAttribute(attrib);
5747 e->SetVertices(v);
5748 AddElement(e);
5749 parent_elems.Append(i);
5750 }
5751 else if (orig_geom == Geometry::SQUARE)
5752 {
5753 for (int itri=0; itri<quad_ntris; ++itri)
5754 {
5756 e->SetAttribute(attrib);
5757 int *v2 = e->GetVertices();
5758 for (int iv=0; iv<nv_tri; ++iv)
5759 {
5760 v2[iv] = v[quad_trimap[0][itri + iv*quad_ntris]];
5761 }
5762 AddElement(e);
5763 parent_elems.Append(i);
5764 }
5765 }
5766 else if (orig_geom == Geometry::PRISM)
5767 {
5768 int vg[nv_prism];
5769 for (int iv=0; iv<nv_prism; ++iv) { vg[iv] = vglobal[v[iv]]; }
5770 // Rotate the vertices of the prism so that the smallest vertex index
5771 // is in the first place
5772 int irot = find_min(vg, nv_prism);
5773 for (int iv=0; iv<nv_prism; ++iv)
5774 {
5775 int jv = prism_rot[iv + irot*nv_prism];
5776 vg[iv] = v[jv];
5777 }
5778 // Two cases according to which diagonal splits third quad face
5779 int q[nv_quad];
5780 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[prism_f[iv]]]; }
5781 int j = find_min(q, nv_quad);
5782 const int *tetmap = (j == 0 || j == 2) ? prism_tetmaps[0] : prism_tetmaps[1];
5783 for (int itet=0; itet<prism_ntets; ++itet)
5784 {
5786 e->SetAttribute(attrib);
5787 int *v2 = e->GetVertices();
5788 for (int iv=0; iv<nv_tet; ++iv)
5789 {
5790 v2[iv] = vg[tetmap[itet + iv*prism_ntets]];
5791 }
5792 AddElement(e);
5793 parent_elems.Append(i);
5794 }
5795 }
5796 else if (orig_geom == Geometry::CUBE)
5797 {
5798 int vg[nv_hex];
5799 for (int iv=0; iv<nv_hex; ++iv) { vg[iv] = vglobal[v[iv]]; }
5800
5801 // Rotate the vertices of the hex so that the smallest vertex index is
5802 // in the first place
5803 int irot = find_min(vg, nv_hex);
5804 for (int iv=0; iv<nv_hex; ++iv)
5805 {
5806 int jv = hex_rot[iv + irot*nv_hex];
5807 vg[iv] = v[jv];
5808 }
5809
5810 int q[nv_quad];
5811 // Bitmask is three binary digits, each digit is 1 if the diagonal of
5812 // the corresponding face goes through the 7th vertex, and 0 if not.
5813 int bitmask = 0;
5814 int j;
5815 // First quad face
5816 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f0[iv]]]; }
5817 j = find_min(q, nv_quad);
5818 if (j == 0 || j == 2) { bitmask += 4; }
5819 // Second quad face
5820 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f1[iv]]]; }
5821 j = find_min(q, nv_quad);
5822 if (j == 1 || j == 3) { bitmask += 2; }
5823 // Third quad face
5824 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f2[iv]]]; }
5825 j = find_min(q, nv_quad);
5826 if (j == 0 || j == 2) { bitmask += 1; }
5827
5828 // Apply rotations
5829 int nrot = num_rot[bitmask];
5830 for (int k=0; k<nrot; ++k)
5831 {
5832 int vtemp;
5833 vtemp = vg[1];
5834 vg[1] = vg[4];
5835 vg[4] = vg[3];
5836 vg[3] = vtemp;
5837 vtemp = vg[5];
5838 vg[5] = vg[7];
5839 vg[7] = vg[2];
5840 vg[2] = vtemp;
5841 }
5842
5843 // Sum up nonzero bits in bitmask
5844 int ndiags = ((bitmask&4) >> 2) + ((bitmask&2) >> 1) + (bitmask&1);
5845 int ntets = (ndiags == 0) ? 5 : 6;
5846 const int *tetmap = hex_tetmaps[ndiags];
5847 for (int itet=0; itet<ntets; ++itet)
5848 {
5850 e->SetAttribute(attrib);
5851 int *v2 = e->GetVertices();
5852 for (int iv=0; iv<nv_tet; ++iv)
5853 {
5854 v2[iv] = vg[tetmap[itet + iv*ntets]];
5855 }
5856 AddElement(e);
5857 parent_elems.Append(i);
5858 }
5859 }
5860 }
5861 // In 3D, shrink the element array because some hexes have only 5 tets
5862 if (dim == 3) { elements.SetSize(NumOfElements); }
5863
5864 for (int i=0; i<nbe; ++i)
5865 {
5866 const int *v = orig_mesh.boundary[i]->GetVertices();
5867 const int attrib = orig_mesh.GetBdrAttribute(i);
5868 const Geometry::Type orig_geom = orig_mesh.GetBdrElementGeometry(i);
5869 if (num_subdivisions[orig_geom] == 1)
5870 {
5871 Element *be = NewElement(orig_geom);
5872 be->SetAttribute(attrib);
5873 be->SetVertices(v);
5874 AddBdrElement(be);
5875 }
5876 else if (orig_geom == Geometry::SQUARE)
5877 {
5878 int vg[nv_quad];
5879 for (int iv=0; iv<nv_quad; ++iv) { vg[iv] = vglobal[v[iv]]; }
5880 // Split quad according the smallest (global) vertex
5881 int iv_min = find_min(vg, nv_quad);
5882 int isplit = (iv_min == 0 || iv_min == 2) ? 0 : 1;
5883 for (int itri=0; itri<quad_ntris; ++itri)
5884 {
5886 be->SetAttribute(attrib);
5887 int *v2 = be->GetVertices();
5888 for (int iv=0; iv<nv_tri; ++iv)
5889 {
5890 v2[iv] = v[quad_trimap[isplit][itri + iv*quad_ntris]];
5891 }
5892 AddBdrElement(be);
5893 }
5894 }
5895 else
5896 {
5897 MFEM_ABORT("Unreachable");
5898 }
5899 }
5900
5901 FinalizeTopology(false);
5902 sequence = orig_mesh.GetSequence();
5903 last_operation = orig_mesh.last_operation;
5904
5905 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
5906 MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
5907
5908 return parent_elems;
5909}
5910
5911
5913 const Array<int> &parent_elements)
5914{
5915 // Higher order associated to vertices are unchanged, and those for
5916 // previously existing edges. DOFs associated to new elements need to be set.
5917 const int sdim = orig_mesh.SpaceDimension();
5918 auto *orig_fespace = orig_mesh.GetNodes()->FESpace();
5919 SetCurvature(orig_fespace->GetMaxElementOrder(), orig_fespace->IsDGSpace(),
5920 orig_mesh.SpaceDimension(), orig_fespace->GetOrdering());
5921
5922 // The dofs associated with vertices are unchanged, but there can be new dofs
5923 // associated to edges, faces and volumes. Additionally, because we know that
5924 // the set of vertices is unchanged by the splitting operation, we can use
5925 // the vertices to map local coordinates of the "child" elements (the new
5926 // simplices introduced), from the "parent" element (the quad, prism, hex
5927 // that was split).
5928
5929 // For segment, triangle and tetrahedron, the dof values are copied directly.
5930 // For the others, we have to construct a map from the Node locations in the
5931 // new simplex to the parent non-simplex element. This could be sped up by
5932 // not repeatedly access the original FE as the accesses will be coherent
5933 // (i.e. all child elems are consecutive).
5934
5935 Array<int> edofs; // element dofs in new element
5936 Array<int> parent_vertices, child_vertices; // vertices of parent and child.
5937 Array<int> node_map; // node indices of parent from child.
5938 Vector edofvals; // values of elements dofs in original element
5939 // Storage for evaluating node function on parent element, at node locations
5940 // of child element
5941 DenseMatrix shape; // ndof_coarse x nnode_refined.
5942 DenseMatrix point_matrix; // sdim x nnode_refined
5944 child_nodes_in_parent; // The parent nodes that correspond to the child nodes
5945 for (int i = 0; i < parent_elements.Size(); i++)
5946 {
5947 const int ip = parent_elements[i];
5948 const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(ip);
5949 orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
5950 switch (orig_geom)
5951 {
5952 case Geometry::Type::SEGMENT : // fall through
5953 case Geometry::Type::TRIANGLE : // fall through
5955 GetNodes()->FESpace()->GetElementVDofs(i, edofs);
5956 GetNodes()->SetSubVector(edofs, edofvals);
5957 break;
5958 case Geometry::Type::CUBE : // fall through
5959 case Geometry::Type::PRISM : // fall through
5960 case Geometry::Type::PYRAMID : // fall through
5962 {
5963 // Extract the vertices of parent and child, can then form the
5964 // map from child reference coordinates to parent reference
5965 // coordinates. Exploit the fact that for Nodes, the vertex
5966 // entries come first, and their indexing matches the vertex
5967 // numbering. Thus we have already have an inverse index map.
5968 orig_mesh.GetElementVertices(ip, parent_vertices);
5969 GetElementVertices(i, child_vertices);
5970 node_map.SetSize(0);
5971 for (auto cv : child_vertices)
5972 for (int ipv = 0; ipv < parent_vertices.Size(); ipv++)
5973 if (cv == parent_vertices[ipv])
5974 {
5975 node_map.Append(ipv);
5976 break;
5977 }
5978 MFEM_ASSERT(node_map.Size() == Geometry::NumVerts[GetElementBaseGeometry(i)],
5979 "!");
5980 // node_map now says which of the parent vertex nodes map to each
5981 // of the child vertex nodes. Using this can build a basis in the
5982 // parent element from child Node values, exploit the linearity
5983 // to then transform all nodes.
5984 child_nodes_in_parent.SetSize(0);
5985 const auto *orig_FE = orig_mesh.GetNodes()->FESpace()->GetFE(ip);
5986 for (auto pn : node_map)
5987 {
5988 child_nodes_in_parent.Append(orig_FE->GetNodes()[pn]);
5989 }
5990 const auto *simplex_FE = GetNodes()->FESpace()->GetFE(i);
5991 shape.SetSize(orig_FE->GetDof(),
5992 simplex_FE->GetDof()); // One set of evaluations per simplex dof.
5993 Vector col;
5994 for (int j = 0; j < simplex_FE->GetNodes().Size(); j++)
5995 {
5996 const auto &simplex_node = simplex_FE->GetNodes()[j];
5997 IntegrationPoint simplex_node_in_orig;
5998 // Handle the 2D vs 3D case by multiplying .z by zero.
5999 simplex_node_in_orig.Set3(
6000 child_nodes_in_parent[0].x +
6001 simplex_node.x * (child_nodes_in_parent[1].x - child_nodes_in_parent[0].x)
6002 + simplex_node.y * (child_nodes_in_parent[2].x - child_nodes_in_parent[0].x)
6003 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].x -
6004 child_nodes_in_parent[0].x),
6005 child_nodes_in_parent[0].y +
6006 simplex_node.x * (child_nodes_in_parent[1].y - child_nodes_in_parent[0].y)
6007 + simplex_node.y * (child_nodes_in_parent[2].y - child_nodes_in_parent[0].y)
6008 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].y -
6009 child_nodes_in_parent[0].y),
6010 child_nodes_in_parent[0].z +
6011 simplex_node.x * (child_nodes_in_parent[1].z - child_nodes_in_parent[0].z)
6012 + simplex_node.y * (child_nodes_in_parent[2].z - child_nodes_in_parent[0].z)
6013 + simplex_node.z * (child_nodes_in_parent[(Dim > 2) ? 3 : 0].z -
6014 child_nodes_in_parent[0].z));
6015 shape.GetColumnReference(j, col);
6016 orig_FE->CalcShape(simplex_node_in_orig, col);
6017 }
6018 // All the non-simplex basis functions have now been evaluated at
6019 // all the simplex basis function node locations. Now evaluate
6020 // the summations and place back into the Nodes vector.
6021 orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
6022 // Dof values are always returned as
6023 // [[x_1,x_2,x_3,...],
6024 // [y_1,y_2,y_3,...],
6025 // [z_1,z_2,z_3,...]]
6026 DenseMatrix edofvals_mat(edofvals.GetData(), orig_FE->GetDof(), sdim);
6027 point_matrix.SetSize(simplex_FE->GetDof(), sdim);
6028 MultAtB(shape, edofvals_mat, point_matrix);
6029 GetNodes()->FESpace()->GetElementVDofs(i, edofs);
6030 GetNodes()->SetSubVector(edofs, point_matrix.GetData());
6031 }
6032 break;
6033 case Geometry::Type::POINT : // fall through
6036 MFEM_ABORT("Internal Error!");
6037 }
6038 }
6039}
6040
6041
6042Mesh Mesh::MakePeriodic(const Mesh &orig_mesh, const std::vector<int> &v2v)
6043{
6044 Mesh periodic_mesh(orig_mesh, true); // Make a copy of the original mesh
6045 const FiniteElementSpace *nodal_fes = orig_mesh.GetNodalFESpace();
6046 int nodal_order = nodal_fes ? nodal_fes->GetMaxElementOrder() : 1;
6047 periodic_mesh.SetCurvature(nodal_order, true);
6048
6049 // renumber element vertices
6050 for (int i = 0; i < periodic_mesh.GetNE(); i++)
6051 {
6052 Element *el = periodic_mesh.GetElement(i);
6053 int *v = el->GetVertices();
6054 int nv = el->GetNVertices();
6055 for (int j = 0; j < nv; j++)
6056 {
6057 v[j] = v2v[v[j]];
6058 }
6059 }
6060 // renumber boundary element vertices
6061 for (int i = 0; i < periodic_mesh.GetNBE(); i++)
6062 {
6063 Element *el = periodic_mesh.GetBdrElement(i);
6064 int *v = el->GetVertices();
6065 int nv = el->GetNVertices();
6066 for (int j = 0; j < nv; j++)
6067 {
6068 v[j] = v2v[v[j]];
6069 }
6070 }
6071
6072 periodic_mesh.RemoveUnusedVertices();
6073 return periodic_mesh;
6074}
6075
6077 const std::vector<Vector> &translations, real_t tol) const
6078{
6079 const int sdim = SpaceDimension();
6080
6081 Vector coord(sdim), at(sdim), dx(sdim);
6082 Vector xMax(sdim), xMin(sdim), xDiff(sdim);
6083 xMax = xMin = xDiff = 0.0;
6084
6085 // Get a list of all vertices on the boundary
6086 unordered_set<int> bdr_v;
6087 for (int be = 0; be < GetNBE(); be++)
6088 {
6089 Array<int> dofs;
6090 GetBdrElementVertices(be,dofs);
6091
6092 for (int i = 0; i < dofs.Size(); i++)
6093 {
6094 bdr_v.insert(dofs[i]);
6095
6096 coord = GetVertex(dofs[i]);
6097 for (int j = 0; j < sdim; j++)
6098 {
6099 xMax[j] = max(xMax[j], coord[j]);
6100 xMin[j] = min(xMin[j], coord[j]);
6101 }
6102 }
6103 }
6104 add(xMax, -1.0, xMin, xDiff);
6105 real_t dia = xDiff.Norml2(); // compute mesh diameter
6106
6107 // We now identify coincident vertices. Several originally distinct vertices
6108 // may become coincident under the periodic mapping. One of these vertices
6109 // will be identified as the "primary" vertex, and all other coincident
6110 // vertices will be considered as "replicas".
6111
6112 // replica2primary[v] is the index of the primary vertex of replica `v`
6113 unordered_map<int, int> replica2primary;
6114 // primary2replicas[v] is a set of indices of replicas of primary vertex `v`
6115 unordered_map<int, unordered_set<int>> primary2replicas;
6116
6117 // Create a KD-tree containing all the boundary vertices
6118 std::unique_ptr<KDTreeBase<int,real_t>> kdtree;
6119 if (sdim == 1) { kdtree.reset(new KDTree1D); }
6120 else if (sdim == 2) { kdtree.reset(new KDTree2D); }
6121 else if (sdim == 3) { kdtree.reset(new KDTree3D); }
6122 else { MFEM_ABORT("Invalid space dimension."); }
6123
6124 // We begin with the assumption that all vertices are primary, and that there
6125 // are no replicas.
6126 for (const int v : bdr_v)
6127 {
6128 primary2replicas[v];
6129 kdtree->AddPoint(GetVertex(v), v);
6130 }
6131
6132 kdtree->Sort();
6133
6134 // Make `r` and all of `r`'s replicas be replicas of `p`. Delete `r` from the
6135 // list of primary vertices.
6136 auto make_replica = [&replica2primary, &primary2replicas](int r, int p)
6137 {
6138 if (r == p) { return; }
6139 primary2replicas[p].insert(r);
6140 replica2primary[r] = p;
6141 for (const int s : primary2replicas[r])
6142 {
6143 primary2replicas[p].insert(s);
6144 replica2primary[s] = p;
6145 }
6146 primary2replicas.erase(r);
6147 };
6148
6149 for (unsigned int i = 0; i < translations.size(); i++)
6150 {
6151 for (int vi : bdr_v)
6152 {
6153 coord = GetVertex(vi);
6154 add(coord, translations[i], at);
6155
6156 const int vj = kdtree->FindClosestPoint(at.GetData());
6157 coord = GetVertex(vj);
6158 add(at, -1.0, coord, dx);
6159
6160 if (dx.Norml2() > dia*tol) { continue; }
6161
6162 // The two vertices vi and vj are coincident.
6163
6164 // Are vertices `vi` and `vj` already primary?
6165 const bool pi = primary2replicas.find(vi) != primary2replicas.end();
6166 const bool pj = primary2replicas.find(vj) != primary2replicas.end();
6167
6168 if (pi && pj)
6169 {
6170 // Both vertices are currently primary
6171 // Demote `vj` to be a replica of `vi`
6172 make_replica(vj, vi);
6173 }
6174 else if (pi && !pj)
6175 {
6176 // `vi` is primary and `vj` is a replica
6177 const int owner_of_vj = replica2primary[vj];
6178 // Make `vi` and its replicas be replicas of `vj`'s owner
6179 make_replica(vi, owner_of_vj);
6180 }
6181 else if (!pi && pj)
6182 {
6183 // `vi` is currently a replica and `vj` is currently primary
6184 // Make `vj` and its replicas be replicas of `vi`'s owner
6185 const int owner_of_vi = replica2primary[vi];
6186 make_replica(vj, owner_of_vi);
6187 }
6188 else
6189 {
6190 // Both vertices are currently replicas
6191 // Make `vj`'s owner and all of its owner's replicas be replicas
6192 // of `vi`'s owner
6193 const int owner_of_vi = replica2primary[vi];
6194 const int owner_of_vj = replica2primary[vj];
6195 make_replica(owner_of_vj, owner_of_vi);
6196 }
6197 }
6198 }
6199
6200 std::vector<int> v2v(GetNV());
6201 for (size_t i = 0; i < v2v.size(); i++)
6202 {
6203 v2v[i] = static_cast<int>(i);
6204 }
6205 for (const auto &r2p : replica2primary)
6206 {
6207 v2v[r2p.first] = r2p.second;
6208 }
6209 return v2v;
6210}
6211
6212void Mesh::RefineNURBSFromFile(std::string ref_file)
6213{
6214 MFEM_VERIFY(NURBSext,"Mesh::RefineNURBSFromFile: Not a NURBS mesh!");
6215 mfem::out<<"Refining NURBS from refinement file: "<<ref_file<<endl;
6216
6217 int nkv;
6218 ifstream input(ref_file);
6219 input >> nkv;
6220
6221 // Check if the number of knot vectors in the refinement file and mesh match
6222 if ( nkv != NURBSext->GetNKV())
6223 {
6224 mfem::out<<endl;
6225 mfem::out<<"Knot vectors in ref_file: "<<nkv<<endl;
6226 mfem::out<<"Knot vectors in NURBSExt: "<<NURBSext->GetNKV()<<endl;
6227 MFEM_ABORT("Refine file does not have the correct number of knot vectors");
6228 }
6229
6230 // Read knot vectors from file
6231 Array<Vector *> knotVec(nkv);
6232 for (int kv = 0; kv < nkv; kv++)
6233 {
6234 knotVec[kv] = new Vector();
6235 knotVec[kv]-> Load(input);
6236 }
6237 input.close();
6238
6239 // Insert knots
6240 KnotInsert(knotVec);
6241
6242 // Delete knots
6243 for (int kv = 0; kv < nkv; kv++)
6244 {
6245 delete knotVec[kv];
6246 }
6247}
6248
6250{
6251 if (NURBSext == NULL)
6252 {
6253 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
6254 }
6255
6256 if (kv.Size() != NURBSext->GetNKV())
6257 {
6258 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
6259 }
6260
6262
6263 NURBSext->KnotInsert(kv);
6264
6265 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6266 sequence++;
6267
6268 UpdateNURBS();
6269}
6270
6272{
6273 if (NURBSext == NULL)
6274 {
6275 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
6276 }
6277
6278 if (kv.Size() != NURBSext->GetNKV())
6279 {
6280 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
6281 }
6282
6284
6285 NURBSext->KnotInsert(kv);
6286
6287 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6288 sequence++;
6289
6290 UpdateNURBS();
6291}
6292
6294{
6295 if (NURBSext == NULL)
6296 {
6297 mfem_error("Mesh::KnotRemove : Not a NURBS mesh!");
6298 }
6299
6300 if (kv.Size() != NURBSext->GetNKV())
6301 {
6302 mfem_error("Mesh::KnotRemove : KnotVector array size mismatch!");
6303 }
6304
6306
6307 NURBSext->KnotRemove(kv);
6308
6309 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6310 sequence++;
6311
6312 UpdateNURBS();
6313}
6314
6315void Mesh::RefineNURBSWithKVFactors(int rf, const std::string &kvf)
6316{
6317 RefineNURBS(true, 0.0, Array<int>(&rf, 1), kvf);
6318}
6319
6321{
6322 Array<int> rf_array(Dim);
6323 rf_array = rf;
6324 NURBSUniformRefinement(rf_array, tol);
6325}
6326
6328{
6329 MFEM_VERIFY(rf.Size() == Dim,
6330 "Refinement factors must be defined for each dimension");
6331
6332 RefineNURBS(false, tol, rf, "");
6333}
6334
6335void Mesh::RefineNURBS(bool usingKVF, real_t tol, const Array<int> &rf,
6336 const std::string &kvf)
6337{
6338 MFEM_VERIFY(NURBSext, "This type of refinement is only for NURBS meshes");
6340
6341 Array<int> cf;
6343
6344 bool cf1 = true;
6345 for (auto f : cf)
6346 {
6347 cf1 = (cf1 && f == 1);
6348 }
6349
6350 if (!cf1 && NURBSext->NonconformingPatches())
6351 {
6353 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6354 }
6355 else if (!cf1 && !NURBSext->NonconformingPatches())
6356 {
6357 MFEM_VERIFY(!usingKVF, "This refinement type is not supported for this"
6358 " NURBS mesh type");
6359 NURBSext->Coarsen(cf, tol);
6360
6361 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6362 sequence++;
6363 UpdateNURBS();
6364
6366 for (int i=0; i<cf.Size(); ++i) { cf[i] *= rf[i]; }
6368 }
6369
6370 if (cf1 || NURBSext->NonconformingPatches())
6371 {
6372 if (usingKVF || NURBSext->NonconformingPatches())
6373 {
6374 NURBSext->RefineWithKVFactors(rf[0], kvf, !cf1);
6375 }
6376 else
6377 {
6379 }
6380 }
6381
6382 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6383 sequence++;
6384
6385 UpdateNURBS();
6386}
6387
6388void Mesh::DegreeElevate(int rel_degree, int degree)
6389{
6390 if (NURBSext == NULL)
6391 {
6392 mfem_error("Mesh::DegreeElevate : Not a NURBS mesh!");
6393 }
6394
6396
6397 NURBSext->DegreeElevate(rel_degree, degree);
6398
6399 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
6400 sequence++;
6401
6402 UpdateNURBS();
6403}
6404
6406{
6407 ResetLazyData();
6408
6410
6411 Dim = NURBSext->Dimension();
6412 spaceDim = Dim;
6413
6414 if (NumOfElements != NURBSext->GetNE())
6415 {
6416 for (int i = 0; i < elements.Size(); i++)
6417 {
6419 }
6422 }
6423
6425 {
6426 for (int i = 0; i < boundary.Size(); i++)
6427 {
6429 }
6432 }
6433
6434 Nodes->FESpace()->Update();
6435 Nodes->Update();
6436 NodesUpdated();
6438
6439 if (NumOfVertices != NURBSext->GetNV())
6440 {
6442 vertices.SetSize(NumOfVertices);
6443 int vd = Nodes->VectorDim();
6444 for (int i = 0; i < vd; i++)
6445 {
6446 Vector vert_val;
6447 Nodes->GetNodalValues(vert_val, i+1);
6448 for (int j = 0; j < NumOfVertices; j++)
6449 {
6450 vertices[j](i) = vert_val(j);
6451 }
6452 }
6453 }
6454
6455 if (el_to_edge)
6456 {
6458 }
6459
6460 if (el_to_face)
6461 {
6463 }
6464 GenerateFaces();
6465}
6466
6467void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
6468{
6469 SetEmpty();
6470
6471 // Read MFEM NURBS mesh v1.0 or 1.1 format
6472 string ident;
6473
6474 skip_comment_lines(input, '#');
6475
6476 input >> ident; // 'dimension'
6477 input >> Dim;
6478 spaceDim = Dim;
6479
6480 skip_comment_lines(input, '#');
6481
6482 input >> ident; // 'elements'
6483 input >> NumOfElements;
6484 elements.SetSize(NumOfElements);
6485 for (int j = 0; j < NumOfElements; j++)
6486 {
6487 elements[j] = ReadElement(input);
6488 }
6489
6490 skip_comment_lines(input, '#');
6491
6492 input >> ident; // 'boundary'
6493 input >> NumOfBdrElements;
6494 boundary.SetSize(NumOfBdrElements);
6495 for (int j = 0; j < NumOfBdrElements; j++)
6496 {
6497 boundary[j] = ReadElement(input);
6498 }
6499
6500 skip_comment_lines(input, '#');
6501
6502 input >> ident; // 'edges'
6503 input >> NumOfEdges;
6504 if (NumOfEdges > 0)
6505 {
6506 edge_vertex = new Table(NumOfEdges, 2);
6507 edge_to_ukv.SetSize(NumOfEdges);
6508 for (int j = 0; j < NumOfEdges; j++)
6509 {
6510 int *v = edge_vertex->GetRow(j);
6511 input >> edge_to_ukv[j] >> v[0] >> v[1];
6512 if (v[0] > v[1])
6513 {
6514 edge_to_ukv[j] = -1 - edge_to_ukv[j];
6515 }
6516 }
6517 }
6518 else
6519 {
6520 edge_to_ukv.SetSize(0);
6521 }
6522
6523 skip_comment_lines(input, '#');
6524
6525 input >> ident; // 'vertices'
6526 input >> NumOfVertices;
6527 vertices.SetSize(0);
6528
6530 CheckBdrElementOrientation(); // check and fix boundary element orientation
6531
6532 /* Generate edge to knotvector mapping if edges are not specified in the
6533 mesh file. See miniapps/nurbs/meshes/two-squares-nurbs-autoedge.mesh
6534 for an example */
6535 if (edge_to_ukv.Size() == 0)
6536 {
6537 Array<int> ukv_to_rpkv;
6538 GetEdgeToUniqueKnotvector(edge_to_ukv, ukv_to_rpkv);
6539 }
6540}
6541
6543 Array<int> &ukv_to_rpkv) const
6544{
6545 const int dim = Dimension(); // topological (not physical) dimension
6546 const int NP = NumOfElements; // number of patches
6547 const int NPKV = NP * dim; // number of patch knotvectors
6548 constexpr int notset = -9999999;
6549 // Sign convention
6550 auto sign = [](int i) { return -1 - i; };
6551 auto unsign = [](int i) { return (i < 0) ? -1 - i : i; };
6552 // Edge index -> dimension convention
6553 auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
6554
6555 Array<int> v(2); // vertices of an edge
6556
6557 // 1D case is special: edge index = signed element index
6558 // ukv_to_rpkv = Identity
6559 if (dim == 1)
6560 {
6561 edge_to_ukv.SetSize(NP);
6562 ukv_to_rpkv.SetSize(NP);
6563 for (int i = 0; i < NP; i++)
6564 {
6565 GetElementVertices(i, v);
6566 // Sign is based on the edge's vertex indices
6567 edge_to_ukv[i] = (v[1] > v[0]) ? i : sign(i);
6568 ukv_to_rpkv[i] = i;
6569 }
6570 return;
6571 }
6572
6573 // Local (per-patch) variables
6574 Array<int> edges, oedges;
6575 // Edge index -> signed patch knotvector index (p*dim + d)
6576 Array<int> edge_to_pkv(NumOfEdges);
6577 edge_to_pkv.SetSize(NumOfEdges);
6578 edge_to_pkv = notset;
6579
6580 // Initialize pkv_map as identity - this is the storage for the
6581 // disjoint-set/union-find algorithm which will later be used
6582 // to get the map pkv_to_rpkv
6583 Array<int> pkv_map(NPKV);
6584 for (int i = 0; i < NPKV; i++)
6585 {
6586 pkv_map[i] = i;
6587 }
6588 std::function<int(int)> get_root;
6589 get_root = [&pkv_map, &get_root](int i) -> int
6590 {
6591 return (pkv_map[i] == i) ? i : get_root(pkv_map[i]);
6592 };
6593 auto unite = [&pkv_map, &get_root](int i, int j)
6594 {
6595 const int ri = get_root(i);
6596 const int rj = get_root(j);
6597 if (ri == rj) { return; }
6598 // keep the lowest index
6599 (ri < rj) ? pkv_map[rj] = ri : pkv_map[ri] = rj;
6600 };
6601
6602 // Get edge_to_pkv (one edge can link to multiple pkv) and pkv_map
6603 for (int p = 0; p < NP; p++)
6604 {
6605 GetElementEdges(p, edges, oedges);
6606
6607 // First loop checks for if edge has already been set
6608 for (int i = 0; i < edges.Size(); i++)
6609 {
6610 const int edge = edges[i];
6611 const int d = edge_to_dim(i);
6612 const int pkv = p*dim+d;
6613
6614 // We've set this edge already - link this index to it
6615 if (edge_to_pkv[edge] != notset)
6616 {
6617 const int pkv_other = unsign(edge_to_pkv[edge]);
6618 unite(pkv, pkv_other);
6619 }
6620 else
6621 {
6622 GetEdgeVertices(edge, v);
6623 // Sign is based on the edge's vertex indices
6624 edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : sign(pkv);
6625 }
6626 }
6627 }
6628
6629 // Construct the pkv_to_rpkv map by finding the lowest/root index
6630 Array<int> pkv_to_rpkv(NPKV);
6631 ukv_to_rpkv.SetSize(NPKV);
6632 for (int i = 0; i < NPKV; i++)
6633 {
6634 pkv_to_rpkv[i] = get_root(pkv_map[i]);
6635 ukv_to_rpkv[i] = pkv_to_rpkv[i];
6636 }
6637 ukv_to_rpkv.Sort(); // ukv is just a renumbering of rpkv
6638 ukv_to_rpkv.Unique();
6639
6640 // Create inverse map
6641 std::map<int, int> rpkv_to_ukv;
6642 for (int i = 0; i < ukv_to_rpkv.Size(); i++)
6643 {
6644 rpkv_to_ukv[ukv_to_rpkv[i]] = i;
6645 }
6646
6647 // Get edge_to_ukv = edge_to_pkv -> pkv_to_rpkv -> rpkv_to_ukv
6648 edge_to_ukv.SetSize(NumOfEdges);
6649 for (int i = 0; i < NumOfEdges; i++)
6650 {
6651 const int pkv = unsign(edge_to_pkv[i]);
6652 const int rpkv = pkv_to_rpkv[pkv];
6653 const int ukv = rpkv_to_ukv[rpkv];
6654 edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? sign(ukv) : ukv;
6655 }
6656}
6657
6658void Mesh::LoadNonconformingPatchTopo(std::istream &input,
6659 Array<int> &edge_to_ukv)
6660{
6661 SetEmpty();
6662
6663 // Read MFEM NURBS NC-patch mesh v1.0 format
6664 int curved = 0;
6665 int is_nc = 1;
6666
6667 ncmesh = new NCMesh(input, 10, curved, is_nc);
6668
6670
6671 skip_comment_lines(input, '#');
6672
6673 string ident;
6674 int inputNumOfEdges = -1;
6675
6676 input >> ident; // 'edges'
6677 input >> inputNumOfEdges;
6678
6679 MFEM_VERIFY(NumOfEdges == inputNumOfEdges, "");
6680
6681 edge_to_ukv.SetSize(NumOfEdges);
6682 for (int j = 0; j < NumOfEdges; j++)
6683 {
6684 int v[2]; // Vertex indices
6685 int ukv; // Unique KnotVector index
6686 input >> ukv >> v[0] >> v[1];
6687
6688 for (int i=0; i<2; ++i)
6689 {
6690 v[i] = ncmesh->vertex_nodeId[v[i]];
6691 }
6692
6693 if (v[0] > v[1])
6694 {
6695 ukv = -1 - ukv;
6696 }
6697 edge_to_ukv[j] = ukv;
6698 }
6699
6701 CheckBdrElementOrientation(); // check and fix boundary element orientation
6702}
6703
6705{
6706 if (p.Size() >= v.Size())
6707 {
6708 for (int d = 0; d < v.Size(); d++)
6709 {
6710 v(d) = p(d);
6711 }
6712 }
6713 else
6714 {
6715 int d;
6716 for (d = 0; d < p.Size(); d++)
6717 {
6718 v(d) = p(d);
6719 }
6720 for ( ; d < v.Size(); d++)
6721 {
6722 v(d) = 0.0;
6723 }
6724 }
6725}
6726
6728{
6729 if (Nodes == NULL || Nodes->FESpace() != nodes.FESpace())
6730 {
6731 const int newSpaceDim = nodes.FESpace()->GetVDim();
6733 nodes.ProjectCoefficient(xyz);
6734 }
6735 else
6736 {
6737 nodes = *Nodes;
6738 }
6739}
6740
6746
6748{
6749 if (Nodes)
6750 {
6752 if (dynamic_cast<const H1_FECollection*>(fec)
6753 || dynamic_cast<const L2_FECollection*>(fec))
6754 {
6755 return;
6756 }
6757 else // Mesh using a legacy FE_Collection
6758 {
6759 const int order = GetNodalFESpace()->GetMaxElementOrder();
6760 if (NURBSext)
6761 {
6762#ifndef MFEM_USE_MPI
6763 const bool warn = true;
6764#else
6765 ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
6766 const bool warn = !pmesh || pmesh->GetMyRank() == 0;
6767#endif
6768 if (warn)
6769 {
6770 MFEM_WARNING("converting NURBS mesh to order " << order <<
6771 " H1-continuous mesh!\n "
6772 "If this is the desired behavior, you can silence"
6773 " this warning by converting\n "
6774 "the NURBS mesh to high-order mesh in advance by"
6775 " calling the method\n "
6776 "Mesh::SetCurvature().");
6777 }
6778 }
6779 SetCurvature(order, false, -1, Ordering::byVDIM);
6780 }
6781 }
6782 else // First order H1 mesh
6783 {
6784 SetCurvature(1, false, -1, Ordering::byVDIM);
6785 }
6786}
6787
6789{
6790 GetNodes(*nodes);
6791 NewNodes(*nodes, make_owner);
6792}
6793
6795{
6796 return ((Nodes) ? Nodes->FESpace() : NULL);
6797}
6798
6799void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
6800{
6801 if (order <= 0)
6802 {
6803 delete Nodes;
6804 Nodes = nullptr;
6805 return;
6806 }
6807 space_dim = (space_dim == -1) ? spaceDim : space_dim;
6809 if (discont)
6810 {
6811 const int type = 1; // Gauss-Lobatto points
6812 nfec = new L2_FECollection(order, Dim, type);
6813 }
6814 else
6815 {
6816 nfec = new H1_FECollection(order, Dim);
6817 }
6818 FiniteElementSpace* nfes = new FiniteElementSpace(this, nfec, space_dim,
6819 ordering);
6820
6821 const int old_space_dim = spaceDim;
6822 SetNodalFESpace(nfes);
6823 Nodes->MakeOwner(nfec);
6824
6825 if (spaceDim != old_space_dim)
6826 {
6827 // Fix dimension of the vertices if the space dimension changes
6829 }
6830}
6831
6833{
6834 MFEM_ASSERT(nodes != NULL, "");
6835 for (int i = 0; i < spaceDim; i++)
6836 {
6837 Vector vert_val;
6838 nodes->GetNodalValues(vert_val, i+1);
6839 for (int j = 0; j < NumOfVertices; j++)
6840 {
6841 vertices[j](i) = vert_val(j);
6842 }
6843 }
6844}
6845
6847{
6848 switch (Dim)
6849 {
6850 case 1: return GetNV();
6851 case 2: return GetNEdges();
6852 case 3: return GetNFaces();
6853 }
6854 return 0;
6855}
6856
6858{
6859 return faces_info.Size();
6860}
6861
6863{
6864 const bool isInt = type==FaceType::Interior;
6865 int &nf = isInt ? nbInteriorFaces : nbBoundaryFaces;
6866 if (nf<0)
6867 {
6868 nf = 0;
6869 for (int f = 0; f < GetNumFacesWithGhost(); ++f)
6870 {
6872 if (face.IsOfFaceType(type))
6873 {
6874 if (face.IsNonconformingCoarse())
6875 {
6876 // We don't count nonconforming coarse faces.
6877 continue;
6878 }
6879 nf++;
6880 }
6881 }
6882 }
6883 return nf;
6884}
6885
6886#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
6887static const char *fixed_or_not[] = { "fixed", "NOT FIXED" };
6888#endif
6889
6891{
6892 int i, j, k, wo = 0, fo = 0;
6893 real_t *v[4];
6894
6895 if (Dim == 2 && spaceDim == 2)
6896 {
6897 DenseMatrix J(2, 2);
6898
6899 for (i = 0; i < NumOfElements; i++)
6900 {
6901 int *vi = elements[i]->GetVertices();
6902 if (Nodes == NULL)
6903 {
6904 for (j = 0; j < 3; j++)
6905 {
6906 v[j] = vertices[vi[j]]();
6907 }
6908 for (j = 0; j < 2; j++)
6909 for (k = 0; k < 2; k++)
6910 {
6911 J(j, k) = v[j+1][k] - v[0][k];
6912 }
6913 }
6914 else
6915 {
6916 // only check the Jacobian at the center of the element
6917 GetElementJacobian(i, J);
6918 }
6919 if (J.Det() < 0.0)
6920 {
6921 if (fix_it)
6922 {
6923 switch (GetElementType(i))
6924 {
6925 case Element::TRIANGLE:
6926 mfem::Swap(vi[0], vi[1]);
6927 break;
6929 mfem::Swap(vi[1], vi[3]);
6930 break;
6931 default:
6932 MFEM_ABORT("Invalid 2D element type \""
6933 << GetElementType(i) << "\"");
6934 break;
6935 }
6936 fo++;
6937 }
6938 wo++;
6939 }
6940 }
6941 }
6942
6943 if (Dim == 3)
6944 {
6945 DenseMatrix J(3, 3);
6946
6947 for (i = 0; i < NumOfElements; i++)
6948 {
6949 int *vi = elements[i]->GetVertices();
6950 switch (GetElementType(i))
6951 {
6953 if (Nodes == NULL)
6954 {
6955 for (j = 0; j < 4; j++)
6956 {
6957 v[j] = vertices[vi[j]]();
6958 }
6959 for (j = 0; j < 3; j++)
6960 for (k = 0; k < 3; k++)
6961 {
6962 J(j, k) = v[j+1][k] - v[0][k];
6963 }
6964 }
6965 else
6966 {
6967 // only check the Jacobian at the center of the element
6968 GetElementJacobian(i, J);
6969 }
6970 if (J.Det() < 0.0)
6971 {
6972 wo++;
6973 if (fix_it)
6974 {
6975 mfem::Swap(vi[0], vi[1]);
6976 fo++;
6977 }
6978 }
6979 break;
6980
6981 case Element::WEDGE:
6982 // only check the Jacobian at the center of the element
6983 GetElementJacobian(i, J);
6984 if (J.Det() < 0.0)
6985 {
6986 wo++;
6987 if (fix_it)
6988 {
6989 // how?
6990 }
6991 }
6992 break;
6993
6994 case Element::PYRAMID:
6995 // only check the Jacobian at the center of the element
6996 GetElementJacobian(i, J);
6997 if (J.Det() < 0.0)
6998 {
6999 wo++;
7000 if (fix_it)
7001 {
7002 mfem::Swap(vi[1], vi[3]);
7003 fo++;
7004 }
7005 }
7006 break;
7007
7009 // only check the Jacobian at the center of the element
7010 GetElementJacobian(i, J);
7011 if (J.Det() < 0.0)
7012 {
7013 wo++;
7014 if (fix_it)
7015 {
7016 // how?
7017 }
7018 }
7019 break;
7020
7021 default:
7022 MFEM_ABORT("Invalid 3D element type \""
7023 << GetElementType(i) << "\"");
7024 break;
7025 }
7026 }
7027 }
7028#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
7029 if (wo > 0)
7030 {
7031 mfem::out << "Elements with wrong orientation: " << wo << " / "
7032 << NumOfElements << " (" << fixed_or_not[(wo == fo) ? 0 : 1]
7033 << ")" << endl;
7034 }
7035#else
7036 MFEM_CONTRACT_VAR(fo);
7037#endif
7038 return wo;
7039}
7040
7041int Mesh::GetTriOrientation(const int *base, const int *test)
7042{
7043 // Static method.
7044 // This function computes the index 'j' of the permutation that transforms
7045 // test into base: test[tri_orientation[j][i]]=base[i].
7046 // tri_orientation = Geometry::Constants<Geometry::TRIANGLE>::Orient
7047 int orient;
7048
7049 if (test[0] == base[0])
7050 if (test[1] == base[1])
7051 {
7052 orient = 0; // (0, 1, 2)
7053 }
7054 else
7055 {
7056 orient = 5; // (0, 2, 1)
7057 }
7058 else if (test[0] == base[1])
7059 if (test[1] == base[0])
7060 {
7061 orient = 1; // (1, 0, 2)
7062 }
7063 else
7064 {
7065 orient = 2; // (1, 2, 0)
7066 }
7067 else // test[0] == base[2]
7068 if (test[1] == base[0])
7069 {
7070 orient = 4; // (2, 0, 1)
7071 }
7072 else
7073 {
7074 orient = 3; // (2, 1, 0)
7075 }
7076
7077#ifdef MFEM_DEBUG
7078 const int *aor = tri_t::Orient[orient];
7079 for (int j = 0; j < 3; j++)
7080 if (test[aor[j]] != base[j])
7081 {
7082 mfem::err << "Mesh::GetTriOrientation(...)" << endl;
7083 mfem::err << " base = [";
7084 for (int k = 0; k < 3; k++)
7085 {
7086 mfem::err << " " << base[k];
7087 }
7088 mfem::err << " ]\n test = [";
7089 for (int k = 0; k < 3; k++)
7090 {
7091 mfem::err << " " << test[k];
7092 }
7093 mfem::err << " ]" << endl;
7094 mfem_error();
7095 }
7096#endif
7097
7098 return orient;
7099}
7100
7101int Mesh::ComposeTriOrientations(int ori_a_b, int ori_b_c)
7102{
7103 // Static method.
7104 // Given three, possibly different, configurations of triangular face
7105 // vertices: va, vb, and vc. This function returns the relative orientation
7106 // GetTriOrientation(va, vc) by composing previously computed orientations
7107 // ori_a_b = GetTriOrientation(va, vb) and
7108 // ori_b_c = GetTriOrientation(vb, vc) without accessing the vertices.
7109
7110 const int oo[6][6] =
7111 {
7112 {0, 1, 2, 3, 4, 5},
7113 {1, 0, 5, 4, 3, 2},
7114 {2, 3, 4, 5, 0, 1},
7115 {3, 2, 1, 0, 5, 4},
7116 {4, 5, 0, 1, 2, 3},
7117 {5, 4, 3, 2, 1, 0}
7118 };
7119
7120 int ori_a_c = oo[ori_a_b][ori_b_c];
7121 return ori_a_c;
7122}
7123
7125{
7126 const int inv_ori[6] = {0, 1, 4, 3, 2, 5};
7127 return inv_ori[ori];
7128}
7129
7130int Mesh::GetQuadOrientation(const int *base, const int *test)
7131{
7132 int i;
7133
7134 for (i = 0; i < 4; i++)
7135 if (test[i] == base[0])
7136 {
7137 break;
7138 }
7139
7140#ifdef MFEM_DEBUG
7141 int orient;
7142 if (test[(i+1)%4] == base[1])
7143 {
7144 orient = 2*i;
7145 }
7146 else
7147 {
7148 orient = 2*i+1;
7149 }
7150 const int *aor = quad_t::Orient[orient];
7151 for (int j = 0; j < 4; j++)
7152 if (test[aor[j]] != base[j])
7153 {
7154 mfem::err << "Mesh::GetQuadOrientation(...)" << endl;
7155 mfem::err << " base = [";
7156 for (int k = 0; k < 4; k++)
7157 {
7158 mfem::err << " " << base[k];
7159 }
7160 mfem::err << " ]\n test = [";
7161 for (int k = 0; k < 4; k++)
7162 {
7163 mfem::err << " " << test[k];
7164 }
7165 mfem::err << " ]" << endl;
7166 mfem_error();
7167 }
7168#endif
7169
7170 if (test[(i+1)%4] == base[1])
7171 {
7172 return 2*i;
7173 }
7174
7175 return 2*i+1;
7176}
7177
7178int Mesh::ComposeQuadOrientations(int ori_a_b, int ori_b_c)
7179{
7180 // Static method.
7181 // Given three, possibly different, configurations of quadrilateral face
7182 // vertices: va, vb, and vc. This function returns the relative orientation
7183 // GetQuadOrientation(va, vc) by composing previously computed orientations
7184 // ori_a_b = GetQuadOrientation(va, vb) and
7185 // ori_b_c = GetQuadOrientation(vb, vc) without accessing the vertices.
7186
7187 const int oo[8][8] =
7188 {
7189 {0, 1, 2, 3, 4, 5, 6, 7},
7190 {1, 0, 3, 2, 5, 4, 7, 6},
7191 {2, 7, 4, 1, 6, 3, 0, 5},
7192 {3, 6, 5, 0, 7, 2, 1, 4},
7193 {4, 5, 6, 7, 0, 1, 2, 3},
7194 {5, 4, 7, 6, 1, 0, 3, 2},
7195 {6, 3, 0, 5, 2, 7, 4, 1},
7196 {7, 2, 1, 4, 3, 6, 5, 0}
7197 };
7198
7199 int ori_a_c = oo[ori_a_b][ori_b_c];
7200 return ori_a_c;
7201}
7202
7204{
7205 const int inv_ori[8] = {0, 1, 6, 3, 4, 5, 2, 7};
7206 return inv_ori[ori];
7207}
7208
7209int Mesh::GetTetOrientation(const int *base, const int *test)
7210{
7211 // Static method.
7212 // This function computes the index 'j' of the permutation that transforms
7213 // test into base: test[tet_orientation[j][i]]=base[i].
7214 // tet_orientation = Geometry::Constants<Geometry::TETRAHEDRON>::Orient
7215 int orient;
7216
7217 if (test[0] == base[0])
7218 if (test[1] == base[1])
7219 if (test[2] == base[2])
7220 {
7221 orient = 0; // (0, 1, 2, 3)
7222 }
7223 else
7224 {
7225 orient = 1; // (0, 1, 3, 2)
7226 }
7227 else if (test[2] == base[1])
7228 if (test[3] == base[2])
7229 {
7230 orient = 2; // (0, 2, 3, 1)
7231 }
7232 else
7233 {
7234 orient = 3; // (0, 2, 1, 3)
7235 }
7236 else // test[3] == base[1]
7237 if (test[1] == base[2])
7238 {
7239 orient = 4; // (0, 3, 1, 2)
7240 }
7241 else
7242 {
7243 orient = 5; // (0, 3, 2, 1)
7244 }
7245 else if (test[1] == base[0])
7246 if (test[2] == base[1])
7247 if (test[0] == base[2])
7248 {
7249 orient = 6; // (1, 2, 0, 3)
7250 }
7251 else
7252 {
7253 orient = 7; // (1, 2, 3, 0)
7254 }
7255 else if (test[3] == base[1])
7256 if (test[2] == base[2])
7257 {
7258 orient = 8; // (1, 3, 2, 0)
7259 }
7260 else
7261 {
7262 orient = 9; // (1, 3, 0, 2)
7263 }
7264 else // test[0] == base[1]
7265 if (test[3] == base[2])
7266 {
7267 orient = 10; // (1, 0, 3, 2)
7268 }
7269 else
7270 {
7271 orient = 11; // (1, 0, 2, 3)
7272 }
7273 else if (test[2] == base[0])
7274 if (test[3] == base[1])
7275 if (test[0] == base[2])
7276 {
7277 orient = 12; // (2, 3, 0, 1)
7278 }
7279 else
7280 {
7281 orient = 13; // (2, 3, 1, 0)
7282 }
7283 else if (test[0] == base[1])
7284 if (test[1] == base[2])
7285 {
7286 orient = 14; // (2, 0, 1, 3)
7287 }
7288 else
7289 {
7290 orient = 15; // (2, 0, 3, 1)
7291 }
7292 else // test[1] == base[1]
7293 if (test[3] == base[2])
7294 {
7295 orient = 16; // (2, 1, 3, 0)
7296 }
7297 else
7298 {
7299 orient = 17; // (2, 1, 0, 3)
7300 }
7301 else // (test[3] == base[0])
7302 if (test[0] == base[1])
7303 if (test[2] == base[2])
7304 {
7305 orient = 18; // (3, 0, 2, 1)
7306 }
7307 else
7308 {
7309 orient = 19; // (3, 0, 1, 2)
7310 }
7311 else if (test[1] == base[1])
7312 if (test[0] == base[2])
7313 {
7314 orient = 20; // (3, 1, 0, 2)
7315 }
7316 else
7317 {
7318 orient = 21; // (3, 1, 2, 0)
7319 }
7320 else // test[2] == base[1]
7321 if (test[1] == base[2])
7322 {
7323 orient = 22; // (3, 2, 1, 0)
7324 }
7325 else
7326 {
7327 orient = 23; // (3, 2, 0, 1)
7328 }
7329
7330#ifdef MFEM_DEBUG
7331 const int *aor = tet_t::Orient[orient];
7332 for (int j = 0; j < 4; j++)
7333 if (test[aor[j]] != base[j])
7334 {
7335 mfem_error("Mesh::GetTetOrientation(...)");
7336 }
7337#endif
7338
7339 return orient;
7340}
7341
7343{
7344 int wo = 0; // count wrong orientations
7345
7346 if (Dim == 2)
7347 {
7348 if (el_to_edge == NULL) // edges were not generated
7349 {
7350 el_to_edge = new Table;
7352 GenerateFaces(); // 'Faces' in 2D refers to the edges
7353 }
7354 for (int i = 0; i < NumOfBdrElements; i++)
7355 {
7356 if (faces_info[be_to_face[i]].Elem2No < 0) // boundary face
7357 {
7358 int *bv = boundary[i]->GetVertices();
7359 int *fv = faces[be_to_face[i]]->GetVertices();
7360 if (bv[0] != fv[0])
7361 {
7362 if (fix_it)
7363 {
7364 mfem::Swap<int>(bv[0], bv[1]);
7365 }
7366 wo++;
7367 }
7368 }
7369 }
7370 }
7371
7372 if (Dim == 3)
7373 {
7374 for (int i = 0; i < NumOfBdrElements; i++)
7375 {
7376 const int fi = be_to_face[i];
7377
7378 if (faces_info[fi].Elem2No >= 0) { continue; }
7379
7380 // boundary face
7381 int *bv = boundary[i]->GetVertices();
7382 // Make sure the 'faces' are generated:
7383 MFEM_ASSERT(fi < faces.Size(), "internal error");
7384 const int *fv = faces[fi]->GetVertices();
7385 int orientation; // orientation of the bdr. elem. w.r.t. the
7386 // corresponding face element (that's the base)
7387 const Element::Type bdr_type = GetBdrElementType(i);
7388 switch (bdr_type)
7389 {
7390 case Element::TRIANGLE:
7391 {
7392 orientation = GetTriOrientation(fv, bv);
7393 break;
7394 }
7396 {
7397 orientation = GetQuadOrientation(fv, bv);
7398 break;
7399 }
7400 default:
7401 MFEM_ABORT("Invalid 2D boundary element type \""
7402 << bdr_type << "\"");
7403 orientation = 0; // suppress a warning
7404 break;
7405 }
7406
7407 if (orientation % 2 == 0) { continue; }
7408 wo++;
7409 if (!fix_it) { continue; }
7410
7411 switch (bdr_type)
7412 {
7413 case Element::TRIANGLE:
7414 {
7415 // swap vertices 0 and 1 so that we don't change the marked edge:
7416 // (0,1,2) -> (1,0,2)
7417 mfem::Swap(bv[0], bv[1]);
7418 if (bel_to_edge)
7419 {
7420 int *be = bel_to_edge->GetRow(i);
7421 mfem::Swap(be[1], be[2]);
7422 }
7423 break;
7424 }
7426 {
7427 mfem::Swap(bv[0], bv[2]);
7428 if (bel_to_edge)
7429 {
7430 int *be = bel_to_edge->GetRow(i);
7431 mfem::Swap(be[0], be[1]);
7432 mfem::Swap(be[2], be[3]);
7433 }
7434 break;
7435 }
7436 default: // unreachable
7437 break;
7438 }
7439 }
7440 }
7441 // #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
7442#ifdef MFEM_DEBUG
7443 if (wo > 0)
7444 {
7445 mfem::out << "Boundary elements with wrong orientation: " << wo << " / "
7446 << NumOfBdrElements << " (" << fixed_or_not[fix_it ? 0 : 1]
7447 << ")" << endl;
7448 }
7449#endif
7450 return wo;
7451}
7452
7454 const IntegrationPoint &ip)
7455{
7456 IntegrationPoint fip = ip;
7457 if (geom == Geometry::POINT)
7458 {
7459 return fip;
7460 }
7461 else if (geom == Geometry::SEGMENT)
7462 {
7463 MFEM_ASSERT(o >= 0 && o < 2, "Invalid orientation for Geometry::SEGMENT!");
7464 if (o == 0)
7465 {
7466 fip.x = ip.x;
7467 }
7468 else if (o == 1)
7469 {
7470 fip.x = 1.0 - ip.x;
7471 }
7472 }
7473 else if (geom == Geometry::TRIANGLE)
7474 {
7475 MFEM_ASSERT(o >= 0 && o < 6, "Invalid orientation for Geometry::TRIANGLE!");
7476 if (o == 0) // 0, 1, 2
7477 {
7478 fip.x = ip.x;
7479 fip.y = ip.y;
7480 }
7481 else if (o == 5) // 0, 2, 1
7482 {
7483 fip.x = ip.y;
7484 fip.y = ip.x;
7485 }
7486 else if (o == 2) // 1, 2, 0
7487 {
7488 fip.x = 1.0 - ip.x - ip.y;
7489 fip.y = ip.x;
7490 }
7491 else if (o == 1) // 1, 0, 2
7492 {
7493 fip.x = 1.0 - ip.x - ip.y;
7494 fip.y = ip.y;
7495 }
7496 else if (o == 4) // 2, 0, 1
7497 {
7498 fip.x = ip.y;
7499 fip.y = 1.0 - ip.x - ip.y;
7500 }
7501 else if (o == 3) // 2, 1, 0
7502 {
7503 fip.x = ip.x;
7504 fip.y = 1.0 - ip.x - ip.y;
7505 }
7506 }
7507 else if (geom == Geometry::SQUARE)
7508 {
7509 MFEM_ASSERT(o >= 0 && o < 8, "Invalid orientation for Geometry::SQUARE!");
7510 if (o == 0) // 0, 1, 2, 3
7511 {
7512 fip.x = ip.x;
7513 fip.y = ip.y;
7514 }
7515 else if (o == 1) // 0, 3, 2, 1
7516 {
7517 fip.x = ip.y;
7518 fip.y = ip.x;
7519 }
7520 else if (o == 2) // 1, 2, 3, 0
7521 {
7522 fip.x = ip.y;
7523 fip.y = 1.0 - ip.x;
7524 }
7525 else if (o == 3) // 1, 0, 3, 2
7526 {
7527 fip.x = 1.0 - ip.x;
7528 fip.y = ip.y;
7529 }
7530 else if (o == 4) // 2, 3, 0, 1
7531 {
7532 fip.x = 1.0 - ip.x;
7533 fip.y = 1.0 - ip.y;
7534 }
7535 else if (o == 5) // 2, 1, 0, 3
7536 {
7537 fip.x = 1.0 - ip.y;
7538 fip.y = 1.0 - ip.x;
7539 }
7540 else if (o == 6) // 3, 0, 1, 2
7541 {
7542 fip.x = 1.0 - ip.y;
7543 fip.y = ip.x;
7544 }
7545 else if (o == 7) // 3, 2, 1, 0
7546 {
7547 fip.x = ip.x;
7548 fip.y = 1.0 - ip.y;
7549 }
7550 }
7551 else
7552 {
7553 MFEM_ABORT("Unsupported face geometry for TransformBdrElementToFace!");
7554 }
7555 return fip;
7556}
7557
7559{
7560 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
7561 int num_geoms = 0;
7562 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
7563 {
7564 if (HasGeometry(Geometry::Type(g))) { num_geoms++; }
7565 }
7566 return num_geoms;
7567}
7568
7570{
7571 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
7572 el_geoms.SetSize(0);
7573 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
7574 {
7576 {
7577 el_geoms.Append(Geometry::Type(g));
7578 }
7579 }
7580}
7581
7583{
7584 // Return true if meshgen has more than one bit set, zero otherwise
7585 return meshgen & (meshgen - 1);
7586}
7587
7588void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
7589{
7590 if (el_to_edge)
7591 {
7592 el_to_edge->GetRow(i, edges);
7593 }
7594 else
7595 {
7596 mfem_error("Mesh::GetElementEdges(...) element to edge table "
7597 "is not generated.");
7598 }
7599
7600 const int *v = elements[i]->GetVertices();
7601 const int ne = elements[i]->GetNEdges();
7602 cor.SetSize(ne);
7603 for (int j = 0; j < ne; j++)
7604 {
7605 const int *e = elements[i]->GetEdgeVertices(j);
7606 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
7607 }
7608}
7609
7610void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
7611{
7612 if (Dim == 2)
7613 {
7614 edges.SetSize(1);
7615 cor.SetSize(1);
7616 edges[0] = be_to_face[i];
7617 const int *v = boundary[i]->GetVertices();
7618 cor[0] = (v[0] < v[1]) ? (1) : (-1);
7619 }
7620 else if (Dim == 3)
7621 {
7622 if (bel_to_edge)
7623 {
7624 bel_to_edge->GetRow(i, edges);
7625 }
7626 else
7627 {
7628 mfem_error("Mesh::GetBdrElementEdges(...)");
7629 }
7630
7631 const int *v = boundary[i]->GetVertices();
7632 const int ne = boundary[i]->GetNEdges();
7633 cor.SetSize(ne);
7634 for (int j = 0; j < ne; j++)
7635 {
7636 const int *e = boundary[i]->GetEdgeVertices(j);
7637 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
7638 }
7639 }
7640}
7641
7642void Mesh::GetFaceEdges(int i, Array<int> &edges, Array<int> &o) const
7643{
7644 if (Dim == 2)
7645 {
7646 edges.SetSize(1);
7647 edges[0] = i;
7648 o.SetSize(1);
7649 const int *v = faces[i]->GetVertices();
7650 o[0] = (v[0] < v[1]) ? (1) : (-1);
7651 }
7652
7653 if (Dim != 3)
7654 {
7655 return;
7656 }
7657
7658 GetFaceEdgeTable(); // generate face_edge Table (if not generated)
7659
7660 face_edge->GetRow(i, edges);
7661
7662 const int *v = faces[i]->GetVertices();
7663 const int ne = faces[i]->GetNEdges();
7664 o.SetSize(ne);
7665 for (int j = 0; j < ne; j++)
7666 {
7667 const int *e = faces[i]->GetEdgeVertices(j);
7668 o[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
7669 }
7670}
7671
7672void Mesh::GetEdgeVertices(int i, Array<int> &vert) const
7673{
7674 // the two vertices are sorted: vert[0] < vert[1]
7675 // this is consistent with the global edge orientation
7676 // generate edge_vertex Table (if not generated)
7677 if (!edge_vertex) { GetEdgeVertexTable(); }
7678 edge_vertex->GetRow(i, vert);
7679}
7680
7682{
7683 if (face_edge)
7684 {
7685 return face_edge;
7686 }
7687
7688 if (Dim != 3)
7689 {
7690 return NULL;
7691 }
7692
7693#ifdef MFEM_DEBUG
7694 if (faces.Size() != NumOfFaces)
7695 {
7696 mfem_error("Mesh::GetFaceEdgeTable : faces were not generated!");
7697 }
7698#endif
7699
7700 DSTable v_to_v(NumOfVertices);
7701 GetVertexToVertexTable(v_to_v);
7702
7703 face_edge = new Table;
7705
7706 return (face_edge);
7707}
7708
7710{
7711 if (edge_vertex)
7712 {
7713 return edge_vertex;
7714 }
7715
7716 DSTable v_to_v(NumOfVertices);
7717 GetVertexToVertexTable(v_to_v);
7718
7719 int nedges = v_to_v.NumberOfEntries();
7720 edge_vertex = new Table(nedges, 2);
7721 for (int i = 0; i < NumOfVertices; i++)
7722 {
7723 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
7724 {
7725 int j = it.Index();
7726 edge_vertex->Push(j, i);
7727 edge_vertex->Push(j, it.Column());
7728 }
7729 }
7731
7732 return edge_vertex;
7733}
7734
7736{
7737 Table *vert_elem = new Table;
7738
7739 vert_elem->MakeI(NumOfVertices);
7740
7741 for (int i = 0; i < NumOfElements; i++)
7742 {
7743 const int nv = elements[i]->GetNVertices();
7744 const int *v = elements[i]->GetVertices();
7745 for (int j = 0; j < nv; j++)
7746 {
7747 vert_elem->AddAColumnInRow(v[j]);
7748 }
7749 }
7750
7751 vert_elem->MakeJ();
7752
7753 for (int i = 0; i < NumOfElements; i++)
7754 {
7755 const int nv = elements[i]->GetNVertices();
7756 const int *v = elements[i]->GetVertices();
7757 for (int j = 0; j < nv; j++)
7758 {
7759 vert_elem->AddConnection(v[j], i);
7760 }
7761 }
7762
7763 vert_elem->ShiftUpI();
7764
7765 return vert_elem;
7766}
7767
7769{
7770 Table *vert_bdr_elem = new Table;
7771
7772 vert_bdr_elem->MakeI(NumOfVertices);
7773
7774 for (int i = 0; i < NumOfBdrElements; i++)
7775 {
7776 const int nv = boundary[i]->GetNVertices();
7777 const int *v = boundary[i]->GetVertices();
7778 for (int j = 0; j < nv; j++)
7779 {
7780 vert_bdr_elem->AddAColumnInRow(v[j]);
7781 }
7782 }
7783
7784 vert_bdr_elem->MakeJ();
7785
7786 for (int i = 0; i < NumOfBdrElements; i++)
7787 {
7788 const int nv = boundary[i]->GetNVertices();
7789 const int *v = boundary[i]->GetVertices();
7790 for (int j = 0; j < nv; j++)
7791 {
7792 vert_bdr_elem->AddConnection(v[j], i);
7793 }
7794 }
7795
7796 vert_bdr_elem->ShiftUpI();
7797
7798 return vert_bdr_elem;
7799}
7800
7802{
7803 Table *face_elem = new Table;
7804
7805 face_elem->MakeI(faces_info.Size());
7806
7807 for (int i = 0; i < faces_info.Size(); i++)
7808 {
7809 if (faces_info[i].Elem2No >= 0)
7810 {
7811 face_elem->AddColumnsInRow(i, 2);
7812 }
7813 else
7814 {
7815 face_elem->AddAColumnInRow(i);
7816 }
7817 }
7818
7819 face_elem->MakeJ();
7820
7821 for (int i = 0; i < faces_info.Size(); i++)
7822 {
7823 face_elem->AddConnection(i, faces_info[i].Elem1No);
7824 if (faces_info[i].Elem2No >= 0)
7825 {
7826 face_elem->AddConnection(i, faces_info[i].Elem2No);
7827 }
7828 }
7829
7830 face_elem->ShiftUpI();
7831
7832 return face_elem;
7833}
7834
7835void Mesh::GetElementFaces(int i, Array<int> &el_faces, Array<int> &ori) const
7836{
7837 MFEM_VERIFY(el_to_face != NULL, "el_to_face not generated");
7838
7839 el_to_face->GetRow(i, el_faces);
7840
7841 int n = el_faces.Size();
7842 ori.SetSize(n);
7843
7844 for (int j = 0; j < n; j++)
7845 {
7846 if (faces_info[el_faces[j]].Elem1No == i)
7847 {
7848 ori[j] = faces_info[el_faces[j]].Elem1Inf % 64;
7849 }
7850 else
7851 {
7852 MFEM_ASSERT(faces_info[el_faces[j]].Elem2No == i, "internal error");
7853 ori[j] = faces_info[el_faces[j]].Elem2Inf % 64;
7854 }
7855 }
7856}
7857
7859{
7860 if (face_to_elem == NULL)
7861 {
7863 }
7864
7865 Array<int> elem_faces;
7866 Array<int> ori;
7867 GetElementFaces(elem, elem_faces, ori);
7868
7869 Array<int> nghb;
7870 for (auto f : elem_faces)
7871 {
7872 Array<int> row;
7873 face_to_elem->GetRow(f, row);
7874 for (auto r : row)
7875 {
7876 nghb.Append(r);
7877 }
7878 }
7879
7880 nghb.Sort();
7881 nghb.Unique();
7882
7883 return nghb;
7884}
7885
7886void Mesh::GetBdrElementFace(int i, int *f, int *o) const
7887{
7889
7890 const int *fv = (Dim > 1) ? faces[*f]->GetVertices() : NULL;
7891 const int *bv = boundary[i]->GetVertices();
7892
7893 // find the orientation of the bdr. elem. w.r.t.
7894 // the corresponding face element (that's the base)
7895 switch (GetBdrElementGeometry(i))
7896 {
7897 case Geometry::POINT: *o = 0; break;
7898 case Geometry::SEGMENT: *o = (fv[0] == bv[0]) ? 0 : 1; break;
7899 case Geometry::TRIANGLE: *o = GetTriOrientation(fv, bv); break;
7900 case Geometry::SQUARE: *o = GetQuadOrientation(fv, bv); break;
7901 default: MFEM_ABORT("invalid geometry");
7902 }
7903}
7904
7905void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
7906{
7907 int fid = GetBdrElementFaceIndex(bdr_el);
7908
7909 const FaceInfo &fi = faces_info[fid];
7910 MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
7911
7912 const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
7913 const int *bv = boundary[bdr_el]->GetVertices();
7914 int ori;
7915 switch (GetBdrElementGeometry(bdr_el))
7916 {
7917 case Geometry::POINT: ori = 0; break;
7918 case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
7919 case Geometry::TRIANGLE: ori = GetTriOrientation(fv, bv); break;
7920 case Geometry::SQUARE: ori = GetQuadOrientation(fv, bv); break;
7921 default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
7922 }
7923 el = fi.Elem1No;
7924 info = fi.Elem1Inf + ori;
7925}
7926
7928 int bdr_el, int &el, int &info) const
7929{
7930 int fid = GetBdrElementFaceIndex(bdr_el);
7931
7932 const FaceInfo &fi = faces_info[fid];
7933 MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
7934
7935 const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
7936 const int *bv = boundary[bdr_el]->GetVertices();
7937 int ori;
7938 switch (GetBdrElementGeometry(bdr_el))
7939 {
7940 case Geometry::POINT: ori = 0; break;
7941 case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
7942 case Geometry::TRIANGLE: ori = GetTriOrientation(bv, fv); break;
7943 case Geometry::SQUARE: ori = GetQuadOrientation(bv, fv); break;
7944 default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
7945 }
7946 el = fi.Elem1No;
7947 info = fi.Elem1Inf + ori;
7948}
7949
7950void Mesh::SetAttribute(int i, int attr)
7951{
7952 elements[i]->SetAttribute(attr);
7953 if (elem_attrs_cache.Size() == GetNE())
7954 {
7955 // update the existing cache instead of deleting it
7957 elem_attrs_cache[i] = attr;
7958 }
7959 if (ncmesh) { ncmesh->SetAttribute(i, attr); }
7960}
7961
7963{
7964 return elements[i]->GetType();
7965}
7966
7968{
7969 return boundary[i]->GetType();
7970}
7971
7972void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
7973{
7974 int k, j, nv;
7975 const int *v;
7976
7977 v = elements[i]->GetVertices();
7978 nv = elements[i]->GetNVertices();
7979
7980 pointmat.SetSize(spaceDim, nv);
7981 for (k = 0; k < spaceDim; k++)
7982 {
7983 for (j = 0; j < nv; j++)
7984 {
7985 pointmat(k, j) = vertices[v[j]](k);
7986 }
7987 }
7988}
7989
7990void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
7991{
7992 int k, j, nv;
7993 const int *v;
7994
7995 v = boundary[i]->GetVertices();
7996 nv = boundary[i]->GetNVertices();
7997
7998 pointmat.SetSize(spaceDim, nv);
7999 for (k = 0; k < spaceDim; k++)
8000 for (j = 0; j < nv; j++)
8001 {
8002 pointmat(k, j) = vertices[v[j]](k);
8003 }
8004}
8005
8006real_t Mesh::GetLength(int i, int j) const
8007{
8008 const real_t *vi = vertices[i]();
8009 const real_t *vj = vertices[j]();
8010 real_t length = 0.;
8011
8012 for (int k = 0; k < spaceDim; k++)
8013 {
8014 length += (vi[k]-vj[k])*(vi[k]-vj[k]);
8015 }
8016
8017 return sqrt(length);
8018}
8019
8020// static method
8022 const DSTable &v_to_v, Table &el_to_edge)
8023{
8024 el_to_edge.MakeI(elem_array.Size());
8025 for (int i = 0; i < elem_array.Size(); i++)
8026 {
8027 el_to_edge.AddColumnsInRow(i, elem_array[i]->GetNEdges());
8028 }
8029 el_to_edge.MakeJ();
8030 for (int i = 0; i < elem_array.Size(); i++)
8031 {
8032 const int *v = elem_array[i]->GetVertices();
8033 const int ne = elem_array[i]->GetNEdges();
8034 for (int j = 0; j < ne; j++)
8035 {
8036 const int *e = elem_array[i]->GetEdgeVertices(j);
8037 el_to_edge.AddConnection(i, v_to_v(v[e[0]], v[e[1]]));
8038 }
8039 }
8041}
8042
8044{
8045 if (edge_vertex)
8046 {
8047 for (int i = 0; i < edge_vertex->Size(); i++)
8048 {
8049 const int *v = edge_vertex->GetRow(i);
8050 v_to_v.Push(v[0], v[1]);
8051 }
8052 }
8053 else
8054 {
8055 for (int i = 0; i < NumOfElements; i++)
8056 {
8057 const int *v = elements[i]->GetVertices();
8058 const int ne = elements[i]->GetNEdges();
8059 for (int j = 0; j < ne; j++)
8060 {
8061 const int *e = elements[i]->GetEdgeVertices(j);
8062 v_to_v.Push(v[e[0]], v[e[1]]);
8063 }
8064 }
8065 }
8066}
8067
8069{
8070 int i, NumberOfEdges;
8071
8072 DSTable v_to_v(NumOfVertices);
8073 GetVertexToVertexTable(v_to_v);
8074
8075 NumberOfEdges = v_to_v.NumberOfEntries();
8076
8077 // Fill the element to edge table
8078 GetElementArrayEdgeTable(elements, v_to_v, e_to_f);
8079
8080 if (Dim == 2)
8081 {
8082 // Initialize the indices for the boundary elements.
8084 for (i = 0; i < NumOfBdrElements; i++)
8085 {
8086 const int *v = boundary[i]->GetVertices();
8087 be_to_face[i] = v_to_v(v[0], v[1]);
8088 }
8089 }
8090 else if (Dim == 3)
8091 {
8092 if (bel_to_edge == NULL)
8093 {
8094 bel_to_edge = new Table;
8095 }
8097 }
8098 else
8099 {
8100 mfem_error("1D GetElementToEdgeTable is not yet implemented.");
8101 }
8102
8103 // Return the number of edges
8104 return NumberOfEdges;
8105}
8106
8108{
8109 if (el_to_el)
8110 {
8111 return *el_to_el;
8112 }
8113
8114 // Note that, for ParNCMeshes, faces_info will contain also the ghost faces
8115 MFEM_ASSERT(faces_info.Size() >= GetNumFaces(), "faces were not generated!");
8116
8117 Array<Connection> conn;
8118 conn.Reserve(2*faces_info.Size());
8119
8120 for (int i = 0; i < faces_info.Size(); i++)
8121 {
8122 const FaceInfo &fi = faces_info[i];
8123 if (fi.Elem2No >= 0)
8124 {
8125 conn.Append(Connection(fi.Elem1No, fi.Elem2No));
8126 conn.Append(Connection(fi.Elem2No, fi.Elem1No));
8127 }
8128 else if (fi.Elem2Inf >= 0)
8129 {
8130 int nbr_elem_idx = NumOfElements - 1 - fi.Elem2No;
8131 conn.Append(Connection(fi.Elem1No, nbr_elem_idx));
8132 conn.Append(Connection(nbr_elem_idx, fi.Elem1No));
8133 }
8134 }
8135
8136 conn.Sort();
8137 conn.Unique();
8138 el_to_el = new Table(NumOfElements, conn);
8139
8140 return *el_to_el;
8141}
8142
8144{
8145 if (el_to_face == NULL)
8146 {
8147 mfem_error("Mesh::ElementToFaceTable()");
8148 }
8149 return *el_to_face;
8150}
8151
8153{
8154 if (el_to_edge == NULL)
8155 {
8156 mfem_error("Mesh::ElementToEdgeTable()");
8157 }
8158 return *el_to_edge;
8159}
8160
8161void Mesh::AddPointFaceElement(int lf, int gf, int el)
8162{
8163 if (faces[gf] == NULL) // this will be elem1
8164 {
8165 faces[gf] = new Point(&gf);
8166 faces_info[gf].Elem1No = el;
8167 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8168 faces_info[gf].Elem2No = -1; // in case there's no other side
8169 faces_info[gf].Elem2Inf = -1; // face is not shared
8170 }
8171 else // this will be elem2
8172 {
8173 /* WARNING: Without the following check the mesh faces_info data structure
8174 may contain unreliable data. Normally, the order in which elements are
8175 processed could swap which elements appear as Elem1No and Elem2No. In
8176 branched meshes, where more than two elements can meet at a given node,
8177 the indices stored in Elem1No and Elem2No will be the first and last,
8178 respectively, elements found which touch a given node. This can lead to
8179 inconsistencies in any algorithms which rely on this data structure. To
8180 properly support branched meshes this data structure should be extended
8181 to support multiple elements per face. */
8182 /*
8183 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8184 "Interior point found connecting 1D elements "
8185 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8186 << " and " << el << ".");
8187 */
8188 faces_info[gf].Elem2No = el;
8189 faces_info[gf].Elem2Inf = 64 * lf + 1;
8190 }
8191}
8192
8193void Mesh::AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
8194{
8195 if (faces[gf] == NULL) // this will be elem1
8196 {
8197 faces[gf] = new Segment(v0, v1);
8198 faces_info[gf].Elem1No = el;
8199 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8200 faces_info[gf].Elem2No = -1; // in case there's no other side
8201 faces_info[gf].Elem2Inf = -1; // face is not shared
8202 }
8203 else // this will be elem2
8204 {
8205 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8206 "Interior edge found between 2D elements "
8207 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8208 << " and " << el << ".");
8209 int *v = faces[gf]->GetVertices();
8210 faces_info[gf].Elem2No = el;
8211 if (v[1] == v0 && v[0] == v1)
8212 {
8213 faces_info[gf].Elem2Inf = 64 * lf + 1;
8214 }
8215 else if (v[0] == v0 && v[1] == v1)
8216 {
8217 // Temporarily allow even edge orientations: see the remark in
8218 // AddTriangleFaceElement().
8219 // Also, in a non-orientable surface mesh, the orientation will be even
8220 // for edges that connect elements with opposite orientations.
8221 faces_info[gf].Elem2Inf = 64 * lf;
8222 }
8223 else
8224 {
8225 MFEM_ABORT("internal error");
8226 }
8227 }
8228}
8229
8230void Mesh::AddTriangleFaceElement(int lf, int gf, int el,
8231 int v0, int v1, int v2)
8232{
8233 if (faces[gf] == NULL) // this will be elem1
8234 {
8235 faces[gf] = new Triangle(v0, v1, v2);
8236 faces_info[gf].Elem1No = el;
8237 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8238 faces_info[gf].Elem2No = -1; // in case there's no other side
8239 faces_info[gf].Elem2Inf = -1; // face is not shared
8240 }
8241 else // this will be elem2
8242 {
8243 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8244 "Interior triangular face found connecting elements "
8245 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8246 << " and " << el << ".");
8247 int orientation, vv[3] = { v0, v1, v2 };
8248 orientation = GetTriOrientation(faces[gf]->GetVertices(), vv);
8249 // In a valid mesh, we should have (orientation % 2 != 0), however, if
8250 // one of the adjacent elements has wrong orientation, both face
8251 // orientations can be even, until the element orientations are fixed.
8252 // MFEM_ASSERT(orientation % 2 != 0, "");
8253 faces_info[gf].Elem2No = el;
8254 faces_info[gf].Elem2Inf = 64 * lf + orientation;
8255 }
8256}
8257
8258void Mesh::AddQuadFaceElement(int lf, int gf, int el,
8259 int v0, int v1, int v2, int v3)
8260{
8261 if (faces_info[gf].Elem1No < 0) // this will be elem1
8262 {
8263 faces[gf] = new Quadrilateral(v0, v1, v2, v3);
8264 faces_info[gf].Elem1No = el;
8265 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
8266 faces_info[gf].Elem2No = -1; // in case there's no other side
8267 faces_info[gf].Elem2Inf = -1; // face is not shared
8268 }
8269 else // this will be elem2
8270 {
8271 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
8272 "Interior quadrilateral face found connecting elements "
8273 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
8274 << " and " << el << ".");
8275 int vv[4] = { v0, v1, v2, v3 };
8276 int oo = GetQuadOrientation(faces[gf]->GetVertices(), vv);
8277 // Temporarily allow even face orientations: see the remark in
8278 // AddTriangleFaceElement().
8279 // MFEM_ASSERT(oo % 2 != 0, "");
8280 faces_info[gf].Elem2No = el;
8281 faces_info[gf].Elem2Inf = 64 * lf + oo;
8282 }
8283}
8284
8286{
8287 int nfaces = GetNumFaces();
8288 for (auto &f : faces)
8289 {
8290 FreeElement(f);
8291 }
8292
8293 // delete caches
8294 face_indices[0].SetSize(0);
8295 face_indices[1].SetSize(0);
8296 inv_face_indices[0].clear();
8297 inv_face_indices[1].clear();
8298
8299 // (re)generate the interior faces and the info for them
8300 faces.SetSize(nfaces);
8301 faces_info.SetSize(nfaces);
8302 for (int i = 0; i < nfaces; ++i)
8303 {
8304 faces[i] = NULL;
8305 faces_info[i].Elem1No = -1;
8306 faces_info[i].NCFace = -1;
8307 }
8308
8309 Array<int> v;
8310 for (int i = 0; i < NumOfElements; ++i)
8311 {
8312 elements[i]->GetVertices(v);
8313 if (Dim == 1)
8314 {
8315 AddPointFaceElement(0, v[0], i);
8316 AddPointFaceElement(1, v[1], i);
8317 }
8318 else if (Dim == 2)
8319 {
8320 const int * const ef = el_to_edge->GetRow(i);
8321 const int ne = elements[i]->GetNEdges();
8322 for (int j = 0; j < ne; j++)
8323 {
8324 const int *e = elements[i]->GetEdgeVertices(j);
8325 AddSegmentFaceElement(j, ef[j], i, v[e[0]], v[e[1]]);
8326 }
8327 }
8328 else
8329 {
8330 const int * const ef = el_to_face->GetRow(i);
8331 switch (GetElementType(i))
8332 {
8334 {
8335 for (int j = 0; j < 4; j++)
8336 {
8337 const int *fv = tet_t::FaceVert[j];
8338 AddTriangleFaceElement(j, ef[j], i,
8339 v[fv[0]], v[fv[1]], v[fv[2]]);
8340 }
8341 break;
8342 }
8343 case Element::WEDGE:
8344 {
8345 for (int j = 0; j < 2; j++)
8346 {
8347 const int *fv = pri_t::FaceVert[j];
8348 AddTriangleFaceElement(j, ef[j], i,
8349 v[fv[0]], v[fv[1]], v[fv[2]]);
8350 }
8351 for (int j = 2; j < 5; j++)
8352 {
8353 const int *fv = pri_t::FaceVert[j];
8354 AddQuadFaceElement(j, ef[j], i,
8355 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8356 }
8357 break;
8358 }
8359 case Element::PYRAMID:
8360 {
8361 for (int j = 0; j < 1; j++)
8362 {
8363 const int *fv = pyr_t::FaceVert[j];
8364 AddQuadFaceElement(j, ef[j], i,
8365 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8366 }
8367 for (int j = 1; j < 5; j++)
8368 {
8369 const int *fv = pyr_t::FaceVert[j];
8370 AddTriangleFaceElement(j, ef[j], i,
8371 v[fv[0]], v[fv[1]], v[fv[2]]);
8372 }
8373 break;
8374 }
8376 {
8377 for (int j = 0; j < 6; j++)
8378 {
8379 const int *fv = hex_t::FaceVert[j];
8380 AddQuadFaceElement(j, ef[j], i,
8381 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8382 }
8383 break;
8384 }
8385 default:
8386 MFEM_ABORT("Unexpected type of Element.");
8387 }
8388 }
8389 }
8390}
8391
8393{
8394 MFEM_VERIFY(ncmesh, "missing NCMesh.");
8395
8396 for (auto &x : faces_info)
8397 {
8398 x.NCFace = -1;
8399 }
8400
8401 const NCMesh::NCList &list =
8402 (Dim == 2) ? ncmesh->GetEdgeList() : ncmesh->GetFaceList();
8403
8404 nc_faces_info.SetSize(0);
8405 nc_faces_info.Reserve(list.masters.Size() + list.slaves.Size());
8406
8407 int nfaces = GetNumFaces();
8408
8409 // add records for master faces
8410 for (const NCMesh::Master &master : list.masters)
8411 {
8412 if (master.index >= nfaces) { continue; }
8413
8414 FaceInfo &master_fi = faces_info[master.index];
8415 master_fi.NCFace = nc_faces_info.Size();
8416 nc_faces_info.Append(NCFaceInfo(false, master.local, NULL));
8417 // NOTE: one of the unused members stores local face no. to be used below
8418 MFEM_ASSERT(master_fi.Elem2No == -1, "internal error");
8419 MFEM_ASSERT(master_fi.Elem2Inf == -1, "internal error");
8420 }
8421
8422 // add records for slave faces
8423 for (const NCMesh::Slave &slave : list.slaves)
8424 {
8425 if (slave.index < 0 || // degenerate slave face
8426 slave.index >= nfaces || // ghost slave
8427 slave.master >= nfaces) // has ghost master
8428 {
8429 continue;
8430 }
8431
8432 FaceInfo &slave_fi = faces_info[slave.index];
8433 FaceInfo &master_fi = faces_info[slave.master];
8434 NCFaceInfo &master_nc = nc_faces_info[master_fi.NCFace];
8435
8436 slave_fi.NCFace = nc_faces_info.Size();
8437 slave_fi.Elem2No = master_fi.Elem1No;
8438 slave_fi.Elem2Inf = 64 * master_nc.MasterFace; // get lf no. stored above
8439 // NOTE: In 3D, the orientation part of Elem2Inf is encoded in the point
8440 // matrix. In 2D, the point matrix has the orientation of the parent
8441 // edge, so its columns need to be flipped when applying it, see
8442 // ApplyLocalSlaveTransformation.
8443
8444 nc_faces_info.Append(
8445 NCFaceInfo(true, slave.master,
8446 list.point_matrices[slave.geom][slave.matrix]));
8447 }
8448}
8449
8451{
8452 STable3D *faces_tbl = new STable3D(NumOfVertices);
8453 for (int i = 0; i < NumOfElements; i++)
8454 {
8455 const int *v = elements[i]->GetVertices();
8456 switch (GetElementType(i))
8457 {
8459 {
8460 for (int j = 0; j < 4; j++)
8461 {
8462 const int *fv = tet_t::FaceVert[j];
8463 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8464 }
8465 break;
8466 }
8467 case Element::PYRAMID:
8468 {
8469 for (int j = 0; j < 1; j++)
8470 {
8471 const int *fv = pyr_t::FaceVert[j];
8472 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8473 }
8474 for (int j = 1; j < 5; j++)
8475 {
8476 const int *fv = pyr_t::FaceVert[j];
8477 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8478 }
8479 break;
8480 }
8481 case Element::WEDGE:
8482 {
8483 for (int j = 0; j < 2; j++)
8484 {
8485 const int *fv = pri_t::FaceVert[j];
8486 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
8487 }
8488 for (int j = 2; j < 5; j++)
8489 {
8490 const int *fv = pri_t::FaceVert[j];
8491 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8492 }
8493 break;
8494 }
8496 {
8497 // find the face by the vertices with the smallest 3 numbers
8498 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
8499 for (int j = 0; j < 6; j++)
8500 {
8501 const int *fv = hex_t::FaceVert[j];
8502 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
8503 }
8504 break;
8505 }
8506 default:
8507 MFEM_ABORT("Unexpected type of Element: " << GetElementType(i));
8508 }
8509 }
8510 return faces_tbl;
8511}
8512
8514{
8515 Array<int> v;
8516 STable3D *faces_tbl;
8517
8518 if (el_to_face != NULL)
8519 {
8520 delete el_to_face;
8521 }
8522 el_to_face = new Table(NumOfElements, 6); // must be 6 for hexahedra
8523 faces_tbl = new STable3D(NumOfVertices);
8524 for (int i = 0; i < NumOfElements; i++)
8525 {
8526 elements[i]->GetVertices(v);
8527 switch (GetElementType(i))
8528 {
8530 {
8531 for (int j = 0; j < 4; j++)
8532 {
8533 const int *fv = tet_t::FaceVert[j];
8535 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
8536 }
8537 break;
8538 }
8539 case Element::WEDGE:
8540 {
8541 for (int j = 0; j < 2; j++)
8542 {
8543 const int *fv = pri_t::FaceVert[j];
8545 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
8546 }
8547 for (int j = 2; j < 5; j++)
8548 {
8549 const int *fv = pri_t::FaceVert[j];
8551 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
8552 }
8553 break;
8554 }
8555 case Element::PYRAMID:
8556 {
8557 for (int j = 0; j < 1; j++)
8558 {
8559 const int *fv = pyr_t::FaceVert[j];
8561 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
8562 }
8563 for (int j = 1; j < 5; j++)
8564 {
8565 const int *fv = pyr_t::FaceVert[j];
8567 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
8568 }
8569 break;
8570 }
8572 {
8573 // find the face by the vertices with the smallest 3 numbers
8574 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
8575 for (int j = 0; j < 6; j++)
8576 {
8577 const int *fv = hex_t::FaceVert[j];
8579 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
8580 }
8581 break;
8582 }
8583 default:
8584 MFEM_ABORT("Unexpected type of Element.");
8585 }
8586 }
8588 NumOfFaces = faces_tbl->NumberOfElements();
8590
8591 for (int i = 0; i < NumOfBdrElements; i++)
8592 {
8593 boundary[i]->GetVertices(v);
8594 switch (GetBdrElementType(i))
8595 {
8596 case Element::TRIANGLE:
8597 {
8598 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2]);
8599 break;
8600 }
8602 {
8603 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
8604 break;
8605 }
8606 default:
8607 MFEM_ABORT("Unexpected type of boundary Element.");
8608 }
8609 }
8610
8611 if (ret_ftbl)
8612 {
8613 return faces_tbl;
8614 }
8615 delete faces_tbl;
8616 return NULL;
8617}
8618
8619// shift cyclically 3 integers so that the smallest is first
8620static inline
8621void Rotate3(int &a, int &b, int &c)
8622{
8623 if (a < b)
8624 {
8625 if (a > c)
8626 {
8627 ShiftRight(a, b, c);
8628 }
8629 }
8630 else
8631 {
8632 if (b < c)
8633 {
8634 ShiftRight(c, b, a);
8635 }
8636 else
8637 {
8638 ShiftRight(a, b, c);
8639 }
8640 }
8641}
8642
8644{
8645 if (Dim != 3 || !(meshgen & 1))
8646 {
8647 return;
8648 }
8649
8650 ResetLazyData();
8651
8652 DSTable *old_v_to_v = NULL;
8653 Table *old_elem_vert = NULL;
8654
8655 if (Nodes)
8656 {
8657 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
8658 }
8659
8660 for (int i = 0; i < NumOfElements; i++)
8661 {
8663 {
8664 int *v = elements[i]->GetVertices();
8665
8666 Rotate3(v[0], v[1], v[2]);
8667 if (v[0] < v[3])
8668 {
8669 Rotate3(v[1], v[2], v[3]);
8670 }
8671 else
8672 {
8673 ShiftRight(v[0], v[1], v[3]);
8674 }
8675 }
8676 }
8677
8678 for (int i = 0; i < NumOfBdrElements; i++)
8679 {
8681 {
8682 int *v = boundary[i]->GetVertices();
8683
8684 Rotate3(v[0], v[1], v[2]);
8685 }
8686 }
8687
8688 if (!Nodes)
8689 {
8691 GenerateFaces();
8692 if (el_to_edge)
8693 {
8695 }
8696 }
8697 else
8698 {
8699 DoNodeReorder(old_v_to_v, old_elem_vert);
8700 delete old_elem_vert;
8701 delete old_v_to_v;
8702 }
8703}
8704
8706{
8707 int *partitioning;
8708 real_t pmin[3] = { infinity(), infinity(), infinity() };
8709 real_t pmax[3] = { -infinity(), -infinity(), -infinity() };
8710 // find a bounding box using the vertices
8711 for (int vi = 0; vi < NumOfVertices; vi++)
8712 {
8713 const real_t *p = vertices[vi]();
8714 for (int i = 0; i < spaceDim; i++)
8715 {
8716 if (p[i] < pmin[i]) { pmin[i] = p[i]; }
8717 if (p[i] > pmax[i]) { pmax[i] = p[i]; }
8718 }
8719 }
8720
8721 partitioning = new int[NumOfElements];
8722
8723 // determine the partitioning using the centers of the elements
8724 real_t ppt[3];
8725 Vector pt(ppt, spaceDim);
8726 for (int el = 0; el < NumOfElements; el++)
8727 {
8728 GetElementTransformation(el)->Transform(
8730 int part = 0;
8731 for (int i = spaceDim-1; i >= 0; i--)
8732 {
8733 int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i])));
8734 if (idx < 0) { idx = 0; }
8735 if (idx >= nxyz[i]) { idx = nxyz[i]-1; }
8736 part = part * nxyz[i] + idx;
8737 }
8738 partitioning[el] = part;
8739 }
8740
8741 return partitioning;
8742}
8743
8744void FindPartitioningComponents(Table &elem_elem,
8745 const Array<int> &partitioning,
8746 Array<int> &component,
8747 Array<int> &num_comp);
8748
8749int *Mesh::GeneratePartitioning(int nparts, int part_method)
8750{
8751#ifdef MFEM_USE_METIS
8752
8753 int print_messages = 1;
8754 // If running in parallel, print messages only from rank 0.
8755#ifdef MFEM_USE_MPI
8756 int init_flag, fin_flag;
8757 MPI_Initialized(&init_flag);
8758 MPI_Finalized(&fin_flag);
8759 if (init_flag && !fin_flag)
8760 {
8761 int rank;
8762 MPI_Comm_rank(GetGlobalMPI_Comm(), &rank);
8763 if (rank != 0) { print_messages = 0; }
8764 }
8765#endif
8766
8767 int i, *partitioning;
8768
8770
8771 partitioning = new int[NumOfElements];
8772
8773 if (nparts == 1)
8774 {
8775 for (i = 0; i < NumOfElements; i++)
8776 {
8777 partitioning[i] = 0;
8778 }
8779 }
8780 else if (NumOfElements <= nparts)
8781 {
8782 for (i = 0; i < NumOfElements; i++)
8783 {
8784 partitioning[i] = i;
8785 }
8786 }
8787 else
8788 {
8789 idx_t *I, *J, n;
8790#ifndef MFEM_USE_METIS_5
8791 idx_t wgtflag = 0;
8792 idx_t numflag = 0;
8793 idx_t options[5];
8794#else
8795 idx_t ncon = 1;
8796 idx_t errflag;
8797 idx_t options[40];
8798#endif
8799 idx_t edgecut;
8800
8801 // In case METIS have been compiled with 64bit indices
8802 bool freedata = false;
8803 idx_t mparts = (idx_t) nparts;
8804 idx_t *mpartitioning;
8805
8806 n = NumOfElements;
8807 if (sizeof(idx_t) == sizeof(int))
8808 {
8809 I = (idx_t*) el_to_el->GetI();
8810 J = (idx_t*) el_to_el->GetJ();
8811 mpartitioning = (idx_t*) partitioning;
8812 }
8813 else
8814 {
8815 int *iI = el_to_el->GetI();
8816 int *iJ = el_to_el->GetJ();
8817 int m = iI[n];
8818 I = new idx_t[n+1];
8819 J = new idx_t[m];
8820 for (int k = 0; k < n+1; k++) { I[k] = iI[k]; }
8821 for (int k = 0; k < m; k++) { J[k] = iJ[k]; }
8822 mpartitioning = new idx_t[n];
8823 freedata = true;
8824 }
8825#ifndef MFEM_USE_METIS_5
8826 options[0] = 0;
8827#else
8828 METIS_SetDefaultOptions(options);
8829 options[METIS_OPTION_CONTIG] = 1; // set METIS_OPTION_CONTIG
8830 // If the mesh is disconnected, disable METIS_OPTION_CONTIG.
8831 {
8832 Array<int> part(partitioning, NumOfElements);
8833 part = 0; // single part for the whole mesh
8834 Array<int> component; // size will be set to num. elem.
8835 Array<int> num_comp; // size will be set to num. parts (1)
8836 FindPartitioningComponents(*el_to_el, part, component, num_comp);
8837 if (num_comp[0] > 1) { options[METIS_OPTION_CONTIG] = 0; }
8838 }
8839#endif
8840
8841 // Sort the neighbor lists
8842 if (part_method >= 0 && part_method <= 2)
8843 {
8844 for (i = 0; i < n; i++)
8845 {
8846 // Sort in increasing order.
8847 // std::sort(J+I[i], J+I[i+1]);
8848
8849 // Sort in decreasing order, as in previous versions of MFEM.
8850 std::sort(J+I[i], J+I[i+1], std::greater<idx_t>());
8851 }
8852 }
8853
8854 // This function should be used to partition a graph into a small
8855 // number of partitions (less than 8).
8856 if (part_method == 0 || part_method == 3)
8857 {
8858#ifndef MFEM_USE_METIS_5
8860 I,
8861 J,
8862 NULL,
8863 NULL,
8864 &wgtflag,
8865 &numflag,
8866 &mparts,
8867 options,
8868 &edgecut,
8869 mpartitioning);
8870#else
8871 errflag = METIS_PartGraphRecursive(&n,
8872 &ncon,
8873 I,
8874 J,
8875 NULL,
8876 NULL,
8877 NULL,
8878 &mparts,
8879 NULL,
8880 NULL,
8881 options,
8882 &edgecut,
8883 mpartitioning);
8884 if (errflag != 1)
8885 {
8886 mfem_error("Mesh::GeneratePartitioning: "
8887 " error in METIS_PartGraphRecursive!");
8888 }
8889#endif
8890 }
8891
8892 // This function should be used to partition a graph into a large
8893 // number of partitions (greater than 8).
8894 if (part_method == 1 || part_method == 4)
8895 {
8896#ifndef MFEM_USE_METIS_5
8898 I,
8899 J,
8900 NULL,
8901 NULL,
8902 &wgtflag,
8903 &numflag,
8904 &mparts,
8905 options,
8906 &edgecut,
8907 mpartitioning);
8908#else
8909 errflag = METIS_PartGraphKway(&n,
8910 &ncon,
8911 I,
8912 J,
8913 NULL,
8914 NULL,
8915 NULL,
8916 &mparts,
8917 NULL,
8918 NULL,
8919 options,
8920 &edgecut,
8921 mpartitioning);
8922 if (errflag != 1)
8923 {
8924 mfem_error("Mesh::GeneratePartitioning: "
8925 " error in METIS_PartGraphKway!");
8926 }
8927#endif
8928 }
8929
8930 // The objective of this partitioning is to minimize the total
8931 // communication volume
8932 if (part_method == 2 || part_method == 5)
8933 {
8934#ifndef MFEM_USE_METIS_5
8936 I,
8937 J,
8938 NULL,
8939 NULL,
8940 &wgtflag,
8941 &numflag,
8942 &mparts,
8943 options,
8944 &edgecut,
8945 mpartitioning);
8946#else
8947 options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL;
8948 errflag = METIS_PartGraphKway(&n,
8949 &ncon,
8950 I,
8951 J,
8952 NULL,
8953 NULL,
8954 NULL,
8955 &mparts,
8956 NULL,
8957 NULL,
8958 options,
8959 &edgecut,
8960 mpartitioning);
8961 if (errflag != 1)
8962 {
8963 mfem_error("Mesh::GeneratePartitioning: "
8964 " error in METIS_PartGraphKway!");
8965 }
8966#endif
8967 }
8968
8969#ifdef MFEM_DEBUG
8970 if (print_messages)
8971 {
8972 mfem::out << "Mesh::GeneratePartitioning(...): edgecut = "
8973 << edgecut << endl;
8974 }
8975#endif
8976 nparts = (int) mparts;
8977 if (mpartitioning != (idx_t*)partitioning)
8978 {
8979 for (int k = 0; k<NumOfElements; k++)
8980 {
8981 partitioning[k] = mpartitioning[k];
8982 }
8983 }
8984 if (freedata)
8985 {
8986 delete[] I;
8987 delete[] J;
8988 delete[] mpartitioning;
8989 }
8990 }
8991
8992 delete el_to_el;
8993 el_to_el = NULL;
8994
8995 // Check for empty partitionings (a "feature" in METIS)
8996 if (nparts > 1 && NumOfElements > nparts)
8997 {
8998 Array< Pair<int,int> > psize(nparts);
8999 int empty_parts;
9000
9001 // Count how many elements are in each partition, and store the result in
9002 // psize, where psize[i].one is the number of elements, and psize[i].two
9003 // is partition index. Keep track of the number of empty parts.
9004 auto count_partition_elements = [&]()
9005 {
9006 for (i = 0; i < nparts; i++)
9007 {
9008 psize[i].one = 0;
9009 psize[i].two = i;
9010 }
9011
9012 for (i = 0; i < NumOfElements; i++)
9013 {
9014 psize[partitioning[i]].one++;
9015 }
9016
9017 empty_parts = 0;
9018 for (i = 0; i < nparts; i++)
9019 {
9020 if (psize[i].one == 0) { empty_parts++; }
9021 }
9022 };
9023
9024 count_partition_elements();
9025
9026 // This code just split the largest partitionings in two.
9027 // Do we need to replace it with something better?
9028 while (empty_parts)
9029 {
9030 if (print_messages)
9031 {
9032 mfem::err << "Mesh::GeneratePartitioning(...): METIS returned "
9033 << empty_parts << " empty parts!"
9034 << " Applying a simple fix ..." << endl;
9035 }
9036
9037 SortPairs<int,int>(psize, nparts);
9038
9039 for (i = nparts-1; i > nparts-1-empty_parts; i--)
9040 {
9041 psize[i].one /= 2;
9042 }
9043
9044 for (int j = 0; j < NumOfElements; j++)
9045 {
9046 for (i = nparts-1; i > nparts-1-empty_parts; i--)
9047 {
9048 if (psize[i].one == 0 || partitioning[j] != psize[i].two)
9049 {
9050 continue;
9051 }
9052 else
9053 {
9054 partitioning[j] = psize[nparts-1-i].two;
9055 psize[i].one--;
9056 }
9057 }
9058 }
9059
9060 // Check for empty partitionings again
9061 count_partition_elements();
9062 }
9063 }
9064
9065 return partitioning;
9066
9067#else
9068
9069 mfem_error("Mesh::GeneratePartitioning(...): "
9070 "MFEM was compiled without Metis.");
9071
9072 return NULL;
9073
9074#endif
9075}
9076
9077/* required: 0 <= partitioning[i] < num_part */
9079 const Array<int> &partitioning,
9080 Array<int> &component,
9081 Array<int> &num_comp)
9082{
9083 int i, j, k;
9084 int num_elem, *i_elem_elem, *j_elem_elem;
9085
9086 num_elem = elem_elem.Size();
9087 i_elem_elem = elem_elem.GetI();
9088 j_elem_elem = elem_elem.GetJ();
9089
9090 component.SetSize(num_elem);
9091
9092 Array<int> elem_stack(num_elem);
9093 int stack_p, stack_top_p, elem;
9094 int num_part;
9095
9096 num_part = -1;
9097 for (i = 0; i < num_elem; i++)
9098 {
9099 if (partitioning[i] > num_part)
9100 {
9101 num_part = partitioning[i];
9102 }
9103 component[i] = -1;
9104 }
9105 num_part++;
9106
9107 num_comp.SetSize(num_part);
9108 for (i = 0; i < num_part; i++)
9109 {
9110 num_comp[i] = 0;
9111 }
9112
9113 stack_p = 0;
9114 stack_top_p = 0; // points to the first unused element in the stack
9115 for (elem = 0; elem < num_elem; elem++)
9116 {
9117 if (component[elem] >= 0)
9118 {
9119 continue;
9120 }
9121
9122 component[elem] = num_comp[partitioning[elem]]++;
9123
9124 elem_stack[stack_top_p++] = elem;
9125
9126 for ( ; stack_p < stack_top_p; stack_p++)
9127 {
9128 i = elem_stack[stack_p];
9129 for (j = i_elem_elem[i]; j < i_elem_elem[i+1]; j++)
9130 {
9131 k = j_elem_elem[j];
9132 if (partitioning[k] == partitioning[i])
9133 {
9134 if (component[k] < 0)
9135 {
9136 component[k] = component[i];
9137 elem_stack[stack_top_p++] = k;
9138 }
9139 else if (component[k] != component[i])
9140 {
9141 mfem_error("FindPartitioningComponents");
9142 }
9143 }
9144 }
9145 }
9146 }
9147}
9148
9149void Mesh::CheckPartitioning(int *partitioning_)
9150{
9151 int i, n_empty, n_mcomp;
9152 Array<int> component, num_comp;
9153 const Array<int> partitioning(partitioning_, GetNE());
9154
9156
9157 FindPartitioningComponents(*el_to_el, partitioning, component, num_comp);
9158
9159 n_empty = n_mcomp = 0;
9160 for (i = 0; i < num_comp.Size(); i++)
9161 if (num_comp[i] == 0)
9162 {
9163 n_empty++;
9164 }
9165 else if (num_comp[i] > 1)
9166 {
9167 n_mcomp++;
9168 }
9169
9170 if (n_empty > 0)
9171 {
9172 mfem::out << "Mesh::CheckPartitioning(...) :\n"
9173 << "The following subdomains are empty :\n";
9174 for (i = 0; i < num_comp.Size(); i++)
9175 if (num_comp[i] == 0)
9176 {
9177 mfem::out << ' ' << i;
9178 }
9179 mfem::out << endl;
9180 }
9181 if (n_mcomp > 0)
9182 {
9183 mfem::out << "Mesh::CheckPartitioning(...) :\n"
9184 << "The following subdomains are NOT connected :\n";
9185 for (i = 0; i < num_comp.Size(); i++)
9186 if (num_comp[i] > 1)
9187 {
9188 mfem::out << ' ' << i;
9189 }
9190 mfem::out << endl;
9191 }
9192 if (n_empty == 0 && n_mcomp == 0)
9193 mfem::out << "Mesh::CheckPartitioning(...) : "
9194 "All subdomains are connected." << endl;
9195
9196 if (el_to_el)
9197 {
9198 delete el_to_el;
9199 }
9200 el_to_el = NULL;
9201}
9202
9203// compute the coefficients of the polynomial in t:
9204// c(0)+c(1)*t+...+c(d)*t^d = det(A+t*B)
9205// where A, B are (d x d), d=2,3
9206void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
9207{
9208 const real_t *a = A.Data();
9209 const real_t *b = B.Data();
9210
9211 c.SetSize(A.Width()+1);
9212 switch (A.Width())
9213 {
9214 case 2:
9215 {
9216 // det(A+t*B) = |a0 a2| / |a0 b2| + |b0 a2| \ |b0 b2|
9217 // |a1 a3| + \ |a1 b3| |b1 a3| / * t + |b1 b3| * t^2
9218 c(0) = a[0]*a[3]-a[1]*a[2];
9219 c(1) = a[0]*b[3]-a[1]*b[2]+b[0]*a[3]-b[1]*a[2];
9220 c(2) = b[0]*b[3]-b[1]*b[2];
9221 }
9222 break;
9223
9224 case 3:
9225 {
9226 /* |a0 a3 a6|
9227 * det(A+t*B) = |a1 a4 a7| +
9228 * |a2 a5 a8|
9229
9230 * / |b0 a3 a6| |a0 b3 a6| |a0 a3 b6| \
9231 * + | |b1 a4 a7| + |a1 b4 a7| + |a1 a4 b7| | * t +
9232 * \ |b2 a5 a8| |a2 b5 a8| |a2 a5 b8| /
9233
9234 * / |a0 b3 b6| |b0 a3 b6| |b0 b3 a6| \
9235 * + | |a1 b4 b7| + |b1 a4 b7| + |b1 b4 a7| | * t^2 +
9236 * \ |a2 b5 b8| |b2 a5 b8| |b2 b5 a8| /
9237
9238 * |b0 b3 b6|
9239 * + |b1 b4 b7| * t^3
9240 * |b2 b5 b8| */
9241 c(0) = (a[0] * (a[4] * a[8] - a[5] * a[7]) +
9242 a[1] * (a[5] * a[6] - a[3] * a[8]) +
9243 a[2] * (a[3] * a[7] - a[4] * a[6]));
9244
9245 c(1) = (b[0] * (a[4] * a[8] - a[5] * a[7]) +
9246 b[1] * (a[5] * a[6] - a[3] * a[8]) +
9247 b[2] * (a[3] * a[7] - a[4] * a[6]) +
9248
9249 a[0] * (b[4] * a[8] - b[5] * a[7]) +
9250 a[1] * (b[5] * a[6] - b[3] * a[8]) +
9251 a[2] * (b[3] * a[7] - b[4] * a[6]) +
9252
9253 a[0] * (a[4] * b[8] - a[5] * b[7]) +
9254 a[1] * (a[5] * b[6] - a[3] * b[8]) +
9255 a[2] * (a[3] * b[7] - a[4] * b[6]));
9256
9257 c(2) = (a[0] * (b[4] * b[8] - b[5] * b[7]) +
9258 a[1] * (b[5] * b[6] - b[3] * b[8]) +
9259 a[2] * (b[3] * b[7] - b[4] * b[6]) +
9260
9261 b[0] * (a[4] * b[8] - a[5] * b[7]) +
9262 b[1] * (a[5] * b[6] - a[3] * b[8]) +
9263 b[2] * (a[3] * b[7] - a[4] * b[6]) +
9264
9265 b[0] * (b[4] * a[8] - b[5] * a[7]) +
9266 b[1] * (b[5] * a[6] - b[3] * a[8]) +
9267 b[2] * (b[3] * a[7] - b[4] * a[6]));
9268
9269 c(3) = (b[0] * (b[4] * b[8] - b[5] * b[7]) +
9270 b[1] * (b[5] * b[6] - b[3] * b[8]) +
9271 b[2] * (b[3] * b[7] - b[4] * b[6]));
9272 }
9273 break;
9274
9275 default:
9276 mfem_error("DetOfLinComb(...)");
9277 }
9278}
9279
9280// compute the real roots of
9281// z(0)+z(1)*x+...+z(d)*x^d = 0, d=2,3;
9282// the roots are returned in x, sorted in increasing order;
9283// it is assumed that x is at least of size d;
9284// return the number of roots counting multiplicity;
9285// return -1 if all z(i) are 0.
9286int FindRoots(const Vector &z, Vector &x)
9287{
9288 int d = z.Size()-1;
9289 if (d > 3 || d < 0)
9290 {
9291 mfem_error("FindRoots(...)");
9292 }
9293
9294 while (z(d) == 0.0)
9295 {
9296 if (d == 0)
9297 {
9298 return (-1);
9299 }
9300 d--;
9301 }
9302 switch (d)
9303 {
9304 case 0:
9305 {
9306 return 0;
9307 }
9308
9309 case 1:
9310 {
9311 x(0) = -z(0)/z(1);
9312 return 1;
9313 }
9314
9315 case 2:
9316 {
9317 real_t a = z(2), b = z(1), c = z(0);
9318 real_t D = b*b-4*a*c;
9319 if (D < 0.0)
9320 {
9321 return 0;
9322 }
9323 if (D == 0.0)
9324 {
9325 x(0) = x(1) = -0.5 * b / a;
9326 return 2; // root with multiplicity 2
9327 }
9328 if (b == 0.0)
9329 {
9330 x(0) = -(x(1) = fabs(0.5 * sqrt(D) / a));
9331 return 2;
9332 }
9333 else
9334 {
9335 real_t t;
9336 if (b > 0.0)
9337 {
9338 t = -0.5 * (b + sqrt(D));
9339 }
9340 else
9341 {
9342 t = -0.5 * (b - sqrt(D));
9343 }
9344 x(0) = t / a;
9345 x(1) = c / t;
9346 if (x(0) > x(1))
9347 {
9348 Swap<real_t>(x(0), x(1));
9349 }
9350 return 2;
9351 }
9352 }
9353
9354 case 3:
9355 {
9356 real_t a = z(2)/z(3), b = z(1)/z(3), c = z(0)/z(3);
9357
9358 // find the real roots of x^3 + a x^2 + b x + c = 0
9359 real_t Q = (a * a - 3 * b) / 9;
9360 real_t R = (2 * a * a * a - 9 * a * b + 27 * c) / 54;
9361 real_t Q3 = Q * Q * Q;
9362 real_t R2 = R * R;
9363
9364 if (R2 == Q3)
9365 {
9366 if (Q == 0)
9367 {
9368 x(0) = x(1) = x(2) = - a / 3;
9369 }
9370 else
9371 {
9372 real_t sqrtQ = sqrt(Q);
9373
9374 if (R > 0)
9375 {
9376 x(0) = -2 * sqrtQ - a / 3;
9377 x(1) = x(2) = sqrtQ - a / 3;
9378 }
9379 else
9380 {
9381 x(0) = x(1) = - sqrtQ - a / 3;
9382 x(2) = 2 * sqrtQ - a / 3;
9383 }
9384 }
9385 return 3;
9386 }
9387 else if (R2 < Q3)
9388 {
9389 real_t theta = acos(R / sqrt(Q3));
9390 real_t A = -2 * sqrt(Q);
9391 real_t x0, x1, x2;
9392 x0 = A * cos(theta / 3) - a / 3;
9393 x1 = A * cos((theta + 2.0 * M_PI) / 3) - a / 3;
9394 x2 = A * cos((theta - 2.0 * M_PI) / 3) - a / 3;
9395
9396 /* Sort x0, x1, x2 */
9397 if (x0 > x1)
9398 {
9399 Swap<real_t>(x0, x1);
9400 }
9401 if (x1 > x2)
9402 {
9403 Swap<real_t>(x1, x2);
9404 if (x0 > x1)
9405 {
9406 Swap<real_t>(x0, x1);
9407 }
9408 }
9409 x(0) = x0;
9410 x(1) = x1;
9411 x(2) = x2;
9412 return 3;
9413 }
9414 else
9415 {
9416 real_t A;
9417 if (R >= 0.0)
9418 {
9419 A = -pow(sqrt(R2 - Q3) + R, 1.0/3.0);
9420 }
9421 else
9422 {
9423 A = pow(sqrt(R2 - Q3) - R, 1.0/3.0);
9424 }
9425 x(0) = A + Q / A - a / 3;
9426 return 1;
9427 }
9428 }
9429 }
9430 return 0;
9431}
9432
9433void FindTMax(Vector &c, Vector &x, real_t &tmax,
9434 const real_t factor, const int Dim)
9435{
9436 const real_t c0 = c(0);
9437 c(0) = c0 * (1.0 - pow(factor, -Dim));
9438 int nr = FindRoots(c, x);
9439 for (int j = 0; j < nr; j++)
9440 {
9441 if (x(j) > tmax)
9442 {
9443 break;
9444 }
9445 if (x(j) >= 0.0)
9446 {
9447 tmax = x(j);
9448 break;
9449 }
9450 }
9451 c(0) = c0 * (1.0 - pow(factor, Dim));
9452 nr = FindRoots(c, x);
9453 for (int j = 0; j < nr; j++)
9454 {
9455 if (x(j) > tmax)
9456 {
9457 break;
9458 }
9459 if (x(j) >= 0.0)
9460 {
9461 tmax = x(j);
9462 break;
9463 }
9464 }
9465}
9466
9467void Mesh::CheckDisplacements(const Vector &displacements, real_t &tmax)
9468{
9469 int nvs = vertices.Size();
9470 DenseMatrix P, V, DS, PDS(spaceDim), VDS(spaceDim);
9471 Vector c(spaceDim+1), x(spaceDim);
9472 const real_t factor = 2.0;
9473
9474 // check for tangling assuming constant speed
9475 if (tmax < 1.0)
9476 {
9477 tmax = 1.0;
9478 }
9479 for (int i = 0; i < NumOfElements; i++)
9480 {
9481 Element *el = elements[i];
9482 int nv = el->GetNVertices();
9483 int *v = el->GetVertices();
9484 P.SetSize(spaceDim, nv);
9485 V.SetSize(spaceDim, nv);
9486 for (int j = 0; j < spaceDim; j++)
9487 for (int k = 0; k < nv; k++)
9488 {
9489 P(j, k) = vertices[v[k]](j);
9490 V(j, k) = displacements(v[k]+j*nvs);
9491 }
9492 DS.SetSize(nv, spaceDim);
9493 const FiniteElement *fe =
9495 // check if det(P.DShape+t*V.DShape) > 0 for all x and 0<=t<=1
9496 switch (el->GetType())
9497 {
9498 case Element::TRIANGLE:
9500 {
9501 // DS is constant
9503 Mult(P, DS, PDS);
9504 Mult(V, DS, VDS);
9505 DetOfLinComb(PDS, VDS, c);
9506 if (c(0) <= 0.0)
9507 {
9508 tmax = 0.0;
9509 }
9510 else
9511 {
9512 FindTMax(c, x, tmax, factor, Dim);
9513 }
9514 }
9515 break;
9516
9518 {
9519 const IntegrationRule &ir = fe->GetNodes();
9520 for (int j = 0; j < nv; j++)
9521 {
9522 fe->CalcDShape(ir.IntPoint(j), DS);
9523 Mult(P, DS, PDS);
9524 Mult(V, DS, VDS);
9525 DetOfLinComb(PDS, VDS, c);
9526 if (c(0) <= 0.0)
9527 {
9528 tmax = 0.0;
9529 }
9530 else
9531 {
9532 FindTMax(c, x, tmax, factor, Dim);
9533 }
9534 }
9535 }
9536 break;
9537
9538 default:
9539 mfem_error("Mesh::CheckDisplacements(...)");
9540 }
9541 }
9542}
9543
9544void Mesh::MoveVertices(const Vector &displacements)
9545{
9546 for (int i = 0, nv = vertices.Size(); i < nv; i++)
9547 for (int j = 0; j < spaceDim; j++)
9548 {
9549 vertices[i](j) += displacements(j*nv+i);
9550 }
9551}
9552
9553void Mesh::GetVertices(Vector &vert_coord) const
9554{
9555 int nv = vertices.Size();
9556 vert_coord.SetSize(nv*spaceDim);
9557 for (int i = 0; i < nv; i++)
9558 for (int j = 0; j < spaceDim; j++)
9559 {
9560 vert_coord(j*nv+i) = vertices[i](j);
9561 }
9562}
9563
9564void Mesh::SetVertices(const Vector &vert_coord)
9565{
9566 for (int i = 0, nv = vertices.Size(); i < nv; i++)
9567 for (int j = 0; j < spaceDim; j++)
9568 {
9569 vertices[i](j) = vert_coord(j*nv+i);
9570 }
9571}
9572
9573void Mesh::GetNode(int i, real_t *coord) const
9574{
9575 if (Nodes)
9576 {
9578 for (int j = 0; j < spaceDim; j++)
9579 {
9580 coord[j] = AsConst(*Nodes)(fes->DofToVDof(i, j));
9581 }
9582 }
9583 else
9584 {
9585 for (int j = 0; j < spaceDim; j++)
9586 {
9587 coord[j] = vertices[i](j);
9588 }
9589 }
9590}
9591
9592void Mesh::SetNode(int i, const real_t *coord)
9593{
9594 if (Nodes)
9595 {
9597 for (int j = 0; j < spaceDim; j++)
9598 {
9599 (*Nodes)(fes->DofToVDof(i, j)) = coord[j];
9600 }
9601 }
9602 else
9603 {
9604 for (int j = 0; j < spaceDim; j++)
9605 {
9606 vertices[i](j) = coord[j];
9607 }
9608
9609 }
9610}
9611
9612void Mesh::MoveNodes(const Vector &displacements)
9613{
9614 if (Nodes)
9615 {
9616 (*Nodes) += displacements;
9617 }
9618 else
9619 {
9620 MoveVertices(displacements);
9621 }
9622
9623 // Invalidate the old geometric factors
9624 NodesUpdated();
9625}
9626
9627void Mesh::GetNodes(Vector &node_coord) const
9628{
9629 if (Nodes)
9630 {
9631 node_coord = (*Nodes);
9632 }
9633 else
9634 {
9635 GetVertices(node_coord);
9636 }
9637}
9638
9639void Mesh::SetNodes(const Vector &node_coord)
9640{
9641 if (Nodes)
9642 {
9643 (*Nodes) = node_coord;
9644 }
9645 else
9646 {
9647 SetVertices(node_coord);
9648 }
9649
9650 // Invalidate the old geometric factors
9651 NodesUpdated();
9652}
9653
9654void Mesh::NewNodes(GridFunction &nodes, bool make_owner)
9655{
9656 if (own_nodes) { delete Nodes; }
9657 Nodes = &nodes;
9658 spaceDim = Nodes->FESpace()->GetVDim();
9659 own_nodes = (int)make_owner;
9660
9661 if (NURBSext != nodes.FESpace()->GetNURBSext())
9662 {
9663 delete NURBSext;
9664 NURBSext = nodes.FESpace()->StealNURBSext();
9665 }
9666
9667 if (ncmesh)
9668 {
9670 }
9671
9672 // Invalidate the old geometric factors
9673 NodesUpdated();
9674}
9675
9676void Mesh::SwapNodes(GridFunction *&nodes, int &own_nodes_)
9677{
9678 // If this is a nonconforming mesh without nodes, ncmesh->coordinates will
9679 // be non-empty; so if the 'nodes' argument is not NULL, we will create an
9680 // inconsistent state where the Mesh has nodes and ncmesh->coordinates is not
9681 // empty. This was creating an issue for Mesh::Print() since both the
9682 // "coordinates" and "nodes" sections were written, leading to crashes during
9683 // loading. This issue is now fixed in Mesh::Printer() by temporarily
9684 // swapping ncmesh->coordinates with an empty array when the Mesh has nodes.
9685
9687 mfem::Swap<int>(own_nodes, own_nodes_);
9688 // TODO:
9689 // if (nodes)
9690 // nodes->FESpace()->MakeNURBSextOwner();
9691 // NURBSext = (Nodes) ? Nodes->FESpace()->StealNURBSext() : NULL;
9692
9693 // Invalidate the old geometric factors
9694 NodesUpdated();
9695}
9696
9697void Mesh::AverageVertices(const int *indexes, int n, int result)
9698{
9699 int j, k;
9700
9701 for (k = 0; k < spaceDim; k++)
9702 {
9703 vertices[result](k) = vertices[indexes[0]](k);
9704 }
9705
9706 for (j = 1; j < n; j++)
9707 for (k = 0; k < spaceDim; k++)
9708 {
9709 vertices[result](k) += vertices[indexes[j]](k);
9710 }
9711
9712 for (k = 0; k < spaceDim; k++)
9713 {
9714 vertices[result](k) *= (1.0 / n);
9715 }
9716}
9717
9719{
9720 if (Nodes)
9721 {
9722 Nodes->FESpace()->Update();
9723 Nodes->Update();
9724
9725 // update vertex coordinates for compatibility (e.g., GetVertex())
9727
9728 // Invalidate the old geometric factors
9729 NodesUpdated();
9730 }
9731}
9732
9733void Mesh::UniformRefinement2D_base(bool update_nodes)
9734{
9735 ResetLazyData();
9736
9737 if (el_to_edge == NULL)
9738 {
9739 el_to_edge = new Table;
9741 }
9742
9743 int quad_counter = 0;
9744 for (int i = 0; i < NumOfElements; i++)
9745 {
9746 if (elements[i]->GetType() == Element::QUADRILATERAL)
9747 {
9748 quad_counter++;
9749 }
9750 }
9751
9752 const int oedge = NumOfVertices;
9753 const int oelem = oedge + NumOfEdges;
9754
9755 Array<Element*> new_elements;
9756 Array<Element*> new_boundary;
9757
9758 vertices.SetSize(oelem + quad_counter);
9759 new_elements.SetSize(4 * NumOfElements);
9760 quad_counter = 0;
9761
9762 for (int i = 0, j = 0; i < NumOfElements; i++)
9763 {
9764 const Element::Type el_type = elements[i]->GetType();
9765 const int attr = elements[i]->GetAttribute();
9766 int *v = elements[i]->GetVertices();
9767 const int *e = el_to_edge->GetRow(i);
9768 int vv[2];
9769
9770 if (el_type == Element::TRIANGLE)
9771 {
9772 for (int ei = 0; ei < 3; ei++)
9773 {
9774 for (int k = 0; k < 2; k++)
9775 {
9776 vv[k] = v[tri_t::Edges[ei][k]];
9777 }
9778 AverageVertices(vv, 2, oedge+e[ei]);
9779 }
9780
9781 new_elements[j++] =
9782 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
9783 new_elements[j++] =
9784 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
9785 new_elements[j++] =
9786 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
9787 new_elements[j++] =
9788 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
9789 }
9790 else if (el_type == Element::QUADRILATERAL)
9791 {
9792 const int qe = quad_counter;
9793 quad_counter++;
9794 AverageVertices(v, 4, oelem+qe);
9795
9796 for (int ei = 0; ei < 4; ei++)
9797 {
9798 for (int k = 0; k < 2; k++)
9799 {
9800 vv[k] = v[quad_t::Edges[ei][k]];
9801 }
9802 AverageVertices(vv, 2, oedge+e[ei]);
9803 }
9804
9805 new_elements[j++] =
9806 new Quadrilateral(v[0], oedge+e[0], oelem+qe, oedge+e[3], attr);
9807 new_elements[j++] =
9808 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oelem+qe, attr);
9809 new_elements[j++] =
9810 new Quadrilateral(oelem+qe, oedge+e[1], v[2], oedge+e[2], attr);
9811 new_elements[j++] =
9812 new Quadrilateral(oedge+e[3], oelem+qe, oedge+e[2], v[3], attr);
9813 }
9814 else
9815 {
9816 MFEM_ABORT("unknown element type: " << el_type);
9817 }
9819 }
9820 mfem::Swap(elements, new_elements);
9821
9822 // refine boundary elements
9823 new_boundary.SetSize(2 * NumOfBdrElements);
9824 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
9825 {
9826 const int attr = boundary[i]->GetAttribute();
9827 int *v = boundary[i]->GetVertices();
9828
9829 new_boundary[j++] = new Segment(v[0], oedge+be_to_face[i], attr);
9830 new_boundary[j++] = new Segment(oedge+be_to_face[i], v[1], attr);
9831
9833 }
9834 mfem::Swap(boundary, new_boundary);
9835
9836 static const real_t A = 0.0, B = 0.5, C = 1.0;
9837 static real_t tri_children[2*3*4] =
9838 {
9839 A,A, B,A, A,B,
9840 B,B, A,B, B,A,
9841 B,A, C,A, B,B,
9842 A,B, B,B, A,C
9843 };
9844 static real_t quad_children[2*4*4] =
9845 {
9846 A,A, B,A, B,B, A,B, // lower-left
9847 B,A, C,A, C,B, B,B, // lower-right
9848 B,B, C,B, C,C, B,C, // upper-right
9849 A,B, B,B, B,C, A,C // upper-left
9850 };
9851
9853 .UseExternalData(tri_children, 2, 3, 4);
9855 .UseExternalData(quad_children, 2, 4, 4);
9856 CoarseFineTr.embeddings.SetSize(elements.Size());
9857
9858 for (int i = 0; i < elements.Size(); i++)
9859 {
9861 emb.parent = i / 4;
9862 emb.matrix = i % 4;
9863 }
9864
9865 NumOfVertices = vertices.Size();
9868 NumOfFaces = 0;
9869
9871 GenerateFaces();
9872
9874 sequence++;
9875
9876 if (update_nodes) { UpdateNodes(); }
9877
9878#ifdef MFEM_DEBUG
9879 if (!Nodes || update_nodes)
9880 {
9882 }
9884#endif
9885}
9886
9887static inline real_t sqr(const real_t &x)
9888{
9889 return x*x;
9890}
9891
9893 bool update_nodes)
9894{
9895 ResetLazyData();
9896
9897 if (el_to_edge == NULL)
9898 {
9899 el_to_edge = new Table;
9901 }
9902
9903 if (el_to_face == NULL)
9904 {
9906 }
9907
9908 Array<int> f2qf_loc;
9909 Array<int> &f2qf = f2qf_ptr ? *f2qf_ptr : f2qf_loc;
9910 f2qf.SetSize(0);
9911
9912 int NumOfQuadFaces = 0;
9914 {
9916 {
9917 f2qf.SetSize(faces.Size());
9918 for (int i = 0; i < faces.Size(); i++)
9919 {
9920 if (faces[i]->GetType() == Element::QUADRILATERAL)
9921 {
9922 f2qf[i] = NumOfQuadFaces;
9923 NumOfQuadFaces++;
9924 }
9925 }
9926 }
9927 else
9928 {
9929 NumOfQuadFaces = faces.Size();
9930 }
9931 }
9932
9933 int hex_counter = 0;
9935 {
9936 for (int i = 0; i < elements.Size(); i++)
9937 {
9938 if (elements[i]->GetType() == Element::HEXAHEDRON)
9939 {
9940 hex_counter++;
9941 }
9942 }
9943 }
9944
9945 int pyr_counter = 0;
9947 {
9948 for (int i = 0; i < elements.Size(); i++)
9949 {
9950 if (elements[i]->GetType() == Element::PYRAMID)
9951 {
9952 pyr_counter++;
9953 }
9954 }
9955 }
9956
9957 // Map from edge-index to vertex-index, needed for ReorientTetMesh() for
9958 // parallel meshes.
9959 // Note: with the removal of ReorientTetMesh() this may no longer
9960 // be needed. Unfortunately, it's hard to be sure.
9961 Array<int> e2v;
9963 {
9964 e2v.SetSize(NumOfEdges);
9965
9966 DSTable *v_to_v_ptr = v_to_v_p;
9967 if (!v_to_v_p)
9968 {
9969 v_to_v_ptr = new DSTable(NumOfVertices);
9970 GetVertexToVertexTable(*v_to_v_ptr);
9971 }
9972
9973 Array<Pair<int,int> > J_v2v(NumOfEdges); // (second vertex id, edge id)
9974 J_v2v.SetSize(0);
9975 for (int i = 0; i < NumOfVertices; i++)
9976 {
9977 Pair<int,int> *row_start = J_v2v.end();
9978 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
9979 {
9980 J_v2v.Append(Pair<int,int>(it.Column(), it.Index()));
9981 }
9982 std::sort(row_start, J_v2v.end());
9983 }
9984
9985 for (int i = 0; i < J_v2v.Size(); i++)
9986 {
9987 e2v[J_v2v[i].two] = i;
9988 }
9989
9990 if (!v_to_v_p)
9991 {
9992 delete v_to_v_ptr;
9993 }
9994 else
9995 {
9996 for (int i = 0; i < NumOfVertices; i++)
9997 {
9998 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
9999 {
10000 it.SetIndex(e2v[it.Index()]);
10001 }
10002 }
10003 }
10004 }
10005
10006 // Offsets for new vertices from edges, faces (quads only), and elements
10007 // (hexes only); each of these entities generates one new vertex.
10008 const int oedge = NumOfVertices;
10009 const int oface = oedge + NumOfEdges;
10010 const int oelem = oface + NumOfQuadFaces;
10011
10012 Array<Element*> new_elements;
10013 Array<Element*> new_boundary;
10014
10015 vertices.SetSize(oelem + hex_counter);
10016 new_elements.SetSize(8 * NumOfElements + 2 * pyr_counter);
10017 CoarseFineTr.embeddings.SetSize(new_elements.Size());
10018
10019 hex_counter = 0;
10020 for (int i = 0, j = 0; i < NumOfElements; i++)
10021 {
10022 const Element::Type el_type = elements[i]->GetType();
10023 const int attr = elements[i]->GetAttribute();
10024 int *v = elements[i]->GetVertices();
10025 const int *e = el_to_edge->GetRow(i);
10026 int vv[4], ev[12];
10027
10028 if (e2v.Size())
10029 {
10030 const int ne = el_to_edge->RowSize(i);
10031 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
10032 e = ev;
10033 }
10034
10035 switch (el_type)
10036 {
10038 {
10039 for (int ei = 0; ei < 6; ei++)
10040 {
10041 for (int k = 0; k < 2; k++)
10042 {
10043 vv[k] = v[tet_t::Edges[ei][k]];
10044 }
10045 AverageVertices(vv, 2, oedge+e[ei]);
10046 }
10047
10048 // Algorithm for choosing refinement type:
10049 // 0: smallest octahedron diagonal
10050 // 1: best aspect ratio
10051 const int rt_algo = 1;
10052 // Refinement type:
10053 // 0: (v0,v1)-(v2,v3), 1: (v0,v2)-(v1,v3), 2: (v0,v3)-(v1,v2)
10054 // 0: e0-e5, 1: e1-e4, 2: e2-e3
10055 int rt;
10058 const DenseMatrix &J = T->Jacobian();
10059 if (rt_algo == 0)
10060 {
10061 // smallest octahedron diagonal
10062 real_t len_sqr, min_len;
10063
10064 min_len = sqr(J(0,0)-J(0,1)-J(0,2)) +
10065 sqr(J(1,0)-J(1,1)-J(1,2)) +
10066 sqr(J(2,0)-J(2,1)-J(2,2));
10067 rt = 0;
10068
10069 len_sqr = sqr(J(0,1)-J(0,0)-J(0,2)) +
10070 sqr(J(1,1)-J(1,0)-J(1,2)) +
10071 sqr(J(2,1)-J(2,0)-J(2,2));
10072 if (len_sqr < min_len) { min_len = len_sqr; rt = 1; }
10073
10074 len_sqr = sqr(J(0,2)-J(0,0)-J(0,1)) +
10075 sqr(J(1,2)-J(1,0)-J(1,1)) +
10076 sqr(J(2,2)-J(2,0)-J(2,1));
10077 if (len_sqr < min_len) { rt = 2; }
10078 }
10079 else
10080 {
10081 // best aspect ratio
10082 real_t Em_data[18], Js_data[9], Jp_data[9];
10083 DenseMatrix Em(Em_data, 3, 6);
10084 DenseMatrix Js(Js_data, 3, 3), Jp(Jp_data, 3, 3);
10085 real_t ar1, ar2, kappa, kappa_min;
10086
10087 for (int s = 0; s < 3; s++)
10088 {
10089 for (int t = 0; t < 3; t++)
10090 {
10091 Em(t,s) = 0.5*J(t,s);
10092 }
10093 }
10094 for (int t = 0; t < 3; t++)
10095 {
10096 Em(t,3) = 0.5*(J(t,0)+J(t,1));
10097 Em(t,4) = 0.5*(J(t,0)+J(t,2));
10098 Em(t,5) = 0.5*(J(t,1)+J(t,2));
10099 }
10100
10101 // rt = 0; Em: {0,5,1,2}, {0,5,2,4}
10102 for (int t = 0; t < 3; t++)
10103 {
10104 Js(t,0) = Em(t,5)-Em(t,0);
10105 Js(t,1) = Em(t,1)-Em(t,0);
10106 Js(t,2) = Em(t,2)-Em(t,0);
10107 }
10109 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10110 for (int t = 0; t < 3; t++)
10111 {
10112 Js(t,0) = Em(t,5)-Em(t,0);
10113 Js(t,1) = Em(t,2)-Em(t,0);
10114 Js(t,2) = Em(t,4)-Em(t,0);
10115 }
10117 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10118 kappa_min = std::max(ar1, ar2);
10119 rt = 0;
10120
10121 // rt = 1; Em: {1,0,4,2}, {1,2,4,5}
10122 for (int t = 0; t < 3; t++)
10123 {
10124 Js(t,0) = Em(t,0)-Em(t,1);
10125 Js(t,1) = Em(t,4)-Em(t,1);
10126 Js(t,2) = Em(t,2)-Em(t,1);
10127 }
10129 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10130 for (int t = 0; t < 3; t++)
10131 {
10132 Js(t,0) = Em(t,2)-Em(t,1);
10133 Js(t,1) = Em(t,4)-Em(t,1);
10134 Js(t,2) = Em(t,5)-Em(t,1);
10135 }
10137 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10138 kappa = std::max(ar1, ar2);
10139 if (kappa < kappa_min) { kappa_min = kappa; rt = 1; }
10140
10141 // rt = 2; Em: {2,0,1,3}, {2,1,5,3}
10142 for (int t = 0; t < 3; t++)
10143 {
10144 Js(t,0) = Em(t,0)-Em(t,2);
10145 Js(t,1) = Em(t,1)-Em(t,2);
10146 Js(t,2) = Em(t,3)-Em(t,2);
10147 }
10149 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10150 for (int t = 0; t < 3; t++)
10151 {
10152 Js(t,0) = Em(t,1)-Em(t,2);
10153 Js(t,1) = Em(t,5)-Em(t,2);
10154 Js(t,2) = Em(t,3)-Em(t,2);
10155 }
10157 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
10158 kappa = std::max(ar1, ar2);
10159 if (kappa < kappa_min) { rt = 2; }
10160 }
10161
10162 static const int mv_all[3][4][4] =
10163 {
10164 { {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1} }, // rt = 0
10165 { {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0} }, // rt = 1
10166 { {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3} } // rt = 2
10167 };
10168 const int (&mv)[4][4] = mv_all[rt];
10169
10170#ifndef MFEM_USE_MEMALLOC
10171 new_elements[j+0] =
10172 new Tetrahedron(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
10173 new_elements[j+1] =
10174 new Tetrahedron(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
10175 new_elements[j+2] =
10176 new Tetrahedron(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
10177 new_elements[j+3] =
10178 new Tetrahedron(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
10179
10180 for (int k = 0; k < 4; k++)
10181 {
10182 new_elements[j+4+k] =
10183 new Tetrahedron(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
10184 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
10185 }
10186#else
10187 Tetrahedron *tet;
10188 new_elements[j+0] = tet = TetMemory.Alloc();
10189 tet->Init(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
10190
10191 new_elements[j+1] = tet = TetMemory.Alloc();
10192 tet->Init(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
10193
10194 new_elements[j+2] = tet = TetMemory.Alloc();
10195 tet->Init(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
10196
10197 new_elements[j+3] = tet = TetMemory.Alloc();
10198 tet->Init(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
10199
10200 for (int k = 0; k < 4; k++)
10201 {
10202 new_elements[j+4+k] = tet = TetMemory.Alloc();
10203 tet->Init(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
10204 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
10205 }
10206#endif
10207 for (int k = 0; k < 4; k++)
10208 {
10209 CoarseFineTr.embeddings[j+k].parent = i;
10210 CoarseFineTr.embeddings[j+k].matrix = k;
10211 }
10212 for (int k = 0; k < 4; k++)
10213 {
10214 CoarseFineTr.embeddings[j+4+k].parent = i;
10215 CoarseFineTr.embeddings[j+4+k].matrix = 4*(rt+1)+k;
10216 }
10217
10218 j += 8;
10219 }
10220 break;
10221
10222 case Element::WEDGE:
10223 {
10224 const int *f = el_to_face->GetRow(i);
10225
10226 for (int fi = 2; fi < 5; fi++)
10227 {
10228 for (int k = 0; k < 4; k++)
10229 {
10230 vv[k] = v[pri_t::FaceVert[fi][k]];
10231 }
10232 AverageVertices(vv, 4, oface + f2qf[f[fi]]);
10233 }
10234
10235 for (int ei = 0; ei < 9; ei++)
10236 {
10237 for (int k = 0; k < 2; k++)
10238 {
10239 vv[k] = v[pri_t::Edges[ei][k]];
10240 }
10241 AverageVertices(vv, 2, oedge+e[ei]);
10242 }
10243
10244 const int qf2 = f2qf[f[2]];
10245 const int qf3 = f2qf[f[3]];
10246 const int qf4 = f2qf[f[4]];
10247
10248 new_elements[j++] =
10249 new Wedge(v[0], oedge+e[0], oedge+e[2],
10250 oedge+e[6], oface+qf2, oface+qf4, attr);
10251
10252 new_elements[j++] =
10253 new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
10254 oface+qf3, oface+qf4, oface+qf2, attr);
10255
10256 new_elements[j++] =
10257 new Wedge(oedge+e[0], v[1], oedge+e[1],
10258 oface+qf2, oedge+e[7], oface+qf3, attr);
10259
10260 new_elements[j++] =
10261 new Wedge(oedge+e[2], oedge+e[1], v[2],
10262 oface+qf4, oface+qf3, oedge+e[8], attr);
10263
10264 new_elements[j++] =
10265 new Wedge(oedge+e[6], oface+qf2, oface+qf4,
10266 v[3], oedge+e[3], oedge+e[5], attr);
10267
10268 new_elements[j++] =
10269 new Wedge(oface+qf3, oface+qf4, oface+qf2,
10270 oedge+e[4], oedge+e[5], oedge+e[3], attr);
10271
10272 new_elements[j++] =
10273 new Wedge(oface+qf2, oedge+e[7], oface+qf3,
10274 oedge+e[3], v[4], oedge+e[4], attr);
10275
10276 new_elements[j++] =
10277 new Wedge(oface+qf4, oface+qf3, oedge+e[8],
10278 oedge+e[5], oedge+e[4], v[5], attr);
10279 }
10280 break;
10281
10282 case Element::PYRAMID:
10283 {
10284 const int *f = el_to_face->GetRow(i);
10285 // pyr_counter++;
10286
10287 for (int fi = 0; fi < 1; fi++)
10288 {
10289 for (int k = 0; k < 4; k++)
10290 {
10291 vv[k] = v[pyr_t::FaceVert[fi][k]];
10292 }
10293 AverageVertices(vv, 4, oface + f2qf[f[fi]]);
10294 }
10295
10296 for (int ei = 0; ei < 8; ei++)
10297 {
10298 for (int k = 0; k < 2; k++)
10299 {
10300 vv[k] = v[pyr_t::Edges[ei][k]];
10301 }
10302 AverageVertices(vv, 2, oedge+e[ei]);
10303 }
10304
10305 const int qf0 = f2qf[f[0]];
10306
10307 new_elements[j++] =
10308 new Pyramid(v[0], oedge+e[0], oface+qf0,
10309 oedge+e[3], oedge+e[4], attr);
10310
10311 new_elements[j++] =
10312 new Pyramid(oedge+e[0], v[1], oedge+e[1],
10313 oface+qf0, oedge+e[5], attr);
10314
10315 new_elements[j++] =
10316 new Pyramid(oface+qf0, oedge+e[1], v[2],
10317 oedge+e[2], oedge+e[6], attr);
10318
10319 new_elements[j++] =
10320 new Pyramid(oedge+e[3], oface+qf0, oedge+e[2],
10321 v[3], oedge+e[7], attr);
10322
10323 new_elements[j++] =
10324 new Pyramid(oedge+e[4], oedge+e[5], oedge+e[6],
10325 oedge+e[7], v[4], attr);
10326
10327 new_elements[j++] =
10328 new Pyramid(oedge+e[7], oedge+e[6], oedge+e[5],
10329 oedge+e[4], oface+qf0, attr);
10330
10331#ifndef MFEM_USE_MEMALLOC
10332 new_elements[j++] =
10333 new Tetrahedron(oedge+e[0], oedge+e[4], oedge+e[5],
10334 oface+qf0, attr);
10335
10336 new_elements[j++] =
10337 new Tetrahedron(oedge+e[1], oedge+e[5], oedge+e[6],
10338 oface+qf0, attr);
10339
10340 new_elements[j++] =
10341 new Tetrahedron(oedge+e[2], oedge+e[6], oedge+e[7],
10342 oface+qf0, attr);
10343
10344 new_elements[j++] =
10345 new Tetrahedron(oedge+e[3], oedge+e[7], oedge+e[4],
10346 oface+qf0, attr);
10347#else
10348 Tetrahedron *tet;
10349 new_elements[j++] = tet = TetMemory.Alloc();
10350 tet->Init(oedge+e[0], oedge+e[4], oedge+e[5],
10351 oface+qf0, attr);
10352
10353 new_elements[j++] = tet = TetMemory.Alloc();
10354 tet->Init(oedge+e[1], oedge+e[5], oedge+e[6],
10355 oface+qf0, attr);
10356
10357 new_elements[j++] = tet = TetMemory.Alloc();
10358 tet->Init(oedge+e[2], oedge+e[6], oedge+e[7],
10359 oface+qf0, attr);
10360
10361 new_elements[j++] = tet = TetMemory.Alloc();
10362 tet->Init(oedge+e[3], oedge+e[7], oedge+e[4],
10363 oface+qf0, attr);
10364#endif
10365 // Tetrahedral elements may be new to this mesh so ensure that
10366 // the relevant flags are switched on
10368 meshgen |= 1;
10369 }
10370 break;
10371
10373 {
10374 const int *f = el_to_face->GetRow(i);
10375 const int he = hex_counter;
10376 hex_counter++;
10377
10378 const int *qf;
10379 int qf_data[6];
10380 if (f2qf.Size() == 0)
10381 {
10382 qf = f;
10383 }
10384 else
10385 {
10386 for (int k = 0; k < 6; k++) { qf_data[k] = f2qf[f[k]]; }
10387 qf = qf_data;
10388 }
10389
10390 AverageVertices(v, 8, oelem+he);
10391
10392 for (int fi = 0; fi < 6; fi++)
10393 {
10394 for (int k = 0; k < 4; k++)
10395 {
10396 vv[k] = v[hex_t::FaceVert[fi][k]];
10397 }
10398 AverageVertices(vv, 4, oface + qf[fi]);
10399 }
10400
10401 for (int ei = 0; ei < 12; ei++)
10402 {
10403 for (int k = 0; k < 2; k++)
10404 {
10405 vv[k] = v[hex_t::Edges[ei][k]];
10406 }
10407 AverageVertices(vv, 2, oedge+e[ei]);
10408 }
10409
10410 new_elements[j++] =
10411 new Hexahedron(v[0], oedge+e[0], oface+qf[0],
10412 oedge+e[3], oedge+e[8], oface+qf[1],
10413 oelem+he, oface+qf[4], attr);
10414 new_elements[j++] =
10415 new Hexahedron(oedge+e[0], v[1], oedge+e[1],
10416 oface+qf[0], oface+qf[1], oedge+e[9],
10417 oface+qf[2], oelem+he, attr);
10418 new_elements[j++] =
10419 new Hexahedron(oface+qf[0], oedge+e[1], v[2],
10420 oedge+e[2], oelem+he, oface+qf[2],
10421 oedge+e[10], oface+qf[3], attr);
10422 new_elements[j++] =
10423 new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
10424 v[3], oface+qf[4], oelem+he,
10425 oface+qf[3], oedge+e[11], attr);
10426 new_elements[j++] =
10427 new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
10428 oface+qf[4], v[4], oedge+e[4],
10429 oface+qf[5], oedge+e[7], attr);
10430 new_elements[j++] =
10431 new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
10432 oelem+he, oedge+e[4], v[5],
10433 oedge+e[5], oface+qf[5], attr);
10434 new_elements[j++] =
10435 new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
10436 oface+qf[3], oface+qf[5], oedge+e[5],
10437 v[6], oedge+e[6], attr);
10438 new_elements[j++] =
10439 new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
10440 oedge+e[11], oedge+e[7], oface+qf[5],
10441 oedge+e[6], v[7], attr);
10442 }
10443 break;
10444
10445 default:
10446 MFEM_ABORT("Unknown 3D element type \"" << el_type << "\"");
10447 break;
10448 }
10450 }
10451 mfem::Swap(elements, new_elements);
10452
10453 // refine boundary elements
10454 new_boundary.SetSize(4 * NumOfBdrElements);
10455 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
10456 {
10457 const Element::Type bdr_el_type = boundary[i]->GetType();
10458 const int attr = boundary[i]->GetAttribute();
10459 int *v = boundary[i]->GetVertices();
10460 const int *e = bel_to_edge->GetRow(i);
10461 int ev[4];
10462
10463 if (e2v.Size())
10464 {
10465 const int ne = bel_to_edge->RowSize(i);
10466 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
10467 e = ev;
10468 }
10469
10470 if (bdr_el_type == Element::TRIANGLE)
10471 {
10472 new_boundary[j++] =
10473 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
10474 new_boundary[j++] =
10475 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
10476 new_boundary[j++] =
10477 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
10478 new_boundary[j++] =
10479 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
10480 }
10481 else if (bdr_el_type == Element::QUADRILATERAL)
10482 {
10483 const int qf =
10484 (f2qf.Size() == 0) ? be_to_face[i] : f2qf[be_to_face[i]];
10485
10486 new_boundary[j++] =
10487 new Quadrilateral(v[0], oedge+e[0], oface+qf, oedge+e[3], attr);
10488 new_boundary[j++] =
10489 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oface+qf, attr);
10490 new_boundary[j++] =
10491 new Quadrilateral(oface+qf, oedge+e[1], v[2], oedge+e[2], attr);
10492 new_boundary[j++] =
10493 new Quadrilateral(oedge+e[3], oface+qf, oedge+e[2], v[3], attr);
10494 }
10495 else
10496 {
10497 MFEM_ABORT("boundary Element is not a triangle or a quad!");
10498 }
10500 }
10501 mfem::Swap(boundary, new_boundary);
10502
10503 static const real_t A = 0.0, B = 0.5, C = 1.0, D = -1.0;
10504 static real_t tet_children[3*4*16] =
10505 {
10506 A,A,A, B,A,A, A,B,A, A,A,B,
10507 B,A,A, C,A,A, B,B,A, B,A,B,
10508 A,B,A, B,B,A, A,C,A, A,B,B,
10509 A,A,B, B,A,B, A,B,B, A,A,C,
10510 // edge coordinates:
10511 // 0 -> B,A,A 1 -> A,B,A 2 -> A,A,B
10512 // 3 -> B,B,A 4 -> B,A,B 5 -> A,B,B
10513 // rt = 0: {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1}
10514 B,A,A, A,B,B, A,B,A, A,A,B,
10515 B,A,A, A,B,B, A,A,B, B,A,B,
10516 B,A,A, A,B,B, B,A,B, B,B,A,
10517 B,A,A, A,B,B, B,B,A, A,B,A,
10518 // rt = 1: {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0}
10519 A,B,A, B,A,A, B,A,B, A,A,B,
10520 A,B,A, A,A,B, B,A,B, A,B,B,
10521 A,B,A, A,B,B, B,A,B, B,B,A,
10522 A,B,A, B,B,A, B,A,B, B,A,A,
10523 // rt = 2: {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3}
10524 A,A,B, B,A,A, A,B,A, B,B,A,
10525 A,A,B, A,B,A, A,B,B, B,B,A,
10526 A,A,B, A,B,B, B,A,B, B,B,A,
10527 A,A,B, B,A,B, B,A,A, B,B,A
10528 };
10529 static real_t pyr_children[3*5*10] =
10530 {
10531 A,A,A, B,A,A, B,B,A, A,B,A, A,A,B,
10532 B,A,A, C,A,A, C,B,A, B,B,A, B,A,B,
10533 B,B,A, C,B,A, C,C,A, B,C,A, B,B,B,
10534 A,B,A, B,B,A, B,C,A, A,C,A, A,B,B,
10535 A,A,B, B,A,B, B,B,B, A,B,B, A,A,C,
10536 A,B,B, B,B,B, B,A,B, A,A,B, B,B,A,
10537 B,A,A, A,A,B, B,A,B, B,B,A, D,D,D,
10538 C,B,A, B,A,B, B,B,B, B,B,A, D,D,D,
10539 B,C,A, B,B,B, A,B,B, B,B,A, D,D,D,
10540 A,B,A, A,B,B, A,A,B, B,B,A, D,D,D
10541 };
10542 static real_t pri_children[3*6*8] =
10543 {
10544 A,A,A, B,A,A, A,B,A, A,A,B, B,A,B, A,B,B,
10545 B,B,A, A,B,A, B,A,A, B,B,B, A,B,B, B,A,B,
10546 B,A,A, C,A,A, B,B,A, B,A,B, C,A,B, B,B,B,
10547 A,B,A, B,B,A, A,C,A, A,B,B, B,B,B, A,C,B,
10548 A,A,B, B,A,B, A,B,B, A,A,C, B,A,C, A,B,C,
10549 B,B,B, A,B,B, B,A,B, B,B,C, A,B,C, B,A,C,
10550 B,A,B, C,A,B, B,B,B, B,A,C, C,A,C, B,B,C,
10551 A,B,B, B,B,B, A,C,B, A,B,C, B,B,C, A,C,C
10552 };
10553 static real_t hex_children[3*8*8] =
10554 {
10555 A,A,A, B,A,A, B,B,A, A,B,A, A,A,B, B,A,B, B,B,B, A,B,B,
10556 B,A,A, C,A,A, C,B,A, B,B,A, B,A,B, C,A,B, C,B,B, B,B,B,
10557 B,B,A, C,B,A, C,C,A, B,C,A, B,B,B, C,B,B, C,C,B, B,C,B,
10558 A,B,A, B,B,A, B,C,A, A,C,A, A,B,B, B,B,B, B,C,B, A,C,B,
10559 A,A,B, B,A,B, B,B,B, A,B,B, A,A,C, B,A,C, B,B,C, A,B,C,
10560 B,A,B, C,A,B, C,B,B, B,B,B, B,A,C, C,A,C, C,B,C, B,B,C,
10561 B,B,B, C,B,B, C,C,B, B,C,B, B,B,C, C,B,C, C,C,C, B,C,C,
10562 A,B,B, B,B,B, B,C,B, A,C,B, A,B,C, B,B,C, B,C,C, A,C,C
10563 };
10564
10566 .UseExternalData(tet_children, 3, 4, 16);
10568 .UseExternalData(pyr_children, 3, 5, 10);
10570 .UseExternalData(pri_children, 3, 6, 8);
10572 .UseExternalData(hex_children, 3, 8, 8);
10573
10574 for (int i = 0; i < elements.Size(); i++)
10575 {
10576 // tetrahedron elements are handled above:
10577 if (elements[i]->GetType() == Element::TETRAHEDRON) { continue; }
10578
10580 emb.parent = i / 8;
10581 emb.matrix = i % 8;
10582 }
10583
10584 NumOfVertices = vertices.Size();
10585 NumOfElements = 8 * NumOfElements + 2 * pyr_counter;
10587
10589 GenerateFaces();
10590
10591#ifdef MFEM_DEBUG
10593#endif
10594
10596
10598 sequence++;
10599
10600 if (update_nodes) { UpdateNodes(); }
10601}
10602
10603void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
10604{
10605 int i, j, ind, nedges;
10606 Array<int> v;
10607
10608 ResetLazyData();
10609
10610 if (ncmesh)
10611 {
10612 MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
10613 }
10614
10616
10617 if (Dim == 1) // --------------------------------------------------------
10618 {
10619 int cne = NumOfElements, cnv = NumOfVertices;
10620 NumOfVertices += marked_el.Size();
10621 NumOfElements += marked_el.Size();
10622 vertices.SetSize(NumOfVertices);
10623 elements.SetSize(NumOfElements);
10625
10626 for (j = 0; j < marked_el.Size(); j++)
10627 {
10628 i = marked_el[j];
10629 Segment *c_seg = (Segment *)elements[i];
10630 int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
10631 int new_v = cnv + j, new_e = cne + j;
10632 AverageVertices(vert, 2, new_v);
10633 elements[new_e] = new Segment(new_v, vert[1], attr);
10634 vert[1] = new_v;
10635
10638 }
10639
10640 static real_t seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
10642 UseExternalData(seg_children, 1, 2, 3);
10643
10644 GenerateFaces();
10645
10646 } // end of 'if (Dim == 1)'
10647 else if (Dim == 2) // ---------------------------------------------------
10648 {
10649 // 1. Get table of vertex to vertex connections.
10650 DSTable v_to_v(NumOfVertices);
10651 GetVertexToVertexTable(v_to_v);
10652
10653 // 2. Get edge to element connections in arrays edge1 and edge2
10654 nedges = v_to_v.NumberOfEntries();
10655 int *edge1 = new int[nedges];
10656 int *edge2 = new int[nedges];
10657 int *middle = new int[nedges];
10658
10659 for (i = 0; i < nedges; i++)
10660 {
10661 edge1[i] = edge2[i] = middle[i] = -1;
10662 }
10663
10664 for (i = 0; i < NumOfElements; i++)
10665 {
10666 elements[i]->GetVertices(v);
10667 for (j = 1; j < v.Size(); j++)
10668 {
10669 ind = v_to_v(v[j-1], v[j]);
10670 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
10671 }
10672 ind = v_to_v(v[0], v[v.Size()-1]);
10673 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
10674 }
10675
10676 // 3. Do the red refinement.
10677 for (i = 0; i < marked_el.Size(); i++)
10678 {
10679 RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
10680 }
10681
10682 // 4. Do the green refinement (to get conforming mesh).
10683 int need_refinement;
10684 do
10685 {
10686 need_refinement = 0;
10687 for (i = 0; i < nedges; i++)
10688 {
10689 if (middle[i] != -1 && edge1[i] != -1)
10690 {
10691 need_refinement = 1;
10692 GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
10693 }
10694 }
10695 }
10696 while (need_refinement == 1);
10697
10698 // 5. Update the boundary elements.
10699 int v1[2], v2[2], bisect, temp;
10700 temp = NumOfBdrElements;
10701 for (i = 0; i < temp; i++)
10702 {
10703 boundary[i]->GetVertices(v);
10704 bisect = v_to_v(v[0], v[1]);
10705 if (middle[bisect] != -1) // the element was refined (needs updating)
10706 {
10707 if (boundary[i]->GetType() == Element::SEGMENT)
10708 {
10709 v1[0] = v[0]; v1[1] = middle[bisect];
10710 v2[0] = middle[bisect]; v2[1] = v[1];
10711
10712 boundary[i]->SetVertices(v1);
10714 }
10715 else
10716 mfem_error("Only bisection of segment is implemented"
10717 " for bdr elem.");
10718 }
10719 }
10720 NumOfBdrElements = boundary.Size();
10721
10722 // 6. Free the allocated memory.
10723 delete [] edge1;
10724 delete [] edge2;
10725 delete [] middle;
10726
10727 if (el_to_edge != NULL)
10728 {
10730 GenerateFaces();
10731 }
10732
10733 }
10734 else if (Dim == 3) // ---------------------------------------------------
10735 {
10736 // 1. Hash table of vertex to vertex connections corresponding to refined
10737 // edges.
10738 HashTable<Hashed2> v_to_v;
10739
10740 MFEM_VERIFY(GetNE() == 0 ||
10741 ((Tetrahedron*)elements[0])->GetRefinementFlag() != 0,
10742 "tetrahedral mesh is not marked for refinement:"
10743 " call Finalize(true)");
10744
10745 // 2. Do the red refinement.
10746 int ii;
10747 switch (type)
10748 {
10749 case 1:
10750 for (i = 0; i < marked_el.Size(); i++)
10751 {
10752 Bisection(marked_el[i], v_to_v);
10753 }
10754 break;
10755 case 2:
10756 for (i = 0; i < marked_el.Size(); i++)
10757 {
10758 Bisection(marked_el[i], v_to_v);
10759
10760 Bisection(NumOfElements - 1, v_to_v);
10761 Bisection(marked_el[i], v_to_v);
10762 }
10763 break;
10764 case 3:
10765 for (i = 0; i < marked_el.Size(); i++)
10766 {
10767 Bisection(marked_el[i], v_to_v);
10768
10769 ii = NumOfElements - 1;
10770 Bisection(ii, v_to_v);
10771 Bisection(NumOfElements - 1, v_to_v);
10772 Bisection(ii, v_to_v);
10773
10774 Bisection(marked_el[i], v_to_v);
10775 Bisection(NumOfElements-1, v_to_v);
10776 Bisection(marked_el[i], v_to_v);
10777 }
10778 break;
10779 }
10780
10781 // 3. Do the green refinement (to get conforming mesh).
10782 int need_refinement;
10783 // int need_refinement, onoe, max_gen = 0;
10784 do
10785 {
10786 // int redges[2], type, flag;
10787 need_refinement = 0;
10788 // onoe = NumOfElements;
10789 // for (i = 0; i < onoe; i++)
10790 for (i = 0; i < NumOfElements; i++)
10791 {
10792 // ((Tetrahedron *)elements[i])->
10793 // ParseRefinementFlag(redges, type, flag);
10794 // if (flag > max_gen) max_gen = flag;
10795 if (elements[i]->NeedRefinement(v_to_v))
10796 {
10797 need_refinement = 1;
10798 Bisection(i, v_to_v);
10799 }
10800 }
10801 }
10802 while (need_refinement == 1);
10803
10804 // mfem::out << "Maximum generation: " << max_gen << endl;
10805
10806 // 4. Update the boundary elements.
10807 do
10808 {
10809 need_refinement = 0;
10810 for (i = 0; i < NumOfBdrElements; i++)
10811 if (boundary[i]->NeedRefinement(v_to_v))
10812 {
10813 need_refinement = 1;
10814 BdrBisection(i, v_to_v);
10815 }
10816 }
10817 while (need_refinement == 1);
10818
10819 NumOfVertices = vertices.Size();
10820 NumOfBdrElements = boundary.Size();
10821
10822 // 5. Update element-to-edge and element-to-face relations.
10823 if (el_to_edge != NULL)
10824 {
10826 }
10827 if (el_to_face != NULL)
10828 {
10830 GenerateFaces();
10831 }
10832
10833 } // end 'if (Dim == 3)'
10834
10836 sequence++;
10837
10838 UpdateNodes();
10839
10840#ifdef MFEM_DEBUG
10842#endif
10843}
10844
10846 int nc_limit)
10847{
10848 MFEM_VERIFY(!NURBSext, "Nonconforming refinement of NURBS meshes is "
10849 "not supported. Project the NURBS to Nodes first.");
10850
10851 ResetLazyData();
10852
10853 if (!ncmesh)
10854 {
10855 // start tracking refinement hierarchy
10856 ncmesh = new NCMesh(this);
10857 }
10858
10859 if (!refinements.Size())
10860 {
10862 return;
10863 }
10864
10865 // do the refinements
10867 ncmesh->Refine(refinements);
10868
10869 if (nc_limit > 0)
10870 {
10871 ncmesh->LimitNCLevel(nc_limit);
10872 }
10873
10874 // create a second mesh containing the finest elements from 'ncmesh'
10875 Mesh* mesh2 = new Mesh(*ncmesh);
10876 ncmesh->OnMeshUpdated(mesh2);
10877
10878 // now swap the meshes, the second mesh will become the old coarse mesh
10879 // and this mesh will be the new fine mesh
10880 Swap(*mesh2, false);
10881 delete mesh2;
10882
10884
10886 sequence++;
10887
10888 UpdateNodes();
10889}
10890
10892 const int *fine, int nfine, int op)
10893{
10894 real_t error = (op == 3) ? std::pow(elem_error[fine[0]],
10895 2.0) : elem_error[fine[0]];
10896
10897 for (int i = 1; i < nfine; i++)
10898 {
10899 MFEM_VERIFY(fine[i] < elem_error.Size(), "");
10900
10901 real_t err_fine = elem_error[fine[i]];
10902 switch (op)
10903 {
10904 case 0: error = std::min(error, err_fine); break;
10905 case 1: error += err_fine; break;
10906 case 2: error = std::max(error, err_fine); break;
10907 case 3: error += std::pow(err_fine, 2.0); break;
10908 default: MFEM_ABORT("Invalid operation.");
10909 }
10910 }
10911 return (op == 3) ? std::sqrt(error) : error;
10912}
10913
10915 real_t threshold, int nc_limit, int op)
10916{
10917 MFEM_VERIFY(ncmesh, "Only supported for non-conforming meshes.");
10918 MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
10919 "Project the NURBS to Nodes first.");
10920
10921 ResetLazyData();
10922
10923 const Table &dt = ncmesh->GetDerefinementTable();
10924
10925 Array<int> level_ok;
10926 if (nc_limit > 0)
10927 {
10928 ncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
10929 }
10930
10931 Array<int> derefs;
10932 for (int i = 0; i < dt.Size(); i++)
10933 {
10934 if (nc_limit > 0 && !level_ok[i]) { continue; }
10935
10936 real_t error =
10937 AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
10938
10939 if (error < threshold) { derefs.Append(i); }
10940 }
10941
10942 if (!derefs.Size()) { return false; }
10943
10944 ncmesh->Derefine(derefs);
10945
10946 Mesh* mesh2 = new Mesh(*ncmesh);
10947 ncmesh->OnMeshUpdated(mesh2);
10948
10949 Swap(*mesh2, false);
10950 delete mesh2;
10951
10953
10955 sequence++;
10956
10957 UpdateNodes();
10958
10959 return true;
10960}
10961
10962bool Mesh::DerefineByError(Array<real_t> &elem_error, real_t threshold,
10963 int nc_limit, int op)
10964{
10965 // NOTE: the error array is not const because it will be expanded in parallel
10966 // by ghost element errors
10967 if (Nonconforming())
10968 {
10969 return NonconformingDerefinement(elem_error, threshold, nc_limit, op);
10970 }
10971 else
10972 {
10973 MFEM_ABORT("Derefinement is currently supported for non-conforming "
10974 "meshes only.");
10975 return false;
10976 }
10977}
10978
10979bool Mesh::DerefineByError(const Vector &elem_error, real_t threshold,
10980 int nc_limit, int op)
10981{
10982 Array<real_t> tmp(elem_error.Size());
10983 for (int i = 0; i < tmp.Size(); i++)
10984 {
10985 tmp[i] = elem_error(i);
10986 }
10987 return DerefineByError(tmp, threshold, nc_limit, op);
10988}
10989
10990
10991void Mesh::InitFromNCMesh(const NCMesh &ncmesh_)
10992{
10993 Dim = ncmesh_.Dimension();
10994 spaceDim = ncmesh_.SpaceDimension();
10995
10996 DeleteTables();
10997
10998 ncmesh_.GetMeshComponents(*this);
10999
11000 NumOfVertices = vertices.Size();
11001 NumOfElements = elements.Size();
11002 NumOfBdrElements = boundary.Size();
11003
11004 SetMeshGen(); // set the mesh type: 'meshgen', ...
11005
11006 NumOfEdges = NumOfFaces = 0;
11008
11009 if (Dim > 1)
11010 {
11011 el_to_edge = new Table;
11013 }
11014 if (Dim > 2)
11015 {
11017 }
11018 GenerateFaces();
11019#ifdef MFEM_DEBUG
11021#endif
11022
11023 // NOTE: ncmesh->OnMeshUpdated() and GenerateNCFaceInfo() should be called
11024 // outside after this method.
11025}
11026
11027Mesh::Mesh(const NCMesh &ncmesh_)
11028 : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
11029{
11030 Init();
11031 InitTables();
11032 InitFromNCMesh(ncmesh_);
11033 SetAttributes();
11034}
11035
11036void Mesh::Swap(Mesh& other, bool non_geometry)
11037{
11038 mfem::Swap(Dim, other.Dim);
11040
11046
11047 mfem::Swap(meshgen, other.meshgen);
11049
11053 mfem::Swap(faces, other.faces);
11058
11067
11070
11073
11074#ifdef MFEM_USE_MEMALLOC
11075 TetMemory.Swap(other.TetMemory);
11076#endif
11077
11078 if (non_geometry)
11079 {
11081 mfem::Swap(ncmesh, other.ncmesh);
11082
11083 mfem::Swap(Nodes, other.Nodes);
11084 if (Nodes) { Nodes->FESpace()->UpdateMeshPointer(this); }
11085 if (other.Nodes) { other.Nodes->FESpace()->UpdateMeshPointer(&other); }
11087
11089
11093 }
11094
11095 // copy attribute caches
11098
11099 mfem::Swap(face_indices[0], other.face_indices[0]);
11100 mfem::Swap(face_indices[1], other.face_indices[1]);
11101 inv_face_indices[0].swap(other.inv_face_indices[0]);
11102 inv_face_indices[1].swap(other.inv_face_indices[1]);
11103}
11104
11105void Mesh::GetElementData(const Array<Element*> &elem_array, int geom,
11106 Array<int> &elem_vtx, Array<int> &attr) const
11107{
11108 // protected method
11109 const int nv = Geometry::NumVerts[geom];
11110 int num_elems = 0;
11111 for (int i = 0; i < elem_array.Size(); i++)
11112 {
11113 if (elem_array[i]->GetGeometryType() == geom)
11114 {
11115 num_elems++;
11116 }
11117 }
11118 elem_vtx.SetSize(nv*num_elems);
11119 attr.SetSize(num_elems);
11120 elem_vtx.SetSize(0);
11121 attr.SetSize(0);
11122 for (int i = 0; i < elem_array.Size(); i++)
11123 {
11124 Element *el = elem_array[i];
11125 if (el->GetGeometryType() != geom) { continue; }
11126
11127 Array<int> loc_vtx(el->GetVertices(), nv);
11128 elem_vtx.Append(loc_vtx);
11129 attr.Append(el->GetAttribute());
11130 }
11131}
11132
11133static Array<int>& AllElements(Array<int> &list, int nelem)
11134{
11135 list.SetSize(nelem);
11136 for (int i = 0; i < nelem; i++) { list[i] = i; }
11137 return list;
11138}
11139
11140void Mesh::UniformRefinement(int ref_algo)
11141{
11142 Array<int> list;
11143
11144 if (NURBSext)
11145 {
11147 }
11148 else if (ncmesh)
11149 {
11150 GeneralRefinement(AllElements(list, GetNE()));
11151 }
11152 else if (ref_algo == 1 && meshgen == 1 && Dim == 3)
11153 {
11154 // algorithm "B" for an all-tet mesh
11155 LocalRefinement(AllElements(list, GetNE()));
11156 }
11157 else
11158 {
11159 switch (Dim)
11160 {
11161 case 1: LocalRefinement(AllElements(list, GetNE())); break;
11162 case 2: UniformRefinement2D(); break;
11163 case 3: UniformRefinement3D(); break;
11164 default: MFEM_ABORT("internal error");
11165 }
11166 }
11167}
11168
11170{
11171 if (NURBSext && cf > 1)
11172 {
11174 Array<int> initialCoarsening; // Initial coarsening factors
11175 NURBSext->GetCoarseningFactors(initialCoarsening);
11176
11177 // If refinement formulas are nested, then initial coarsening is skipped.
11178 bool noInitialCoarsening = true;
11179 for (auto f : initialCoarsening)
11180 {
11181 noInitialCoarsening = (noInitialCoarsening && f == 1);
11182 }
11183
11184 if (noInitialCoarsening)
11185 {
11186 NURBSext->Coarsen(cf, tol);
11187 }
11188 else
11189 {
11190 // Perform an initial full coarsening, and then refine. This is
11191 // necessary only for non-nested refinement formulas.
11192 NURBSext->Coarsen(initialCoarsening, tol);
11193
11194 // FiniteElementSpace::Update is not supported
11196 sequence++;
11197
11198 UpdateNURBS();
11199
11200 // Prepare for refinement by factors.
11202
11203 Array<int> rf(initialCoarsening);
11204 bool divisible = true;
11205 for (int i=0; i<rf.Size(); ++i)
11206 {
11207 rf[i] /= cf;
11208 divisible = divisible && cf * rf[i] == initialCoarsening[i];
11209 }
11210
11211 MFEM_VERIFY(divisible, "Invalid coarsening");
11212
11213 // Refine from the fully coarsened mesh to the mesh coarsened by the
11214 // factor cf.
11216 }
11217
11218 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
11219 sequence++;
11220
11221 UpdateNURBS();
11222 }
11223}
11224
11226 int nonconforming, int nc_limit)
11227{
11228 if (ncmesh)
11229 {
11230 nonconforming = 1;
11231 }
11232 else if (Dim == 1 || (Dim == 3 && (meshgen & 1)))
11233 {
11234 nonconforming = 0;
11235 }
11236 else if (nonconforming < 0)
11237 {
11238 // determine if nonconforming refinement is suitable
11239 if ((meshgen & 2) || (meshgen & 4) || (meshgen & 8))
11240 {
11241 nonconforming = 1; // tensor product elements and wedges
11242 }
11243 else
11244 {
11245 nonconforming = 0; // simplices
11246 }
11247 }
11248
11249 if (nonconforming)
11250 {
11251 // non-conforming refinement (hanging nodes)
11252 NonconformingRefinement(refinements, nc_limit);
11253 }
11254 else
11255 {
11256 Array<int> el_to_refine(refinements.Size());
11257 for (int i = 0; i < refinements.Size(); i++)
11258 {
11259 el_to_refine[i] = refinements[i].index;
11260 }
11261
11262 // infer 'type' of local refinement from first element's 'ref_type'
11263 int type, rt = (refinements.Size() ? refinements[0].GetType() : 7);
11264 if (rt == 1 || rt == 2 || rt == 4)
11265 {
11266 type = 1; // bisection
11267 }
11268 else if (rt == 3 || rt == 5 || rt == 6)
11269 {
11270 type = 2; // quadrisection
11271 }
11272 else
11273 {
11274 type = 3; // octasection
11275 }
11276
11277 // red-green refinement and bisection, no hanging nodes
11278 LocalRefinement(el_to_refine, type);
11279 }
11280}
11281
11282void Mesh::GeneralRefinement(const Array<int> &el_to_refine, int nonconforming,
11283 int nc_limit)
11284{
11285 Array<Refinement> refinements(el_to_refine.Size());
11286 for (int i = 0; i < el_to_refine.Size(); i++)
11287 {
11288 refinements[i] = Refinement(el_to_refine[i]);
11289 }
11290 GeneralRefinement(refinements, nonconforming, nc_limit);
11291}
11292
11293void Mesh::EnsureNCMesh(bool simplices_nonconforming)
11294{
11295 MFEM_VERIFY(!NURBSext, "Cannot convert a NURBS mesh to an NC mesh. "
11296 "Please project the NURBS to Nodes first, with SetCurvature().");
11297
11298#ifdef MFEM_USE_MPI
11299 MFEM_VERIFY(ncmesh != NULL || dynamic_cast<const ParMesh*>(this) == NULL,
11300 "Sorry, converting a conforming ParMesh to an NC mesh is "
11301 "not possible.");
11302#endif
11303
11304 if (!ncmesh)
11305 {
11306 if ((meshgen & 0x2) /* quads/hexes */ ||
11307 (meshgen & 0x4) /* wedges */ ||
11308 (simplices_nonconforming && (meshgen & 0x1)) /* simplices */)
11309 {
11310 ncmesh = new NCMesh(this);
11311 ncmesh->OnMeshUpdated(this);
11313 }
11314 }
11315}
11316
11317void Mesh::RandomRefinement(real_t prob, bool aniso, int nonconforming,
11318 int nc_limit)
11319{
11320 Array<Refinement> refs;
11321 for (int i = 0; i < GetNE(); i++)
11322 {
11323 if ((real_t) rand() / real_t(RAND_MAX) < prob)
11324 {
11325 int type = 7;
11326 if (aniso)
11327 {
11328 type = (Dim == 3) ? (rand() % 7 + 1) : (rand() % 3 + 1);
11329 }
11330 refs.Append(Refinement(i, type));
11331 }
11332 }
11333 GeneralRefinement(refs, nonconforming, nc_limit);
11334}
11335
11336void Mesh::RefineAtVertex(const Vertex& vert, real_t eps, int nonconforming)
11337{
11338 Array<int> v;
11339 Array<Refinement> refs;
11340 for (int i = 0; i < GetNE(); i++)
11341 {
11342 GetElementVertices(i, v);
11343 bool refine = false;
11344 for (int j = 0; j < v.Size(); j++)
11345 {
11346 real_t dist = 0.0;
11347 for (int l = 0; l < spaceDim; l++)
11348 {
11349 real_t d = vert(l) - vertices[v[j]](l);
11350 dist += d*d;
11351 }
11352 if (dist <= eps*eps) { refine = true; break; }
11353 }
11354 if (refine)
11355 {
11356 refs.Append(Refinement(i));
11357 }
11358 }
11359 GeneralRefinement(refs, nonconforming);
11360}
11361
11362bool Mesh::RefineByError(const Array<real_t> &elem_error, real_t threshold,
11363 int nonconforming, int nc_limit)
11364{
11365 MFEM_VERIFY(elem_error.Size() == GetNE(), "");
11366 Array<Refinement> refs;
11367 for (int i = 0; i < GetNE(); i++)
11368 {
11369 if (elem_error[i] > threshold)
11370 {
11371 refs.Append(Refinement(i));
11372 }
11373 }
11374 if (ReduceInt(refs.Size()))
11375 {
11376 GeneralRefinement(refs, nonconforming, nc_limit);
11377 return true;
11378 }
11379 return false;
11380}
11381
11382bool Mesh::RefineByError(const Vector &elem_error, real_t threshold,
11383 int nonconforming, int nc_limit)
11384{
11385 Array<real_t> tmp(const_cast<real_t*>(elem_error.GetData()),
11386 elem_error.Size());
11387 return RefineByError(tmp, threshold, nonconforming, nc_limit);
11388}
11389
11390
11391void Mesh::Bisection(int i, const DSTable &v_to_v,
11392 int *edge1, int *edge2, int *middle)
11393{
11394 int *vert;
11395 int v[2][4], v_new, bisect, t;
11396 Element *el = elements[i];
11397 Vertex V;
11398
11399 t = el->GetType();
11400 if (t == Element::TRIANGLE)
11401 {
11402 Triangle *tri = (Triangle *) el;
11403
11404 vert = tri->GetVertices();
11405
11406 // 1. Get the index for the new vertex in v_new.
11407 bisect = v_to_v(vert[0], vert[1]);
11408 MFEM_ASSERT(bisect >= 0, "");
11409
11410 if (middle[bisect] == -1)
11411 {
11412 v_new = NumOfVertices++;
11413 for (int d = 0; d < spaceDim; d++)
11414 {
11415 V(d) = 0.5 * (vertices[vert[0]](d) + vertices[vert[1]](d));
11416 }
11417 vertices.Append(V);
11418
11419 // Put the element that may need refinement (because of this
11420 // bisection) in edge1, or -1 if no more refinement is needed.
11421 if (edge1[bisect] == i)
11422 {
11423 edge1[bisect] = edge2[bisect];
11424 }
11425
11426 middle[bisect] = v_new;
11427 }
11428 else
11429 {
11430 v_new = middle[bisect];
11431
11432 // This edge will require no more refinement.
11433 edge1[bisect] = -1;
11434 }
11435
11436 // 2. Set the node indices for the new elements in v[0] and v[1] so that
11437 // the edge marked for refinement is between the first two nodes.
11438 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
11439 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
11440
11441 tri->SetVertices(v[0]); // changes vert[0..2] !!!
11442
11443 Triangle* tri_new = new Triangle(v[1], tri->GetAttribute());
11444 elements.Append(tri_new);
11445
11446 int tr = tri->GetTransform();
11447 tri_new->ResetTransform(tr);
11448
11449 // record the sequence of refinements
11450 tri->PushTransform(4);
11451 tri_new->PushTransform(5);
11452
11453 int coarse = FindCoarseElement(i);
11454 CoarseFineTr.embeddings[i].parent = coarse;
11456
11457 // 3. edge1 and edge2 may have to be changed for the second triangle.
11458 if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
11459 {
11460 bisect = v_to_v(v[1][0], v[1][1]);
11461 MFEM_ASSERT(bisect >= 0, "");
11462
11463 if (edge1[bisect] == i)
11464 {
11465 edge1[bisect] = NumOfElements;
11466 }
11467 else if (edge2[bisect] == i)
11468 {
11469 edge2[bisect] = NumOfElements;
11470 }
11471 }
11472 NumOfElements++;
11473 }
11474 else
11475 {
11476 MFEM_ABORT("Bisection for now works only for triangles.");
11477 }
11478}
11479
11481{
11482 int *vert;
11483 int v[2][4], v_new, bisect, t;
11484 Element *el = elements[i];
11485 Vertex V;
11486
11487 t = el->GetType();
11488 if (t == Element::TETRAHEDRON)
11489 {
11490 Tetrahedron *tet = (Tetrahedron *) el;
11491
11492 MFEM_VERIFY(tet->GetRefinementFlag() != 0,
11493 "TETRAHEDRON element is not marked for refinement.");
11494
11495 vert = tet->GetVertices();
11496
11497 // 1. Get the index for the new vertex in v_new.
11498 bisect = v_to_v.FindId(vert[0], vert[1]);
11499 if (bisect == -1)
11500 {
11501 v_new = NumOfVertices + v_to_v.GetId(vert[0],vert[1]);
11502 for (int j = 0; j < 3; j++)
11503 {
11504 V(j) = 0.5 * (vertices[vert[0]](j) + vertices[vert[1]](j));
11505 }
11506 vertices.Append(V);
11507 }
11508 else
11509 {
11510 v_new = NumOfVertices + bisect;
11511 }
11512
11513 // 2. Set the node indices for the new elements in v[2][4] so that
11514 // the edge marked for refinement is between the first two nodes.
11515 int type, old_redges[2], flag;
11516 tet->ParseRefinementFlag(old_redges, type, flag);
11517
11518 int new_type, new_redges[2][2];
11519 v[0][3] = v_new;
11520 v[1][3] = v_new;
11521 new_redges[0][0] = 2;
11522 new_redges[0][1] = 1;
11523 new_redges[1][0] = 2;
11524 new_redges[1][1] = 1;
11525 int tr1 = -1, tr2 = -1;
11526 switch (old_redges[0])
11527 {
11528 case 2:
11529 v[0][0] = vert[0]; v[0][1] = vert[2]; v[0][2] = vert[3];
11530 if (type == Tetrahedron::TYPE_PF) { new_redges[0][1] = 4; }
11531 tr1 = 0;
11532 break;
11533 case 3:
11534 v[0][0] = vert[3]; v[0][1] = vert[0]; v[0][2] = vert[2];
11535 tr1 = 2;
11536 break;
11537 case 5:
11538 v[0][0] = vert[2]; v[0][1] = vert[3]; v[0][2] = vert[0];
11539 tr1 = 4;
11540 }
11541 switch (old_redges[1])
11542 {
11543 case 1:
11544 v[1][0] = vert[2]; v[1][1] = vert[1]; v[1][2] = vert[3];
11545 if (type == Tetrahedron::TYPE_PF) { new_redges[1][0] = 3; }
11546 tr2 = 1;
11547 break;
11548 case 4:
11549 v[1][0] = vert[1]; v[1][1] = vert[3]; v[1][2] = vert[2];
11550 tr2 = 3;
11551 break;
11552 case 5:
11553 v[1][0] = vert[3]; v[1][1] = vert[2]; v[1][2] = vert[1];
11554 tr2 = 5;
11555 }
11556
11557 int attr = tet->GetAttribute();
11558 tet->SetVertices(v[0]);
11559
11560#ifdef MFEM_USE_MEMALLOC
11561 Tetrahedron *tet2 = TetMemory.Alloc();
11562 tet2->SetVertices(v[1]);
11563 tet2->SetAttribute(attr);
11564#else
11565 Tetrahedron *tet2 = new Tetrahedron(v[1], attr);
11566#endif
11567 tet2->ResetTransform(tet->GetTransform());
11568 elements.Append(tet2);
11569
11570 // record the sequence of refinements
11571 tet->PushTransform(tr1);
11572 tet2->PushTransform(tr2);
11573
11574 int coarse = FindCoarseElement(i);
11575 CoarseFineTr.embeddings[i].parent = coarse;
11577
11578 // 3. Set the bisection flag
11579 switch (type)
11580 {
11582 new_type = Tetrahedron::TYPE_PF; break;
11584 new_type = Tetrahedron::TYPE_A; break;
11585 default:
11586 new_type = Tetrahedron::TYPE_PU;
11587 }
11588
11589 tet->CreateRefinementFlag(new_redges[0], new_type, flag+1);
11590 tet2->CreateRefinementFlag(new_redges[1], new_type, flag+1);
11591
11592 NumOfElements++;
11593 }
11594 else
11595 {
11596 MFEM_ABORT("Bisection with HashTable for now works only for tetrahedra.");
11597 }
11598}
11599
11600void Mesh::BdrBisection(int i, const HashTable<Hashed2> &v_to_v)
11601{
11602 int *vert;
11603 int v[2][3], v_new, bisect, t;
11604 Element *bdr_el = boundary[i];
11605
11606 t = bdr_el->GetType();
11607 if (t == Element::TRIANGLE)
11608 {
11609 Triangle *tri = (Triangle *) bdr_el;
11610
11611 vert = tri->GetVertices();
11612
11613 // 1. Get the index for the new vertex in v_new.
11614 bisect = v_to_v.FindId(vert[0], vert[1]);
11615 MFEM_ASSERT(bisect >= 0, "");
11616 v_new = NumOfVertices + bisect;
11617 MFEM_ASSERT(v_new != -1, "");
11618
11619 // 2. Set the node indices for the new elements in v[0] and v[1] so that
11620 // the edge marked for refinement is between the first two nodes.
11621 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
11622 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
11623
11624 tri->SetVertices(v[0]);
11625
11626 boundary.Append(new Triangle(v[1], tri->GetAttribute()));
11627
11629 }
11630 else
11631 {
11632 MFEM_ABORT("Bisection of boundary elements with HashTable works only for"
11633 " triangles!");
11634 }
11635}
11636
11637void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
11638 int *edge1, int *edge2, int *middle)
11639{
11640 Array<int> v;
11641 int j, v1[3], v2[3], v3[3], v4[3], v_new[3], bisect[3];
11642 Vertex V;
11643
11644 if (elements[i]->GetType() == Element::TRIANGLE)
11645 {
11646 Triangle *tri0 = (Triangle*) elements[i];
11647 tri0->GetVertices(v);
11648
11649 // 1. Get the indices for the new vertices in array v_new
11650 bisect[0] = v_to_v(v[0],v[1]);
11651 bisect[1] = v_to_v(v[1],v[2]);
11652 bisect[2] = v_to_v(v[0],v[2]);
11653 MFEM_ASSERT(bisect[0] >= 0 && bisect[1] >= 0 && bisect[2] >= 0, "");
11654
11655 for (j = 0; j < 3; j++) // for the 3 edges fix v_new
11656 {
11657 if (middle[bisect[j]] == -1)
11658 {
11659 v_new[j] = NumOfVertices++;
11660 for (int d = 0; d < spaceDim; d++)
11661 {
11662 V(d) = (vertices[v[j]](d) + vertices[v[(j+1)%3]](d))/2.;
11663 }
11664 vertices.Append(V);
11665
11666 // Put the element that may need refinement (because of this
11667 // bisection) in edge1, or -1 if no more refinement is needed.
11668 if (edge1[bisect[j]] == i)
11669 {
11670 edge1[bisect[j]] = edge2[bisect[j]];
11671 }
11672
11673 middle[bisect[j]] = v_new[j];
11674 }
11675 else
11676 {
11677 v_new[j] = middle[bisect[j]];
11678
11679 // This edge will require no more refinement.
11680 edge1[bisect[j]] = -1;
11681 }
11682 }
11683
11684 // 2. Set the node indices for the new elements in v1, v2, v3 & v4 so that
11685 // the edges marked for refinement be between the first two nodes.
11686 v1[0] = v[0]; v1[1] = v_new[0]; v1[2] = v_new[2];
11687 v2[0] = v_new[0]; v2[1] = v[1]; v2[2] = v_new[1];
11688 v3[0] = v_new[2]; v3[1] = v_new[1]; v3[2] = v[2];
11689 v4[0] = v_new[1]; v4[1] = v_new[2]; v4[2] = v_new[0];
11690
11691 Triangle* tri1 = new Triangle(v1, tri0->GetAttribute());
11692 Triangle* tri2 = new Triangle(v2, tri0->GetAttribute());
11693 Triangle* tri3 = new Triangle(v3, tri0->GetAttribute());
11694
11695 elements.Append(tri1);
11696 elements.Append(tri2);
11697 elements.Append(tri3);
11698
11699 tri0->SetVertices(v4);
11700
11701 // record the sequence of refinements
11702 unsigned code = tri0->GetTransform();
11703 tri1->ResetTransform(code);
11704 tri2->ResetTransform(code);
11705 tri3->ResetTransform(code);
11706
11707 tri0->PushTransform(3);
11708 tri1->PushTransform(0);
11709 tri2->PushTransform(1);
11710 tri3->PushTransform(2);
11711
11712 // set parent indices
11713 int coarse = FindCoarseElement(i);
11718
11719 NumOfElements += 3;
11720 }
11721 else
11722 {
11723 MFEM_ABORT("Uniform refinement for now works only for triangles.");
11724 }
11725}
11726
11728{
11729 // initialize CoarseFineTr
11732 for (int i = 0; i < NumOfElements; i++)
11733 {
11734 elements[i]->ResetTransform(0);
11736 }
11737}
11738
11740{
11741 int coarse;
11742 while ((coarse = CoarseFineTr.embeddings[i].parent) != i)
11743 {
11744 i = coarse;
11745 }
11746 return coarse;
11747}
11748
11750{
11751 MFEM_VERIFY(GetLastOperation() == Mesh::REFINE, "");
11752
11753 if (ncmesh)
11754 {
11756 }
11757
11758 Mesh::GeometryList elem_geoms(*this);
11759 for (int i = 0; i < elem_geoms.Size(); i++)
11760 {
11761 const Geometry::Type geom = elem_geoms[i];
11762 if (CoarseFineTr.point_matrices[geom].SizeK()) { continue; }
11763
11764 if (geom == Geometry::TRIANGLE ||
11765 geom == Geometry::TETRAHEDRON)
11766 {
11767 std::map<unsigned, int> mat_no;
11768 mat_no[0] = 1; // identity
11769
11770 // assign matrix indices to element transformations
11771 for (int j = 0; j < elements.Size(); j++)
11772 {
11773 int index = 0;
11774 unsigned code = elements[j]->GetTransform();
11775 if (code)
11776 {
11777 int &matrix = mat_no[code];
11778 if (!matrix) { matrix = static_cast<int>(mat_no.size()); }
11779 index = matrix-1;
11780 }
11781 CoarseFineTr.embeddings[j].matrix = index;
11782 }
11783
11785 pmats.SetSize(Dim, Dim+1, static_cast<int>((mat_no.size())));
11786
11787 // calculate the point matrices used
11788 std::map<unsigned, int>::iterator it;
11789 for (it = mat_no.begin(); it != mat_no.end(); ++it)
11790 {
11791 if (geom == Geometry::TRIANGLE)
11792 {
11793 Triangle::GetPointMatrix(it->first, pmats(it->second-1));
11794 }
11795 else
11796 {
11797 Tetrahedron::GetPointMatrix(it->first, pmats(it->second-1));
11798 }
11799 }
11800 }
11801 else
11802 {
11803 MFEM_ABORT("Don't know how to construct CoarseFineTransformations for"
11804 " geom = " << geom);
11805 }
11806 }
11807
11808 // NOTE: quads and hexes already have trivial transformations ready
11809 return CoarseFineTr;
11810}
11811
11812void Mesh::PrintXG(std::ostream &os) const
11813{
11814 MFEM_ASSERT(Dim==spaceDim, "2D Manifold meshes not supported");
11815 int i, j;
11816 Array<int> v;
11817
11818 if (Dim == 2)
11819 {
11820 // Print the type of the mesh.
11821 if (Nodes == NULL)
11822 {
11823 os << "areamesh2\n\n";
11824 }
11825 else
11826 {
11827 os << "curved_areamesh2\n\n";
11828 }
11829
11830 // Print the boundary elements.
11831 os << NumOfBdrElements << '\n';
11832 for (i = 0; i < NumOfBdrElements; i++)
11833 {
11834 boundary[i]->GetVertices(v);
11835
11836 os << boundary[i]->GetAttribute();
11837 for (j = 0; j < v.Size(); j++)
11838 {
11839 os << ' ' << v[j] + 1;
11840 }
11841 os << '\n';
11842 }
11843
11844 // Print the elements.
11845 os << NumOfElements << '\n';
11846 for (i = 0; i < NumOfElements; i++)
11847 {
11848 elements[i]->GetVertices(v);
11849
11850 os << elements[i]->GetAttribute() << ' ' << v.Size();
11851 for (j = 0; j < v.Size(); j++)
11852 {
11853 os << ' ' << v[j] + 1;
11854 }
11855 os << '\n';
11856 }
11857
11858 if (Nodes == NULL)
11859 {
11860 // Print the vertices.
11861 os << NumOfVertices << '\n';
11862 for (i = 0; i < NumOfVertices; i++)
11863 {
11864 os << vertices[i](0);
11865 for (j = 1; j < Dim; j++)
11866 {
11867 os << ' ' << vertices[i](j);
11868 }
11869 os << '\n';
11870 }
11871 }
11872 else
11873 {
11874 os << NumOfVertices << '\n';
11875 Nodes->Save(os);
11876 }
11877 }
11878 else // ===== Dim != 2 =====
11879 {
11880 if (Nodes)
11881 {
11882 mfem_error("Mesh::PrintXG(...) : Curved mesh in 3D");
11883 }
11884
11885 if (meshgen == 1)
11886 {
11887 int nv;
11888 const int *ind;
11889
11890 os << "NETGEN_Neutral_Format\n";
11891 // print the vertices
11892 os << NumOfVertices << '\n';
11893 for (i = 0; i < NumOfVertices; i++)
11894 {
11895 for (j = 0; j < Dim; j++)
11896 {
11897 os << ' ' << vertices[i](j);
11898 }
11899 os << '\n';
11900 }
11901
11902 // print the elements
11903 os << NumOfElements << '\n';
11904 for (i = 0; i < NumOfElements; i++)
11905 {
11906 nv = elements[i]->GetNVertices();
11907 ind = elements[i]->GetVertices();
11908 os << elements[i]->GetAttribute();
11909 for (j = 0; j < nv; j++)
11910 {
11911 os << ' ' << ind[j]+1;
11912 }
11913 os << '\n';
11914 }
11915
11916 // print the boundary information.
11917 os << NumOfBdrElements << '\n';
11918 for (i = 0; i < NumOfBdrElements; i++)
11919 {
11920 nv = boundary[i]->GetNVertices();
11921 ind = boundary[i]->GetVertices();
11922 os << boundary[i]->GetAttribute();
11923 for (j = 0; j < nv; j++)
11924 {
11925 os << ' ' << ind[j]+1;
11926 }
11927 os << '\n';
11928 }
11929 }
11930 else if (meshgen == 2) // TrueGrid
11931 {
11932 int nv;
11933 const int *ind;
11934
11935 os << "TrueGrid\n"
11936 << "1 " << NumOfVertices << " " << NumOfElements
11937 << " 0 0 0 0 0 0 0\n"
11938 << "0 0 0 1 0 0 0 0 0 0 0\n"
11939 << "0 0 " << NumOfBdrElements << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
11940 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
11941 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
11942
11943 for (i = 0; i < NumOfVertices; i++)
11944 os << i+1 << " 0.0 " << vertices[i](0) << ' ' << vertices[i](1)
11945 << ' ' << vertices[i](2) << " 0.0\n";
11946
11947 for (i = 0; i < NumOfElements; i++)
11948 {
11949 nv = elements[i]->GetNVertices();
11950 ind = elements[i]->GetVertices();
11951 os << i+1 << ' ' << elements[i]->GetAttribute();
11952 for (j = 0; j < nv; j++)
11953 {
11954 os << ' ' << ind[j]+1;
11955 }
11956 os << '\n';
11957 }
11958
11959 for (i = 0; i < NumOfBdrElements; i++)
11960 {
11961 nv = boundary[i]->GetNVertices();
11962 ind = boundary[i]->GetVertices();
11963 os << boundary[i]->GetAttribute();
11964 for (j = 0; j < nv; j++)
11965 {
11966 os << ' ' << ind[j]+1;
11967 }
11968 os << " 1.0 1.0 1.0 1.0\n";
11969 }
11970 }
11971 }
11972
11973 os << flush;
11974}
11975
11976void Mesh::Printer(std::ostream &os, std::string section_delimiter,
11977 const std::string &comments) const
11978{
11979 int i, j;
11980
11981 if (NURBSext)
11982 {
11983 // general format
11984 NURBSext->Print(os, comments);
11985 os << '\n';
11986 Nodes->Save(os);
11987
11989 // patch-wise format
11990 // NURBSext->ConvertToPatches(*Nodes);
11991 // NURBSext->Print(os);
11992
11993 return;
11994 }
11995
11996 if (Nonconforming())
11997 {
11998 // Workaround for inconsistent Mesh state where the Mesh has nodes and
11999 // ncmesh->coodrinates is not empty. Such state can be created with the
12000 // method Mesh::SwapNodes(), see the comment at the beginning of its
12001 // implementation.
12002 Array<real_t> coords_save;
12003 if (Nodes) { mfem::Swap(coords_save, ncmesh->coordinates); }
12004
12005 // nonconforming mesh format
12006 ncmesh->Print(os, comments);
12007
12008 if (Nodes)
12009 {
12010 mfem::Swap(coords_save, ncmesh->coordinates);
12011
12012 os << "\n# mesh curvature GridFunction";
12013 os << "\nnodes\n";
12014 Nodes->Save(os);
12015 }
12016
12017 os << "\nmfem_mesh_end" << endl;
12018 return;
12019 }
12020
12021 // serial/parallel conforming mesh format
12022 const bool set_names = attribute_sets.SetsExist() ||
12024 os << (!set_names && section_delimiter.empty()
12025 ? "MFEM mesh v1.0\n" :
12026 (!set_names ? "MFEM mesh v1.2\n" : "MFEM mesh v1.3\n"));
12027
12028 if (set_names && section_delimiter.empty())
12029 {
12030 section_delimiter = "mfem_mesh_end";
12031 }
12032
12033 // optional
12034 if (!comments.empty()) { os << '\n' << comments << '\n'; }
12035
12036 os <<
12037 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
12038 "# POINT = 0\n"
12039 "# SEGMENT = 1\n"
12040 "# TRIANGLE = 2\n"
12041 "# SQUARE = 3\n"
12042 "# TETRAHEDRON = 4\n"
12043 "# CUBE = 5\n"
12044 "# PRISM = 6\n"
12045 "# PYRAMID = 7\n"
12046 "#\n";
12047
12048 os << "\ndimension\n" << Dim;
12049
12050 os << "\n\nelements\n" << NumOfElements << '\n';
12051 for (i = 0; i < NumOfElements; i++)
12052 {
12053 PrintElement(elements[i], os);
12054 }
12055
12056 if (set_names)
12057 {
12058 os << "\nattribute_sets\n";
12060 }
12061
12062 os << "\nboundary\n" << NumOfBdrElements << '\n';
12063 for (i = 0; i < NumOfBdrElements; i++)
12064 {
12065 PrintElement(boundary[i], os);
12066 }
12067
12068 if (set_names)
12069 {
12070 os << "\nbdr_attribute_sets\n";
12072 }
12073
12074 os << "\nvertices\n" << NumOfVertices << '\n';
12075 if (Nodes == NULL)
12076 {
12077 os << spaceDim << '\n';
12078 for (i = 0; i < NumOfVertices; i++)
12079 {
12080 os << vertices[i](0);
12081 for (j = 1; j < spaceDim; j++)
12082 {
12083 os << ' ' << vertices[i](j);
12084 }
12085 os << '\n';
12086 }
12087 os.flush();
12088 }
12089 else
12090 {
12091 os << "\nnodes\n";
12092 Nodes->Save(os);
12093 }
12094
12095 if (!section_delimiter.empty())
12096 {
12097 os << '\n'
12098 << section_delimiter << endl; // only with formats v1.2 and above
12099 }
12100}
12101
12102void Mesh::PrintTopo(std::ostream &os, const Array<int> &e_to_k,
12103 const int version, const std::string &comments) const
12104{
12105 MFEM_VERIFY(version == 10 || version == 11, "Invalid NURBS mesh version");
12106
12107 int i;
12108 Array<int> vert;
12109
12110 os << "MFEM NURBS mesh v" << int(version / 10) << "." << version % 10 << "\n";
12111
12112 // optional
12113 if (!comments.empty()) { os << '\n' << comments << '\n'; }
12114
12115 os <<
12116 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
12117 "# SEGMENT = 1\n"
12118 "# SQUARE = 3\n"
12119 "# CUBE = 5\n"
12120 "#\n";
12121
12122 os << "\ndimension\n" << Dim
12123 << "\n\nelements\n" << NumOfElements << '\n';
12124 for (i = 0; i < NumOfElements; i++)
12125 {
12126 PrintElement(elements[i], os);
12127 }
12128
12129 os << "\nboundary\n" << NumOfBdrElements << '\n';
12130 for (i = 0; i < NumOfBdrElements; i++)
12131 {
12132 PrintElement(boundary[i], os);
12133 }
12134
12135 PrintTopoEdges(os, e_to_k);
12136}
12137
12138void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
12139 bool vmap) const
12140{
12141 Array<int> vert;
12142
12143 os << "\nedges\n" << NumOfEdges << '\n';
12144 for (int i = 0; i < NumOfEdges; i++)
12145 {
12146 edge_vertex->GetRow(i, vert);
12147 int ki = e_to_k[i];
12148 if (ki < 0)
12149 {
12150 ki = -1 - ki;
12151 }
12152
12153 if (vmap)
12154 {
12155 for (int j=0; j<2; ++j)
12156 {
12157 vert[j] = ncmesh->vertex_nodeId[vert[j]];
12158 }
12159
12160 if (e_to_k[i] < 0)
12161 {
12162 // Swap the entries of vert
12163 const int s = vert[0];
12164 vert[0] = vert[1];
12165 vert[1] = s;
12166 }
12167 }
12168
12169 os << ki << ' ' << vert[0] << ' ' << vert[1] << '\n';
12170 }
12171
12172 if (!vmap)
12173 {
12174 os << "\nvertices\n" << NumOfVertices << '\n';
12175 }
12176}
12177
12178void Mesh::Save(const std::string &fname, int precision) const
12179{
12180 ofstream ofs(fname);
12181 ofs.precision(precision);
12182 Print(ofs);
12183}
12184
12185#ifdef MFEM_USE_ADIOS2
12187{
12188 os.Print(*this);
12189}
12190#endif
12191
12192void Mesh::PrintVTK(std::ostream &os)
12193{
12194 os <<
12195 "# vtk DataFile Version 3.0\n"
12196 "Generated by MFEM\n"
12197 "ASCII\n"
12198 "DATASET UNSTRUCTURED_GRID\n";
12199
12200 if (Nodes == NULL)
12201 {
12202 os << "POINTS " << NumOfVertices << " double\n";
12203 for (int i = 0; i < NumOfVertices; i++)
12204 {
12205 os << vertices[i](0);
12206 int j;
12207 for (j = 1; j < spaceDim; j++)
12208 {
12209 os << ' ' << vertices[i](j);
12210 }
12211 for ( ; j < 3; j++)
12212 {
12213 os << ' ' << 0.0;
12214 }
12215 os << '\n';
12216 }
12217 }
12218 else
12219 {
12220 Array<int> vdofs(3);
12221 os << "POINTS " << Nodes->FESpace()->GetNDofs() << " double\n";
12222 for (int i = 0; i < Nodes->FESpace()->GetNDofs(); i++)
12223 {
12224 vdofs.SetSize(1);
12225 vdofs[0] = i;
12226 Nodes->FESpace()->DofsToVDofs(vdofs);
12227 os << (*Nodes)(vdofs[0]);
12228 int j;
12229 for (j = 1; j < spaceDim; j++)
12230 {
12231 os << ' ' << (*Nodes)(vdofs[j]);
12232 }
12233 for ( ; j < 3; j++)
12234 {
12235 os << ' ' << 0.0;
12236 }
12237 os << '\n';
12238 }
12239 }
12240
12241 int order = -1;
12242 if (Nodes == NULL)
12243 {
12244 int size = 0;
12245 for (int i = 0; i < NumOfElements; i++)
12246 {
12247 size += elements[i]->GetNVertices() + 1;
12248 }
12249 os << "CELLS " << NumOfElements << ' ' << size << '\n';
12250 for (int i = 0; i < NumOfElements; i++)
12251 {
12252 const int *v = elements[i]->GetVertices();
12253 const int nv = elements[i]->GetNVertices();
12254 os << nv;
12255 Geometry::Type geom = elements[i]->GetGeometryType();
12256 const int *perm = VTKGeometry::VertexPermutation[geom];
12257 for (int j = 0; j < nv; j++)
12258 {
12259 os << ' ' << v[perm ? perm[j] : j];
12260 }
12261 os << '\n';
12262 }
12263 order = 1;
12264 }
12265 else
12266 {
12267 Array<int> dofs;
12268 int size = 0;
12269 for (int i = 0; i < NumOfElements; i++)
12270 {
12271 Nodes->FESpace()->GetElementDofs(i, dofs);
12272 MFEM_ASSERT(Dim != 0 || dofs.Size() == 1,
12273 "Point meshes should have a single dof per element");
12274 size += dofs.Size() + 1;
12275 }
12276 os << "CELLS " << NumOfElements << ' ' << size << '\n';
12277 const char *fec_name = Nodes->FESpace()->FEColl()->Name();
12278
12279 if (!strcmp(fec_name, "Linear") ||
12280 !strcmp(fec_name, "H1_0D_P1") ||
12281 !strcmp(fec_name, "H1_1D_P1") ||
12282 !strcmp(fec_name, "H1_2D_P1") ||
12283 !strcmp(fec_name, "H1_3D_P1"))
12284 {
12285 order = 1;
12286 }
12287 else if (!strcmp(fec_name, "Quadratic") ||
12288 !strcmp(fec_name, "H1_1D_P2") ||
12289 !strcmp(fec_name, "H1_2D_P2") ||
12290 !strcmp(fec_name, "H1_3D_P2"))
12291 {
12292 order = 2;
12293 }
12294 if (order == -1)
12295 {
12296 mfem::err << "Mesh::PrintVTK : can not save '"
12297 << fec_name << "' elements!" << endl;
12298 mfem_error();
12299 }
12300 for (int i = 0; i < NumOfElements; i++)
12301 {
12302 Nodes->FESpace()->GetElementDofs(i, dofs);
12303 os << dofs.Size();
12304 if (order == 1)
12305 {
12306 for (int j = 0; j < dofs.Size(); j++)
12307 {
12308 os << ' ' << dofs[j];
12309 }
12310 }
12311 else if (order == 2)
12312 {
12313 const int *vtk_mfem;
12314 switch (elements[i]->GetGeometryType())
12315 {
12316 case Geometry::SEGMENT:
12317 case Geometry::TRIANGLE:
12318 case Geometry::SQUARE:
12319 vtk_mfem = vtk_quadratic_hex; break; // identity map
12321 vtk_mfem = vtk_quadratic_tet; break;
12322 case Geometry::PRISM:
12323 vtk_mfem = vtk_quadratic_wedge; break;
12324 case Geometry::CUBE:
12325 default:
12326 vtk_mfem = vtk_quadratic_hex; break;
12327 }
12328 for (int j = 0; j < dofs.Size(); j++)
12329 {
12330 os << ' ' << dofs[vtk_mfem[j]];
12331 }
12332 }
12333 os << '\n';
12334 }
12335 }
12336
12337 os << "CELL_TYPES " << NumOfElements << '\n';
12338 for (int i = 0; i < NumOfElements; i++)
12339 {
12340 int vtk_cell_type = 5;
12342 if (order == 1) { vtk_cell_type = VTKGeometry::Map[geom]; }
12343 else if (order == 2) { vtk_cell_type = VTKGeometry::QuadraticMap[geom]; }
12344 os << vtk_cell_type << '\n';
12345 }
12346
12347 // write attributes
12348 os << "CELL_DATA " << NumOfElements << '\n'
12349 << "SCALARS material int\n"
12350 << "LOOKUP_TABLE default\n";
12351 for (int i = 0; i < NumOfElements; i++)
12352 {
12353 os << elements[i]->GetAttribute() << '\n';
12354 }
12355 os.flush();
12356}
12357
12358void Mesh::PrintVTU(std::string fname,
12359 VTKFormat format,
12360 bool high_order_output,
12361 int compression_level,
12362 bool bdr_elements)
12363{
12364 int ref = (high_order_output && Nodes)
12365 ? Nodes->FESpace()->GetMaxElementOrder() : 1;
12366
12367 fname = fname + ".vtu";
12368 std::fstream os(fname.c_str(),std::ios::out);
12369 os << "<VTKFile type=\"UnstructuredGrid\" version=\"2.2\"";
12370 if (compression_level != 0)
12371 {
12372 os << " compressor=\"vtkZLibDataCompressor\"";
12373 }
12374 os << " byte_order=\"" << VTKByteOrder() << "\">\n";
12375 os << "<UnstructuredGrid>\n";
12376 PrintVTU(os, ref, format, high_order_output, compression_level, bdr_elements);
12377 os << "</Piece>\n"; // need to close the piece open in the PrintVTU method
12378 os << "</UnstructuredGrid>\n";
12379 os << "</VTKFile>" << std::endl;
12380
12381 os.close();
12382}
12383
12384void Mesh::PrintBdrVTU(std::string fname,
12385 VTKFormat format,
12386 bool high_order_output,
12387 int compression_level)
12388{
12389 PrintVTU(fname, format, high_order_output, compression_level, true);
12390}
12391
12392void Mesh::PrintVTU(std::ostream &os, int ref, VTKFormat format,
12393 bool high_order_output, int compression_level,
12394 bool bdr_elements)
12395{
12396 RefinedGeometry *RefG;
12397 DenseMatrix pmat;
12398
12399 const char *fmt_str = (format == VTKFormat::ASCII) ? "ascii" : "binary";
12400 const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
12401 std::vector<char> buf;
12402
12403 auto get_geom = [&](int i)
12404 {
12405 if (bdr_elements) { return GetBdrElementGeometry(i); }
12406 else { return GetElementBaseGeometry(i); }
12407 };
12408
12409 int ne = bdr_elements ? GetNBE() : GetNE();
12410 // count the number of points and cells
12411 int np = 0, nc_ref = 0;
12412 for (int i = 0; i < ne; i++)
12413 {
12414 Geometry::Type geom = get_geom(i);
12415 int nv = Geometries.GetVertices(geom)->GetNPoints();
12416 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12417 np += RefG->RefPts.GetNPoints();
12418 nc_ref += RefG->RefGeoms.Size() / nv;
12419 }
12420
12421 os << "<Piece NumberOfPoints=\"" << np << "\" NumberOfCells=\""
12422 << (high_order_output ? ne : nc_ref) << "\">\n";
12423
12424 // print out the points
12425 os << "<Points>\n";
12426 os << "<DataArray type=\"" << type_str
12427 << "\" NumberOfComponents=\"3\" format=\"" << fmt_str << "\">\n";
12428 for (int i = 0; i < ne; i++)
12429 {
12430 RefG = GlobGeometryRefiner.Refine(get_geom(i), ref, 1);
12431
12432 if (bdr_elements)
12433 {
12435 }
12436 else
12437 {
12438 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
12439 }
12440
12441 for (int j = 0; j < pmat.Width(); j++)
12442 {
12443 WriteBinaryOrASCII(os, buf, pmat(0,j), " ", format);
12444 if (pmat.Height() > 1)
12445 {
12446 WriteBinaryOrASCII(os, buf, pmat(1,j), " ", format);
12447 }
12448 else
12449 {
12450 WriteBinaryOrASCII(os, buf, 0.0, " ", format);
12451 }
12452 if (pmat.Height() > 2)
12453 {
12454 WriteBinaryOrASCII(os, buf, pmat(2,j), "", format);
12455 }
12456 else
12457 {
12458 WriteBinaryOrASCII(os, buf, 0.0, "", format);
12459 }
12460 if (format == VTKFormat::ASCII) { os << '\n'; }
12461 }
12462 }
12463 if (format != VTKFormat::ASCII)
12464 {
12465 WriteBase64WithSizeAndClear(os, buf, compression_level);
12466 }
12467 os << "</DataArray>" << std::endl;
12468 os << "</Points>" << std::endl;
12469
12470 os << "<Cells>" << std::endl;
12471 os << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\""
12472 << fmt_str << "\">" << std::endl;
12473 // connectivity
12474 std::vector<int> offset;
12475
12476 np = 0;
12477 if (high_order_output)
12478 {
12479 Array<int> local_connectivity;
12480 for (int iel = 0; iel < ne; iel++)
12481 {
12482 Geometry::Type geom = get_geom(iel);
12483 CreateVTKElementConnectivity(local_connectivity, geom, ref);
12484 int nnodes = local_connectivity.Size();
12485 for (int i=0; i<nnodes; ++i)
12486 {
12487 WriteBinaryOrASCII(os, buf, np+local_connectivity[i], " ",
12488 format);
12489 }
12490 if (format == VTKFormat::ASCII) { os << '\n'; }
12491 np += nnodes;
12492 offset.push_back(np);
12493 }
12494 }
12495 else
12496 {
12497 int coff = 0;
12498 for (int i = 0; i < ne; i++)
12499 {
12500 Geometry::Type geom = get_geom(i);
12501 int nv = Geometries.GetVertices(geom)->GetNPoints();
12502 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12503 Array<int> &RG = RefG->RefGeoms;
12504 for (int j = 0; j < RG.Size(); )
12505 {
12506 coff = coff+nv;
12507 offset.push_back(coff);
12508 const int *p = VTKGeometry::VertexPermutation[geom];
12509 for (int k = 0; k < nv; k++, j++)
12510 {
12511 WriteBinaryOrASCII(os, buf, np + RG[p ? (j - k + p[k]) : j], " ",
12512 format);
12513 }
12514 if (format == VTKFormat::ASCII) { os << '\n'; }
12515 }
12516 np += RefG->RefPts.GetNPoints();
12517 }
12518 }
12519 if (format != VTKFormat::ASCII)
12520 {
12521 WriteBase64WithSizeAndClear(os, buf, compression_level);
12522 }
12523 os << "</DataArray>" << std::endl;
12524
12525 os << "<DataArray type=\"Int32\" Name=\"offsets\" format=\""
12526 << fmt_str << "\">" << std::endl;
12527 // offsets
12528 for (size_t ii=0; ii<offset.size(); ii++)
12529 {
12530 WriteBinaryOrASCII(os, buf, offset[ii], "\n", format);
12531 }
12532 if (format != VTKFormat::ASCII)
12533 {
12534 WriteBase64WithSizeAndClear(os, buf, compression_level);
12535 }
12536 os << "</DataArray>" << std::endl;
12537 os << "<DataArray type=\"UInt8\" Name=\"types\" format=\""
12538 << fmt_str << "\">" << std::endl;
12539 // cell types
12540 const int *vtk_geom_map =
12541 high_order_output ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
12542 for (int i = 0; i < ne; i++)
12543 {
12544 Geometry::Type geom = get_geom(i);
12545 uint8_t vtk_cell_type = 5;
12546
12547 vtk_cell_type = vtk_geom_map[geom];
12548
12549 if (high_order_output)
12550 {
12551 WriteBinaryOrASCII(os, buf, vtk_cell_type, "\n", format);
12552 }
12553 else
12554 {
12555 int nv = Geometries.GetVertices(geom)->GetNPoints();
12556 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12557 Array<int> &RG = RefG->RefGeoms;
12558 for (int j = 0; j < RG.Size(); j += nv)
12559 {
12560 WriteBinaryOrASCII(os, buf, vtk_cell_type, "\n", format);
12561 }
12562 }
12563 }
12564 if (format != VTKFormat::ASCII)
12565 {
12566 WriteBase64WithSizeAndClear(os, buf, compression_level);
12567 }
12568 os << "</DataArray>" << std::endl;
12569 os << "</Cells>" << std::endl;
12570
12571 os << "<CellData Scalars=\"attribute\">" << std::endl;
12572 os << "<DataArray type=\"Int32\" Name=\"attribute\" format=\""
12573 << fmt_str << "\">" << std::endl;
12574 for (int i = 0; i < ne; i++)
12575 {
12576 int attr = bdr_elements ? GetBdrAttribute(i) : GetAttribute(i);
12577 if (high_order_output)
12578 {
12579 WriteBinaryOrASCII(os, buf, attr, "\n", format);
12580 }
12581 else
12582 {
12583 Geometry::Type geom = get_geom(i);
12584 int nv = Geometries.GetVertices(geom)->GetNPoints();
12585 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12586 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
12587 {
12588 WriteBinaryOrASCII(os, buf, attr, "\n", format);
12589 }
12590 }
12591 }
12592 if (format != VTKFormat::ASCII)
12593 {
12594 WriteBase64WithSizeAndClear(os, buf, compression_level);
12595 }
12596 os << "</DataArray>" << std::endl;
12597 os << "</CellData>" << std::endl;
12598}
12599
12600
12601void Mesh::PrintVTK(std::ostream &os, int ref, int field_data)
12602{
12603 int np, nc, size;
12604 RefinedGeometry *RefG;
12605 DenseMatrix pmat;
12606
12607 os <<
12608 "# vtk DataFile Version 3.0\n"
12609 "Generated by MFEM\n"
12610 "ASCII\n"
12611 "DATASET UNSTRUCTURED_GRID\n";
12612
12613 // additional dataset information
12614 if (field_data)
12615 {
12616 os << "FIELD FieldData 1\n"
12617 << "MaterialIds " << 1 << " " << attributes.Size() << " int\n";
12618 for (int i = 0; i < attributes.Size(); i++)
12619 {
12620 os << ' ' << attributes[i];
12621 }
12622 os << '\n';
12623 }
12624
12625 // count the points, cells, size
12626 np = nc = size = 0;
12627 for (int i = 0; i < GetNE(); i++)
12628 {
12630 int nv = Geometries.GetVertices(geom)->GetNPoints();
12631 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12632 np += RefG->RefPts.GetNPoints();
12633 nc += RefG->RefGeoms.Size() / nv;
12634 size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
12635 }
12636 os << "POINTS " << np << " double\n";
12637 // write the points
12638 for (int i = 0; i < GetNE(); i++)
12639 {
12641 GetElementBaseGeometry(i), ref, 1);
12642
12643 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
12644
12645 for (int j = 0; j < pmat.Width(); j++)
12646 {
12647 os << pmat(0, j) << ' ';
12648 if (pmat.Height() > 1)
12649 {
12650 os << pmat(1, j) << ' ';
12651 if (pmat.Height() > 2)
12652 {
12653 os << pmat(2, j);
12654 }
12655 else
12656 {
12657 os << 0.0;
12658 }
12659 }
12660 else
12661 {
12662 os << 0.0 << ' ' << 0.0;
12663 }
12664 os << '\n';
12665 }
12666 }
12667
12668 // write the cells
12669 os << "CELLS " << nc << ' ' << size << '\n';
12670 np = 0;
12671 for (int i = 0; i < GetNE(); i++)
12672 {
12674 int nv = Geometries.GetVertices(geom)->GetNPoints();
12675 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12676 Array<int> &RG = RefG->RefGeoms;
12677
12678 for (int j = 0; j < RG.Size(); )
12679 {
12680 os << nv;
12681 for (int k = 0; k < nv; k++, j++)
12682 {
12683 os << ' ' << np + RG[j];
12684 }
12685 os << '\n';
12686 }
12687 np += RefG->RefPts.GetNPoints();
12688 }
12689 os << "CELL_TYPES " << nc << '\n';
12690 for (int i = 0; i < GetNE(); i++)
12691 {
12693 int nv = Geometries.GetVertices(geom)->GetNPoints();
12694 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12695 Array<int> &RG = RefG->RefGeoms;
12696 int vtk_cell_type = VTKGeometry::Map[geom];
12697
12698 for (int j = 0; j < RG.Size(); j += nv)
12699 {
12700 os << vtk_cell_type << '\n';
12701 }
12702 }
12703 // write attributes (materials)
12704 os << "CELL_DATA " << nc << '\n'
12705 << "SCALARS material int\n"
12706 << "LOOKUP_TABLE default\n";
12707 for (int i = 0; i < GetNE(); i++)
12708 {
12710 int nv = Geometries.GetVertices(geom)->GetNPoints();
12711 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12712 int attr = GetAttribute(i);
12713 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
12714 {
12715 os << attr << '\n';
12716 }
12717 }
12718
12719 if (Dim > 1)
12720 {
12721 Array<int> coloring;
12722 srand((unsigned)time(0));
12723 real_t a = rand_real();
12724 int el0 = (int)floor(a * GetNE());
12725 GetElementColoring(coloring, el0);
12726 os << "SCALARS element_coloring int\n"
12727 << "LOOKUP_TABLE default\n";
12728 for (int i = 0; i < GetNE(); i++)
12729 {
12731 int nv = Geometries.GetVertices(geom)->GetNPoints();
12732 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
12733 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
12734 {
12735 os << coloring[i] + 1 << '\n';
12736 }
12737 }
12738 }
12739
12740 // prepare to write data
12741 os << "POINT_DATA " << np << '\n' << flush;
12742}
12743
12744#ifdef MFEM_USE_HDF5
12745
12746void Mesh::SaveVTKHDF(const std::string &fname, bool high_order)
12747{
12748#ifdef MFEM_USE_MPI
12749 if (ParMesh *pmesh = dynamic_cast<ParMesh*>(this))
12750 {
12751#ifdef MFEM_PARALLEL_HDF5
12752 VTKHDF vtkhdf(fname, pmesh->GetComm());
12753 vtkhdf.SaveMesh(*this, high_order);
12754 return;
12755#else
12756 MFEM_ABORT("Requires HDF5 library with parallel support enabled");
12757#endif
12758 }
12759#endif
12760 VTKHDF vtkhdf(fname);
12761 vtkhdf.SaveMesh(*this, high_order);
12762}
12763
12764#endif
12765
12767{
12768 int delete_el_to_el = (el_to_el) ? (0) : (1);
12769 const Table &el_el = ElementToElementTable();
12770 int num_el = GetNE(), stack_p, stack_top_p, max_num_col;
12771 Array<int> el_stack(num_el);
12772
12773 const int *i_el_el = el_el.GetI();
12774 const int *j_el_el = el_el.GetJ();
12775
12776 colors.SetSize(num_el);
12777 colors = -2;
12778 max_num_col = 1;
12779 stack_p = stack_top_p = 0;
12780 for (int el = el0; stack_top_p < num_el; el=(el+1)%num_el)
12781 {
12782 if (colors[el] != -2)
12783 {
12784 continue;
12785 }
12786
12787 colors[el] = -1;
12788 el_stack[stack_top_p++] = el;
12789
12790 for ( ; stack_p < stack_top_p; stack_p++)
12791 {
12792 int i = el_stack[stack_p];
12793 int num_nb = i_el_el[i+1] - i_el_el[i];
12794 if (max_num_col < num_nb + 1)
12795 {
12796 max_num_col = num_nb + 1;
12797 }
12798 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
12799 {
12800 int k = j_el_el[j];
12801 if (colors[k] == -2)
12802 {
12803 colors[k] = -1;
12804 el_stack[stack_top_p++] = k;
12805 }
12806 }
12807 }
12808 }
12809
12810 Array<int> col_marker(max_num_col);
12811
12812 for (stack_p = 0; stack_p < stack_top_p; stack_p++)
12813 {
12814 int i = el_stack[stack_p], col;
12815 col_marker = 0;
12816 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
12817 {
12818 col = colors[j_el_el[j]];
12819 if (col != -1)
12820 {
12821 col_marker[col] = 1;
12822 }
12823 }
12824
12825 for (col = 0; col < max_num_col; col++)
12826 if (col_marker[col] == 0)
12827 {
12828 break;
12829 }
12830
12831 colors[i] = col;
12832 }
12833
12834 if (delete_el_to_el)
12835 {
12836 delete el_to_el;
12837 el_to_el = NULL;
12838 }
12839}
12840
12841void Mesh::PrintWithPartitioning(int *partitioning, std::ostream &os,
12842 int elem_attr) const
12843{
12844 if (Dim != 3 && Dim != 2) { return; }
12845
12846 int i, j, k, l, nv, nbe, *v;
12847
12848 os << "MFEM mesh v1.0\n";
12849
12850 // optional
12851 os <<
12852 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
12853 "# POINT = 0\n"
12854 "# SEGMENT = 1\n"
12855 "# TRIANGLE = 2\n"
12856 "# SQUARE = 3\n"
12857 "# TETRAHEDRON = 4\n"
12858 "# CUBE = 5\n"
12859 "# PRISM = 6\n"
12860 "#\n";
12861
12862 os << "\ndimension\n" << Dim
12863 << "\n\nelements\n" << NumOfElements << '\n';
12864 for (i = 0; i < NumOfElements; i++)
12865 {
12866 os << int((elem_attr) ? partitioning[i]+1 : elements[i]->GetAttribute())
12867 << ' ' << elements[i]->GetGeometryType();
12868 nv = elements[i]->GetNVertices();
12869 v = elements[i]->GetVertices();
12870 for (j = 0; j < nv; j++)
12871 {
12872 os << ' ' << v[j];
12873 }
12874 os << '\n';
12875 }
12876 nbe = 0;
12877 for (i = 0; i < faces_info.Size(); i++)
12878 {
12879 if ((l = faces_info[i].Elem2No) >= 0)
12880 {
12881 k = partitioning[faces_info[i].Elem1No];
12882 l = partitioning[l];
12883 if (k != l)
12884 {
12885 nbe++;
12886 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
12887 {
12888 nbe++;
12889 }
12890 }
12891 }
12892 else
12893 {
12894 nbe++;
12895 }
12896 }
12897 os << "\nboundary\n" << nbe << '\n';
12898 for (i = 0; i < faces_info.Size(); i++)
12899 {
12900 if ((l = faces_info[i].Elem2No) >= 0)
12901 {
12902 k = partitioning[faces_info[i].Elem1No];
12903 l = partitioning[l];
12904 if (k != l)
12905 {
12906 nv = faces[i]->GetNVertices();
12907 v = faces[i]->GetVertices();
12908 os << k+1 << ' ' << faces[i]->GetGeometryType();
12909 for (j = 0; j < nv; j++)
12910 {
12911 os << ' ' << v[j];
12912 }
12913 os << '\n';
12914 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
12915 {
12916 os << l+1 << ' ' << faces[i]->GetGeometryType();
12917 for (j = nv-1; j >= 0; j--)
12918 {
12919 os << ' ' << v[j];
12920 }
12921 os << '\n';
12922 }
12923 }
12924 }
12925 else
12926 {
12927 k = partitioning[faces_info[i].Elem1No];
12928 nv = faces[i]->GetNVertices();
12929 v = faces[i]->GetVertices();
12930 os << k+1 << ' ' << faces[i]->GetGeometryType();
12931 for (j = 0; j < nv; j++)
12932 {
12933 os << ' ' << v[j];
12934 }
12935 os << '\n';
12936 }
12937 }
12938 os << "\nvertices\n" << NumOfVertices << '\n';
12939 if (Nodes == NULL)
12940 {
12941 os << spaceDim << '\n';
12942 for (i = 0; i < NumOfVertices; i++)
12943 {
12944 os << vertices[i](0);
12945 for (j = 1; j < spaceDim; j++)
12946 {
12947 os << ' ' << vertices[i](j);
12948 }
12949 os << '\n';
12950 }
12951 os.flush();
12952 }
12953 else
12954 {
12955 os << "\nnodes\n";
12956 Nodes->Save(os);
12957 }
12958}
12959
12961 std::ostream &os,
12962 int interior_faces)
12963{
12964 MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported\n");
12965 if (Dim != 3 && Dim != 2) { return; }
12966
12967 int *vcount = new int[NumOfVertices];
12968 for (int i = 0; i < NumOfVertices; i++)
12969 {
12970 vcount[i] = 0;
12971 }
12972 for (int i = 0; i < NumOfElements; i++)
12973 {
12974 int nv = elements[i]->GetNVertices();
12975 const int *ind = elements[i]->GetVertices();
12976 for (int j = 0; j < nv; j++)
12977 {
12978 vcount[ind[j]]++;
12979 }
12980 }
12981
12982 int *voff = new int[NumOfVertices+1];
12983 voff[0] = 0;
12984 for (int i = 1; i <= NumOfVertices; i++)
12985 {
12986 voff[i] = vcount[i-1] + voff[i-1];
12987 }
12988
12989 int **vown = new int*[NumOfVertices];
12990 for (int i = 0; i < NumOfVertices; i++)
12991 {
12992 vown[i] = new int[vcount[i]];
12993 }
12994
12995 // 2D
12996 if (Dim == 2)
12997 {
12998 Table edge_el;
12999 Transpose(ElementToEdgeTable(), edge_el);
13000
13001 // Fake printing of the elements.
13002 for (int i = 0; i < NumOfElements; i++)
13003 {
13004 int nv = elements[i]->GetNVertices();
13005 const int *ind = elements[i]->GetVertices();
13006 for (int j = 0; j < nv; j++)
13007 {
13008 vcount[ind[j]]--;
13009 vown[ind[j]][vcount[ind[j]]] = i;
13010 }
13011 }
13012
13013 for (int i = 0; i < NumOfVertices; i++)
13014 {
13015 vcount[i] = voff[i+1] - voff[i];
13016 }
13017
13018 int nbe = 0;
13019 for (int i = 0; i < edge_el.Size(); i++)
13020 {
13021 const int *el = edge_el.GetRow(i);
13022 if (edge_el.RowSize(i) > 1)
13023 {
13024 int k = partitioning[el[0]];
13025 int l = partitioning[el[1]];
13026 if (interior_faces || k != l)
13027 {
13028 nbe += 2;
13029 }
13030 }
13031 else
13032 {
13033 nbe++;
13034 }
13035 }
13036
13037 // Print the type of the mesh and the boundary elements.
13038 os << "areamesh2\n\n" << nbe << '\n';
13039
13040 for (int i = 0; i < edge_el.Size(); i++)
13041 {
13042 const int *el = edge_el.GetRow(i);
13043 if (edge_el.RowSize(i) > 1)
13044 {
13045 int k = partitioning[el[0]];
13046 int l = partitioning[el[1]];
13047 if (interior_faces || k != l)
13048 {
13049 Array<int> ev;
13050 GetEdgeVertices(i,ev);
13051 os << k+1; // attribute
13052 for (int j = 0; j < 2; j++)
13053 for (int s = 0; s < vcount[ev[j]]; s++)
13054 if (vown[ev[j]][s] == el[0])
13055 {
13056 os << ' ' << voff[ev[j]]+s+1;
13057 }
13058 os << '\n';
13059 os << l+1; // attribute
13060 for (int j = 1; j >= 0; j--)
13061 for (int s = 0; s < vcount[ev[j]]; s++)
13062 if (vown[ev[j]][s] == el[1])
13063 {
13064 os << ' ' << voff[ev[j]]+s+1;
13065 }
13066 os << '\n';
13067 }
13068 }
13069 else
13070 {
13071 int k = partitioning[el[0]];
13072 Array<int> ev;
13073 GetEdgeVertices(i,ev);
13074 os << k+1; // attribute
13075 for (int j = 0; j < 2; j++)
13076 for (int s = 0; s < vcount[ev[j]]; s++)
13077 if (vown[ev[j]][s] == el[0])
13078 {
13079 os << ' ' << voff[ev[j]]+s+1;
13080 }
13081 os << '\n';
13082 }
13083 }
13084
13085 // Print the elements.
13086 os << NumOfElements << '\n';
13087 for (int i = 0; i < NumOfElements; i++)
13088 {
13089 int nv = elements[i]->GetNVertices();
13090 const int *ind = elements[i]->GetVertices();
13091 os << partitioning[i]+1 << ' '; // use subdomain number as attribute
13092 os << nv << ' ';
13093 for (int j = 0; j < nv; j++)
13094 {
13095 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13096 vown[ind[j]][vcount[ind[j]]] = i;
13097 }
13098 os << '\n';
13099 }
13100
13101 for (int i = 0; i < NumOfVertices; i++)
13102 {
13103 vcount[i] = voff[i+1] - voff[i];
13104 }
13105
13106 // Print the vertices.
13107 os << voff[NumOfVertices] << '\n';
13108 for (int i = 0; i < NumOfVertices; i++)
13109 for (int k = 0; k < vcount[i]; k++)
13110 {
13111 for (int j = 0; j < Dim; j++)
13112 {
13113 os << vertices[i](j) << ' ';
13114 }
13115 os << '\n';
13116 }
13117 }
13118 // Dim is 3
13119 else if (meshgen == 1)
13120 {
13121 os << "NETGEN_Neutral_Format\n";
13122 // print the vertices
13123 os << voff[NumOfVertices] << '\n';
13124 for (int i = 0; i < NumOfVertices; i++)
13125 for (int k = 0; k < vcount[i]; k++)
13126 {
13127 for (int j = 0; j < Dim; j++)
13128 {
13129 os << ' ' << vertices[i](j);
13130 }
13131 os << '\n';
13132 }
13133
13134 // print the elements
13135 os << NumOfElements << '\n';
13136 for (int i = 0; i < NumOfElements; i++)
13137 {
13138 int nv = elements[i]->GetNVertices();
13139 const int *ind = elements[i]->GetVertices();
13140 os << partitioning[i]+1; // use subdomain number as attribute
13141 for (int j = 0; j < nv; j++)
13142 {
13143 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13144 vown[ind[j]][vcount[ind[j]]] = i;
13145 }
13146 os << '\n';
13147 }
13148
13149 for (int i = 0; i < NumOfVertices; i++)
13150 {
13151 vcount[i] = voff[i+1] - voff[i];
13152 }
13153
13154 // print the boundary information.
13155 int nbe = 0;
13156 for (int i = 0; i < NumOfFaces; i++)
13157 {
13158 int l = faces_info[i].Elem2No;
13159 if (l >= 0)
13160 {
13161 int k = partitioning[faces_info[i].Elem1No];
13162 l = partitioning[l];
13163 if (interior_faces || k != l)
13164 {
13165 nbe += 2;
13166 }
13167 }
13168 else
13169 {
13170 nbe++;
13171 }
13172 }
13173
13174 os << nbe << '\n';
13175 for (int i = 0; i < NumOfFaces; i++)
13176 {
13177 int l = faces_info[i].Elem2No;
13178 if (l >= 0)
13179 {
13180 int k = partitioning[faces_info[i].Elem1No];
13181 l = partitioning[l];
13182 if (interior_faces || k != l)
13183 {
13184 int nv = faces[i]->GetNVertices();
13185 const int *ind = faces[i]->GetVertices();
13186 os << k+1; // attribute
13187 for (int j = 0; j < nv; j++)
13188 for (int s = 0; s < vcount[ind[j]]; s++)
13189 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13190 {
13191 os << ' ' << voff[ind[j]]+s+1;
13192 }
13193 os << '\n';
13194 os << l+1; // attribute
13195 for (int j = nv-1; j >= 0; j--)
13196 for (int s = 0; s < vcount[ind[j]]; s++)
13197 if (vown[ind[j]][s] == faces_info[i].Elem2No)
13198 {
13199 os << ' ' << voff[ind[j]]+s+1;
13200 }
13201 os << '\n';
13202 }
13203 }
13204 else
13205 {
13206 int k = partitioning[faces_info[i].Elem1No];
13207 int nv = faces[i]->GetNVertices();
13208 const int *ind = faces[i]->GetVertices();
13209 os << k+1; // attribute
13210 for (int j = 0; j < nv; j++)
13211 for (int s = 0; s < vcount[ind[j]]; s++)
13212 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13213 {
13214 os << ' ' << voff[ind[j]]+s+1;
13215 }
13216 os << '\n';
13217 }
13218 }
13219 }
13220 // Dim is 3
13221 else if (meshgen == 2) // TrueGrid
13222 {
13223 // count the number of the boundary elements.
13224 int nbe = 0;
13225 for (int i = 0; i < NumOfFaces; i++)
13226 {
13227 int l = faces_info[i].Elem2No;
13228 if (l >= 0)
13229 {
13230 int k = partitioning[faces_info[i].Elem1No];
13231 l = partitioning[l];
13232 if (interior_faces || k != l)
13233 {
13234 nbe += 2;
13235 }
13236 }
13237 else
13238 {
13239 nbe++;
13240 }
13241 }
13242
13243 os << "TrueGrid\n"
13244 << "1 " << voff[NumOfVertices] << " " << NumOfElements
13245 << " 0 0 0 0 0 0 0\n"
13246 << "0 0 0 1 0 0 0 0 0 0 0\n"
13247 << "0 0 " << nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
13248 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
13249 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
13250
13251 for (int i = 0; i < NumOfVertices; i++)
13252 for (int k = 0; k < vcount[i]; k++)
13253 os << voff[i]+k << " 0.0 " << vertices[i](0) << ' '
13254 << vertices[i](1) << ' ' << vertices[i](2) << " 0.0\n";
13255
13256 for (int i = 0; i < NumOfElements; i++)
13257 {
13258 int nv = elements[i]->GetNVertices();
13259 const int *ind = elements[i]->GetVertices();
13260 os << i+1 << ' ' << partitioning[i]+1; // partitioning as attribute
13261 for (int j = 0; j < nv; j++)
13262 {
13263 os << ' ' << voff[ind[j]]+vcount[ind[j]]--;
13264 vown[ind[j]][vcount[ind[j]]] = i;
13265 }
13266 os << '\n';
13267 }
13268
13269 for (int i = 0; i < NumOfVertices; i++)
13270 {
13271 vcount[i] = voff[i+1] - voff[i];
13272 }
13273
13274 // boundary elements
13275 for (int i = 0; i < NumOfFaces; i++)
13276 {
13277 int l = faces_info[i].Elem2No;
13278 if (l >= 0)
13279 {
13280 int k = partitioning[faces_info[i].Elem1No];
13281 l = partitioning[l];
13282 if (interior_faces || k != l)
13283 {
13284 int nv = faces[i]->GetNVertices();
13285 const int *ind = faces[i]->GetVertices();
13286 os << k+1; // attribute
13287 for (int j = 0; j < nv; j++)
13288 for (int s = 0; s < vcount[ind[j]]; s++)
13289 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13290 {
13291 os << ' ' << voff[ind[j]]+s+1;
13292 }
13293 os << " 1.0 1.0 1.0 1.0\n";
13294 os << l+1; // attribute
13295 for (int j = nv-1; j >= 0; j--)
13296 for (int s = 0; s < vcount[ind[j]]; s++)
13297 if (vown[ind[j]][s] == faces_info[i].Elem2No)
13298 {
13299 os << ' ' << voff[ind[j]]+s+1;
13300 }
13301 os << " 1.0 1.0 1.0 1.0\n";
13302 }
13303 }
13304 else
13305 {
13306 int k = partitioning[faces_info[i].Elem1No];
13307 int nv = faces[i]->GetNVertices();
13308 const int *ind = faces[i]->GetVertices();
13309 os << k+1; // attribute
13310 for (int j = 0; j < nv; j++)
13311 for (int s = 0; s < vcount[ind[j]]; s++)
13312 if (vown[ind[j]][s] == faces_info[i].Elem1No)
13313 {
13314 os << ' ' << voff[ind[j]]+s+1;
13315 }
13316 os << " 1.0 1.0 1.0 1.0\n";
13317 }
13318 }
13319 }
13320
13321 os << flush;
13322
13323 for (int i = 0; i < NumOfVertices; i++)
13324 {
13325 delete [] vown[i];
13326 }
13327
13328 delete [] vcount;
13329 delete [] voff;
13330 delete [] vown;
13331}
13332
13333void Mesh::PrintSurfaces(const Table & Aface_face, std::ostream &os) const
13334{
13335 int i, j;
13336
13337 if (NURBSext)
13338 {
13339 mfem_error("Mesh::PrintSurfaces"
13340 " NURBS mesh is not supported!");
13341 return;
13342 }
13343
13344 os << "MFEM mesh v1.0\n";
13345
13346 // optional
13347 os <<
13348 "\n#\n# MFEM Geometry Types (see fem/geom.hpp):\n#\n"
13349 "# POINT = 0\n"
13350 "# SEGMENT = 1\n"
13351 "# TRIANGLE = 2\n"
13352 "# SQUARE = 3\n"
13353 "# TETRAHEDRON = 4\n"
13354 "# CUBE = 5\n"
13355 "# PRISM = 6\n"
13356 "#\n";
13357
13358 os << "\ndimension\n" << Dim
13359 << "\n\nelements\n" << NumOfElements << '\n';
13360 for (i = 0; i < NumOfElements; i++)
13361 {
13362 PrintElement(elements[i], os);
13363 }
13364
13365 os << "\nboundary\n" << Aface_face.Size_of_connections() << '\n';
13366 const int * const i_AF_f = Aface_face.GetI();
13367 const int * const j_AF_f = Aface_face.GetJ();
13368
13369 for (int iAF=0; iAF < Aface_face.Size(); ++iAF)
13370 for (const int * iface = j_AF_f + i_AF_f[iAF];
13371 iface < j_AF_f + i_AF_f[iAF+1];
13372 ++iface)
13373 {
13374 os << iAF+1 << ' ';
13375 PrintElementWithoutAttr(faces[*iface],os);
13376 }
13377
13378 os << "\nvertices\n" << NumOfVertices << '\n';
13379 if (Nodes == NULL)
13380 {
13381 os << spaceDim << '\n';
13382 for (i = 0; i < NumOfVertices; i++)
13383 {
13384 os << vertices[i](0);
13385 for (j = 1; j < spaceDim; j++)
13386 {
13387 os << ' ' << vertices[i](j);
13388 }
13389 os << '\n';
13390 }
13391 os.flush();
13392 }
13393 else
13394 {
13395 os << "\nnodes\n";
13396 Nodes->Save(os);
13397 }
13398}
13399
13401{
13402 int i,j,k;
13403 Array<int> vert;
13404 DenseMatrix pointmat;
13405 int na = attributes.Size();
13406 real_t *cg = new real_t[na*spaceDim];
13407 int *nbea = new int[na];
13408
13409 int *vn = new int[NumOfVertices];
13410 for (i = 0; i < NumOfVertices; i++)
13411 {
13412 vn[i] = 0;
13413 }
13414 for (i = 0; i < na; i++)
13415 {
13416 for (j = 0; j < spaceDim; j++)
13417 {
13418 cg[i*spaceDim+j] = 0.0;
13419 }
13420 nbea[i] = 0;
13421 }
13422
13423 for (i = 0; i < NumOfElements; i++)
13424 {
13425 GetElementVertices(i, vert);
13426 for (k = 0; k < vert.Size(); k++)
13427 {
13428 vn[vert[k]] = 1;
13429 }
13430 }
13431
13432 for (i = 0; i < NumOfElements; i++)
13433 {
13434 int bea = GetAttribute(i)-1;
13435 GetPointMatrix(i, pointmat);
13436 GetElementVertices(i, vert);
13437
13438 for (k = 0; k < vert.Size(); k++)
13439 if (vn[vert[k]] == 1)
13440 {
13441 nbea[bea]++;
13442 for (j = 0; j < spaceDim; j++)
13443 {
13444 cg[bea*spaceDim+j] += pointmat(j,k);
13445 }
13446 vn[vert[k]] = 2;
13447 }
13448 }
13449
13450 for (i = 0; i < NumOfElements; i++)
13451 {
13452 int bea = GetAttribute(i)-1;
13453 GetElementVertices (i, vert);
13454
13455 for (k = 0; k < vert.Size(); k++)
13456 if (vn[vert[k]])
13457 {
13458 for (j = 0; j < spaceDim; j++)
13459 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
13460 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
13461 vn[vert[k]] = 0;
13462 }
13463 }
13464
13465 delete [] cg;
13466 delete [] nbea;
13467 delete [] vn;
13468}
13469
13471{
13472 int i,j,k;
13473 Array<int> vert;
13474 DenseMatrix pointmat;
13475 int na = NumOfElements;
13476 real_t *cg = new real_t[na*spaceDim];
13477 int *nbea = new int[na];
13478
13479 int *vn = new int[NumOfVertices];
13480 for (i = 0; i < NumOfVertices; i++)
13481 {
13482 vn[i] = 0;
13483 }
13484 for (i = 0; i < na; i++)
13485 {
13486 for (j = 0; j < spaceDim; j++)
13487 {
13488 cg[i*spaceDim+j] = 0.0;
13489 }
13490 nbea[i] = 0;
13491 }
13492
13493 for (i = 0; i < NumOfElements; i++)
13494 {
13495 GetElementVertices(i, vert);
13496 for (k = 0; k < vert.Size(); k++)
13497 {
13498 vn[vert[k]] = 1;
13499 }
13500 }
13501
13502 for (i = 0; i < NumOfElements; i++)
13503 {
13504 int bea = i;
13505 GetPointMatrix(i, pointmat);
13506 GetElementVertices(i, vert);
13507
13508 for (k = 0; k < vert.Size(); k++)
13509 if (vn[vert[k]] == 1)
13510 {
13511 nbea[bea]++;
13512 for (j = 0; j < spaceDim; j++)
13513 {
13514 cg[bea*spaceDim+j] += pointmat(j,k);
13515 }
13516 vn[vert[k]] = 2;
13517 }
13518 }
13519
13520 for (i = 0; i < NumOfElements; i++)
13521 {
13522 int bea = i;
13523 GetElementVertices(i, vert);
13524
13525 for (k = 0; k < vert.Size(); k++)
13526 if (vn[vert[k]])
13527 {
13528 for (j = 0; j < spaceDim; j++)
13529 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
13530 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
13531 vn[vert[k]] = 0;
13532 }
13533 }
13534
13535 delete [] cg;
13536 delete [] nbea;
13537 delete [] vn;
13538}
13539
13540void Mesh::Transform(std::function<void(const Vector &, Vector&)> f)
13541{
13542 // TODO: support for different new spaceDim.
13543 if (Nodes == NULL)
13544 {
13545 Vector vold(spaceDim), vnew(NULL, spaceDim);
13546 for (int i = 0; i < vertices.Size(); i++)
13547 {
13548 for (int j = 0; j < spaceDim; j++)
13549 {
13550 vold(j) = vertices[i](j);
13551 }
13552 vnew.SetData(vertices[i]());
13553 f(vold, vnew);
13554 }
13555 }
13556 else
13557 {
13558 GridFunction xnew(Nodes->FESpace());
13560 xnew.ProjectCoefficient(f_pert);
13561 *Nodes = xnew;
13562 }
13563 NodesUpdated();
13564}
13565
13567{
13568 MFEM_VERIFY(spaceDim == deformation.GetVDim(),
13569 "incompatible vector dimensions");
13570 if (Nodes == NULL)
13571 {
13574 GridFunction xnew(&fes);
13575 xnew.ProjectCoefficient(deformation);
13576 for (int i = 0; i < NumOfVertices; i++)
13577 for (int d = 0; d < spaceDim; d++)
13578 {
13579 vertices[i](d) = xnew(d + spaceDim*i);
13580 }
13581 }
13582 else
13583 {
13584 GridFunction xnew(Nodes->FESpace());
13585 xnew.ProjectCoefficient(deformation);
13586 *Nodes = xnew;
13587 }
13588 NodesUpdated();
13589}
13590
13592{
13593 if (NURBSext || ncmesh) { return; }
13594
13595 Array<int> v2v(GetNV());
13596 v2v = -1;
13597 for (int i = 0; i < GetNE(); i++)
13598 {
13599 Element *el = GetElement(i);
13600 int nv = el->GetNVertices();
13601 int *v = el->GetVertices();
13602 for (int j = 0; j < nv; j++)
13603 {
13604 v2v[v[j]] = 0;
13605 }
13606 }
13607 for (int i = 0; i < GetNBE(); i++)
13608 {
13609 Element *el = GetBdrElement(i);
13610 int *v = el->GetVertices();
13611 int nv = el->GetNVertices();
13612 for (int j = 0; j < nv; j++)
13613 {
13614 v2v[v[j]] = 0;
13615 }
13616 }
13617 int num_vert = 0;
13618 for (int i = 0; i < v2v.Size(); i++)
13619 {
13620 if (v2v[i] == 0)
13621 {
13622 vertices[num_vert] = vertices[i];
13623 v2v[i] = num_vert++;
13624 }
13625 }
13626
13627 if (num_vert == v2v.Size()) { return; }
13628
13629 Vector nodes_by_element;
13630 Array<int> vdofs;
13631 if (Nodes)
13632 {
13633 int s = 0;
13634 for (int i = 0; i < GetNE(); i++)
13635 {
13636 Nodes->FESpace()->GetElementVDofs(i, vdofs);
13637 s += vdofs.Size();
13638 }
13639 nodes_by_element.SetSize(s);
13640 s = 0;
13641 for (int i = 0; i < GetNE(); i++)
13642 {
13643 Nodes->FESpace()->GetElementVDofs(i, vdofs);
13644 Nodes->GetSubVector(vdofs, &nodes_by_element(s));
13645 s += vdofs.Size();
13646 }
13647 }
13648 vertices.SetSize(num_vert);
13649 NumOfVertices = num_vert;
13650 for (int i = 0; i < GetNE(); i++)
13651 {
13652 Element *el = GetElement(i);
13653 int *v = el->GetVertices();
13654 int nv = el->GetNVertices();
13655 for (int j = 0; j < nv; j++)
13656 {
13657 v[j] = v2v[v[j]];
13658 }
13659 }
13660 for (int i = 0; i < GetNBE(); i++)
13661 {
13662 Element *el = GetBdrElement(i);
13663 int *v = el->GetVertices();
13664 int nv = el->GetNVertices();
13665 for (int j = 0; j < nv; j++)
13666 {
13667 v[j] = v2v[v[j]];
13668 }
13669 }
13670 DeleteTables();
13671 if (Dim > 1)
13672 {
13673 // generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
13674 el_to_edge = new Table;
13676 }
13677 if (Dim > 2)
13678 {
13679 // generate el_to_face, be_to_face
13681 }
13682 // Update faces and faces_info
13683 GenerateFaces();
13684 if (Nodes)
13685 {
13686 Nodes->FESpace()->Update();
13687 Nodes->Update();
13688 int s = 0;
13689 for (int i = 0; i < GetNE(); i++)
13690 {
13691 Nodes->FESpace()->GetElementVDofs(i, vdofs);
13692 Nodes->SetSubVector(vdofs, &nodes_by_element(s));
13693 s += vdofs.Size();
13694 }
13695 }
13696}
13697
13699{
13700 if (NURBSext || ncmesh) { return; }
13701
13702 int num_bdr_elem = 0;
13703 int new_bel_to_edge_nnz = 0;
13704 for (int i = 0; i < GetNBE(); i++)
13705 {
13707 {
13709 }
13710 else
13711 {
13712 num_bdr_elem++;
13713 if (Dim == 3)
13714 {
13715 new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
13716 }
13717 }
13718 }
13719
13720 if (num_bdr_elem == GetNBE()) { return; }
13721
13722 Array<Element *> new_boundary(num_bdr_elem);
13723 Array<int> new_be_to_face;
13724 Table *new_bel_to_edge = NULL;
13725 new_boundary.SetSize(0);
13726 new_be_to_face.Reserve(num_bdr_elem);
13727 if (Dim == 3)
13728 {
13729 new_bel_to_edge = new Table;
13730 new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
13731 }
13732 for (int i = 0; i < GetNBE(); i++)
13733 {
13735 {
13736 new_boundary.Append(boundary[i]);
13737 int row = new_be_to_face.Size();
13738 new_be_to_face.Append(be_to_face[i]);
13739 if (Dim == 3)
13740 {
13741 int *e = bel_to_edge->GetRow(i);
13742 int ne = bel_to_edge->RowSize(i);
13743 int *new_e = new_bel_to_edge->GetRow(row);
13744 for (int j = 0; j < ne; j++)
13745 {
13746 new_e[j] = e[j];
13747 }
13748 new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
13749 }
13750 }
13751 }
13752
13753 NumOfBdrElements = new_boundary.Size();
13754 mfem::Swap(boundary, new_boundary);
13755
13756 mfem::Swap(be_to_face, new_be_to_face);
13757
13758 if (Dim == 3)
13759 {
13760 delete bel_to_edge;
13761 bel_to_edge = new_bel_to_edge;
13762 }
13763
13764 Array<int> attribs(num_bdr_elem);
13765 for (int i = 0; i < attribs.Size(); i++)
13766 {
13767 attribs[i] = GetBdrAttribute(i);
13768 }
13769 attribs.Sort();
13770 attribs.Unique();
13772 attribs.Copy(bdr_attributes);
13773}
13774
13776{
13777#ifdef MFEM_USE_MEMALLOC
13778 if (E)
13779 {
13780 if (E->GetType() == Element::TETRAHEDRON)
13781 {
13782 TetMemory.Free((Tetrahedron*) E);
13783 }
13784 else
13785 {
13786 delete E;
13787 }
13788 }
13789#else
13790 delete E;
13791#endif
13792}
13793
13794std::ostream &operator<<(std::ostream &os, const Mesh &mesh)
13795{
13796 mesh.Print(os);
13797 return os;
13798}
13799
13800int Mesh::FindPoints(DenseMatrix &point_mat, Array<int>& elem_ids,
13801 Array<IntegrationPoint>& ips, bool warn,
13803{
13804 const int npts = point_mat.Width();
13805 if (!npts) { return 0; }
13806 MFEM_VERIFY(point_mat.Height() == spaceDim,"Invalid points matrix");
13807 elem_ids.SetSize(npts);
13808 ips.SetSize(npts);
13809 elem_ids = -1;
13810 if (!GetNE()) { return 0; }
13811
13812 real_t *data = point_mat.GetData();
13813 InverseElementTransformation *inv_tr = inv_trans;
13814 inv_tr = inv_tr ? inv_tr : new InverseElementTransformation;
13815
13816 // For each point in 'point_mat', find the element whose center is closest.
13817 Vector min_dist(npts);
13818 Array<int> e_idx(npts);
13819 min_dist = std::numeric_limits<real_t>::max();
13820 e_idx = -1;
13821
13822 Vector pt(spaceDim);
13823 for (int i = 0; i < GetNE(); i++)
13824 {
13825 GetElementTransformation(i)->Transform(
13827 for (int k = 0; k < npts; k++)
13828 {
13829 real_t dist = pt.DistanceTo(data+k*spaceDim);
13830 if (dist < min_dist(k))
13831 {
13832 min_dist(k) = dist;
13833 e_idx[k] = i;
13834 }
13835 }
13836 }
13837
13838 // Checks if the points lie in the closest element
13839 int pts_found = 0;
13840 pt.NewDataAndSize(NULL, spaceDim);
13841 for (int k = 0; k < npts; k++)
13842 {
13843 pt.SetData(data+k*spaceDim);
13844 inv_tr->SetTransformation(*GetElementTransformation(e_idx[k]));
13845 int res = inv_tr->Transform(pt, ips[k]);
13847 {
13848 elem_ids[k] = e_idx[k];
13849 pts_found++;
13850 }
13851 }
13852 if (pts_found != npts)
13853 {
13854 Array<int> elvertices;
13855 Table *vtoel = GetVertexToElementTable();
13856 for (int k = 0; k < npts; k++)
13857 {
13858 if (elem_ids[k] != -1) { continue; }
13859 // Try all vertex-neighbors of element e_idx[k]
13860 pt.SetData(data+k*spaceDim);
13861 GetElementVertices(e_idx[k], elvertices);
13862 for (int v = 0; v < elvertices.Size(); v++)
13863 {
13864 int vv = elvertices[v];
13865 int ne = vtoel->RowSize(vv);
13866 const int* els = vtoel->GetRow(vv);
13867 for (int e = 0; e < ne; e++)
13868 {
13869 if (els[e] == e_idx[k]) { continue; }
13871 int res = inv_tr->Transform(pt, ips[k]);
13873 {
13874 elem_ids[k] = els[e];
13875 pts_found++;
13876 goto next_point;
13877 }
13878 }
13879 }
13880 // Try neighbors for non-conforming meshes
13881 if (ncmesh)
13882 {
13883 Array<int> neigh;
13884 int le = ncmesh->leaf_elements[e_idx[k]];
13885 ncmesh->FindNeighbors(le,neigh);
13886 for (int e = 0; e < neigh.Size(); e++)
13887 {
13888 int nn = neigh[e];
13889 if (ncmesh->IsGhost(ncmesh->elements[nn])) { continue; }
13890 int el = ncmesh->elements[nn].index;
13892 int res = inv_tr->Transform(pt, ips[k]);
13894 {
13895 elem_ids[k] = el;
13896 pts_found++;
13897 goto next_point;
13898 }
13899 }
13900 }
13901 next_point: ;
13902 }
13903 delete vtoel;
13904 }
13905 if (inv_trans == NULL) { delete inv_tr; }
13906
13907 if (warn && pts_found != npts)
13908 {
13909 MFEM_WARNING((npts-pts_found) << " points were not found");
13910 }
13911 return pts_found;
13912}
13913
13915 real_t &volume,
13916 Vector &aspr,
13917 Vector &skew,
13918 Vector &ori) const
13919{
13920 J.HostRead();
13921 aspr.HostWrite();
13922 skew.HostWrite();
13923 ori.HostWrite();
13924 MFEM_VERIFY(Dim == 2 || Dim == 3, "Only 2D/3D meshes supported right now.");
13925 MFEM_VERIFY(Dim == spaceDim, "Surface meshes not currently supported.");
13926 if (Dim == 2)
13927 {
13928 aspr.SetSize(1);
13929 skew.SetSize(1);
13930 ori.SetSize(1);
13931 Vector col1, col2;
13932 J.GetColumn(0, col1);
13933 J.GetColumn(1, col2);
13934
13935 // Area/Volume
13936 volume = J.Det();
13937
13938 // Aspect-ratio
13939 aspr(0) = col2.Norml2()/col1.Norml2();
13940
13941 // Skewness
13942 skew(0) = std::atan2(J.Det(), col1 * col2);
13943
13944 // Orientation
13945 ori(0) = std::atan2(J(1,0), J(0,0));
13946 }
13947 else if (Dim == 3)
13948 {
13949 aspr.SetSize(4);
13950 skew.SetSize(3);
13951 ori.SetSize(4);
13952 Vector col1, col2, col3;
13953 J.GetColumn(0, col1);
13954 J.GetColumn(1, col2);
13955 J.GetColumn(2, col3);
13956 real_t len1 = col1.Norml2(),
13957 len2 = col2.Norml2(),
13958 len3 = col3.Norml2();
13959
13960 Vector col1unit = col1,
13961 col2unit = col2,
13962 col3unit = col3;
13963 col1unit *= 1.0/len1;
13964 col2unit *= 1.0/len2;
13965 col3unit *= 1.0/len3;
13966
13967 // Area/Volume
13968 volume = J.Det();
13969
13970 // Aspect-ratio - non-dimensional
13971 aspr(0) = len1/std::sqrt(len2*len3),
13972 aspr(1) = len2/std::sqrt(len1*len3);
13973
13974 // Aspect-ratio - dimensional - needed for TMOP
13975 aspr(2) = std::sqrt(len1/(len2*len3)),
13976 aspr(3) = std::sqrt(len2/(len1*len3));
13977
13978 // Skewness
13979 Vector crosscol12, crosscol13;
13980 col1.cross3D(col2, crosscol12);
13981 col1.cross3D(col3, crosscol13);
13982 skew(0) = std::acos(col1unit*col2unit);
13983 skew(1) = std::acos(col1unit*col3unit);
13984 skew(2) = std::atan(len1*volume/(crosscol12*crosscol13));
13985
13986 // Orientation
13987 // First we define the rotation matrix
13988 DenseMatrix rot(Dim);
13989 // First column
13990 for (int d=0; d<Dim; d++) { rot(d, 0) = col1unit(d); }
13991 // Second column
13992 Vector rot2 = col2unit;
13993 Vector rot1 = col1unit;
13994 rot1 *= col1unit*col2unit;
13995 rot2 -= rot1;
13996 col1unit.cross3D(col2unit, rot1);
13997 rot2 /= rot1.Norml2();
13998 for (int d=0; d < Dim; d++) { rot(d, 1) = rot2(d); }
13999 // Third column
14000 rot1 /= rot1.Norml2();
14001 for (int d=0; d < Dim; d++) { rot(d, 2) = rot1(d); }
14002 real_t delta = sqrt(pow(rot(2,1)-rot(1,2), 2.0) +
14003 pow(rot(0,2)-rot(2,0), 2.0) +
14004 pow(rot(1,0)-rot(0,1), 2.0));
14005 ori = 0.0;
14006 if (delta == 0.0) // Matrix is symmetric. Check if it is Identity.
14007 {
14008 DenseMatrix Iden(Dim);
14009 for (int d = 0; d < Dim; d++) { Iden(d, d) = 1.0; };
14010 Iden -= rot;
14011 if (Iden.FNorm2() != 0)
14012 {
14013 // TODO: Handling of these cases.
14014 rot.Print();
14015 MFEM_ABORT("Invalid rotation matrix. Contact TMOP Developers.");
14016 }
14017 }
14018 else
14019 {
14020 ori(0) = (1./delta)*(rot(2,1)-rot(1,2));
14021 ori(1) = (1./delta)*(rot(0,2)-rot(2,0));
14022 ori(2) = (1./delta)*(rot(1,0)-rot(0,1));
14023 ori(3) = std::acos(0.5*(rot.Trace()-1.0));
14024 }
14025 }
14026}
14027
14028
14030 int dim_, const Array<int> (&entity_to_vertex_)[Geometry::NumGeom])
14031 : dim(dim_),
14032 entity_to_vertex(entity_to_vertex_)
14033{
14034 int geom_offset = 0;
14035 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
14036 {
14037 geom_offsets[g] = geom_offset;
14038 geom_offset += entity_to_vertex[g].Size()/Geometry::NumVerts[g];
14039 }
14040 geom_offsets[Geometry::DimStart[dim+1]] = geom_offset;
14041 num_entities = geom_offset;
14042}
14043
14045{
14046 // Find the 'geom' that corresponds to 'bytype_entity_id'
14047 int geom = Geometry::DimStart[dim];
14048 while (geom_offsets[geom+1] <= bytype_entity_id) { geom++; }
14049 MFEM_ASSERT(geom < Geometry::NumGeom, "internal error");
14050 MFEM_ASSERT(Geometry::Dimension[geom] == dim, "internal error");
14051 const int nv = Geometry::NumVerts[geom];
14052 const int geom_elem_id = bytype_entity_id - geom_offsets[geom];
14053 const int *v = &entity_to_vertex[geom][nv*geom_elem_id];
14054 return { geom, nv, v };
14055}
14056
14057void MeshPart::Print(std::ostream &os) const
14058{
14059 os << "MFEM mesh v1.2\n";
14060
14061 // optional
14062 os <<
14063 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
14064 "# POINT = 0\n"
14065 "# SEGMENT = 1\n"
14066 "# TRIANGLE = 2\n"
14067 "# SQUARE = 3\n"
14068 "# TETRAHEDRON = 4\n"
14069 "# CUBE = 5\n"
14070 "# PRISM = 6\n"
14071 "# PYRAMID = 7\n"
14072 "#\n";
14073
14074 const int dim = dimension;
14075 os << "\ndimension\n" << dim;
14076
14077 os << "\n\nelements\n" << num_elements << '\n';
14078 {
14079 const bool have_element_map = (element_map.Size() == num_elements);
14080 MFEM_ASSERT(have_element_map || element_map.Size() == 0,
14081 "invalid MeshPart state");
14082 EntityHelper elem_helper(dim, entity_to_vertex);
14083 MFEM_ASSERT(elem_helper.num_entities == num_elements,
14084 "invalid MeshPart state");
14085 for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
14086 {
14087 const int bytype_elem_id = have_element_map ?
14088 element_map[nat_elem_id] : nat_elem_id;
14089 const Entity ent = elem_helper.FindEntity(bytype_elem_id);
14090 // Print the element
14091 os << attributes[nat_elem_id] << ' ' << ent.geom;
14092 for (int i = 0; i < ent.num_verts; i++)
14093 {
14094 os << ' ' << ent.verts[i];
14095 }
14096 os << '\n';
14097 }
14098 }
14099
14100 os << "\nboundary\n" << num_bdr_elements << '\n';
14101 {
14102 const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
14103 MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
14104 "invalid MeshPart state");
14105 EntityHelper bdr_helper(dim-1, entity_to_vertex);
14106 MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
14107 "invalid MeshPart state");
14108 for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
14109 {
14110 const int bytype_bdr_id = have_boundary_map ?
14111 boundary_map[nat_bdr_id] : nat_bdr_id;
14112 const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
14113 // Print the boundary element
14114 os << bdr_attributes[nat_bdr_id] << ' ' << ent.geom;
14115 for (int i = 0; i < ent.num_verts; i++)
14116 {
14117 os << ' ' << ent.verts[i];
14118 }
14119 os << '\n';
14120 }
14121 }
14122
14123 os << "\nvertices\n" << num_vertices << '\n';
14124 if (!nodes)
14125 {
14126 const int sdim = space_dimension;
14127 os << sdim << '\n';
14128 for (int i = 0; i < num_vertices; i++)
14129 {
14130 os << vertex_coordinates[i*sdim];
14131 for (int d = 1; d < sdim; d++)
14132 {
14133 os << ' ' << vertex_coordinates[i*sdim+d];
14134 }
14135 os << '\n';
14136 }
14137 }
14138 else
14139 {
14140 os << "\nnodes\n";
14141 nodes->Save(os);
14142 }
14143
14144 os << "\nmfem_serial_mesh_end\n";
14145
14146 // Start: GroupTopology::Save
14147 const int num_groups = my_groups.Size();
14148 os << "\ncommunication_groups\n";
14149 os << "number_of_groups " << num_groups << "\n\n";
14150
14151 os << "# number of entities in each group, followed by ranks in group\n";
14152 for (int group_id = 0; group_id < num_groups; ++group_id)
14153 {
14154 const int group_size = my_groups.RowSize(group_id);
14155 const int *group_ptr = my_groups.GetRow(group_id);
14156 os << group_size;
14157 for (int group_member_index = 0; group_member_index < group_size;
14158 ++group_member_index)
14159 {
14160 os << ' ' << group_ptr[group_member_index];
14161 }
14162 os << '\n';
14163 }
14164 // End: GroupTopology::Save
14165
14170
14171 MFEM_VERIFY(g2v.RowSize(0) == 0, "internal erroor");
14172 os << "\ntotal_shared_vertices " << g2v.Size_of_connections() << '\n';
14173 if (dimension >= 2)
14174 {
14175 MFEM_VERIFY(g2ev.RowSize(0) == 0, "internal erroor");
14176 os << "total_shared_edges " << g2ev.Size_of_connections()/2 << '\n';
14177 }
14178 if (dimension >= 3)
14179 {
14180 MFEM_VERIFY(g2tv.RowSize(0) == 0, "internal erroor");
14181 MFEM_VERIFY(g2qv.RowSize(0) == 0, "internal erroor");
14182 const int total_shared_faces =
14183 g2tv.Size_of_connections()/3 + g2qv.Size_of_connections()/4;
14184 os << "total_shared_faces " << total_shared_faces << '\n';
14185 }
14186 os << "\n# group 0 has no shared entities\n";
14187 for (int gr = 1; gr < num_groups; gr++)
14188 {
14189 {
14190 const int nv = g2v.RowSize(gr);
14191 const int *sv = g2v.GetRow(gr);
14192 os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
14193 for (int i = 0; i < nv; i++)
14194 {
14195 os << sv[i] << '\n';
14196 }
14197 }
14198 if (dimension >= 2)
14199 {
14200 const int ne = g2ev.RowSize(gr)/2;
14201 const int *se = g2ev.GetRow(gr);
14202 os << "\nshared_edges " << ne << '\n';
14203 for (int i = 0; i < ne; i++)
14204 {
14205 const int *v = se + 2*i;
14206 os << v[0] << ' ' << v[1] << '\n';
14207 }
14208 }
14209 if (dimension >= 3)
14210 {
14211 const int nt = g2tv.RowSize(gr)/3;
14212 const int *st = g2tv.GetRow(gr);
14213 const int nq = g2qv.RowSize(gr)/4;
14214 const int *sq = g2qv.GetRow(gr);
14215 os << "\nshared_faces " << nt+nq << '\n';
14216 for (int i = 0; i < nt; i++)
14217 {
14218 os << Geometry::TRIANGLE;
14219 const int *v = st + 3*i;
14220 for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
14221 os << '\n';
14222 }
14223 for (int i = 0; i < nq; i++)
14224 {
14225 os << Geometry::SQUARE;
14226 const int *v = sq + 4*i;
14227 for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
14228 os << '\n';
14229 }
14230 }
14231 }
14232
14233 // Write out section end tag for mesh.
14234 os << "\nmfem_mesh_end" << endl;
14235}
14236
14238{
14239 if (mesh) { return *mesh; }
14240
14241 mesh.reset(new Mesh(dimension,
14246
14247 // Add elements
14248 {
14249 const bool have_element_map = (element_map.Size() == num_elements);
14250 MFEM_ASSERT(have_element_map || element_map.Size() == 0,
14251 "invalid MeshPart state");
14253 MFEM_ASSERT(elem_helper.num_entities == num_elements,
14254 "invalid MeshPart state");
14255 const bool have_tet_refine_flags = (tet_refine_flags.Size() > 0);
14256 for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
14257 {
14258 const int bytype_elem_id = have_element_map ?
14259 element_map[nat_elem_id] : nat_elem_id;
14260 const Entity ent = elem_helper.FindEntity(bytype_elem_id);
14261 Element *el = mesh->NewElement(ent.geom);
14262 el->SetVertices(ent.verts);
14263 el->SetAttribute(attributes[nat_elem_id]);
14264 if (ent.geom == Geometry::TETRAHEDRON && have_tet_refine_flags)
14265 {
14266 constexpr int geom_tet = Geometry::TETRAHEDRON;
14267 const int tet_id = (ent.verts - entity_to_vertex[geom_tet])/4;
14268 const int ref_flag = tet_refine_flags[tet_id];
14269 static_cast<Tetrahedron*>(el)->SetRefinementFlag(ref_flag);
14270 }
14271 mesh->AddElement(el);
14272 }
14273 }
14274
14275 // Add boundary elements
14276 {
14277 const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
14278 MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
14279 "invalid MeshPart state");
14281 MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
14282 "invalid MeshPart state");
14283 for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
14284 {
14285 const int bytype_bdr_id = have_boundary_map ?
14286 boundary_map[nat_bdr_id] : nat_bdr_id;
14287 const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
14288 Element *bdr = mesh->NewElement(ent.geom);
14289 bdr->SetVertices(ent.verts);
14290 bdr->SetAttribute(bdr_attributes[nat_bdr_id]);
14291 mesh->AddBdrElement(bdr);
14292 }
14293 }
14294
14295 // Add vertices
14297 {
14298 MFEM_ASSERT(!nodes, "invalid MeshPart state");
14299 for (int vert_id = 0; vert_id < num_vertices; vert_id++)
14300 {
14301 mesh->AddVertex(vertex_coordinates + space_dimension*vert_id);
14302 }
14303 }
14304 else
14305 {
14306 MFEM_ASSERT(vertex_coordinates.Size() == 0, "invalid MeshPart state");
14307 for (int vert_id = 0; vert_id < num_vertices; vert_id++)
14308 {
14309 mesh->AddVertex(0., 0., 0.);
14310 }
14311 // 'mesh.Nodes' cannot be set here -- they can be set later, if needed
14312 }
14313
14314 mesh->FinalizeTopology(/* generate_bdr: */ false);
14315
14316 return *mesh;
14317}
14318
14319
14321 int num_parts_,
14322 const int *partitioning_,
14323 int part_method)
14324 : mesh(mesh_)
14325{
14326 if (partitioning_)
14327 {
14328 partitioning.MakeRef(const_cast<int *>(partitioning_), mesh.GetNE(),
14329 false);
14330 }
14331 else
14332 {
14333 // Mesh::GeneratePartitioning always uses new[] to allocate the,
14334 // partitioning, so we need to tell the memory manager to free it with
14335 // delete[] (even if a different host memory type has been selected).
14336 constexpr MemoryType mt = MemoryType::HOST;
14337 partitioning.MakeRef(mesh.GeneratePartitioning(num_parts_, part_method),
14338 mesh.GetNE(), mt, true);
14339 }
14340
14342 // Note: the element ids in each row of 'part_to_element' are sorted.
14343
14344 const int dim = mesh.Dimension();
14345 if (dim >= 2)
14346 {
14348 }
14349
14350 Array<int> boundary_to_part(mesh.GetNBE());
14351 // Same logic as in ParMesh::BuildLocalBoundary
14352 if (dim >= 3)
14353 {
14354 for (int i = 0; i < boundary_to_part.Size(); i++)
14355 {
14356 int face, o, el1, el2;
14357 mesh.GetBdrElementFace(i, &face, &o);
14358 mesh.GetFaceElements(face, &el1, &el2);
14359 boundary_to_part[i] =
14360 partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2];
14361 }
14362 }
14363 else if (dim == 2)
14364 {
14365 for (int i = 0; i < boundary_to_part.Size(); i++)
14366 {
14367 int edge = mesh.GetBdrElementFaceIndex(i);
14368 int el1 = edge_to_element.GetRow(edge)[0];
14369 boundary_to_part[i] = partitioning[el1];
14370 }
14371 }
14372 else if (dim == 1)
14373 {
14374 for (int i = 0; i < boundary_to_part.Size(); i++)
14375 {
14376 int vert = mesh.GetBdrElementFaceIndex(i);
14377 int el1, el2;
14378 mesh.GetFaceElements(vert, &el1, &el2);
14379 boundary_to_part[i] = partitioning[el1];
14380 }
14381 }
14382 Transpose(boundary_to_part, part_to_boundary, num_parts_);
14383 // Note: the boundary element ids in each row of 'part_to_boundary' are
14384 // sorted.
14385 boundary_to_part.DeleteAll();
14386
14387 Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
14388 vertex_to_element.Swap(*vert_element);
14389 delete vert_element;
14390}
14391
14392void MeshPartitioner::ExtractPart(int part_id, MeshPart &mesh_part) const
14393{
14394 const int num_parts = part_to_element.Size();
14395
14396 MFEM_VERIFY(0 <= part_id && part_id < num_parts,
14397 "invalid part_id = " << part_id
14398 << ", num_parts = " << num_parts);
14399
14400 const int dim = mesh.Dimension();
14401 const int sdim = mesh.SpaceDimension();
14402 const int num_elems = part_to_element.RowSize(part_id);
14403 const int *elem_list = part_to_element.GetRow(part_id); // sorted
14404 const int num_bdr_elems = part_to_boundary.RowSize(part_id);
14405 const int *bdr_elem_list = part_to_boundary.GetRow(part_id); // sorted
14406
14407 // Initialize 'mesh_part'
14408 mesh_part.dimension = dim;
14409 mesh_part.space_dimension = sdim;
14410 mesh_part.num_vertices = 0;
14411 mesh_part.num_elements = num_elems;
14412 mesh_part.num_bdr_elements = num_bdr_elems;
14413 for (int g = 0; g < Geometry::NumGeom; g++)
14414 {
14415 mesh_part.entity_to_vertex[g].SetSize(0); // can reuse Array allocation
14416 }
14417 mesh_part.tet_refine_flags.SetSize(0);
14418 mesh_part.element_map.SetSize(0); // 0 or 'num_elements', if needed
14419 mesh_part.boundary_map.SetSize(0); // 0 or 'num_bdr_elements', if needed
14420 mesh_part.attributes.SetSize(num_elems);
14421 mesh_part.bdr_attributes.SetSize(num_bdr_elems);
14422 mesh_part.vertex_coordinates.SetSize(0);
14423
14424 mesh_part.num_parts = num_parts;
14425 mesh_part.my_part_id = part_id;
14426 mesh_part.my_groups.Clear();
14427 for (int g = 0; g < Geometry::NumGeom; g++)
14428 {
14429 mesh_part.group_shared_entity_to_vertex[g].Clear();
14430 }
14431 mesh_part.nodes.reset(nullptr);
14432 mesh_part.nodal_fes.reset(nullptr);
14433 mesh_part.mesh.reset(nullptr);
14434
14435 // Initialize:
14436 // - 'mesh_part.entity_to_vertex' for the elements (boundary elements are
14437 // set later); vertex ids are global at this point - they will be mapped to
14438 // local ids later
14439 // - 'mesh_part.attributes'
14440 // - 'mesh_part.tet_refine_flags' if needed
14441 int geom_marker = 0, num_geom = 0;
14442 for (int i = 0; i < num_elems; i++)
14443 {
14444 const Element *elem = mesh.GetElement(elem_list[i]);
14445 const int geom = elem->GetGeometryType();
14446 const int nv = Geometry::NumVerts[geom];
14447 const int *v = elem->GetVertices();
14448 MFEM_VERIFY(numeric_limits<int>::max() - nv >=
14449 mesh_part.entity_to_vertex[geom].Size(),
14450 "overflow in 'entity_to_vertex[geom]', geom: "
14451 << Geometry::Name[geom]);
14452 mesh_part.entity_to_vertex[geom].Append(v, nv);
14453 mesh_part.attributes[i] = elem->GetAttribute();
14454 if (geom == Geometry::TETRAHEDRON)
14455 {
14456 // Create 'mesh_part.tet_refine_flags' but only if we find at least one
14457 // non-zero flag in a tetrahedron.
14458 const Tetrahedron *tet = static_cast<const Tetrahedron*>(elem);
14459 const int ref_flag = tet->GetRefinementFlag();
14460 if (mesh_part.tet_refine_flags.Size() == 0)
14461 {
14462 if (ref_flag)
14463 {
14464 // This is the first time we encounter non-zero 'ref_flag'
14465 const int num_tets = mesh_part.entity_to_vertex[geom].Size()/nv;
14466 mesh_part.tet_refine_flags.SetSize(num_tets, 0);
14467 mesh_part.tet_refine_flags.Last() = ref_flag;
14468 }
14469 }
14470 else
14471 {
14472 mesh_part.tet_refine_flags.Append(ref_flag);
14473 }
14474 }
14475 if ((geom_marker & (1 << geom)) == 0)
14476 {
14477 geom_marker |= (1 << geom);
14478 num_geom++;
14479 }
14480 }
14481 MFEM_ASSERT(mesh_part.tet_refine_flags.Size() == 0 ||
14482 mesh_part.tet_refine_flags.Size() ==
14484 "internal error");
14485 // Initialize 'mesh_part.element_map' if needed
14486 if (num_geom > 1)
14487 {
14488 int offsets[Geometry::NumGeom];
14489 int offset = 0;
14490 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
14491 {
14492 offsets[g] = offset;
14493 offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
14494 }
14495 mesh_part.element_map.SetSize(num_elems);
14496 for (int i = 0; i < num_elems; i++)
14497 {
14498 const int geom = mesh.GetElementGeometry(elem_list[i]);
14499 mesh_part.element_map[i] = offsets[geom]++;
14500 }
14501 }
14502
14503 // Initialize:
14504 // - 'mesh_part.entity_to_vertex' for the boundary elements; vertex ids are
14505 // global at this point - they will be mapped to local ids later
14506 // - 'mesh_part.bdr_attributes'
14507 geom_marker = 0; num_geom = 0;
14508 for (int i = 0; i < num_bdr_elems; i++)
14509 {
14510 const Element *bdr_elem = mesh.GetBdrElement(bdr_elem_list[i]);
14511 const int geom = bdr_elem->GetGeometryType();
14512 const int nv = Geometry::NumVerts[geom];
14513 const int *v = bdr_elem->GetVertices();
14514 MFEM_VERIFY(numeric_limits<int>::max() - nv >=
14515 mesh_part.entity_to_vertex[geom].Size(),
14516 "overflow in 'entity_to_vertex[geom]', geom: "
14517 << Geometry::Name[geom]);
14518 mesh_part.entity_to_vertex[geom].Append(v, nv);
14519 mesh_part.bdr_attributes[i] = bdr_elem->GetAttribute();
14520 if ((geom_marker & (1 << geom)) == 0)
14521 {
14522 geom_marker |= (1 << geom);
14523 num_geom++;
14524 }
14525 }
14526 // Initialize 'mesh_part.boundary_map' if needed
14527 if (num_geom > 1)
14528 {
14529 int offsets[Geometry::NumGeom];
14530 int offset = 0;
14531 for (int g = Geometry::DimStart[dim-1]; g < Geometry::DimStart[dim]; g++)
14532 {
14533 offsets[g] = offset;
14534 offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
14535 }
14536 mesh_part.boundary_map.SetSize(num_bdr_elems);
14537 for (int i = 0; i < num_bdr_elems; i++)
14538 {
14539 const int geom = mesh.GetBdrElementGeometry(bdr_elem_list[i]);
14540 mesh_part.boundary_map[i] = offsets[geom]++;
14541 }
14542 }
14543
14544 // Create the vertex id map, 'vertex_loc_to_glob', which maps local ids to
14545 // global ones; the map is sorted, preserving the global ordering.
14546 Array<int> vertex_loc_to_glob;
14547 {
14548 std::unordered_set<int> vertex_set;
14549 for (int i = 0; i < num_elems; i++)
14550 {
14551 const Element *elem = mesh.GetElement(elem_list[i]);
14552 const int geom = elem->GetGeometryType();
14553 const int nv = Geometry::NumVerts[geom];
14554 const int *v = elem->GetVertices();
14555 vertex_set.insert(v, v + nv);
14556 }
14557 vertex_loc_to_glob.SetSize(static_cast<int>(vertex_set.size()));
14558 std::copy(vertex_set.begin(), vertex_set.end(), // src
14559 vertex_loc_to_glob.begin()); // dest
14560 }
14561 vertex_loc_to_glob.Sort();
14562
14563 // Initialize 'mesh_part.num_vertices'
14564 mesh_part.num_vertices = vertex_loc_to_glob.Size();
14565
14566 // Update the vertex ids in the arrays 'mesh_part.entity_to_vertex' from
14567 // global to local.
14568 for (int g = 0; g < Geometry::NumGeom; g++)
14569 {
14570 Array<int> &vert_array = mesh_part.entity_to_vertex[g];
14571 for (int i = 0; i < vert_array.Size(); i++)
14572 {
14573 const int glob_id = vert_array[i];
14574 const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
14575 MFEM_ASSERT(loc_id >= 0, "internal error: global vertex id not found");
14576 vert_array[i] = loc_id;
14577 }
14578 }
14579
14580 // Initialize one of 'mesh_part.vertex_coordinates' or 'mesh_part.nodes'
14581 if (!mesh.GetNodes())
14582 {
14583 MFEM_VERIFY(numeric_limits<int>::max()/sdim >= vertex_loc_to_glob.Size(),
14584 "overflow in 'vertex_coordinates', num_vertices = "
14585 << vertex_loc_to_glob.Size() << ", sdim = " << sdim);
14586 mesh_part.vertex_coordinates.SetSize(sdim*vertex_loc_to_glob.Size());
14587 for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
14588 {
14589 const real_t *coord = mesh.GetVertex(vertex_loc_to_glob[i]);
14590 for (int d = 0; d < sdim; d++)
14591 {
14592 mesh_part.vertex_coordinates[i*sdim+d] = coord[d];
14593 }
14594 }
14595 }
14596 else
14597 {
14598 const GridFunction &glob_nodes = *mesh.GetNodes();
14599 mesh_part.nodal_fes = ExtractFESpace(mesh_part, *glob_nodes.FESpace());
14600 // Initialized 'mesh_part.mesh'.
14601 // Note: the nodes of 'mesh_part.mesh' are not set.
14602
14603 mesh_part.nodes = ExtractGridFunction(mesh_part, glob_nodes,
14604 *mesh_part.nodal_fes);
14605
14606 // Attach the 'mesh_part.nodes' to the 'mesh_part.mesh'.
14607 mesh_part.mesh->NewNodes(*mesh_part.nodes, /* make_owner: */ false);
14608 // Note: the vertices of 'mesh_part.mesh' are not set.
14609 }
14610
14611 // Begin constructing the "neighbor" groups, i.e. the groups that contain
14612 // 'part_id'.
14613 ListOfIntegerSets groups;
14614 {
14615 // the first group is the local one
14616 IntegerSet group;
14617 group.Recreate(1, &part_id);
14618 groups.Insert(group);
14619 }
14620
14621 // 'shared_faces' : shared face id -> (global_face_id, group_id)
14622 // Note: 'shared_faces' will be sorted by 'global_face_id'.
14623 Array<Pair<int,int>> shared_faces;
14624
14625 // Add "neighbor" groups defined by faces
14626 // Construct 'shared_faces'.
14627 if (dim >= 3)
14628 {
14629 std::unordered_set<int> face_set;
14630 // Construct 'face_set'
14631 const Table &elem_to_face = mesh.ElementToFaceTable();
14632 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
14633 {
14634 const int glob_elem_id = elem_list[loc_elem_id];
14635 const int nfaces = elem_to_face.RowSize(glob_elem_id);
14636 const int *faces = elem_to_face.GetRow(glob_elem_id);
14637 face_set.insert(faces, faces + nfaces);
14638 }
14639 // Construct 'shared_faces'; add "neighbor" groups defined by faces.
14640 IntegerSet group;
14641 for (int glob_face_id : face_set)
14642 {
14643 int el[2];
14644 mesh.GetFaceElements(glob_face_id, &el[0], &el[1]);
14645 if (el[1] < 0) { continue; }
14646 el[0] = partitioning[el[0]];
14647 el[1] = partitioning[el[1]];
14648 MFEM_ASSERT(el[0] == part_id || el[1] == part_id, "internal error");
14649 if (el[0] != part_id || el[1] != part_id)
14650 {
14651 group.Recreate(2, el);
14652 const int group_id = groups.Insert(group);
14653 shared_faces.Append(Pair<int,int>(glob_face_id, group_id));
14654 }
14655 }
14656 shared_faces.Sort(); // sort the shared faces by 'glob_face_id'
14657 }
14658
14659 // 'shared_edges' : shared edge id -> (global_edge_id, group_id)
14660 // Note: 'shared_edges' will be sorted by 'global_edge_id'.
14661 Array<Pair<int,int>> shared_edges;
14662
14663 // Add "neighbor" groups defined by edges.
14664 // Construct 'shared_edges'.
14665 if (dim >= 2)
14666 {
14667 std::unordered_set<int> edge_set;
14668 // Construct 'edge_set'
14669 const Table &elem_to_edge = mesh.ElementToEdgeTable();
14670 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
14671 {
14672 const int glob_elem_id = elem_list[loc_elem_id];
14673 const int nedges = elem_to_edge.RowSize(glob_elem_id);
14674 const int *edges = elem_to_edge.GetRow(glob_elem_id);
14675 edge_set.insert(edges, edges + nedges);
14676 }
14677 // Construct 'shared_edges'; add "neighbor" groups defined by edges.
14678 IntegerSet group;
14679 for (int glob_edge_id : edge_set)
14680 {
14681 const int nelem = edge_to_element.RowSize(glob_edge_id);
14682 const int *elem = edge_to_element.GetRow(glob_edge_id);
14683 Array<int> &gr = group; // reference to the 'group' internal Array
14684 gr.SetSize(nelem);
14685 for (int j = 0; j < nelem; j++)
14686 {
14687 gr[j] = partitioning[elem[j]];
14688 }
14689 gr.Sort();
14690 gr.Unique();
14691 MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
14692 if (group.Size() > 1)
14693 {
14694 const int group_id = groups.Insert(group);
14695 shared_edges.Append(Pair<int,int>(glob_edge_id, group_id));
14696 }
14697 }
14698 shared_edges.Sort(); // sort the shared edges by 'glob_edge_id'
14699 }
14700
14701 // 'shared_verts' : shared vertex id -> (global_vertex_id, group_id)
14702 // Note: 'shared_verts' will be sorted by 'global_vertex_id'.
14703 Array<Pair<int,int>> shared_verts;
14704
14705 // Add "neighbor" groups defined by vertices.
14706 // Construct 'shared_verts'.
14707 {
14708 IntegerSet group;
14709 for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
14710 {
14711 // 'vertex_to_element' maps global vertex ids to global element ids
14712 const int glob_vertex_id = vertex_loc_to_glob[i];
14713 const int nelem = vertex_to_element.RowSize(glob_vertex_id);
14714 const int *elem = vertex_to_element.GetRow(glob_vertex_id);
14715 Array<int> &gr = group; // reference to the 'group' internal Array
14716 gr.SetSize(nelem);
14717 for (int j = 0; j < nelem; j++)
14718 {
14719 gr[j] = partitioning[elem[j]];
14720 }
14721 gr.Sort();
14722 gr.Unique();
14723 MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
14724 if (group.Size() > 1)
14725 {
14726 const int group_id = groups.Insert(group);
14727 shared_verts.Append(Pair<int,int>(glob_vertex_id, group_id));
14728 }
14729 }
14730 }
14731
14732 // Done constructing the "neighbor" groups in 'groups'.
14733 const int num_groups = groups.Size();
14734
14735 // Define 'mesh_part.my_groups'
14736 groups.AsTable(mesh_part.my_groups);
14737
14738 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::POINT]'
14739 Table &group__shared_vertex_to_vertex =
14741 group__shared_vertex_to_vertex.MakeI(num_groups);
14742 for (int sv = 0; sv < shared_verts.Size(); sv++)
14743 {
14744 const int group_id = shared_verts[sv].two;
14745 group__shared_vertex_to_vertex.AddAColumnInRow(group_id);
14746 }
14747 group__shared_vertex_to_vertex.MakeJ();
14748 for (int sv = 0; sv < shared_verts.Size(); sv++)
14749 {
14750 const int glob_vertex_id = shared_verts[sv].one;
14751 const int group_id = shared_verts[sv].two;
14752 const int loc_vertex_id = vertex_loc_to_glob.FindSorted(glob_vertex_id);
14753 MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
14754 group__shared_vertex_to_vertex.AddConnection(group_id, loc_vertex_id);
14755 }
14756 group__shared_vertex_to_vertex.ShiftUpI();
14757
14758 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::SEGMENT]'
14759 if (dim >= 2)
14760 {
14761 Table &group__shared_edge_to_vertex =
14763 group__shared_edge_to_vertex.MakeI(num_groups);
14764 for (int se = 0; se < shared_edges.Size(); se++)
14765 {
14766 const int group_id = shared_edges[se].two;
14767 group__shared_edge_to_vertex.AddColumnsInRow(group_id, 2);
14768 }
14769 group__shared_edge_to_vertex.MakeJ();
14770 const Table &edge_to_vertex = *mesh.GetEdgeVertexTable();
14771 for (int se = 0; se < shared_edges.Size(); se++)
14772 {
14773 const int glob_edge_id = shared_edges[se].one;
14774 const int group_id = shared_edges[se].two;
14775 const int *v = edge_to_vertex.GetRow(glob_edge_id);
14776 for (int i = 0; i < 2; i++)
14777 {
14778 const int loc_vertex_id = vertex_loc_to_glob.FindSorted(v[i]);
14779 MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
14780 group__shared_edge_to_vertex.AddConnection(group_id, loc_vertex_id);
14781 }
14782 }
14783 group__shared_edge_to_vertex.ShiftUpI();
14784 }
14785
14786 // Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::TRIANGLE]'
14787 // and 'mesh_part.group_shared_entity_to_vertex[Geometry::SQUARE]'.
14788 if (dim >= 3)
14789 {
14790 Table &group__shared_tria_to_vertex =
14792 Table &group__shared_quad_to_vertex =
14794 Array<int> vertex_ids;
14795 group__shared_tria_to_vertex.MakeI(num_groups);
14796 group__shared_quad_to_vertex.MakeI(num_groups);
14797 for (int sf = 0; sf < shared_faces.Size(); sf++)
14798 {
14799 const int glob_face_id = shared_faces[sf].one;
14800 const int group_id = shared_faces[sf].two;
14801 const int geom = mesh.GetFaceGeometry(glob_face_id);
14802 mesh_part.group_shared_entity_to_vertex[geom].
14803 AddColumnsInRow(group_id, Geometry::NumVerts[geom]);
14804 }
14805 group__shared_tria_to_vertex.MakeJ();
14806 group__shared_quad_to_vertex.MakeJ();
14807 for (int sf = 0; sf < shared_faces.Size(); sf++)
14808 {
14809 const int glob_face_id = shared_faces[sf].one;
14810 const int group_id = shared_faces[sf].two;
14811 const int geom = mesh.GetFaceGeometry(glob_face_id);
14812 mesh.GetFaceVertices(glob_face_id, vertex_ids);
14813 // Rotate shared triangles that have an adjacent tetrahedron with a
14814 // nonzero refinement flag.
14815 // See also ParMesh::BuildSharedFaceElems.
14816 if (geom == Geometry::TRIANGLE)
14817 {
14818 int glob_el_id[2];
14819 mesh.GetFaceElements(glob_face_id, &glob_el_id[0], &glob_el_id[1]);
14820 int side = 0;
14821 const Element *el = mesh.GetElement(glob_el_id[0]);
14822 const Tetrahedron *tet = nullptr;
14824 {
14825 tet = static_cast<const Tetrahedron*>(el);
14826 }
14827 else
14828 {
14829 side = 1;
14830 el = mesh.GetElement(glob_el_id[1]);
14832 {
14833 tet = static_cast<const Tetrahedron*>(el);
14834 }
14835 }
14836 if (tet && tet->GetRefinementFlag())
14837 {
14838 // mark the shared face for refinement by reorienting
14839 // it according to the refinement flag in the tetrahedron
14840 // to which this shared face belongs to.
14841 int info[2];
14842 mesh.GetFaceInfos(glob_face_id, &info[0], &info[1]);
14843 tet->GetMarkedFace(info[side]/64, &vertex_ids[0]);
14844 }
14845 }
14846 for (int i = 0; i < vertex_ids.Size(); i++)
14847 {
14848 const int glob_id = vertex_ids[i];
14849 const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
14850 MFEM_ASSERT(loc_id >= 0, "internal error");
14851 vertex_ids[i] = loc_id;
14852 }
14853 mesh_part.group_shared_entity_to_vertex[geom].
14854 AddConnections(group_id, vertex_ids, vertex_ids.Size());
14855 }
14856 group__shared_tria_to_vertex.ShiftUpI();
14857 group__shared_quad_to_vertex.ShiftUpI();
14858 }
14859}
14860
14861std::unique_ptr<FiniteElementSpace>
14863 const FiniteElementSpace &global_fespace) const
14864{
14865 mesh_part.GetMesh(); // initialize 'mesh_part.mesh'
14866 // Note: the nodes of 'mesh_part.mesh' are not set by GetMesh() unless they
14867 // were already constructed, e.g. by ExtractPart().
14868
14869 return std::unique_ptr<FiniteElementSpace>(
14870 new FiniteElementSpace(mesh_part.mesh.get(),
14871 global_fespace.FEColl(),
14872 global_fespace.GetVDim(),
14873 global_fespace.GetOrdering()));
14874}
14875
14876std::unique_ptr<GridFunction>
14878 const GridFunction &global_gf,
14879 FiniteElementSpace &local_fespace) const
14880{
14881 std::unique_ptr<GridFunction> local_gf(new GridFunction(&local_fespace));
14882
14883 // Transfer data from 'global_gf' to 'local_gf'.
14884 Array<int> gvdofs, lvdofs;
14885 Vector loc_vals;
14886 const int part_id = mesh_part.my_part_id;
14887 const int num_elems = part_to_element.RowSize(part_id);
14888 const int *elem_list = part_to_element.GetRow(part_id); // sorted
14889 for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
14890 {
14891 const int glob_elem_id = elem_list[loc_elem_id];
14892 DofTransformation glob_dt, local_dt;
14893 global_gf.FESpace()->GetElementVDofs(glob_elem_id, gvdofs, glob_dt);
14894 global_gf.GetSubVector(gvdofs, loc_vals);
14895 glob_dt.InvTransformPrimal(loc_vals);
14896 local_fespace.GetElementVDofs(loc_elem_id, lvdofs, local_dt);
14897 local_dt.TransformPrimal(loc_vals);
14898 local_gf->SetSubVector(lvdofs, loc_vals);
14899 }
14900 return local_gf;
14901}
14902
14903
14905 int flags, MemoryType d_mt)
14906{
14907 this->mesh = mesh;
14908 IntRule = &ir;
14909 computed_factors = flags;
14910
14911 MFEM_ASSERT(mesh->GetNumGeometries(mesh->Dimension()) <= 1,
14912 "mixed meshes are not supported!");
14913 MFEM_ASSERT(mesh->GetNodes(), "meshes without nodes are not supported!");
14914
14915 Compute(*mesh->GetNodes(), d_mt);
14916}
14917
14919 const IntegrationRule &ir,
14920 int flags, MemoryType d_mt)
14921{
14922 this->mesh = nodes.FESpace()->GetMesh();
14923 IntRule = &ir;
14924 computed_factors = flags;
14925
14926 Compute(nodes, d_mt);
14927}
14928
14929void GeometricFactors::Compute(const GridFunction &nodes,
14930 MemoryType d_mt)
14931{
14932
14933 const FiniteElementSpace *fespace = nodes.FESpace();
14934 const FiniteElement *fe = fespace->GetTypicalFE();
14935 const int dim = fe->GetDim();
14936 const int vdim = fespace->GetVDim();
14937 const int NE = fespace->GetNE();
14938 const int ND = fe->GetDof();
14939 const int NQ = IntRule->GetNPoints();
14940
14941 unsigned eval_flags = 0;
14942 MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
14943 Device::GetDeviceMemoryType();
14945 {
14946 X.SetSize(vdim*NQ*NE, my_d_mt); // NQ x SDIM x NE
14947 eval_flags |= QuadratureInterpolator::VALUES;
14948 }
14950 {
14951 J.SetSize(dim*vdim*NQ*NE, my_d_mt); // NQ x SDIM x DIM x NE
14953 }
14955 {
14956 detJ.SetSize(NQ*NE, my_d_mt); // NQ x NE
14958 }
14959
14960 const QuadratureInterpolator *qi = fespace->GetQuadratureInterpolator(*IntRule);
14961 // All X, J, and detJ use this layout:
14963
14964 const bool use_tensor_products = UsesTensorBasis(*fespace);
14965
14966 qi->DisableTensorProducts(!use_tensor_products);
14967 const ElementDofOrdering e_ordering = use_tensor_products ?
14970 const Operator *elem_restr = fespace->GetElementRestriction(e_ordering);
14971
14972 if (elem_restr) // Always true as of 2021-04-27
14973 {
14974 Vector Enodes(vdim*ND*NE, my_d_mt);
14975 elem_restr->Mult(nodes, Enodes);
14976 qi->Mult(Enodes, eval_flags, X, J, detJ);
14977 }
14978 else
14979 {
14980 qi->Mult(nodes, eval_flags, X, J, detJ);
14981 }
14982}
14983
14985 const IntegrationRule &ir,
14986 int flags, FaceType type,
14987 MemoryType d_mt)
14988 : type(type)
14989{
14990 this->mesh = mesh;
14991 IntRule = &ir;
14992 computed_factors = flags;
14993
14994 const GridFunction *nodes = mesh->GetNodes();
14995 const FiniteElementSpace *fespace = nodes->FESpace();
14996 const int vdim = fespace->GetVDim();
14997 const int NF = fespace->GetNFbyType(type);
14998 const int NQ = ir.GetNPoints();
14999
15000 const FaceRestriction *face_restr = fespace->GetFaceRestriction(
15002 type,
15004
15005
15006 MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
15008
15009 Vector Fnodes(face_restr->Height(), my_d_mt);
15010 face_restr->Mult(*nodes, Fnodes);
15011
15012 unsigned eval_flags = 0;
15013
15015 {
15016 X.SetSize(vdim*NQ*NF, my_d_mt);
15018 }
15020 {
15021 J.SetSize(vdim*(mesh->Dimension() - 1)*NQ*NF, my_d_mt);
15023 }
15025 {
15026 detJ.SetSize(NQ*NF, my_d_mt);
15028 }
15030 {
15031 normal.SetSize(vdim*NQ*NF, my_d_mt);
15033 }
15034
15035 const FaceQuadratureInterpolator *qi =
15036 fespace->GetFaceQuadratureInterpolator(ir, type);
15037 // All face data vectors assume layout byNODES.
15039 const bool use_tensor_products = UsesTensorBasis(*fespace);
15040 qi->DisableTensorProducts(!use_tensor_products);
15041
15042 qi->Mult(Fnodes, eval_flags, X, J, detJ, normal);
15043}
15044
15046 const real_t s_)
15047 : VectorCoefficient(dim), n(n_), s(s_), tip(p, dim-1)
15048{
15049}
15050
15052 const IntegrationPoint &ip)
15053{
15054 V.SetSize(vdim);
15055 T.Transform(ip, tip);
15056 V(0) = p[0];
15057 if (vdim == 2)
15058 {
15059 V(1) = s * ((ip.y + layer) / n);
15060 }
15061 else
15062 {
15063 V(1) = p[1];
15064 V(2) = s * ((ip.z + layer) / n);
15065 }
15066}
15067
15068
15069Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy, const bool closed)
15070{
15071 if (mesh->Dimension() != 1)
15072 {
15073 mfem::err << "Extrude1D : Not a 1D mesh!" << endl;
15074 mfem_error();
15075 }
15076
15077 int nvy = (closed) ? (ny) : (ny + 1);
15078 int nvt = mesh->GetNV() * nvy;
15079
15080 Mesh *mesh2d;
15081
15082 if (closed)
15083 {
15084 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny, mesh->GetNBE()*ny);
15085 }
15086 else
15087 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny,
15088 mesh->GetNBE()*ny+2*mesh->GetNE());
15089
15090 // vertices
15091 real_t vc[2];
15092 for (int i = 0; i < mesh->GetNV(); i++)
15093 {
15094 vc[0] = mesh->GetVertex(i)[0];
15095 for (int j = 0; j < nvy; j++)
15096 {
15097 vc[1] = sy * (real_t(j) / ny);
15098 mesh2d->AddVertex(vc);
15099 }
15100 }
15101 // elements
15102 Array<int> vert;
15103 for (int i = 0; i < mesh->GetNE(); i++)
15104 {
15105 const Element *elem = mesh->GetElement(i);
15106 elem->GetVertices(vert);
15107 const int attr = elem->GetAttribute();
15108 for (int j = 0; j < ny; j++)
15109 {
15110 int qv[4];
15111 qv[0] = vert[0] * nvy + j;
15112 qv[1] = vert[1] * nvy + j;
15113 qv[2] = vert[1] * nvy + (j + 1) % nvy;
15114 qv[3] = vert[0] * nvy + (j + 1) % nvy;
15115
15116 mesh2d->AddQuad(qv, attr);
15117 }
15118 }
15119 // 2D boundary from the 1D boundary
15120 for (int i = 0; i < mesh->GetNBE(); i++)
15121 {
15122 const Element *elem = mesh->GetBdrElement(i);
15123 elem->GetVertices(vert);
15124 const int attr = elem->GetAttribute();
15125 for (int j = 0; j < ny; j++)
15126 {
15127 int sv[2];
15128 sv[0] = vert[0] * nvy + j;
15129 sv[1] = vert[0] * nvy + (j + 1) % nvy;
15130
15131 if (attr%2)
15132 {
15133 Swap<int>(sv[0], sv[1]);
15134 }
15135
15136 mesh2d->AddBdrSegment(sv, attr);
15137 }
15138 }
15139
15140 if (!closed)
15141 {
15142 // 2D boundary from the 1D elements (bottom + top)
15143 int nba = (mesh->bdr_attributes.Size() > 0 ?
15144 mesh->bdr_attributes.Max() : 0);
15145 for (int i = 0; i < mesh->GetNE(); i++)
15146 {
15147 const Element *elem = mesh->GetElement(i);
15148 elem->GetVertices(vert);
15149 const int attr = nba + elem->GetAttribute();
15150 int sv[2];
15151 sv[0] = vert[0] * nvy;
15152 sv[1] = vert[1] * nvy;
15153
15154 mesh2d->AddBdrSegment(sv, attr);
15155
15156 sv[0] = vert[1] * nvy + ny;
15157 sv[1] = vert[0] * nvy + ny;
15158
15159 mesh2d->AddBdrSegment(sv, attr);
15160 }
15161 }
15162
15163 mesh2d->FinalizeQuadMesh(1, 0, false);
15164
15165 GridFunction *nodes = mesh->GetNodes();
15166 if (nodes)
15167 {
15168 // duplicate the fec of the 1D mesh so that it can be deleted safely
15169 // along with its nodes, fes and fec
15170 FiniteElementCollection *fec2d = NULL;
15171 FiniteElementSpace *fes2d;
15172 const char *name = nodes->FESpace()->FEColl()->Name();
15173 string cname = name;
15174 if (cname == "Linear")
15175 {
15176 fec2d = new LinearFECollection;
15177 }
15178 else if (cname == "Quadratic")
15179 {
15180 fec2d = new QuadraticFECollection;
15181 }
15182 else if (cname == "Cubic")
15183 {
15184 fec2d = new CubicFECollection;
15185 }
15186 else if (!strncmp(name, "H1_", 3))
15187 {
15188 fec2d = new H1_FECollection(atoi(name + 7), 2);
15189 }
15190 else if (!strncmp(name, "L2_T", 4))
15191 {
15192 fec2d = new L2_FECollection(atoi(name + 10), 2, atoi(name + 4));
15193 }
15194 else if (!strncmp(name, "L2_", 3))
15195 {
15196 fec2d = new L2_FECollection(atoi(name + 7), 2);
15197 }
15198 else
15199 {
15200 delete mesh2d;
15201 mfem::err << "Extrude1D : The mesh uses unknown FE collection : "
15202 << cname << endl;
15203 mfem_error();
15204 }
15205 fes2d = new FiniteElementSpace(mesh2d, fec2d, 2);
15206 mesh2d->SetNodalFESpace(fes2d);
15207 GridFunction *nodes2d = mesh2d->GetNodes();
15208 nodes2d->MakeOwner(fec2d);
15209
15210 NodeExtrudeCoefficient ecoeff(2, ny, sy);
15211 Vector lnodes;
15212 Array<int> vdofs2d;
15213 for (int i = 0; i < mesh->GetNE(); i++)
15214 {
15216 for (int j = ny-1; j >= 0; j--)
15217 {
15218 fes2d->GetElementVDofs(i*ny+j, vdofs2d);
15219 lnodes.SetSize(vdofs2d.Size());
15220 ecoeff.SetLayer(j);
15221 fes2d->GetFE(i*ny+j)->Project(ecoeff, T, lnodes);
15222 nodes2d->SetSubVector(vdofs2d, lnodes);
15223 }
15224 }
15225 }
15226 return mesh2d;
15227}
15228
15229Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz)
15230{
15231 if (mesh->Dimension() != 2)
15232 {
15233 mfem::err << "Extrude2D : Not a 2D mesh!" << endl;
15234 mfem_error();
15235 }
15236
15237 int nvz = nz + 1;
15238 int nvt = mesh->GetNV() * nvz;
15239
15240 Mesh *mesh3d = new Mesh(3, nvt, mesh->GetNE()*nz,
15241 mesh->GetNBE()*nz+2*mesh->GetNE());
15242
15243 bool wdgMesh = false;
15244 bool hexMesh = false;
15245
15246 // vertices
15247 real_t vc[3];
15248 for (int i = 0; i < mesh->GetNV(); i++)
15249 {
15250 vc[0] = mesh->GetVertex(i)[0];
15251 vc[1] = mesh->GetVertex(i)[1];
15252 for (int j = 0; j < nvz; j++)
15253 {
15254 vc[2] = sz * (real_t(j) / nz);
15255 mesh3d->AddVertex(vc);
15256 }
15257 }
15258 // elements
15259 Array<int> vert;
15260 for (int i = 0; i < mesh->GetNE(); i++)
15261 {
15262 const Element *elem = mesh->GetElement(i);
15263 elem->GetVertices(vert);
15264 const int attr = elem->GetAttribute();
15265 Geometry::Type geom = elem->GetGeometryType();
15266 switch (geom)
15267 {
15268 case Geometry::TRIANGLE:
15269 wdgMesh = true;
15270 for (int j = 0; j < nz; j++)
15271 {
15272 int pv[6];
15273 pv[0] = vert[0] * nvz + j;
15274 pv[1] = vert[1] * nvz + j;
15275 pv[2] = vert[2] * nvz + j;
15276 pv[3] = vert[0] * nvz + (j + 1) % nvz;
15277 pv[4] = vert[1] * nvz + (j + 1) % nvz;
15278 pv[5] = vert[2] * nvz + (j + 1) % nvz;
15279
15280 mesh3d->AddWedge(pv, attr);
15281 }
15282 break;
15283 case Geometry::SQUARE:
15284 hexMesh = true;
15285 for (int j = 0; j < nz; j++)
15286 {
15287 int hv[8];
15288 hv[0] = vert[0] * nvz + j;
15289 hv[1] = vert[1] * nvz + j;
15290 hv[2] = vert[2] * nvz + j;
15291 hv[3] = vert[3] * nvz + j;
15292 hv[4] = vert[0] * nvz + (j + 1) % nvz;
15293 hv[5] = vert[1] * nvz + (j + 1) % nvz;
15294 hv[6] = vert[2] * nvz + (j + 1) % nvz;
15295 hv[7] = vert[3] * nvz + (j + 1) % nvz;
15296
15297 mesh3d->AddHex(hv, attr);
15298 }
15299 break;
15300 default:
15301 mfem::err << "Extrude2D : Invalid 2D element type \'"
15302 << geom << "\'" << endl;
15303 mfem_error();
15304 break;
15305 }
15306 }
15307 // 3D boundary from the 2D boundary
15308 for (int i = 0; i < mesh->GetNBE(); i++)
15309 {
15310 const Element *elem = mesh->GetBdrElement(i);
15311 elem->GetVertices(vert);
15312 const int attr = elem->GetAttribute();
15313 for (int j = 0; j < nz; j++)
15314 {
15315 int qv[4];
15316 qv[0] = vert[0] * nvz + j;
15317 qv[1] = vert[1] * nvz + j;
15318 qv[2] = vert[1] * nvz + (j + 1) % nvz;
15319 qv[3] = vert[0] * nvz + (j + 1) % nvz;
15320
15321 mesh3d->AddBdrQuad(qv, attr);
15322 }
15323 }
15324
15325 // 3D boundary from the 2D elements (bottom + top)
15326 int nba = (mesh->bdr_attributes.Size() > 0 ?
15327 mesh->bdr_attributes.Max() : 0);
15328 for (int i = 0; i < mesh->GetNE(); i++)
15329 {
15330 const Element *elem = mesh->GetElement(i);
15331 elem->GetVertices(vert);
15332 const int attr = nba + elem->GetAttribute();
15333 Geometry::Type geom = elem->GetGeometryType();
15334 switch (geom)
15335 {
15336 case Geometry::TRIANGLE:
15337 {
15338 int tv[3];
15339 tv[0] = vert[0] * nvz;
15340 tv[1] = vert[2] * nvz;
15341 tv[2] = vert[1] * nvz;
15342
15343 mesh3d->AddBdrTriangle(tv, attr);
15344
15345 tv[0] = vert[0] * nvz + nz;
15346 tv[1] = vert[1] * nvz + nz;
15347 tv[2] = vert[2] * nvz + nz;
15348
15349 mesh3d->AddBdrTriangle(tv, attr);
15350 }
15351 break;
15352 case Geometry::SQUARE:
15353 {
15354 int qv[4];
15355 qv[0] = vert[0] * nvz;
15356 qv[1] = vert[3] * nvz;
15357 qv[2] = vert[2] * nvz;
15358 qv[3] = vert[1] * nvz;
15359
15360 mesh3d->AddBdrQuad(qv, attr);
15361
15362 qv[0] = vert[0] * nvz + nz;
15363 qv[1] = vert[1] * nvz + nz;
15364 qv[2] = vert[2] * nvz + nz;
15365 qv[3] = vert[3] * nvz + nz;
15366
15367 mesh3d->AddBdrQuad(qv, attr);
15368 }
15369 break;
15370 default:
15371 mfem::err << "Extrude2D : Invalid 2D element type \'"
15372 << geom << "\'" << endl;
15373 mfem_error();
15374 break;
15375 }
15376 }
15377
15378 if ( hexMesh && wdgMesh )
15379 {
15380 mesh3d->FinalizeMesh(0, false);
15381 }
15382 else if ( hexMesh )
15383 {
15384 mesh3d->FinalizeHexMesh(1, 0, false);
15385 }
15386 else if ( wdgMesh )
15387 {
15388 mesh3d->FinalizeWedgeMesh(1, 0, false);
15389 }
15390
15391 GridFunction *nodes = mesh->GetNodes();
15392 if (nodes)
15393 {
15394 // duplicate the fec of the 2D mesh so that it can be deleted safely
15395 // along with its nodes, fes and fec
15396 FiniteElementCollection *fec3d = NULL;
15397 FiniteElementSpace *fes3d;
15398 const char *name = nodes->FESpace()->FEColl()->Name();
15399 string cname = name;
15400 if (cname == "Linear")
15401 {
15402 fec3d = new LinearFECollection;
15403 }
15404 else if (cname == "Quadratic")
15405 {
15406 fec3d = new QuadraticFECollection;
15407 }
15408 else if (cname == "Cubic")
15409 {
15410 fec3d = new CubicFECollection;
15411 }
15412 else if (!strncmp(name, "H1_", 3))
15413 {
15414 fec3d = new H1_FECollection(atoi(name + 7), 3);
15415 }
15416 else if (!strncmp(name, "L2_T", 4))
15417 {
15418 fec3d = new L2_FECollection(atoi(name + 10), 3, atoi(name + 4));
15419 }
15420 else if (!strncmp(name, "L2_", 3))
15421 {
15422 fec3d = new L2_FECollection(atoi(name + 7), 3);
15423 }
15424 else
15425 {
15426 delete mesh3d;
15427 mfem::err << "Extrude3D : The mesh uses unknown FE collection : "
15428 << cname << endl;
15429 mfem_error();
15430 }
15431 fes3d = new FiniteElementSpace(mesh3d, fec3d, 3);
15432 mesh3d->SetNodalFESpace(fes3d);
15433 GridFunction *nodes3d = mesh3d->GetNodes();
15434 nodes3d->MakeOwner(fec3d);
15435
15436 NodeExtrudeCoefficient ecoeff(3, nz, sz);
15437 Vector lnodes;
15438 Array<int> vdofs3d;
15439 for (int i = 0; i < mesh->GetNE(); i++)
15440 {
15442 for (int j = nz-1; j >= 0; j--)
15443 {
15444 fes3d->GetElementVDofs(i*nz+j, vdofs3d);
15445 lnodes.SetSize(vdofs3d.Size());
15446 ecoeff.SetLayer(j);
15447 fes3d->GetFE(i*nz+j)->Project(ecoeff, T, lnodes);
15448 nodes3d->SetSubVector(vdofs3d, lnodes);
15449 }
15450 }
15451 }
15452 return mesh3d;
15453}
15454
15456{
15457 if (NURBSext)
15458 {
15459 // NURBS meshes are always conforming (element-wise). NURBS patch
15460 // conformity is indicated by NURBSExtension::NonconformingPatches.
15461 return true;
15462 }
15463 else
15464 {
15465 return ncmesh == NULL;
15466 }
15467}
15468
15469#ifdef MFEM_DEBUG
15470void Mesh::DebugDump(std::ostream &os) const
15471{
15472 // dump vertices and edges (NCMesh "nodes")
15473 os << NumOfVertices + NumOfEdges << "\n";
15474 for (int i = 0; i < NumOfVertices; i++)
15475 {
15476 const real_t *v = GetVertex(i);
15477 os << i << " " << v[0] << " " << v[1] << " " << v[2]
15478 << " 0 0 " << i << " -1 0\n";
15479 }
15480
15481 Array<int> ev;
15482 for (int i = 0; i < NumOfEdges; i++)
15483 {
15484 GetEdgeVertices(i, ev);
15485 real_t mid[3] = {0, 0, 0};
15486 for (int j = 0; j < 2; j++)
15487 {
15488 for (int k = 0; k < spaceDim; k++)
15489 {
15490 mid[k] += GetVertex(ev[j])[k];
15491 }
15492 }
15493 os << NumOfVertices+i << " "
15494 << mid[0]/2 << " " << mid[1]/2 << " " << mid[2]/2 << " "
15495 << ev[0] << " " << ev[1] << " -1 " << i << " 0\n";
15496 }
15497
15498 // dump elements
15499 os << NumOfElements << "\n";
15500 for (int i = 0; i < NumOfElements; i++)
15501 {
15502 const Element* e = elements[i];
15503 os << e->GetNVertices() << " ";
15504 for (int j = 0; j < e->GetNVertices(); j++)
15505 {
15506 os << e->GetVertices()[j] << " ";
15507 }
15508 os << e->GetAttribute() << " 0 " << i << "\n";
15509 }
15510
15511 // dump faces
15512 os << "0\n";
15513}
15514#endif
15515
15516}
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:981
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:312
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:184
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
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:166
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:1053
void DeleteAll()
Delete the whole array.
Definition array.hpp:1033
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:912
T * GetData()
Returns the data.
Definition array.hpp:140
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1042
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:320
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:401
T * end()
STL-like end. Returns pointer after the last element of the array.
Definition array.hpp:369
T * begin()
STL-like begin. Returns pointer to the first element of the array.
Definition array.hpp:366
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:393
T & Last()
Return the last element in the array.
Definition array.hpp:945
bool SetsExist() const
Return true if any named sets are currently defined.
bool AttributeSetExists(const std::string &name) const
Return true is the named attribute set is present.
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
Piecewise-(bi)cubic continuous finite elements.
Definition fe_coll.hpp:959
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:489
void GetColumnReference(int c, Vector &col)
Definition densemat.hpp:331
real_t * Data() const
Returns the matrix data array. Warning: this method casts away constness.
Definition densemat.hpp:122
real_t * GetData() const
Returns the matrix data array. Warning: this method casts away constness.
Definition densemat.hpp:126
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:116
real_t Weight() const
Definition densemat.cpp:553
real_t 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:288
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:124
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition device.hpp:277
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:3130
Vector normal
Normals at all quadrature points.
Definition mesh.hpp:3176
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3163
const IntegrationRule * IntRule
Definition mesh.hpp:3133
Vector X
Mapped (physical) coordinates of all quadrature points.
Definition mesh.hpp:3154
FaceGeometricFactors(const Mesh *mesh, const IntegrationRule &ir, int flags, FaceType type, MemoryType d_mt=MemoryType::DEFAULT)
Definition mesh.cpp:14984
Vector detJ
Determinants of the Jacobians at all quadrature points.
Definition mesh.hpp:3169
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:208
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:230
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:3805
const FiniteElement * GetBE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th boundary fac...
Definition fespace.cpp:3870
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:3502
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:337
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3750
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition fespace.hpp:823
virtual void UpdateMeshPointer(Mesh *new_mesh)
Definition fespace.cpp:4317
const QuadratureInterpolator * GetQuadratureInterpolator(const IntegrationRule &ir) const
Return a QuadratureInterpolator that interpolates E-vectors to quadrature point values and/or derivat...
Definition fespace.cpp:1552
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:304
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3824
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:854
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:869
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1480
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:3933
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:331
virtual void Update(bool want_transform=true)
Reflect changes in the mesh: update number of DOFs, etc. Also, calculate GridFunction transformation ...
Definition fespace.cpp:4146
const FiniteElement * GetTraceElement(int i, Geometry::Type geom_type) const
Return the trace element from element 'i' to the given 'geom_type'.
Definition fespace.cpp:3942
void SetRelaxedHpConformity(bool relaxed=true)
Definition fespace.hpp:1517
int GetNFDofs() const
Number of all scalar face-interior dofs.
Definition fespace.hpp:863
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3903
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
Definition fespace.cpp:168
int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type.
Definition fespace.hpp:886
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:856
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:644
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:3607
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:819
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:1591
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1503
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:3781
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:3860
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:674
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:3760
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:319
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:266
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:1513
Abstract class for all finite elements.
Definition fe_base.hpp:247
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:324
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:403
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:334
virtual void Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
Given a coefficient and a transformation, compute its projection (approximation) in the local finite ...
Definition fe_base.cpp:136
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:337
Structure for storing mesh geometric factors: coordinates, Jacobians, and determinants of the Jacobia...
Definition mesh.hpp:3076
Vector X
Mapped (physical) coordinates of all quadrature points.
Definition mesh.hpp:3106
const Mesh * mesh
Definition mesh.hpp:3082
const IntegrationRule * IntRule
Definition mesh.hpp:3083
Vector detJ
Determinants of the Jacobians at all quadrature points.
Definition mesh.hpp:3121
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3115
GeometricFactors(const Mesh *mesh, const IntegrationRule &ir, int flags, MemoryType d_mt=MemoryType::DEFAULT)
Definition mesh.cpp:14904
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:32
virtual void Update()
Transform by the Space UpdateMatrix (e.g., on Mesh change).
Definition gridfunc.cpp:168
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec_owned and fes.
Definition gridfunc.hpp:123
FiniteElementSpace * FESpace()
virtual void ProjectCoefficient(Coefficient &coeff)
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
Definition gridfunc.cpp:347
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:397
void GetVectorValues(int i, const IntegrationRule &ir, DenseMatrix &vals, DenseMatrix &tr) const
Definition gridfunc.cpp:707
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:279
const int * GetDofMap(Geometry::Type GeomType) const
Get the Cartesian to local H1 dof map.
Definition fe_coll.cpp:2088
int GetId(int p1, int p2)
Get the "id" of the item whose parents are p1, p2, this "id" corresponding to the index of the item i...
Definition hash.hpp:684
int FindId(int p1, int p2) const
Find the "id" of an item whose parents are p1, p2. Return -1 if it does not exist.
Definition hash.hpp:775
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:60
void Set3(const real_t x1, const real_t x2, const real_t x3)
Definition intrules.hpp:79
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:256
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:259
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:350
Piecewise-(bi/tri)linear continuous finite elements.
Definition fe_coll.hpp:879
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:2765
Array< real_t > vertex_coordinates
Definition mesh.hpp:2865
int dimension
Reference space dimension of the elements.
Definition mesh.hpp:2782
int num_vertices
Number of vertices.
Definition mesh.hpp:2788
Table group_shared_entity_to_vertex[Geometry::NumGeom]
Definition mesh.hpp:2928
Array< int > entity_to_vertex[Geometry::NumGeom]
Definition mesh.hpp:2817
std::unique_ptr< Mesh > mesh
Definition mesh.hpp:2872
Array< int > boundary_map
Optional re-ordering for the boundary elements, similar to 'element_map'.
Definition mesh.hpp:2841
std::unique_ptr< FiniteElementSpace > nodal_fes
Definition mesh.hpp:2878
int num_parts
Total number of MeshParts.
Definition mesh.hpp:2892
Array< int > tet_refine_flags
Store the refinement flags for tetraheral elements. If all tets have zero refinement flags then this ...
Definition mesh.hpp:2821
int space_dimension
Dimension of the physical space into which the MeshPart is embedded.
Definition mesh.hpp:2785
int num_bdr_elements
Number of boundary elements with reference space dimension equal to 'dimension'-1.
Definition mesh.hpp:2795
int num_elements
Number of elements with reference space dimension equal to 'dimension'.
Definition mesh.hpp:2791
Mesh & GetMesh()
Construct a serial Mesh object from the MeshPart.
Definition mesh.cpp:14237
int my_part_id
Index of the part described by this MeshPart: 0 <= 'my_part_id' < 'num_parts'.
Definition mesh.hpp:2896
Array< int > element_map
Definition mesh.hpp:2838
Array< int > attributes
Definition mesh.hpp:2847
Table my_groups
Definition mesh.hpp:2909
std::unique_ptr< GridFunction > nodes
Definition mesh.hpp:2886
void Print(std::ostream &os) const
Write the MeshPart to a stream using the parallel format "MFEM mesh v1.2".
Definition mesh.cpp:14057
Array< int > bdr_attributes
Definition mesh.hpp:2854
Array< int > partitioning
Definition mesh.hpp:2996
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:14862
MeshPartitioner(Mesh &mesh_, int num_parts_, const int *partitioning_=nullptr, int part_method=1)
Construct a MeshPartitioner.
Definition mesh.cpp:14320
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:14877
void ExtractPart(int part_id, MeshPart &mesh_part) const
Construct a MeshPart corresponding to the given part_id.
Definition mesh.cpp:14392
List of mesh geometries stored as Array<Geometry::Type>.
Definition mesh.hpp:1590
Mesh data type.
Definition mesh.hpp:65
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition mesh.cpp:6890
Array< Vertex > vertices
Definition mesh.hpp:107
void GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:7642
void GetEdgeOrdering(const DSTable &v_to_v, Array< int > &order)
Definition mesh.cpp:2996
void GetLocalFaceTransformation(int face_type, int elem_type, IsoparametricTransformation &Transf, int info) const
A helper method that constructs a transformation from the reference space of a face to the reference ...
Definition mesh.cpp:1038
void NURBSCoarsening(int cf=2, real_t tol=1.0e-12)
Definition mesh.cpp:11169
void SetVerticesFromNodes(const GridFunction *nodes)
Helper to set vertex coordinates given a high-order curvature function.
Definition mesh.cpp:6832
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
int GetPatchBdrAttribute(int i) const
Return the attribute of patch boundary element i, for a NURBS mesh.
Definition mesh.cpp:3371
int GetElementToEdgeTable(Table &)
Definition mesh.cpp:8068
int meshgen
Definition mesh.hpp:90
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:6658
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:7569
void SetVertices(const Vector &vert_coord)
Definition mesh.cpp:9564
Element * NewElement(int geom)
Definition mesh.cpp:4818
Operation GetLastOperation() const
Return type of last modification of the mesh.
Definition mesh.hpp:2513
IsoparametricTransformation Transformation2
Definition mesh.hpp:256
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1383
void MarkForRefinement()
Definition mesh.cpp:2964
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:7886
void InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
Begin construction of a mesh.
Definition mesh.cpp:1977
Table * GetVertexToBdrElementTable()
Definition mesh.cpp:7768
static void PrintElement(const Element *el, std::ostream &os)
Definition mesh.cpp:4884
Array< FaceInfo > faces_info
Definition mesh.hpp:239
int EulerNumber() const
Equals 1 + num_holes - num_loops.
Definition mesh.hpp:1312
CoarseFineTransformations CoarseFineTr
Definition mesh.hpp:262
void GetElementJacobian(int i, DenseMatrix &J, const IntegrationPoint *ip=NULL)
Definition mesh.cpp:65
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:2057
int AddBdrElement(Element *elem)
Definition mesh.cpp:2370
void GetElementColoring(Array< int > &colors, int el0=0)
Definition mesh.cpp:12766
virtual FaceElementTransformations * GetFaceElementTransformations(int FaceNo, int mask=31)
Definition mesh.cpp:1104
void FinalizeMesh(int refine=0, bool fix_orientation=true)
Finalize the construction of any type of Mesh.
Definition mesh.cpp:3496
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:304
static void PrintElementWithoutAttr(const Element *el, std::ostream &os)
Definition mesh.cpp:4860
MemAlloc< Tetrahedron, 1024 > TetMemory
Definition mesh.hpp:277
void RedRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:421
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:312
void ReadTrueGridMesh(std::istream &input)
static const int vtk_quadratic_tet[10]
Definition mesh.hpp:270
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
Definition mesh.cpp:1563
friend class ParNCMesh
Definition mesh.hpp:71
virtual void GetExteriorFaceMarker(Array< int > &face_marker) const
Populate a marker array identifying exterior faces.
Definition mesh.cpp:1656
IsoparametricTransformation EdgeTransformation
Definition mesh.hpp:258
static FiniteElement * GetTransformationFEforElementType(Element::Type)
Return FiniteElement for reference element of the specified type.
Definition mesh.cpp:339
int AddBdrQuad(int v1, int v2, int v3, int v4, int attr=1)
Definition mesh.cpp:2417
int * CartesianPartitioning(int nxyz[])
Definition mesh.cpp:8705
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:7858
static int GetQuadOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7130
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:7962
void GetLocalSegToQuadTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:719
virtual long long ReduceInt(int value) const
Utility function: sum integers from all processors (Allreduce).
Definition mesh.hpp:2728
const Array< int > & GetFaceIndices(FaceType ftype) const
Map from boundary or interior face indices to mesh face indices.
Definition mesh.cpp:1003
int NumOfBdrElements
Definition mesh.hpp:81
void BdrBisection(int i, const HashTable< Hashed2 > &)
Bisect a boundary triangle: boundary element with index i is bisected.
Definition mesh.cpp:11600
Element::Type GetBdrElementType(int i) const
Returns the type of boundary element i.
Definition mesh.cpp:7967
std::unordered_map< int, int > inv_face_indices[2]
cache for FaceIndices(ftype)
Definition mesh.hpp:285
const Table & ElementToEdgeTable() const
Definition mesh.cpp:8152
bool Conforming() const
Definition mesh.cpp:15455
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6846
virtual void UnmarkNamedBoundaries(const std::string &set_name, Array< int > &bdr_marker) const
Unmark boundary attributes in the named set.
Definition mesh.cpp:1720
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:1613
Array< int > face_indices[2]
cache for FaceIndices(ftype)
Definition mesh.hpp:283
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1576
void GeneralRefinement(const Array< Refinement > &refinements, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11225
Array< int > bdr_face_attrs_cache
internal cache for boundary element attributes
Definition mesh.hpp:114
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1535
Geometry::Type GetBdrElementGeometry(int i) const
Definition mesh.hpp:1547
void MakeHigherOrderSimplicial_(const Mesh &orig_mesh, const Array< int > &parent_elements)
Helper function for constructing higher order nodes from a mesh transformed into simplices....
Definition mesh.cpp:5912
int AddTri(const int *vi, int attr=1)
Adds a triangle to the mesh given by 3 vertices vi.
Definition mesh.hpp:1019
static Mesh MakeCartesian1D(int n, real_t sx=1.0)
Creates 1D mesh, divided into n equal intervals.
Definition mesh.cpp:4609
int GetAttribute(int i) const
Return the attribute of element i.
Definition mesh.hpp:1484
void NodesUpdated()
This function should be called after the mesh node coordinates have been updated externally,...
Definition mesh.hpp:2312
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:6747
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1609
void UniformRefinement3D_base(Array< int > *f2qf=NULL, DSTable *v_to_v_p=NULL, bool update_nodes=true)
Definition mesh.cpp:9892
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:2085
long nodes_sequence
Counter for geometric factor invalidation.
Definition mesh.hpp:100
virtual void Load(std::istream &input, int generate_edges=0, int refine=1, bool fix_orientation=true)
Definition mesh.hpp:815
void ComputeFaceInfo(FaceType ftype) const
compute face_indices[ftype] and inv_face_indices[type]
Definition mesh.cpp:982
IsoparametricTransformation FaceTransformation
Definition mesh.hpp:258
Array< NCFaceInfo > nc_faces_info
Definition mesh.hpp:240
friend class NCMesh
Definition mesh.hpp:66
Array< int > MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
Internal helper user in MakeSimplicial (and ParMesh::MakeSimplicial). Optional return is used in asse...
Definition mesh.cpp:5575
void MakeRefined_(Mesh &orig_mesh, const Array< int > &ref_factors, int ref_type)
Internal function used in Mesh::MakeRefined.
Definition mesh.cpp:5335
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:2120
Array< int > GetFaceToBdrElMap() const
Definition mesh.cpp:1617
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:4646
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:3342
void FinalizeTetMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a tetrahedral Mesh.
Definition mesh.cpp:3390
real_t GetLength(int i, int j) const
Return the length of the segment from node i to node j.
Definition mesh.cpp:8006
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:6794
void AddBdrQuadAsTriangles(const int *vi, int attr=1)
Definition mesh.cpp:2431
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:2134
void Loader(std::istream &input, int generate_edges=0, std::string parse_tag="")
Definition mesh.cpp:4943
const Table & ElementToElementTable()
Definition mesh.cpp:8107
void ScaleElements(real_t sf)
Definition mesh.cpp:13470
void GenerateNCFaceInfo()
Definition mesh.cpp:8392
void ReadLineMesh(std::istream &input)
void ApplyLocalSlaveTransformation(FaceElementTransformations &FT, const FaceInfo &fi, bool is_ghost) const
Definition mesh.cpp:1254
Array< Element * > faces
Definition mesh.hpp:109
int Dim
Definition mesh.hpp:78
real_t AggregateError(const Array< real_t > &elem_error, const int *fine, int nfine, int op)
Derefinement helper.
Definition mesh.cpp:10891
void CheckPartitioning(int *partitioning_)
Definition mesh.cpp:9149
void DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
Definition mesh.cpp:3110
Geometry::Type GetTypicalElementGeometry() const
If the local mesh is not empty, return GetElementGeometry(0); otherwise, return a typical Geometry pr...
Definition mesh.cpp:1628
void GetLocalPtToSegTransformation(IsoparametricTransformation &, int i) const
Used in GetFaceElementTransformations (...)
Definition mesh.cpp:684
bool Nonconforming() const
Definition mesh.hpp:2499
int GetBdrAttribute(int i) const
Return the attribute of boundary element i.
Definition mesh.hpp:1490
virtual void MarkExternalBoundaries(Array< int > &bdr_marker, bool excl=true) const
Mark boundary attributes of external boundaries.
Definition mesh.cpp:1741
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:254
void UpdateNURBS()
Definition mesh.cpp:6405
static int ComposeQuadOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:7178
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:2071
void GenerateFaces()
Definition mesh.cpp:8285
static const int vtk_quadratic_wedge[18]
Definition mesh.hpp:272
int EulerNumber2D() const
Equals 1 - num_holes.
Definition mesh.hpp:1315
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:310
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:2377
void Destroy()
Definition mesh.cpp:1881
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1689
void GetVertices(Vector &vert_coord) const
Definition mesh.cpp:9553
void InitFromNCMesh(const NCMesh &ncmesh)
Initialize vertices/elements/boundary/tables from a nonconforming mesh.
Definition mesh.cpp:10991
virtual int GetNFbyType(FaceType type) const
Returns the number of faces according to the requested type, does not count master nonconforming face...
Definition mesh.cpp:6862
void Make1D(int n, real_t sx=1.0)
Definition mesh.cpp:4408
friend class Tetrahedron
Definition mesh.hpp:276
void DeleteTables()
Definition mesh.hpp:335
void RefineNURBSFromFile(std::string ref_file)
Definition mesh.cpp:6212
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1434
int AddBdrPoint(int v, int attr=1)
Definition mesh.cpp:2446
void FinalizeWedgeMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a wedge Mesh.
Definition mesh.cpp:3431
static int GetTriOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7041
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:12102
static Mesh MakeSimplicial(const Mesh &orig_mesh)
Definition mesh.cpp:5564
void SetPatchBdrAttribute(int i, int attr)
Set the attribute of patch boundary element i, for a NURBS mesh.
Definition mesh.cpp:3359
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1386
int AddVertexAtMeanCenter(const int *vi, const int nverts, int dim=3)
Definition mesh.cpp:2040
static int GetTetOrientation(const int *base, const int *test)
Returns the orientation of "test" relative to "base".
Definition mesh.cpp:7209
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:2593
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:2177
bool FaceIsTrueInterior(int FaceNo) const
Definition mesh.hpp:610
long GetSequence() const
Definition mesh.hpp:2519
const CoarseFineTransformations & GetRefinementTransforms() const
Definition mesh.cpp:11749
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:4039
bool IsMixedMesh() const
Returns true if the mesh is a mixed mesh, false otherwise.
Definition mesh.cpp:7582
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:965
void GetLocalQuadToWdgTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:836
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:7950
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3502
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Print the mesh to the given stream using the default MFEM mesh format.
Definition mesh.hpp:2567
void DestroyTables()
Definition mesh.cpp:1836
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:6335
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:4655
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:903
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:12841
void Clear()
Clear the contents of the Mesh.
Definition mesh.hpp:827
void PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
Definition mesh.cpp:3044
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:2000
virtual void LocalRefinement(const Array< int > &marked_el, int type=3)
This function is not public anymore. Use GeneralRefinement instead.
Definition mesh.cpp:10603
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
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:3683
virtual void Save(const std::string &fname, int precision=16) const
Definition mesh.cpp:12178
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:2099
void GetBoundingBox(Vector &min, Vector &max, int ref=2)
Returns the minimum and maximum corners of the mesh bounding box.
Definition mesh.cpp:141
void GetBdrPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:7990
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
ElementTransformation * GetTypicalElementTransformation()
If the local mesh is not empty return GetElementTransformation(0); otherwise, return the identity tra...
Definition mesh.cpp:393
Table * el_to_face
Definition mesh.hpp:243
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:11317
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1449
void CheckDisplacements(const Vector &displacements, real_t &tmax)
Definition mesh.cpp:9467
friend class NURBSExtension
Definition mesh.hpp:67
void AddTriangleFaceElement(int lf, int gf, int el, int v0, int v1, int v2)
Definition mesh.cpp:8230
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:2302
void GetNode(int i, real_t *coord) const
Definition mesh.cpp:9573
void ReorderElements(const Array< int > &ordering, bool reorder_vertices=true)
Definition mesh.cpp:2812
void GreenRefinement(int i, const DSTable &v_to_v, int *edge1, int *edge2, int *middle)
Definition mesh.hpp:427
void UpdateNodes()
Update the nodes of a curved mesh after the topological part of a Mesh::Operation,...
Definition mesh.cpp:9718
void PrintElementsWithPartitioning(int *partitioning, std::ostream &os, int interior_faces=0)
Definition mesh.cpp:12960
Mesh & operator=(Mesh &&mesh)
Move assignment operator.
Definition mesh.cpp:4593
void Transform(std::function< void(const Vector &, Vector &)> f)
Definition mesh.cpp:13540
long sequence
Definition mesh.hpp:97
static int InvertQuadOrientation(int ori)
Definition mesh.cpp:7203
Array< FaceGeometricFactors * > face_geom_factors
Definition mesh.hpp:315
void GetLocalTriToPyrTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:789
Table * bel_to_edge
Definition mesh.hpp:247
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:4664
real_t GetElementSize(int i, int type=0)
Get the size of the i-th element relative to the perfect reference element.
Definition mesh.cpp:110
Element * ReadElementWithoutAttr(std::istream &input)
Definition mesh.cpp:4842
int AddBdrSegment(int v1, int v2, int attr=1)
Definition mesh.cpp:2389
bool DerefineByError(Array< real_t > &elem_error, real_t threshold, int nc_limit=0, int op=1)
Definition mesh.cpp:10962
void FinalizeHexMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a hexahedral Mesh.
Definition mesh.cpp:3466
int AddElement(Element *elem)
Definition mesh.cpp:2363
static int DecodeFaceInfoLocalIndex(int info)
Given a "face info int", return the local face index.
Definition mesh.hpp:2173
Table * el_to_edge
Definition mesh.hpp:242
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1293
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition mesh.cpp:6857
void GetElementTransformation(int i, IsoparametricTransformation *ElTr) const
Builds the transformation defining the i-th element in ElTr. ElTr must be allocated in advance and wi...
Definition mesh.cpp:360
void RefineAtVertex(const Vertex &vert, real_t eps=0.0, int nonconforming=-1)
Refine elements sharing the specified vertex. Uses GeneralRefinement.
Definition mesh.cpp:11336
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:7610
static int InvertTriOrientation(int ori)
Definition mesh.cpp:7124
STable3D * GetElementToFaceTable(int ret_ftbl=0)
Definition mesh.cpp:8513
void FinalizeQuadMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a quadrilateral Mesh.
Definition mesh.cpp:2531
void SaveVTKHDF(const std::string &fname, bool high_order=true)
Save the Mesh in VTKHDF format.
Definition mesh.cpp:12746
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:4113
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:10914
void AddVertexParents(int i, int p1, int p2)
Mark vertex i as nonconforming, with parent vertices p1 and p2.
Definition mesh.cpp:2024
MFEM_DEPRECATED void GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const
Deprecated.
Definition mesh.cpp:7927
int GetPatchAttribute(int i) const
Return the attribute of patch i, for a NURBS mesh.
Definition mesh.cpp:3353
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Definition mesh.cpp:1557
void Printer(std::ostream &os=mfem::out, std::string section_delimiter="", const std::string &comments="") const
If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1 format. If section_delimiter is empty,...
Definition mesh.cpp:11976
bool FaceIsInterior(int FaceNo) const
Return true if the given face is interior.
Definition mesh.hpp:1563
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:256
void Init()
Definition mesh.cpp:1804
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:3966
void GetLocalTriToWdgTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:763
void GetElementFaces(int i, Array< int > &faces, Array< int > &ori) const
Return the indices and the orientations of all faces of element i.
Definition mesh.cpp:7835
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1309
void GetNURBSPatches(Array< NURBSPatch * > &patches)
Definition mesh.cpp:3377
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:883
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:4627
Table * edge_vertex
Definition mesh.hpp:254
void GetCharacteristics(real_t &h_min, real_t &h_max, real_t &kappa_min, real_t &kappa_max, Vector *Vh=NULL, Vector *Vk=NULL)
Definition mesh.cpp:205
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition mesh.cpp:6788
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:1937
void SetNode(int i, const real_t *coord)
Definition mesh.cpp:9592
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:9627
int NumOfVertices
Definition mesh.hpp:81
const Array< int > & GetBdrFaceAttributes() const
Returns the attributes for all boundary elements in this mesh.
Definition mesh.cpp:925
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:307
virtual void UniformRefinement3D()
Refine a mixed 3D mesh uniformly.
Definition mesh.hpp:476
static int ComposeTriOrientations(int ori_a_b, int ori_b_c)
Definition mesh.cpp:7101
Array< int > be_to_face
Definition mesh.hpp:245
void PrintBdrVTU(std::string fname, VTKFormat format=VTKFormat::ASCII, bool high_order_output=false, int compression_level=0)
Definition mesh.cpp:12384
const std::unordered_map< int, int > & GetInvFaceIndices(FaceType ftype) const
Inverse of the map FaceIndices(ftype)
Definition mesh.cpp:1013
void AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
Definition mesh.cpp:8193
FaceElementTransformations * GetBdrFaceTransformations(int BdrElemNo)
Builds the transformation defining the given boundary face.
Definition mesh.cpp:1223
int AddBdrTriangle(int v1, int v2, int v3, int attr=1)
Definition mesh.cpp:2403
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:13914
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1374
static int DecodeFaceInfoOrientation(int info)
Given a "face info int", return the face orientation.
Definition mesh.hpp:2170
void GetHilbertElementOrdering(Array< int > &ordering)
Definition mesh.cpp:2760
void GetEdgeToUniqueKnotvector(Array< int > &edge_to_ukv, Array< int > &ukv_to_rpkv) const
Definition mesh.cpp:6542
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7672
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:2242
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:12392
virtual void UniformRefinement2D()
Refine a mixed 2D mesh uniformly.
Definition mesh.hpp:466
GridFunction * Nodes
Definition mesh.hpp:267
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:6042
Element::Type GetFaceElementType(int Face) const
Definition mesh.cpp:1612
int CheckBdrElementOrientation(bool fix_it=true)
Check the orientation of the boundary elements.
Definition mesh.cpp:7342
void 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:7905
Array< int > elem_attrs_cache
internal cache for element attributes
Definition mesh.hpp:112
Table * el_to_el
Definition mesh.hpp:244
void AverageVertices(const int *indexes, int n, int result)
Averages the vertices with given indexes and saves the result in vertices[result].
Definition mesh.cpp:9697
Table * face_edge
Definition mesh.hpp:253
real_t GetElementVolume(int i)
Definition mesh.cpp:124
static Mesh LoadFromFile(const std::string &filename, int generate_edges=0, int refine=1, bool fix_orientation=true)
Definition mesh.cpp:4599
int NumOfElements
Definition mesh.hpp:81
static const int vtk_quadratic_hex[27]
Definition mesh.hpp:273
void Swap(Mesh &other, bool non_geometry)
Definition mesh.cpp:11036
Array< Triple< int, int, int > > tmp_vertex_parents
Definition mesh.hpp:281
virtual void GenerateBoundaryElements()
Definition mesh.cpp:2453
static void GetElementArrayEdgeTable(const Array< Element * > &elem_array, const DSTable &v_to_v, Table &el_to_edge)
Definition mesh.cpp:8021
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:6076
void Bisection(int i, const DSTable &, int *, int *, int *)
Bisect a triangle: element with index i is bisected.
Definition mesh.cpp:11391
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1627
void ResetLazyData()
Definition mesh.cpp:1918
void UniformRefinement2D_base(bool update_nodes=true)
Definition mesh.cpp:9733
IsoparametricTransformation BdrTransformation
Definition mesh.hpp:257
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:12138
void GetLocalSegToTriTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:699
void DestroyPointers()
Definition mesh.cpp:1855
void InitTables()
Definition mesh.cpp:1823
void DegreeElevate(int rel_degree, int degree=16)
Definition mesh.cpp:6388
Table * face_to_elem
Definition mesh.hpp:252
int NumOfFaces
Definition mesh.hpp:82
int spaceDim
Definition mesh.hpp:79
int FindCoarseElement(int i)
Definition mesh.cpp:11739
void FinalizeTriMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a triangular Mesh.
Definition mesh.cpp:2502
void GetElementCenter(int i, Vector &center)
Definition mesh.cpp:80
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:2201
static void PrintElementsByGeometry(int dim, const Array< int > &num_elems_by_geom, std::ostream &os)
Auxiliary method used by PrintCharacteristics().
Definition mesh.cpp:240
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:2148
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:7453
void SetEmpty()
Definition mesh.cpp:1830
virtual void SetNodalFESpace(FiniteElementSpace *nfes)
Definition mesh.cpp:6741
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1380
virtual void Finalize(bool refine=false, bool fix_orientation=false)
Finalize the construction of a general Mesh.
Definition mesh.cpp:3608
void AddQuadFaceElement(int lf, int gf, int el, int v0, int v1, int v2, int v3)
Definition mesh.cpp:8258
void ReadMFEMMesh(std::istream &input, int version, int &curved)
void KnotRemove(Array< Vector * > &kv)
For NURBS meshes, remove the knots in kv, for each direction.
Definition mesh.cpp:6293
virtual int FindPoints(DenseMatrix &point_mat, Array< int > &elem_ids, Array< IntegrationPoint > &ips, bool warn=true, InverseElementTransformation *inv_trans=NULL)
Find the ids of the elements that contain the given points, and their corresponding reference coordin...
Definition mesh.cpp:13800
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:268
void GetElementData(const Array< Element * > &elem_array, int geom, Array< int > &elem_vtx, Array< int > &attr) const
Definition mesh.cpp:11105
void PrintSurfaces(const Table &Aface_face, std::ostream &os) const
Print set of disjoint surfaces:
Definition mesh.cpp:13333
bool IsSlaveFace(const FaceInfo &fi) const
Definition mesh.cpp:1249
void FreeElement(Element *E)
Definition mesh.cpp:13775
Array< Element * > boundary
Definition mesh.hpp:108
virtual void MarkTetMeshForRefinement(const DSTable &v_to_v)
Definition mesh.cpp:3021
void GetPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:7972
FaceElementTransformations * GetInteriorFaceTransformations(int FaceNo)
See GetFaceElementTransformations().
Definition mesh.cpp:1203
void GetLocalTriToTetTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:739
virtual void PrintXG(std::ostream &os=mfem::out) const
Print the mesh to the given stream using Netgen/Truegrid format.
Definition mesh.cpp:11812
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:313
void NewNodes(GridFunction &nodes, bool make_owner=false)
Replace the internal node GridFunction with the given GridFunction.
Definition mesh.cpp:9654
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:6320
int mesh_geoms
Definition mesh.hpp:92
void DebugDump(std::ostream &os) const
Output an NCMesh-compatible debug dump.
Definition mesh.cpp:15470
GridFunction * GetNodes()
Return a pointer to the internal node GridFunction (may be NULL).
Definition mesh.hpp:2359
bool RefineByError(const Array< real_t > &elem_error, real_t threshold, int nonconforming=-1, int nc_limit=0)
Definition mesh.cpp:11362
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:4637
STable3D * GetFacesTable()
Definition mesh.cpp:8450
Table * GetFaceEdgeTable() const
Definition mesh.cpp:7681
void EnsureNCMesh(bool simplices_nonconforming=false)
Definition mesh.cpp:11293
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:1340
virtual MFEM_DEPRECATED void ReorientTetMesh()
Definition mesh.cpp:8643
void PrintVTK(std::ostream &os)
Definition mesh.cpp:12192
virtual void MarkNamedBoundaries(const std::string &set_name, Array< int > &bdr_marker) const
Mark boundary attributes in the named set.
Definition mesh.cpp:1783
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf, bool spacing=false, bool nc=false)
void MoveNodes(const Vector &displacements)
Definition mesh.cpp:9612
Array< GeometricFactors * > geom_factors
Optional geometric factors.
Definition mesh.hpp:314
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition mesh.cpp:4890
int nbBoundaryFaces
Definition mesh.hpp:87
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:4617
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:7588
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:4230
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:2164
FaceElementTransformations FaceElemTr
Definition mesh.hpp:259
void SetNodes(const Vector &node_coord)
Updates the vertex/node locations. Invokes NodesUpdated().
Definition mesh.cpp:9639
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:7558
void FinalizeCheck()
Definition mesh.cpp:2488
void GetLocalQuadToPyrTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:860
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:2183
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:6799
Element * ReadElement(std::istream &input)
Definition mesh.cpp:4872
virtual bool HasBoundaryElements() const
Checks if the mesh has boundary elements.
Definition mesh.hpp:1336
void ScaleSubdomains(real_t sf)
Definition mesh.cpp:13400
void GetVertexToVertexTable(DSTable &) const
Definition mesh.cpp:8043
void GetLocalQuadToHexTransformation(IsoparametricTransformation &loc, int i) const
Definition mesh.cpp:814
int NumOfEdges
Definition mesh.hpp:82
void UniformRefinement(int i, const DSTable &, int *, int *, int *)
Definition mesh.cpp:11637
Geometry::Type GetTypicalFaceGeometry() const
If the local mesh is not empty, return GetFaceGeometry(0); otherwise return a typical face geometry p...
Definition mesh.cpp:1596
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1556
virtual void UnmarkInternalBoundaries(Array< int > &bdr_marker, bool excl=true) const
Unmark boundary attributes of internal boundaries.
Definition mesh.cpp:1675
Operation last_operation
Definition mesh.hpp:329
void SwapNodes(GridFunction *&nodes, int &own_nodes_)
Swap the internal node GridFunction pointer and ownership flag members with the given ones.
Definition mesh.cpp:9676
virtual void RefineNURBSWithKVFactors(int rf, const std::string &kvf)
Definition mesh.cpp:6315
void LoadPatchTopo(std::istream &input, Array< int > &edge_to_ukv)
Read NURBS patch/macro-element mesh.
Definition mesh.cpp:6467
void SetBdrAttribute(int i, int attr)
Set the attribute of boundary element i.
Definition mesh.hpp:1493
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:4708
int nbInteriorFaces
Definition mesh.hpp:87
Table * GetVertexToElementTable()
Definition mesh.cpp:7735
void InitRefinementTransforms()
Definition mesh.cpp:11727
Table * GetEdgeVertexTable() const
Definition mesh.cpp:7709
int * GeneratePartitioning(int nparts, int part_method=1)
Definition mesh.cpp:8749
Table * GetFaceToElementTable() const
Definition mesh.cpp:7801
void MarkTriMeshForRefinement()
Definition mesh.cpp:2981
void ReadNetgen2DMesh(std::istream &input, int &curved)
Array< Element * > elements
Definition mesh.hpp:102
Array< int > attributes
A list of all unique element attributes used by the Mesh.
Definition mesh.hpp:302
void AddPointFaceElement(int lf, int gf, int el)
Used in GenerateFaces()
Definition mesh.cpp:8161
void RemoveInternalBoundaries()
Definition mesh.cpp:13698
virtual void NonconformingRefinement(const Array< Refinement > &refinements, int nc_limit=0)
This function is not public anymore. Use GeneralRefinement instead.
Definition mesh.cpp:10845
void MoveVertices(const Vector &displacements)
Definition mesh.cpp:9544
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1416
void DeleteGeometricFactors()
Destroy all GeometricFactors stored by the Mesh.
Definition mesh.cpp:1022
const Table & ElementToFaceTable() const
Definition mesh.cpp:8143
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:2220
void RemoveUnusedVertices()
Remove unused vertices and rebuild mesh connectivity.
Definition mesh.cpp:13591
void KnotInsert(Array< KnotVector * > &kv)
For NURBS meshes, insert the new knots in kv, for each direction.
Definition mesh.cpp:6249
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:474
int GetNBE() const
Return the number of active boundary elements.
Definition nurbs.hpp:850
void GetPatches(Array< NURBSPatch * > &patches)
Definition nurbs.cpp:5266
void GetCoarseningFactors(Array< int > &f) const
Definition nurbs.cpp:4845
void SetPatchAttribute(int i, int attr)
Set the attribute for patch i, which is set to all elements in the patch.
Definition nurbs.hpp:900
const Array< int > & GetPatchElements(int patch)
Return the array of indices of all elements in patch patch.
Definition nurbs.cpp:5298
void UniformRefinement(int rf=2)
Refine with optional refinement factor rf. Uniform means refinement is done everywhere by the same fa...
Definition nurbs.cpp:4777
void Print(std::ostream &os, const std::string &comments="") const
Writes all patch data to the stream os.
Definition nurbs.cpp:2743
virtual void ReadCoarsePatchCP(std::istream &input)
Read the control points for coarse patches.
Definition nurbs.cpp:5356
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:908
void SetCoordsFromPatches(Vector &Nodes)
Set FE coordinates in Nodes, using data from patches, and erase patches.
Definition nurbs.cpp:4598
void Coarsen(int cf=2, real_t tol=1.0e-12)
Coarsen with optional coarsening factor cf.
Definition nurbs.cpp:4838
int GetPatchAttribute(int i) const
Get the attribute for patch i, which is set to all elements in the patch.
Definition nurbs.hpp:904
void GetElementTopo(Array< Element * > &elements) const
Generate the active mesh elements and return them in elements.
Definition nurbs.cpp:3828
const Array< int > & GetPatchBdrElements(int patch)
Return the array of indices of all boundary elements in patch patch.
Definition nurbs.cpp:5305
int GetNKV() const
Return the number of KnotVectors.
Definition nurbs.hpp:837
void GetVertexLocalToGlobal(Array< int > &lvert_vert)
Get the local to global vertex index map lvert_vert.
Definition nurbs.cpp:4523
bool HavePatches() const
Return true if at least 1 patch is defined, false otherwise.
Definition nurbs.hpp:883
void GetBdrElementTopo(Array< Element * > &boundary) const
Generate the active mesh boundary elements and return them in boundary.
Definition nurbs.cpp:3957
bool NonconformingPatches() const
Return true if the patch topology mesh is nonconforming.
Definition nurbs.hpp:1016
virtual void RefineWithKVFactors(int rf, const std::string &kvf_filename, bool coarsened)
Definition nurbs.cpp:5389
void KnotRemove(Array< Vector * > &kv, real_t tol=1.0e-12)
Definition nurbs.cpp:4994
void GetElementLocalToGlobal(Array< int > &lelem_elem)
Get the local to global element index map lelem_elem.
Definition nurbs.cpp:4533
virtual void PrintCoarsePatches(std::ostream &os)
Print control points for coarse patches.
Definition nurbs.cpp:5361
void FullyCoarsen()
Fully coarsen all structured patches, for non-nested refinement of a mesh with a nonconforming patch ...
Definition nurbs.cpp:4805
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:4882
int GetOrder() const
If all KnotVector orders are identical, return that number. Otherwise, return NURBSFECollection::Vari...
Definition nurbs.hpp:834
int GetNV() const
Return the local number of active vertices.
Definition nurbs.hpp:842
void ConvertToPatches(const Vector &Nodes)
Define patches in IKJ (B-net) format, using FE coordinates in Nodes.
Definition nurbs.cpp:4587
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:821
void SetKnotsFromPatches()
Set KnotVectors from patches and construct mesh and space data.
Definition nurbs.cpp:4606
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:846
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:913
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:4722
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:717
Class for standard nodal finite elements.
Definition fe_base.hpp:724
Class used to extrude the nodes of a mesh.
Definition mesh.hpp:3182
void SetLayer(const int l)
Definition mesh.hpp:3189
NodeExtrudeCoefficient(const int dim, const int n_, const real_t s_)
Definition mesh.cpp:15045
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:15051
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:72
Type
Ordering methods:
Definition ordering.hpp:17
A pair of objects.
Class for parallel meshes.
Definition pmesh.hpp:34
MPI_Comm GetComm() const
Definition pmesh.hpp:405
int GetMyRank() const
Definition pmesh.hpp:407
Parallel version of NURBSExtension.
Definition nurbs.hpp:1036
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:907
static int CheckClosed(int type)
If the Quadrature1D type is not closed return Invalid; otherwise return type.
Definition intrules.cpp:928
@ 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:584
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:748
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:49
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:48
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
real_t infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition vector.hpp:47
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:9286
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:3205
MFEM_EXPORT class Linear3DFiniteElement TetrahedronFE
Definition fe.cpp:36
void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
Definition mesh.cpp:9206
Mesh * Extrude1D(Mesh *mesh, const int ny, const real_t sy, const bool closed)
Extrude a 1D mesh.
Definition mesh.cpp:15069
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:738
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:15229
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:1548
void FindTMax(Vector &c, Vector &x, real_t &tmax, const real_t factor, const int Dim)
Definition mesh.cpp:9433
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:424
float real_t
Definition config.hpp:46
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
const char * VTKByteOrder()
Determine the byte order and return either "BigEndian" or "LittleEndian".
Definition vtk.cpp:602
void SortPairs(Pair< A, B > *pairs, int size)
Sort an array of Pairs with respect to the first element.
MemoryType
Memory types supported by MFEM.
@ HOST
Host memory; using new[] and delete[].
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:9078
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:47
@ 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:6704
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
IntegrationRules IntRules(0, Quadrature1D::GaussLegendre)
A global object with all integration rules (defined in intrules.cpp)
Definition intrules.hpp:492
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:14029
Entity FindEntity(int bytype_entity_id)
Definition mesh.cpp:14044
entity_to_vertex_type & entity_to_vertex
Definition mesh.hpp:2773
int geom_offsets[Geometry::NumGeom+1]
Definition mesh.hpp:2771
const int * verts
Definition mesh.hpp:2767
This structure stores the low level information necessary to interpret the configuration of elements ...
Definition mesh.hpp:176
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2081
ElementLocation location
Definition mesh.hpp:2086
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition mesh.hpp:2159
bool IsOfFaceType(FaceType type) const
Return true if the face is of the same type as type.
Definition mesh.hpp:2128
struct mfem::Mesh::FaceInformation::@15 element[2]
ElementConformity conformity
Definition mesh.hpp:2087
const DenseMatrix * point_matrix
Definition mesh.hpp:2095
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