MFEM  v4.5.1
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
mesh.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, 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 "../fem/fem.hpp"
16 #include "../general/sort_pairs.hpp"
17 #include "../general/binaryio.hpp"
18 #include "../general/text.hpp"
19 #include "../general/device.hpp"
20 #include "../general/tic_toc.hpp"
21 #include "../general/gecko.hpp"
22 #include "../fem/quadinterpolator.hpp"
23 
24 #include <iostream>
25 #include <sstream>
26 #include <fstream>
27 #include <limits>
28 #include <cmath>
29 #include <cstring>
30 #include <ctime>
31 #include <functional>
32 #include <map>
33 #include <set>
34 
35 // Include the METIS header, if using version 5. If using METIS 4, the needed
36 // declarations are inlined below, i.e. no header is needed.
37 #if defined(MFEM_USE_METIS) && defined(MFEM_USE_METIS_5)
38 #include "metis.h"
39 #endif
40 
41 // METIS 4 prototypes
42 #if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
43 typedef int idx_t;
44 typedef int idxtype;
45 extern "C" {
47  int*, int*, int*, int*, int*, idxtype*);
49  int*, int*, int*, int*, int*, idxtype*);
51  int*, int*, int*, int*, int*, idxtype*);
52 }
53 #endif
54 
55 using namespace std;
56 
57 namespace mfem
58 {
59 
60 void Mesh::GetElementJacobian(int i, DenseMatrix &J)
61 {
62  Geometry::Type geom = GetElementBaseGeometry(i);
63  ElementTransformation *eltransf = GetElementTransformation(i);
64  eltransf->SetIntPoint(&Geometries.GetCenter(geom));
65  Geometries.JacToPerfJac(geom, eltransf->Jacobian(), J);
66 }
67 
68 void Mesh::GetElementCenter(int i, Vector &center)
69 {
70  center.SetSize(spaceDim);
71  int geom = GetElementBaseGeometry(i);
72  ElementTransformation *eltransf = GetElementTransformation(i);
73  eltransf->Transform(Geometries.GetCenter(geom), center);
74 }
75 
76 double Mesh::GetElementSize(ElementTransformation *T, int type)
77 {
78  DenseMatrix J(spaceDim, Dim);
79 
80  Geometry::Type geom = T->GetGeometryType();
81  T->SetIntPoint(&Geometries.GetCenter(geom));
82  Geometries.JacToPerfJac(geom, T->Jacobian(), J);
83 
84  if (type == 0)
85  {
86  return pow(fabs(J.Weight()), 1./Dim);
87  }
88  else if (type == 1)
89  {
90  return J.CalcSingularvalue(Dim-1); // h_min
91  }
92  else
93  {
94  return J.CalcSingularvalue(0); // h_max
95  }
96 }
97 
98 double Mesh::GetElementSize(int i, int type)
99 {
100  return GetElementSize(GetElementTransformation(i), type);
101 }
102 
103 double Mesh::GetElementSize(int i, const Vector &dir)
104 {
105  DenseMatrix J(spaceDim, Dim);
106  Vector d_hat(Dim);
107  GetElementJacobian(i, J);
108  J.MultTranspose(dir, d_hat);
109  return sqrt((d_hat * d_hat) / (dir * dir));
110 }
111 
112 double Mesh::GetElementVolume(int i)
113 {
114  ElementTransformation *et = GetElementTransformation(i);
115  const IntegrationRule &ir = IntRules.Get(GetElementBaseGeometry(i),
116  et->OrderJ());
117  double volume = 0.0;
118  for (int j = 0; j < ir.GetNPoints(); j++)
119  {
120  const IntegrationPoint &ip = ir.IntPoint(j);
121  et->SetIntPoint(&ip);
122  volume += ip.weight * et->Weight();
123  }
124 
125  return volume;
126 }
127 
128 // Similar to VisualizationSceneSolution3d::FindNewBox in GLVis
129 void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
130 {
131  min.SetSize(spaceDim);
132  max.SetSize(spaceDim);
133 
134  for (int d = 0; d < spaceDim; d++)
135  {
136  min(d) = infinity();
137  max(d) = -infinity();
138  }
139 
140  if (Nodes == NULL)
141  {
142  double *coord;
143  for (int i = 0; i < NumOfVertices; i++)
144  {
145  coord = GetVertex(i);
146  for (int d = 0; d < spaceDim; d++)
147  {
148  if (coord[d] < min(d)) { min(d) = coord[d]; }
149  if (coord[d] > max(d)) { max(d) = coord[d]; }
150  }
151  }
152  }
153  else
154  {
155  const bool use_boundary = false; // make this a parameter?
156  int ne = use_boundary ? GetNBE() : GetNE();
157  int fn, fo;
158  DenseMatrix pointmat;
159  RefinedGeometry *RefG;
160  IntegrationRule eir;
163 
164  for (int i = 0; i < ne; i++)
165  {
166  if (use_boundary)
167  {
168  GetBdrElementFace(i, &fn, &fo);
169  RefG = GlobGeometryRefiner.Refine(GetFaceBaseGeometry(fn), ref);
170  Tr = GetFaceElementTransformations(fn, 5);
171  eir.SetSize(RefG->RefPts.GetNPoints());
172  Tr->Loc1.Transform(RefG->RefPts, eir);
173  Tr->Elem1->Transform(eir, pointmat);
174  }
175  else
176  {
177  T = GetElementTransformation(i);
178  RefG = GlobGeometryRefiner.Refine(GetElementBaseGeometry(i), ref);
179  T->Transform(RefG->RefPts, pointmat);
180  }
181  for (int j = 0; j < pointmat.Width(); j++)
182  {
183  for (int d = 0; d < pointmat.Height(); d++)
184  {
185  if (pointmat(d,j) < min(d)) { min(d) = pointmat(d,j); }
186  if (pointmat(d,j) > max(d)) { max(d) = pointmat(d,j); }
187  }
188  }
189  }
190  }
191 }
192 
193 void Mesh::GetCharacteristics(double &h_min, double &h_max,
194  double &kappa_min, double &kappa_max,
195  Vector *Vh, Vector *Vk)
196 {
197  int i, dim, sdim;
198  DenseMatrix J;
199  double h, kappa;
200 
201  dim = Dimension();
202  sdim = SpaceDimension();
203 
204  if (Vh) { Vh->SetSize(NumOfElements); }
205  if (Vk) { Vk->SetSize(NumOfElements); }
206 
207  h_min = kappa_min = infinity();
208  h_max = kappa_max = -h_min;
209  if (dim == 0) { if (Vh) { *Vh = 1.0; } if (Vk) {*Vk = 1.0; } return; }
210  J.SetSize(sdim, dim);
211  for (i = 0; i < NumOfElements; i++)
212  {
213  GetElementJacobian(i, J);
214  h = pow(fabs(J.Weight()), 1.0/double(dim));
215  kappa = (dim == sdim) ?
216  J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1) : -1.0;
217  if (Vh) { (*Vh)(i) = h; }
218  if (Vk) { (*Vk)(i) = kappa; }
219 
220  if (h < h_min) { h_min = h; }
221  if (h > h_max) { h_max = h; }
222  if (kappa < kappa_min) { kappa_min = kappa; }
223  if (kappa > kappa_max) { kappa_max = kappa; }
224  }
225 }
226 
227 // static method
228 void Mesh::PrintElementsByGeometry(int dim,
229  const Array<int> &num_elems_by_geom,
230  std::ostream &os)
231 {
232  for (int g = Geometry::DimStart[dim], first = 1;
233  g < Geometry::DimStart[dim+1]; g++)
234  {
235  if (!num_elems_by_geom[g]) { continue; }
236  if (!first) { os << " + "; }
237  else { first = 0; }
238  os << num_elems_by_geom[g] << ' ' << Geometry::Name[g] << "(s)";
239  }
240 }
241 
242 void Mesh::PrintCharacteristics(Vector *Vh, Vector *Vk, std::ostream &os)
243 {
244  double h_min, h_max, kappa_min, kappa_max;
245 
246  os << "Mesh Characteristics:";
247 
248  this->GetCharacteristics(h_min, h_max, kappa_min, kappa_max, Vh, Vk);
249 
250  Array<int> num_elems_by_geom(Geometry::NumGeom);
251  num_elems_by_geom = 0;
252  for (int i = 0; i < GetNE(); i++)
253  {
254  num_elems_by_geom[GetElementBaseGeometry(i)]++;
255  }
256 
257  os << '\n'
258  << "Dimension : " << Dimension() << '\n'
259  << "Space dimension : " << SpaceDimension();
260  if (Dim == 0)
261  {
262  os << '\n'
263  << "Number of vertices : " << GetNV() << '\n'
264  << "Number of elements : " << GetNE() << '\n'
265  << "Number of bdr elem : " << GetNBE() << '\n';
266  }
267  else if (Dim == 1)
268  {
269  os << '\n'
270  << "Number of vertices : " << GetNV() << '\n'
271  << "Number of elements : " << GetNE() << '\n'
272  << "Number of bdr elem : " << GetNBE() << '\n'
273  << "h_min : " << h_min << '\n'
274  << "h_max : " << h_max << '\n';
275  }
276  else if (Dim == 2)
277  {
278  os << '\n'
279  << "Number of vertices : " << GetNV() << '\n'
280  << "Number of edges : " << GetNEdges() << '\n'
281  << "Number of elements : " << GetNE() << " -- ";
282  PrintElementsByGeometry(2, num_elems_by_geom, os);
283  os << '\n'
284  << "Number of bdr elem : " << GetNBE() << '\n'
285  << "Euler Number : " << EulerNumber2D() << '\n'
286  << "h_min : " << h_min << '\n'
287  << "h_max : " << h_max << '\n'
288  << "kappa_min : " << kappa_min << '\n'
289  << "kappa_max : " << kappa_max << '\n';
290  }
291  else
292  {
293  Array<int> num_bdr_elems_by_geom(Geometry::NumGeom);
294  num_bdr_elems_by_geom = 0;
295  for (int i = 0; i < GetNBE(); i++)
296  {
297  num_bdr_elems_by_geom[GetBdrElementBaseGeometry(i)]++;
298  }
299  Array<int> num_faces_by_geom(Geometry::NumGeom);
300  num_faces_by_geom = 0;
301  for (int i = 0; i < GetNFaces(); i++)
302  {
303  num_faces_by_geom[GetFaceBaseGeometry(i)]++;
304  }
305 
306  os << '\n'
307  << "Number of vertices : " << GetNV() << '\n'
308  << "Number of edges : " << GetNEdges() << '\n'
309  << "Number of faces : " << GetNFaces() << " -- ";
310  PrintElementsByGeometry(Dim-1, num_faces_by_geom, os);
311  os << '\n'
312  << "Number of elements : " << GetNE() << " -- ";
313  PrintElementsByGeometry(Dim, num_elems_by_geom, os);
314  os << '\n'
315  << "Number of bdr elem : " << GetNBE() << " -- ";
316  PrintElementsByGeometry(Dim-1, num_bdr_elems_by_geom, os);
317  os << '\n'
318  << "Euler Number : " << EulerNumber() << '\n'
319  << "h_min : " << h_min << '\n'
320  << "h_max : " << h_max << '\n'
321  << "kappa_min : " << kappa_min << '\n'
322  << "kappa_max : " << kappa_max << '\n';
323  }
324  os << '\n' << std::flush;
325 }
326 
327 FiniteElement *Mesh::GetTransformationFEforElementType(Element::Type ElemType)
328 {
329  switch (ElemType)
330  {
331  case Element::POINT : return &PointFE;
332  case Element::SEGMENT : return &SegmentFE;
333  case Element::TRIANGLE : return &TriangleFE;
334  case Element::QUADRILATERAL : return &QuadrilateralFE;
335  case Element::TETRAHEDRON : return &TetrahedronFE;
336  case Element::HEXAHEDRON : return &HexahedronFE;
337  case Element::WEDGE : return &WedgeFE;
338  case Element::PYRAMID : return &PyramidFE;
339  default:
340  MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
341  break;
342  }
343  MFEM_ABORT("Unknown element type");
344  return NULL;
345 }
346 
347 
348 void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
349 {
350  ElTr->Attribute = GetAttribute(i);
351  ElTr->ElementNo = i;
352  ElTr->ElementType = ElementTransformation::ELEMENT;
353  ElTr->mesh = this;
354  ElTr->Reset();
355  if (Nodes == NULL)
356  {
357  GetPointMatrix(i, ElTr->GetPointMat());
358  ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
359  }
360  else
361  {
362  DenseMatrix &pm = ElTr->GetPointMat();
363  Array<int> vdofs;
364  Nodes->FESpace()->GetElementVDofs(i, vdofs);
365  Nodes->HostRead();
366  const GridFunction &nodes = *Nodes;
367  int n = vdofs.Size()/spaceDim;
368  pm.SetSize(spaceDim, n);
369  for (int k = 0; k < spaceDim; k++)
370  {
371  for (int j = 0; j < n; j++)
372  {
373  pm(k,j) = nodes(vdofs[n*k+j]);
374  }
375  }
376  ElTr->SetFE(Nodes->FESpace()->GetFE(i));
377  }
378 }
379 
380 void Mesh::GetElementTransformation(int i, const Vector &nodes,
382 {
383  ElTr->Attribute = GetAttribute(i);
384  ElTr->ElementNo = i;
385  ElTr->ElementType = ElementTransformation::ELEMENT;
386  ElTr->mesh = this;
387  DenseMatrix &pm = ElTr->GetPointMat();
388  ElTr->Reset();
389  nodes.HostRead();
390  if (Nodes == NULL)
391  {
392  MFEM_ASSERT(nodes.Size() == spaceDim*GetNV(), "");
393  int nv = elements[i]->GetNVertices();
394  const int *v = elements[i]->GetVertices();
395  int n = vertices.Size();
396  pm.SetSize(spaceDim, nv);
397  for (int k = 0; k < spaceDim; k++)
398  {
399  for (int j = 0; j < nv; j++)
400  {
401  pm(k, j) = nodes(k*n+v[j]);
402  }
403  }
404  ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
405  }
406  else
407  {
408  MFEM_ASSERT(nodes.Size() == Nodes->Size(), "");
409  Array<int> vdofs;
410  Nodes->FESpace()->GetElementVDofs(i, vdofs);
411  int n = vdofs.Size()/spaceDim;
412  pm.SetSize(spaceDim, n);
413  for (int k = 0; k < spaceDim; k++)
414  {
415  for (int j = 0; j < n; j++)
416  {
417  pm(k,j) = nodes(vdofs[n*k+j]);
418  }
419  }
420  ElTr->SetFE(Nodes->FESpace()->GetFE(i));
421  }
422 }
423 
424 ElementTransformation *Mesh::GetElementTransformation(int i)
425 {
426  GetElementTransformation(i, &Transformation);
427 
428  return &Transformation;
429 }
430 
431 ElementTransformation *Mesh::GetBdrElementTransformation(int i)
432 {
433  GetBdrElementTransformation(i, &BdrTransformation);
434  return &BdrTransformation;
435 }
436 
437 void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
438 {
439  ElTr->Attribute = GetBdrAttribute(i);
440  ElTr->ElementNo = i; // boundary element number
441  ElTr->ElementType = ElementTransformation::BDR_ELEMENT;
442  ElTr->mesh = this;
443  DenseMatrix &pm = ElTr->GetPointMat();
444  ElTr->Reset();
445  if (Nodes == NULL)
446  {
447  GetBdrPointMatrix(i, pm);
448  ElTr->SetFE(GetTransformationFEforElementType(GetBdrElementType(i)));
449  }
450  else
451  {
452  const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
453  Nodes->HostRead();
454  const GridFunction &nodes = *Nodes;
455  if (bdr_el)
456  {
457  Array<int> vdofs;
458  Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
459  int n = vdofs.Size()/spaceDim;
460  pm.SetSize(spaceDim, n);
461  for (int k = 0; k < spaceDim; k++)
462  {
463  for (int j = 0; j < n; j++)
464  {
465  pm(k,j) = nodes(vdofs[n*k+j]);
466  }
467  }
468  ElTr->SetFE(bdr_el);
469  }
470  else // L2 Nodes (e.g., periodic mesh)
471  {
472  int elem_id, face_info;
473  GetBdrElementAdjacentElement2(i, elem_id, face_info);
474 
475  GetLocalFaceTransformation(GetBdrElementType(i),
476  GetElementType(elem_id),
477  FaceElemTr.Loc1.Transf, face_info);
478  // NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
479 
480  Geometry::Type face_geom = GetBdrElementBaseGeometry(i);
481  const FiniteElement *face_el =
482  Nodes->FESpace()->GetTraceElement(elem_id, face_geom);
483  MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
484  "Mesh requires nodal Finite Element.");
485  IntegrationRule eir(face_el->GetDof());
486  FaceElemTr.Loc1.Transf.ElementNo = elem_id;
487  FaceElemTr.Loc1.Transf.mesh = this;
488  FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
489  FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
490  Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
491 
492  ElTr->SetFE(face_el);
493  }
494  }
495 }
496 
497 void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
498 {
499  FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
500  FTr->ElementNo = FaceNo;
501  FTr->ElementType = ElementTransformation::FACE;
502  FTr->mesh = this;
503  DenseMatrix &pm = FTr->GetPointMat();
504  FTr->Reset();
505  if (Nodes == NULL)
506  {
507  const int *v = (Dim == 1) ? &FaceNo : faces[FaceNo]->GetVertices();
508  const int nv = (Dim == 1) ? 1 : faces[FaceNo]->GetNVertices();
509  pm.SetSize(spaceDim, nv);
510  for (int i = 0; i < spaceDim; i++)
511  {
512  for (int j = 0; j < nv; j++)
513  {
514  pm(i, j) = vertices[v[j]](i);
515  }
516  }
517  FTr->SetFE(GetTransformationFEforElementType(GetFaceElementType(FaceNo)));
518  }
519  else // curved mesh
520  {
521  const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
522  Nodes->HostRead();
523  const GridFunction &nodes = *Nodes;
524  if (face_el)
525  {
526  Array<int> vdofs;
527  Nodes->FESpace()->GetFaceVDofs(FaceNo, vdofs);
528  int n = vdofs.Size()/spaceDim;
529  pm.SetSize(spaceDim, n);
530  for (int i = 0; i < spaceDim; i++)
531  {
532  for (int j = 0; j < n; j++)
533  {
534  pm(i, j) = nodes(vdofs[n*i+j]);
535  }
536  }
537  FTr->SetFE(face_el);
538  }
539  else // L2 Nodes (e.g., periodic mesh), go through the volume of Elem1
540  {
541  FaceInfo &face_info = faces_info[FaceNo];
542 
543  Geometry::Type face_geom = GetFaceGeometryType(FaceNo);
544  Element::Type face_type = GetFaceElementType(FaceNo);
545 
546  GetLocalFaceTransformation(face_type,
547  GetElementType(face_info.Elem1No),
548  FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
549  // NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
550 
551  face_el = Nodes->FESpace()->GetTraceElement(face_info.Elem1No,
552  face_geom);
553  MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
554  "Mesh requires nodal Finite Element.");
555 
556  IntegrationRule eir(face_el->GetDof());
557  FaceElemTr.Loc1.Transf.ElementNo = face_info.Elem1No;
558  FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
559  FaceElemTr.Loc1.Transf.mesh = this;
560  FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
561  Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
562 
563  FTr->SetFE(face_el);
564  }
565  }
566 }
567 
568 ElementTransformation *Mesh::GetFaceTransformation(int FaceNo)
569 {
570  GetFaceTransformation(FaceNo, &FaceTransformation);
571  return &FaceTransformation;
572 }
573 
574 void Mesh::GetEdgeTransformation(int EdgeNo, IsoparametricTransformation *EdTr)
575 {
576  if (Dim == 2)
577  {
578  GetFaceTransformation(EdgeNo, EdTr);
579  return;
580  }
581  if (Dim == 1)
582  {
583  mfem_error("Mesh::GetEdgeTransformation not defined in 1D \n");
584  }
585 
586  EdTr->Attribute = 1;
587  EdTr->ElementNo = EdgeNo;
588  EdTr->ElementType = ElementTransformation::EDGE;
589  EdTr->mesh = this;
590  DenseMatrix &pm = EdTr->GetPointMat();
591  EdTr->Reset();
592  if (Nodes == NULL)
593  {
594  Array<int> v;
595  GetEdgeVertices(EdgeNo, v);
596  const int nv = 2;
597  pm.SetSize(spaceDim, nv);
598  for (int i = 0; i < spaceDim; i++)
599  {
600  for (int j = 0; j < nv; j++)
601  {
602  pm(i, j) = vertices[v[j]](i);
603  }
604  }
605  EdTr->SetFE(GetTransformationFEforElementType(Element::SEGMENT));
606  }
607  else
608  {
609  const FiniteElement *edge_el = Nodes->FESpace()->GetEdgeElement(EdgeNo);
610  Nodes->HostRead();
611  const GridFunction &nodes = *Nodes;
612  if (edge_el)
613  {
614  Array<int> vdofs;
615  Nodes->FESpace()->GetEdgeVDofs(EdgeNo, vdofs);
616  int n = vdofs.Size()/spaceDim;
617  pm.SetSize(spaceDim, n);
618  for (int i = 0; i < spaceDim; i++)
619  {
620  for (int j = 0; j < n; j++)
621  {
622  pm(i, j) = nodes(vdofs[n*i+j]);
623  }
624  }
625  EdTr->SetFE(edge_el);
626  }
627  else
628  {
629  MFEM_ABORT("Not implemented.");
630  }
631  }
632 }
633 
634 ElementTransformation *Mesh::GetEdgeTransformation(int EdgeNo)
635 {
636  GetEdgeTransformation(EdgeNo, &EdgeTransformation);
637  return &EdgeTransformation;
638 }
639 
640 
641 void Mesh::GetLocalPtToSegTransformation(
642  IsoparametricTransformation &Transf, int i)
643 {
644  const IntegrationRule *SegVert;
645  DenseMatrix &locpm = Transf.GetPointMat();
646  Transf.Reset();
647 
648  Transf.SetFE(&PointFE);
649  SegVert = Geometries.GetVertices(Geometry::SEGMENT);
650  locpm.SetSize(1, 1);
651  locpm(0, 0) = SegVert->IntPoint(i/64).x;
652  // (i/64) is the local face no. in the segment
653  // (i%64) is the orientation of the point (not used)
654 }
655 
656 void Mesh::GetLocalSegToTriTransformation(
657  IsoparametricTransformation &Transf, int i)
658 {
659  const int *tv, *so;
660  const IntegrationRule *TriVert;
661  DenseMatrix &locpm = Transf.GetPointMat();
662  Transf.Reset();
663 
664  Transf.SetFE(&SegmentFE);
665  tv = tri_t::Edges[i/64]; // (i/64) is the local face no. in the triangle
666  so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
667  TriVert = Geometries.GetVertices(Geometry::TRIANGLE);
668  locpm.SetSize(2, 2);
669  for (int j = 0; j < 2; j++)
670  {
671  locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
672  locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
673  }
674 }
675 
676 void Mesh::GetLocalSegToQuadTransformation(
677  IsoparametricTransformation &Transf, int i)
678 {
679  const int *qv, *so;
680  const IntegrationRule *QuadVert;
681  DenseMatrix &locpm = Transf.GetPointMat();
682  Transf.Reset();
683 
684  Transf.SetFE(&SegmentFE);
685  qv = quad_t::Edges[i/64]; // (i/64) is the local face no. in the quad
686  so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
687  QuadVert = Geometries.GetVertices(Geometry::SQUARE);
688  locpm.SetSize(2, 2);
689  for (int j = 0; j < 2; j++)
690  {
691  locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
692  locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
693  }
694 }
695 
696 void Mesh::GetLocalTriToTetTransformation(
697  IsoparametricTransformation &Transf, int i)
698 {
699  DenseMatrix &locpm = Transf.GetPointMat();
700  Transf.Reset();
701 
702  Transf.SetFE(&TriangleFE);
703  // (i/64) is the local face no. in the tet
704  const int *tv = tet_t::FaceVert[i/64];
705  // (i%64) is the orientation of the tetrahedron face
706  // w.r.t. the face element
707  const int *to = tri_t::Orient[i%64];
708  const IntegrationRule *TetVert =
709  Geometries.GetVertices(Geometry::TETRAHEDRON);
710  locpm.SetSize(3, 3);
711  for (int j = 0; j < 3; j++)
712  {
713  const IntegrationPoint &vert = TetVert->IntPoint(tv[to[j]]);
714  locpm(0, j) = vert.x;
715  locpm(1, j) = vert.y;
716  locpm(2, j) = vert.z;
717  }
718 }
719 
720 void Mesh::GetLocalTriToWdgTransformation(
721  IsoparametricTransformation &Transf, int i)
722 {
723  DenseMatrix &locpm = Transf.GetPointMat();
724  Transf.Reset();
725 
726  Transf.SetFE(&TriangleFE);
727  // (i/64) is the local face no. in the pri
728  MFEM_VERIFY(i < 128, "Local face index " << i/64
729  << " is not a triangular face of a wedge.");
730  const int *pv = pri_t::FaceVert[i/64];
731  // (i%64) is the orientation of the wedge face
732  // w.r.t. the face element
733  const int *to = tri_t::Orient[i%64];
734  const IntegrationRule *PriVert =
735  Geometries.GetVertices(Geometry::PRISM);
736  locpm.SetSize(3, 3);
737  for (int j = 0; j < 3; j++)
738  {
739  const IntegrationPoint &vert = PriVert->IntPoint(pv[to[j]]);
740  locpm(0, j) = vert.x;
741  locpm(1, j) = vert.y;
742  locpm(2, j) = vert.z;
743  }
744 }
745 
746 void Mesh::GetLocalTriToPyrTransformation(
747  IsoparametricTransformation &Transf, int i)
748 {
749  DenseMatrix &locpm = Transf.GetPointMat();
750 
751  Transf.SetFE(&TriangleFE);
752  // (i/64) is the local face no. in the pyr
753  MFEM_VERIFY(i >= 64, "Local face index " << i/64
754  << " is not a triangular face of a pyramid.");
755  const int *pv = pyr_t::FaceVert[i/64];
756  // (i%64) is the orientation of the pyramid face
757  // w.r.t. the face element
758  const int *to = tri_t::Orient[i%64];
759  const IntegrationRule *PyrVert =
760  Geometries.GetVertices(Geometry::PYRAMID);
761  locpm.SetSize(3, 3);
762  for (int j = 0; j < 3; j++)
763  {
764  const IntegrationPoint &vert = PyrVert->IntPoint(pv[to[j]]);
765  locpm(0, j) = vert.x;
766  locpm(1, j) = vert.y;
767  locpm(2, j) = vert.z;
768  }
769 }
770 
771 void Mesh::GetLocalQuadToHexTransformation(
772  IsoparametricTransformation &Transf, int i)
773 {
774  DenseMatrix &locpm = Transf.GetPointMat();
775  Transf.Reset();
776 
777  Transf.SetFE(&QuadrilateralFE);
778  // (i/64) is the local face no. in the hex
779  const int *hv = hex_t::FaceVert[i/64];
780  // (i%64) is the orientation of the quad
781  const int *qo = quad_t::Orient[i%64];
782  const IntegrationRule *HexVert = Geometries.GetVertices(Geometry::CUBE);
783  locpm.SetSize(3, 4);
784  for (int j = 0; j < 4; j++)
785  {
786  const IntegrationPoint &vert = HexVert->IntPoint(hv[qo[j]]);
787  locpm(0, j) = vert.x;
788  locpm(1, j) = vert.y;
789  locpm(2, j) = vert.z;
790  }
791 }
792 
793 void Mesh::GetLocalQuadToWdgTransformation(
794  IsoparametricTransformation &Transf, int i)
795 {
796  DenseMatrix &locpm = Transf.GetPointMat();
797  Transf.Reset();
798 
799  Transf.SetFE(&QuadrilateralFE);
800  // (i/64) is the local face no. in the pri
801  MFEM_VERIFY(i >= 128, "Local face index " << i/64
802  << " is not a quadrilateral face of a wedge.");
803  const int *pv = pri_t::FaceVert[i/64];
804  // (i%64) is the orientation of the quad
805  const int *qo = quad_t::Orient[i%64];
806  const IntegrationRule *PriVert = Geometries.GetVertices(Geometry::PRISM);
807  locpm.SetSize(3, 4);
808  for (int j = 0; j < 4; j++)
809  {
810  const IntegrationPoint &vert = PriVert->IntPoint(pv[qo[j]]);
811  locpm(0, j) = vert.x;
812  locpm(1, j) = vert.y;
813  locpm(2, j) = vert.z;
814  }
815 }
816 
817 void Mesh::GetLocalQuadToPyrTransformation(
818  IsoparametricTransformation &Transf, int i)
819 {
820  DenseMatrix &locpm = Transf.GetPointMat();
821 
822  Transf.SetFE(&QuadrilateralFE);
823  // (i/64) is the local face no. in the pyr
824  MFEM_VERIFY(i < 64, "Local face index " << i/64
825  << " is not a quadrilateral face of a pyramid.");
826  const int *pv = pyr_t::FaceVert[i/64];
827  // (i%64) is the orientation of the quad
828  const int *qo = quad_t::Orient[i%64];
829  const IntegrationRule *PyrVert = Geometries.GetVertices(Geometry::PYRAMID);
830  locpm.SetSize(3, 4);
831  for (int j = 0; j < 4; j++)
832  {
833  const IntegrationPoint &vert = PyrVert->IntPoint(pv[qo[j]]);
834  locpm(0, j) = vert.x;
835  locpm(1, j) = vert.y;
836  locpm(2, j) = vert.z;
837  }
838 }
839 
840 const GeometricFactors* Mesh::GetGeometricFactors(const IntegrationRule& ir,
841  const int flags,
842  MemoryType d_mt)
843 {
844  for (int i = 0; i < geom_factors.Size(); i++)
845  {
846  GeometricFactors *gf = geom_factors[i];
847  if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags)
848  {
849  return gf;
850  }
851  }
852 
853  this->EnsureNodes();
854 
855  GeometricFactors *gf = new GeometricFactors(this, ir, flags, d_mt);
856  geom_factors.Append(gf);
857  return gf;
858 }
859 
860 const FaceGeometricFactors* Mesh::GetFaceGeometricFactors(
861  const IntegrationRule& ir,
862  const int flags, FaceType type, MemoryType d_mt)
863 {
864  for (int i = 0; i < face_geom_factors.Size(); i++)
865  {
866  FaceGeometricFactors *gf = face_geom_factors[i];
867  if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags &&
868  gf->type==type)
869  {
870  return gf;
871  }
872  }
873 
874  this->EnsureNodes();
875 
876  FaceGeometricFactors *gf = new FaceGeometricFactors(this, ir, flags, type,
877  d_mt);
878  face_geom_factors.Append(gf);
879  return gf;
880 }
881 
882 void Mesh::DeleteGeometricFactors()
883 {
884  for (int i = 0; i < geom_factors.Size(); i++)
885  {
886  delete geom_factors[i];
887  }
888  geom_factors.SetSize(0);
889  for (int i = 0; i < face_geom_factors.Size(); i++)
890  {
891  delete face_geom_factors[i];
892  }
893  face_geom_factors.SetSize(0);
894 }
895 
896 void Mesh::GetLocalFaceTransformation(
897  int face_type, int elem_type, IsoparametricTransformation &Transf, int info)
898 {
899  switch (face_type)
900  {
901  case Element::POINT:
902  GetLocalPtToSegTransformation(Transf, info);
903  break;
904 
905  case Element::SEGMENT:
906  if (elem_type == Element::TRIANGLE)
907  {
908  GetLocalSegToTriTransformation(Transf, info);
909  }
910  else
911  {
912  MFEM_ASSERT(elem_type == Element::QUADRILATERAL, "");
913  GetLocalSegToQuadTransformation(Transf, info);
914  }
915  break;
916 
917  case Element::TRIANGLE:
918  if (elem_type == Element::TETRAHEDRON)
919  {
920  GetLocalTriToTetTransformation(Transf, info);
921  }
922  else if (elem_type == Element::WEDGE)
923  {
924  GetLocalTriToWdgTransformation(Transf, info);
925  }
926  else if (elem_type == Element::PYRAMID)
927  {
928  GetLocalTriToPyrTransformation(Transf, info);
929  }
930  else
931  {
932  MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
933  "face type " << face_type
934  << " and element type " << elem_type << "\n");
935  }
936  break;
937 
938  case Element::QUADRILATERAL:
939  if (elem_type == Element::HEXAHEDRON)
940  {
941  GetLocalQuadToHexTransformation(Transf, info);
942  }
943  else if (elem_type == Element::WEDGE)
944  {
945  GetLocalQuadToWdgTransformation(Transf, info);
946  }
947  else if (elem_type == Element::PYRAMID)
948  {
949  GetLocalQuadToPyrTransformation(Transf, info);
950  }
951  else
952  {
953  MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
954  "face type " << face_type
955  << " and element type " << elem_type << "\n");
956  }
957  break;
958  }
959 }
960 
961 FaceElementTransformations *Mesh::GetFaceElementTransformations(int FaceNo,
962  int mask)
963 {
964  FaceInfo &face_info = faces_info[FaceNo];
965 
966  int cmask = 0;
967  FaceElemTr.SetConfigurationMask(cmask);
968  FaceElemTr.Elem1 = NULL;
969  FaceElemTr.Elem2 = NULL;
970 
971  // setup the transformation for the first element
972  FaceElemTr.Elem1No = face_info.Elem1No;
973  if (mask & FaceElementTransformations::HAVE_ELEM1)
974  {
975  GetElementTransformation(FaceElemTr.Elem1No, &Transformation);
976  FaceElemTr.Elem1 = &Transformation;
977  cmask |= 1;
978  }
979 
980  // setup the transformation for the second element
981  // return NULL in the Elem2 field if there's no second element, i.e.
982  // the face is on the "boundary"
983  FaceElemTr.Elem2No = face_info.Elem2No;
984  if ((mask & FaceElementTransformations::HAVE_ELEM2) &&
985  FaceElemTr.Elem2No >= 0)
986  {
987 #ifdef MFEM_DEBUG
988  if (NURBSext && (mask & FaceElementTransformations::HAVE_ELEM1))
989  { MFEM_ABORT("NURBS mesh not supported!"); }
990 #endif
991  GetElementTransformation(FaceElemTr.Elem2No, &Transformation2);
992  FaceElemTr.Elem2 = &Transformation2;
993  cmask |= 2;
994  }
995 
996  // setup the face transformation
997  if (mask & FaceElementTransformations::HAVE_FACE)
998  {
999  GetFaceTransformation(FaceNo, &FaceElemTr);
1000  cmask |= 16;
1001  }
1002  else
1003  {
1004  FaceElemTr.SetGeometryType(GetFaceGeometryType(FaceNo));
1005  }
1006 
1007  // setup Loc1 & Loc2
1008  int face_type = GetFaceElementType(FaceNo);
1009  if (mask & FaceElementTransformations::HAVE_LOC1)
1010  {
1011  int elem_type = GetElementType(face_info.Elem1No);
1012  GetLocalFaceTransformation(face_type, elem_type,
1013  FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
1014  cmask |= 4;
1015  }
1016  if ((mask & FaceElementTransformations::HAVE_LOC2) &&
1017  FaceElemTr.Elem2No >= 0)
1018  {
1019  int elem_type = GetElementType(face_info.Elem2No);
1020  GetLocalFaceTransformation(face_type, elem_type,
1021  FaceElemTr.Loc2.Transf, face_info.Elem2Inf);
1022 
1023  // NC meshes: prepend slave edge/face transformation to Loc2
1024  if (Nonconforming() && IsSlaveFace(face_info))
1025  {
1026  ApplyLocalSlaveTransformation(FaceElemTr, face_info, false);
1027  }
1028  cmask |= 8;
1029  }
1030 
1031  FaceElemTr.SetConfigurationMask(cmask);
1032 
1033  // This check can be useful for internal debugging, however it will fail on
1034  // periodic boundary faces, so we keep it disabled in general.
1035 #if 0
1036 #ifdef MFEM_DEBUG
1037  double dist = FaceElemTr.CheckConsistency();
1038  if (dist >= 1e-12)
1039  {
1040  mfem::out << "\nInternal error: face id = " << FaceNo
1041  << ", dist = " << dist << '\n';
1042  FaceElemTr.CheckConsistency(1); // print coordinates
1043  MFEM_ABORT("internal error");
1044  }
1045 #endif
1046 #endif
1047 
1048  return &FaceElemTr;
1049 }
1050 
1051 bool Mesh::IsSlaveFace(const FaceInfo &fi) const
1052 {
1053  return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
1054 }
1055 
1056 void Mesh::ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
1057  const FaceInfo &fi, bool is_ghost)
1058 {
1059 #ifdef MFEM_THREAD_SAFE
1060  DenseMatrix composition;
1061 #else
1062  static DenseMatrix composition;
1063 #endif
1064  MFEM_ASSERT(fi.NCFace >= 0, "");
1065  MFEM_ASSERT(nc_faces_info[fi.NCFace].Slave, "internal error");
1066  if (!is_ghost)
1067  {
1068  // side 1 -> child side, side 2 -> parent side
1070  LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1071  // In 2D, we need to flip the point matrix since it is aligned with the
1072  // parent side.
1073  if (Dim == 2)
1074  {
1075  // swap points (columns) 0 and 1
1076  std::swap(composition(0,0), composition(0,1));
1077  std::swap(composition(1,0), composition(1,1));
1078  }
1079  LT.SetPointMat(composition);
1080  }
1081  else // is_ghost == true
1082  {
1083  // side 1 -> parent side, side 2 -> child side
1085  LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1086  // In 2D, there is no need to flip the point matrix since it is already
1087  // aligned with the parent side, see also ParNCMesh::GetFaceNeighbors.
1088  // In 3D the point matrix was flipped during construction in
1089  // ParNCMesh::GetFaceNeighbors and due to that it is already aligned with
1090  // the parent side.
1091  LT.SetPointMat(composition);
1092  }
1093 }
1094 
1095 FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
1096 {
1098  int fn = GetBdrFace(BdrElemNo);
1099 
1100  // Check if the face is interior, shared, or nonconforming.
1101  if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
1102  {
1103  return NULL;
1104  }
1105  tr = GetFaceElementTransformations(fn, 21);
1106  tr->Attribute = boundary[BdrElemNo]->GetAttribute();
1107  tr->ElementNo = BdrElemNo;
1108  tr->ElementType = ElementTransformation::BDR_FACE;
1109  tr->mesh = this;
1110  return tr;
1111 }
1112 
1113 int Mesh::GetBdrFace(int BdrElemNo) const
1114 {
1115  int fn;
1116  if (Dim == 3)
1117  {
1118  fn = be_to_face[BdrElemNo];
1119  }
1120  else if (Dim == 2)
1121  {
1122  fn = be_to_edge[BdrElemNo];
1123  }
1124  else
1125  {
1126  fn = boundary[BdrElemNo]->GetVertices()[0];
1127  }
1128  return fn;
1129 }
1130 
1131 Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
1132 {
1133  FaceInformation face;
1134  int e1, e2;
1135  int inf1, inf2;
1136  int ncface;
1137  GetFaceElements(f, &e1, &e2);
1138  GetFaceInfos(f, &inf1, &inf2, &ncface);
1139  face.element[0].index = e1;
1140  face.element[0].location = ElementLocation::Local;
1141  face.element[0].orientation = inf1%64;
1142  face.element[0].local_face_id = inf1/64;
1143  face.element[1].local_face_id = inf2/64;
1144  face.ncface = ncface;
1145  face.point_matrix = nullptr;
1146  // The following figures out face.location, face.conformity,
1147  // face.element[1].index, and face.element[1].orientation.
1148  if (f < GetNumFaces()) // Non-ghost face
1149  {
1150  if (e2>=0)
1151  {
1152  if (ncface==-1)
1153  {
1154  face.tag = FaceInfoTag::LocalConforming;
1155  face.topology = FaceTopology::Conforming;
1156  face.element[1].location = ElementLocation::Local;
1157  face.element[0].conformity = ElementConformity::Coincident;
1158  face.element[1].conformity = ElementConformity::Coincident;
1159  face.element[1].index = e2;
1160  face.element[1].orientation = inf2%64;
1161  }
1162  else // ncface >= 0
1163  {
1164  face.tag = FaceInfoTag::LocalSlaveNonconforming;
1165  face.topology = FaceTopology::Nonconforming;
1166  face.element[1].location = ElementLocation::Local;
1167  face.element[0].conformity = ElementConformity::Coincident;
1168  face.element[1].conformity = ElementConformity::Superset;
1169  face.element[1].index = e2;
1170  MFEM_ASSERT(inf2%64==0, "unexpected slave face orientation.");
1171  face.element[1].orientation = inf2%64;
1172  face.point_matrix = nc_faces_info[ncface].PointMatrix;
1173  }
1174  }
1175  else // e2<0
1176  {
1177  if (ncface==-1)
1178  {
1179  if (inf2<0)
1180  {
1181  face.tag = FaceInfoTag::Boundary;
1182  face.topology = FaceTopology::Boundary;
1183  face.element[1].location = ElementLocation::NA;
1184  face.element[0].conformity = ElementConformity::Coincident;
1185  face.element[1].conformity = ElementConformity::NA;
1186  face.element[1].index = -1;
1187  face.element[1].orientation = -1;
1188  }
1189  else // inf2 >= 0
1190  {
1191  face.tag = FaceInfoTag::SharedConforming;
1192  face.topology = FaceTopology::Conforming;
1193  face.element[0].conformity = ElementConformity::Coincident;
1194  face.element[1].conformity = ElementConformity::Coincident;
1195  face.element[1].location = ElementLocation::FaceNbr;
1196  face.element[1].index = -1 - e2;
1197  face.element[1].orientation = inf2%64;
1198  }
1199  }
1200  else // ncface >= 0
1201  {
1202  if (inf2 < 0)
1203  {
1204  face.tag = FaceInfoTag::MasterNonconforming;
1205  face.topology = FaceTopology::Nonconforming;
1206  face.element[1].location = ElementLocation::NA;
1207  face.element[0].conformity = ElementConformity::Coincident;
1208  face.element[1].conformity = ElementConformity::Subset;
1209  face.element[1].index = -1;
1210  face.element[1].orientation = -1;
1211  }
1212  else
1213  {
1214  face.tag = FaceInfoTag::SharedSlaveNonconforming;
1215  face.topology = FaceTopology::Nonconforming;
1216  face.element[1].location = ElementLocation::FaceNbr;
1217  face.element[0].conformity = ElementConformity::Coincident;
1218  face.element[1].conformity = ElementConformity::Superset;
1219  face.element[1].index = -1 - e2;
1220  face.element[1].orientation = inf2%64;
1221  }
1222  face.point_matrix = nc_faces_info[ncface].PointMatrix;
1223  }
1224  }
1225  }
1226  else // Ghost face
1227  {
1228  if (e1==-1)
1229  {
1230  face.tag = FaceInfoTag::GhostMaster;
1231  face.topology = FaceTopology::NA;
1232  face.element[1].location = ElementLocation::NA;
1233  face.element[0].conformity = ElementConformity::NA;
1234  face.element[1].conformity = ElementConformity::NA;
1235  face.element[1].index = -1;
1236  face.element[1].orientation = -1;
1237  }
1238  else
1239  {
1240  face.tag = FaceInfoTag::GhostSlave;
1241  face.topology = FaceTopology::Nonconforming;
1242  face.element[1].location = ElementLocation::FaceNbr;
1243  face.element[0].conformity = ElementConformity::Superset;
1244  face.element[1].conformity = ElementConformity::Coincident;
1245  face.element[1].index = -1 - e2;
1246  face.element[1].orientation = inf2%64;
1247  face.point_matrix = nc_faces_info[ncface].PointMatrix;
1248  }
1249  }
1250  return face;
1251 }
1252 
1253 Mesh::FaceInformation::operator Mesh::FaceInfo() const
1254 {
1255  FaceInfo res {-1, -1, -1, -1, -1};
1256  switch (tag)
1257  {
1258  case FaceInfoTag::LocalConforming:
1259  res.Elem1No = element[0].index;
1260  res.Elem2No = element[1].index;
1261  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1262  res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1263  res.NCFace = ncface;
1264  break;
1265  case FaceInfoTag::LocalSlaveNonconforming:
1266  res.Elem1No = element[0].index;
1267  res.Elem2No = element[1].index;
1268  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1269  res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1270  res.NCFace = ncface;
1271  break;
1272  case FaceInfoTag::Boundary:
1273  res.Elem1No = element[0].index;
1274  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1275  break;
1276  case FaceInfoTag::SharedConforming:
1277  res.Elem1No = element[0].index;
1278  res.Elem2No = -1 - element[1].index;
1279  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1280  res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1281  break;
1282  case FaceInfoTag::MasterNonconforming:
1283  res.Elem1No = element[0].index;
1284  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1285  break;
1286  case FaceInfoTag::SharedSlaveNonconforming:
1287  res.Elem1No = element[0].index;
1288  res.Elem2No = -1 - element[1].index;
1289  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1290  res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1291  break;
1292  case FaceInfoTag::GhostMaster:
1293  break;
1294  case FaceInfoTag::GhostSlave:
1295  res.Elem1No = element[0].index;
1296  res.Elem2No = -1 - element[1].index;
1297  res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
1298  res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
1299  break;
1300  }
1301  return res;
1302 }
1303 
1304 std::ostream& operator<<(std::ostream& os, const Mesh::FaceInformation& info)
1305 {
1306  os << "face topology=";
1307  switch (info.topology)
1308  {
1309  case Mesh::FaceTopology::Boundary:
1310  os << "Boundary";
1311  break;
1312  case Mesh::FaceTopology::Conforming:
1313  os << "Conforming";
1314  break;
1315  case Mesh::FaceTopology::Nonconforming:
1316  os << "Non-conforming";
1317  break;
1318  case Mesh::FaceTopology::NA:
1319  os << "NA";
1320  break;
1321  }
1322  os << "element[0].location=";
1323  switch (info.element[0].location)
1324  {
1325  case Mesh::ElementLocation::Local:
1326  os << "Local";
1327  break;
1328  case Mesh::ElementLocation::FaceNbr:
1329  os << "FaceNbr";
1330  break;
1331  case Mesh::ElementLocation::NA:
1332  os << "NA";
1333  break;
1334  }
1335  os << std::endl;
1336  os << "element[1].location=";
1337  switch (info.element[1].location)
1338  {
1339  case Mesh::ElementLocation::Local:
1340  os << "Local";
1341  break;
1342  case Mesh::ElementLocation::FaceNbr:
1343  os << "FaceNbr";
1344  break;
1345  case Mesh::ElementLocation::NA:
1346  os << "NA";
1347  break;
1348  }
1349  os << std::endl;
1350  os << "element[0].conformity=";
1351  switch (info.element[0].conformity)
1352  {
1353  case Mesh::ElementConformity::Coincident:
1354  os << "Coincident";
1355  break;
1356  case Mesh::ElementConformity::Superset:
1357  os << "Superset";
1358  break;
1359  case Mesh::ElementConformity::Subset:
1360  os << "Subset";
1361  break;
1362  case Mesh::ElementConformity::NA:
1363  os << "NA";
1364  break;
1365  }
1366  os << std::endl;
1367  os << "element[1].conformity=";
1368  switch (info.element[1].conformity)
1369  {
1370  case Mesh::ElementConformity::Coincident:
1371  os << "Coincident";
1372  break;
1373  case Mesh::ElementConformity::Superset:
1374  os << "Superset";
1375  break;
1376  case Mesh::ElementConformity::Subset:
1377  os << "Subset";
1378  break;
1379  case Mesh::ElementConformity::NA:
1380  os << "NA";
1381  break;
1382  }
1383  os << std::endl;
1384  os << "element[0].index=" << info.element[0].index << std::endl
1385  << "element[1].index=" << info.element[1].index << std::endl
1386  << "element[0].local_face_id=" << info.element[0].local_face_id << std::endl
1387  << "element[1].local_face_id=" << info.element[1].local_face_id << std::endl
1388  << "element[0].orientation=" << info.element[0].orientation << std::endl
1389  << "element[1].orientation=" << info.element[1].orientation << std::endl
1390  << "ncface=" << info.ncface << std::endl;
1391  return os;
1392 }
1393 
1394 void Mesh::GetFaceElements(int Face, int *Elem1, int *Elem2) const
1395 {
1396  *Elem1 = faces_info[Face].Elem1No;
1397  *Elem2 = faces_info[Face].Elem2No;
1398 }
1399 
1400 void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
1401 {
1402  *Inf1 = faces_info[Face].Elem1Inf;
1403  *Inf2 = faces_info[Face].Elem2Inf;
1404 }
1405 
1406 void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
1407 {
1408  *Inf1 = faces_info[Face].Elem1Inf;
1409  *Inf2 = faces_info[Face].Elem2Inf;
1410  *NCFace = faces_info[Face].NCFace;
1411 }
1412 
1413 Geometry::Type Mesh::GetFaceGeometryType(int Face) const
1414 {
1415  switch (Dim)
1416  {
1417  case 1: return Geometry::POINT;
1418  case 2: return Geometry::SEGMENT;
1419  case 3:
1420  if (Face < NumOfFaces) // local (non-ghost) face
1421  {
1422  return faces[Face]->GetGeometryType();
1423  }
1424  // ghost face
1425  const int nc_face_id = faces_info[Face].NCFace;
1426  MFEM_ASSERT(nc_face_id >= 0, "parent ghost faces are not supported");
1427  return faces[nc_faces_info[nc_face_id].MasterFace]->GetGeometryType();
1428  }
1429  return Geometry::INVALID;
1430 }
1431 
1432 Element::Type Mesh::GetFaceElementType(int Face) const
1433 {
1434  return (Dim == 1) ? Element::POINT : faces[Face]->GetType();
1435 }
1436 
1437 Array<int> Mesh::GetFaceToBdrElMap() const
1438 {
1439  Array<int> face_to_be(NumOfFaces);
1440  face_to_be = -1;
1441  for (int i = 0; i < NumOfBdrElements; i++)
1442  {
1443  face_to_be[GetBdrElementEdgeIndex(i)] = i;
1444  }
1445  return face_to_be;
1446 }
1447 
1448 void Mesh::Init()
1449 {
1450  // in order of declaration:
1451  Dim = spaceDim = 0;
1452  NumOfVertices = -1;
1453  NumOfElements = NumOfBdrElements = 0;
1454  NumOfEdges = NumOfFaces = 0;
1455  nbInteriorFaces = -1;
1456  nbBoundaryFaces = -1;
1457  meshgen = mesh_geoms = 0;
1458  sequence = 0;
1459  Nodes = NULL;
1460  own_nodes = 1;
1461  NURBSext = NULL;
1462  ncmesh = NULL;
1463  last_operation = Mesh::NONE;
1464 }
1465 
1466 void Mesh::InitTables()
1467 {
1468  el_to_edge =
1469  el_to_face = el_to_el = bel_to_edge = face_edge = edge_vertex = NULL;
1470 }
1471 
1472 void Mesh::SetEmpty()
1473 {
1474  Init();
1475  InitTables();
1476 }
1477 
1478 void Mesh::DestroyTables()
1479 {
1480  delete el_to_edge;
1481  delete el_to_face;
1482  delete el_to_el;
1483  DeleteGeometricFactors();
1484 
1485  if (Dim == 3)
1486  {
1487  delete bel_to_edge;
1488  }
1489 
1490  delete face_edge;
1491  delete edge_vertex;
1492 }
1493 
1494 void Mesh::DestroyPointers()
1495 {
1496  if (own_nodes) { delete Nodes; }
1497 
1498  delete ncmesh;
1499 
1500  delete NURBSext;
1501 
1502  for (int i = 0; i < NumOfElements; i++)
1503  {
1504  FreeElement(elements[i]);
1505  }
1506 
1507  for (int i = 0; i < NumOfBdrElements; i++)
1508  {
1509  FreeElement(boundary[i]);
1510  }
1511 
1512  for (int i = 0; i < faces.Size(); i++)
1513  {
1514  FreeElement(faces[i]);
1515  }
1516 
1517  DestroyTables();
1518 }
1519 
1520 void Mesh::Destroy()
1521 {
1522  DestroyPointers();
1523 
1524  elements.DeleteAll();
1525  vertices.DeleteAll();
1526  boundary.DeleteAll();
1527  faces.DeleteAll();
1528  faces_info.DeleteAll();
1529  nc_faces_info.DeleteAll();
1530  be_to_edge.DeleteAll();
1531  be_to_face.DeleteAll();
1532 
1533  // TODO:
1534  // IsoparametricTransformations
1535  // Transformation, Transformation2, BdrTransformation, FaceTransformation,
1536  // EdgeTransformation;
1537  // FaceElementTransformations FaceElemTr;
1538 
1539  CoarseFineTr.Clear();
1540 
1541 #ifdef MFEM_USE_MEMALLOC
1542  TetMemory.Clear();
1543 #endif
1544 
1545  attributes.DeleteAll();
1546  bdr_attributes.DeleteAll();
1547 }
1548 
1549 void Mesh::ResetLazyData()
1550 {
1551  delete el_to_el; el_to_el = NULL;
1552  delete face_edge; face_edge = NULL;
1553  delete edge_vertex; edge_vertex = NULL;
1554  DeleteGeometricFactors();
1555  nbInteriorFaces = -1;
1556  nbBoundaryFaces = -1;
1557 }
1558 
1559 void Mesh::SetAttributes()
1560 {
1561  Array<int> attribs;
1562 
1563  attribs.SetSize(GetNBE());
1564  for (int i = 0; i < attribs.Size(); i++)
1565  {
1566  attribs[i] = GetBdrAttribute(i);
1567  }
1568  attribs.Sort();
1569  attribs.Unique();
1570  attribs.Copy(bdr_attributes);
1571  if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1572  {
1573  MFEM_WARNING("Non-positive attributes on the boundary!");
1574  }
1575 
1576  attribs.SetSize(GetNE());
1577  for (int i = 0; i < attribs.Size(); i++)
1578  {
1579  attribs[i] = GetAttribute(i);
1580  }
1581  attribs.Sort();
1582  attribs.Unique();
1583  attribs.Copy(attributes);
1584  if (attributes.Size() > 0 && attributes[0] <= 0)
1585  {
1586  MFEM_WARNING("Non-positive attributes in the domain!");
1587  }
1588 }
1589 
1590 void Mesh::InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
1591 {
1592  SetEmpty();
1593 
1594  Dim = Dim_;
1595  spaceDim = spaceDim_;
1596 
1597  NumOfVertices = 0;
1598  vertices.SetSize(NVert); // just allocate space for vertices
1599 
1600  NumOfElements = 0;
1601  elements.SetSize(NElem); // just allocate space for Element *
1602 
1603  NumOfBdrElements = 0;
1604  boundary.SetSize(NBdrElem); // just allocate space for Element *
1605 }
1606 
1607 template<typename T>
1608 static void CheckEnlarge(Array<T> &array, int size)
1609 {
1610  if (size >= array.Size()) { array.SetSize(size + 1); }
1611 }
1612 
1613 int Mesh::AddVertex(double x, double y, double z)
1614 {
1615  CheckEnlarge(vertices, NumOfVertices);
1616  double *v = vertices[NumOfVertices]();
1617  v[0] = x;
1618  v[1] = y;
1619  v[2] = z;
1620  return NumOfVertices++;
1621 }
1622 
1623 int Mesh::AddVertex(const double *coords)
1624 {
1625  CheckEnlarge(vertices, NumOfVertices);
1626  vertices[NumOfVertices].SetCoords(spaceDim, coords);
1627  return NumOfVertices++;
1628 }
1629 
1630 void Mesh::AddVertexParents(int i, int p1, int p2)
1631 {
1632  tmp_vertex_parents.Append(Triple<int, int, int>(i, p1, p2));
1633 
1634  // if vertex coordinates are defined, make sure the hanging vertex has the
1635  // correct position
1636  if (i < vertices.Size())
1637  {
1638  double *vi = vertices[i](), *vp1 = vertices[p1](), *vp2 = vertices[p2]();
1639  for (int j = 0; j < 3; j++)
1640  {
1641  vi[j] = (vp1[j] + vp2[j]) * 0.5;
1642  }
1643  }
1644 }
1645 
1646 int Mesh::AddSegment(int v1, int v2, int attr)
1647 {
1648  CheckEnlarge(elements, NumOfElements);
1649  elements[NumOfElements] = new Segment(v1, v2, attr);
1650  return NumOfElements++;
1651 }
1652 
1653 int Mesh::AddSegment(const int *vi, int attr)
1654 {
1655  CheckEnlarge(elements, NumOfElements);
1656  elements[NumOfElements] = new Segment(vi, attr);
1657  return NumOfElements++;
1658 }
1659 
1660 int Mesh::AddTriangle(int v1, int v2, int v3, int attr)
1661 {
1662  CheckEnlarge(elements, NumOfElements);
1663  elements[NumOfElements] = new Triangle(v1, v2, v3, attr);
1664  return NumOfElements++;
1665 }
1666 
1667 int Mesh::AddTriangle(const int *vi, int attr)
1668 {
1669  CheckEnlarge(elements, NumOfElements);
1670  elements[NumOfElements] = new Triangle(vi, attr);
1671  return NumOfElements++;
1672 }
1673 
1674 int Mesh::AddQuad(int v1, int v2, int v3, int v4, int attr)
1675 {
1676  CheckEnlarge(elements, NumOfElements);
1677  elements[NumOfElements] = new Quadrilateral(v1, v2, v3, v4, attr);
1678  return NumOfElements++;
1679 }
1680 
1681 int Mesh::AddQuad(const int *vi, int attr)
1682 {
1683  CheckEnlarge(elements, NumOfElements);
1684  elements[NumOfElements] = new Quadrilateral(vi, attr);
1685  return NumOfElements++;
1686 }
1687 
1688 int Mesh::AddTet(int v1, int v2, int v3, int v4, int attr)
1689 {
1690  int vi[4] = {v1, v2, v3, v4};
1691  return AddTet(vi, attr);
1692 }
1693 
1694 int Mesh::AddTet(const int *vi, int attr)
1695 {
1696  CheckEnlarge(elements, NumOfElements);
1697 #ifdef MFEM_USE_MEMALLOC
1698  Tetrahedron *tet;
1699  tet = TetMemory.Alloc();
1700  tet->SetVertices(vi);
1701  tet->SetAttribute(attr);
1702  elements[NumOfElements] = tet;
1703 #else
1704  elements[NumOfElements] = new Tetrahedron(vi, attr);
1705 #endif
1706  return NumOfElements++;
1707 }
1708 
1709 int Mesh::AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr)
1710 {
1711  CheckEnlarge(elements, NumOfElements);
1712  elements[NumOfElements] = new Wedge(v1, v2, v3, v4, v5, v6, attr);
1713  return NumOfElements++;
1714 }
1715 
1716 int Mesh::AddWedge(const int *vi, int attr)
1717 {
1718  CheckEnlarge(elements, NumOfElements);
1719  elements[NumOfElements] = new Wedge(vi, attr);
1720  return NumOfElements++;
1721 }
1722 
1723 int Mesh::AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr)
1724 {
1725  CheckEnlarge(elements, NumOfElements);
1726  elements[NumOfElements] = new Pyramid(v1, v2, v3, v4, v5, attr);
1727  return NumOfElements++;
1728 }
1729 
1730 int Mesh::AddPyramid(const int *vi, int attr)
1731 {
1732  CheckEnlarge(elements, NumOfElements);
1733  elements[NumOfElements] = new Pyramid(vi, attr);
1734  return NumOfElements++;
1735 }
1736 
1737 int Mesh::AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
1738  int attr)
1739 {
1740  CheckEnlarge(elements, NumOfElements);
1741  elements[NumOfElements] =
1742  new Hexahedron(v1, v2, v3, v4, v5, v6, v7, v8, attr);
1743  return NumOfElements++;
1744 }
1745 
1746 int Mesh::AddHex(const int *vi, int attr)
1747 {
1748  CheckEnlarge(elements, NumOfElements);
1749  elements[NumOfElements] = new Hexahedron(vi, attr);
1750  return NumOfElements++;
1751 }
1752 
1753 void Mesh::AddHexAsTets(const int *vi, int attr)
1754 {
1755  static const int hex_to_tet[6][4] =
1756  {
1757  { 0, 1, 2, 6 }, { 0, 5, 1, 6 }, { 0, 4, 5, 6 },
1758  { 0, 2, 3, 6 }, { 0, 3, 7, 6 }, { 0, 7, 4, 6 }
1759  };
1760  int ti[4];
1761 
1762  for (int i = 0; i < 6; i++)
1763  {
1764  for (int j = 0; j < 4; j++)
1765  {
1766  ti[j] = vi[hex_to_tet[i][j]];
1767  }
1768  AddTet(ti, attr);
1769  }
1770 }
1771 
1772 void Mesh::AddHexAsWedges(const int *vi, int attr)
1773 {
1774  static const int hex_to_wdg[2][6] =
1775  {
1776  { 0, 1, 2, 4, 5, 6 }, { 0, 2, 3, 4, 6, 7 }
1777  };
1778  int ti[6];
1779 
1780  for (int i = 0; i < 2; i++)
1781  {
1782  for (int j = 0; j < 6; j++)
1783  {
1784  ti[j] = vi[hex_to_wdg[i][j]];
1785  }
1786  AddWedge(ti, attr);
1787  }
1788 }
1789 
1790 void Mesh::AddHexAsPyramids(const int *vi, int attr)
1791 {
1792  static const int hex_to_pyr[6][5] =
1793  {
1794  { 0, 1, 2, 3, 8 }, { 0, 4, 5, 1, 8 }, { 1, 5, 6, 2, 8 },
1795  { 2, 6, 7, 3, 8 }, { 3, 7, 4, 0, 8 }, { 7, 6, 5, 4, 8 }
1796  };
1797  int ti[5];
1798 
1799  for (int i = 0; i < 6; i++)
1800  {
1801  for (int j = 0; j < 5; j++)
1802  {
1803  ti[j] = vi[hex_to_pyr[i][j]];
1804  }
1805  AddPyramid(ti, attr);
1806  }
1807 }
1808 
1809 int Mesh::AddElement(Element *elem)
1810 {
1811  CheckEnlarge(elements, NumOfElements);
1812  elements[NumOfElements] = elem;
1813  return NumOfElements++;
1814 }
1815 
1816 int Mesh::AddBdrElement(Element *elem)
1817 {
1818  CheckEnlarge(boundary, NumOfBdrElements);
1819  boundary[NumOfBdrElements] = elem;
1820  return NumOfBdrElements++;
1821 }
1822 
1823 int Mesh::AddBdrSegment(int v1, int v2, int attr)
1824 {
1825  CheckEnlarge(boundary, NumOfBdrElements);
1826  boundary[NumOfBdrElements] = new Segment(v1, v2, attr);
1827  return NumOfBdrElements++;
1828 }
1829 
1830 int Mesh::AddBdrSegment(const int *vi, int attr)
1831 {
1832  CheckEnlarge(boundary, NumOfBdrElements);
1833  boundary[NumOfBdrElements] = new Segment(vi, attr);
1834  return NumOfBdrElements++;
1835 }
1836 
1837 int Mesh::AddBdrTriangle(int v1, int v2, int v3, int attr)
1838 {
1839  CheckEnlarge(boundary, NumOfBdrElements);
1840  boundary[NumOfBdrElements] = new Triangle(v1, v2, v3, attr);
1841  return NumOfBdrElements++;
1842 }
1843 
1844 int Mesh::AddBdrTriangle(const int *vi, int attr)
1845 {
1846  CheckEnlarge(boundary, NumOfBdrElements);
1847  boundary[NumOfBdrElements] = new Triangle(vi, attr);
1848  return NumOfBdrElements++;
1849 }
1850 
1851 int Mesh::AddBdrQuad(int v1, int v2, int v3, int v4, int attr)
1852 {
1853  CheckEnlarge(boundary, NumOfBdrElements);
1854  boundary[NumOfBdrElements] = new Quadrilateral(v1, v2, v3, v4, attr);
1855  return NumOfBdrElements++;
1856 }
1857 
1858 int Mesh::AddBdrQuad(const int *vi, int attr)
1859 {
1860  CheckEnlarge(boundary, NumOfBdrElements);
1861  boundary[NumOfBdrElements] = new Quadrilateral(vi, attr);
1862  return NumOfBdrElements++;
1863 }
1864 
1865 void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
1866 {
1867  static const int quad_to_tri[2][3] = { { 0, 1, 2 }, { 0, 2, 3 } };
1868  int ti[3];
1869 
1870  for (int i = 0; i < 2; i++)
1871  {
1872  for (int j = 0; j < 3; j++)
1873  {
1874  ti[j] = vi[quad_to_tri[i][j]];
1875  }
1876  AddBdrTriangle(ti, attr);
1877  }
1878 }
1879 
1880 int Mesh::AddBdrPoint(int v, int attr)
1881 {
1882  CheckEnlarge(boundary, NumOfBdrElements);
1883  boundary[NumOfBdrElements] = new Point(&v, attr);
1884  return NumOfBdrElements++;
1885 }
1886 
1887 void Mesh::GenerateBoundaryElements()
1888 {
1889  int i, j;
1890  Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
1891 
1892  // GenerateFaces();
1893 
1894  for (i = 0; i < boundary.Size(); i++)
1895  {
1896  FreeElement(boundary[i]);
1897  }
1898 
1899  if (Dim == 3)
1900  {
1901  delete bel_to_edge;
1902  bel_to_edge = NULL;
1903  }
1904 
1905  // count the 'NumOfBdrElements'
1906  NumOfBdrElements = 0;
1907  for (i = 0; i < faces_info.Size(); i++)
1908  {
1909  if (faces_info[i].Elem2No < 0) { NumOfBdrElements++; }
1910  }
1911 
1912  boundary.SetSize(NumOfBdrElements);
1913  be2face.SetSize(NumOfBdrElements);
1914  for (j = i = 0; i < faces_info.Size(); i++)
1915  {
1916  if (faces_info[i].Elem2No < 0)
1917  {
1918  boundary[j] = faces[i]->Duplicate(this);
1919  be2face[j++] = i;
1920  }
1921  }
1922  // In 3D, 'bel_to_edge' is destroyed but it's not updated.
1923 }
1924 
1925 void Mesh::FinalizeCheck()
1926 {
1927  MFEM_VERIFY(vertices.Size() == NumOfVertices ||
1928  vertices.Size() == 0,
1929  "incorrect number of vertices: preallocated: " << vertices.Size()
1930  << ", actually added: " << NumOfVertices);
1931  MFEM_VERIFY(elements.Size() == NumOfElements,
1932  "incorrect number of elements: preallocated: " << elements.Size()
1933  << ", actually added: " << NumOfElements);
1934  MFEM_VERIFY(boundary.Size() == NumOfBdrElements,
1935  "incorrect number of boundary elements: preallocated: "
1936  << boundary.Size() << ", actually added: " << NumOfBdrElements);
1937 }
1938 
1939 void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
1940 {
1941  FinalizeCheck();
1942  CheckElementOrientation(fix_orientation);
1943 
1944  if (refine)
1945  {
1946  MarkTriMeshForRefinement();
1947  }
1948 
1949  if (generate_edges)
1950  {
1951  el_to_edge = new Table;
1952  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
1953  GenerateFaces();
1954  CheckBdrElementOrientation();
1955  }
1956  else
1957  {
1958  NumOfEdges = 0;
1959  }
1960 
1961  NumOfFaces = 0;
1962 
1963  SetAttributes();
1964 
1965  SetMeshGen();
1966 }
1967 
1968 void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
1969  bool fix_orientation)
1970 {
1971  FinalizeCheck();
1972  if (fix_orientation)
1973  {
1974  CheckElementOrientation(fix_orientation);
1975  }
1976 
1977  if (generate_edges)
1978  {
1979  el_to_edge = new Table;
1980  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
1981  GenerateFaces();
1982  CheckBdrElementOrientation();
1983  }
1984  else
1985  {
1986  NumOfEdges = 0;
1987  }
1988 
1989  NumOfFaces = 0;
1990 
1991  SetAttributes();
1992 
1993  SetMeshGen();
1994 }
1995 
1996 
1997 class GeckoProgress : public Gecko::Progress
1998 {
1999  double limit;
2000  mutable StopWatch sw;
2001 public:
2002  GeckoProgress(double limit) : limit(limit) { sw.Start(); }
2003  virtual bool quit() const { return limit > 0 && sw.UserTime() > limit; }
2004 };
2005 
2006 class GeckoVerboseProgress : public GeckoProgress
2007 {
2008  using Float = Gecko::Float;
2009  using Graph = Gecko::Graph;
2010  using uint = Gecko::uint;
2011 public:
2012  GeckoVerboseProgress(double limit) : GeckoProgress(limit) {}
2013 
2014  virtual void beginorder(const Graph* graph, Float cost) const
2015  { mfem::out << "Begin Gecko ordering, cost = " << cost << std::endl; }
2016  virtual void endorder(const Graph* graph, Float cost) const
2017  { mfem::out << "End ordering, cost = " << cost << std::endl; }
2018 
2019  virtual void beginiter(const Graph* graph,
2020  uint iter, uint maxiter, uint window) const
2021  {
2022  mfem::out << "Iteration " << iter << "/" << maxiter << ", window "
2023  << window << std::flush;
2024  }
2025  virtual void enditer(const Graph* graph, Float mincost, Float cost) const
2026  { mfem::out << ", cost = " << cost << endl; }
2027 };
2028 
2029 
2030 double Mesh::GetGeckoElementOrdering(Array<int> &ordering,
2031  int iterations, int window,
2032  int period, int seed, bool verbose,
2033  double time_limit)
2034 {
2035  Gecko::Graph graph;
2036  Gecko::FunctionalGeometric functional; // edge product cost
2037 
2038  GeckoProgress progress(time_limit);
2039  GeckoVerboseProgress vprogress(time_limit);
2040 
2041  // insert elements as nodes in the graph
2042  for (int elemid = 0; elemid < GetNE(); ++elemid)
2043  {
2044  graph.insert_node();
2045  }
2046 
2047  // insert graph edges for element neighbors
2048  // NOTE: indices in Gecko are 1 based hence the +1 on insertion
2049  const Table &my_el_to_el = ElementToElementTable();
2050  for (int elemid = 0; elemid < GetNE(); ++elemid)
2051  {
2052  const int *neighid = my_el_to_el.GetRow(elemid);
2053  for (int i = 0; i < my_el_to_el.RowSize(elemid); ++i)
2054  {
2055  graph.insert_arc(elemid + 1, neighid[i] + 1);
2056  }
2057  }
2058 
2059  // get the ordering from Gecko and copy it into the Array<int>
2060  graph.order(&functional, iterations, window, period, seed,
2061  verbose ? &vprogress : &progress);
2062 
2063  ordering.SetSize(GetNE());
2064  Gecko::Node::Index NE = GetNE();
2065  for (Gecko::Node::Index gnodeid = 1; gnodeid <= NE; ++gnodeid)
2066  {
2067  ordering[gnodeid - 1] = graph.rank(gnodeid);
2068  }
2069 
2070  return graph.cost();
2071 }
2072 
2073 
2074 struct HilbertCmp
2075 {
2076  int coord;
2077  bool dir;
2078  const Array<double> &points;
2079  double mid;
2080 
2081  HilbertCmp(int coord, bool dir, const Array<double> &points, double mid)
2082  : coord(coord), dir(dir), points(points), mid(mid) {}
2083 
2084  bool operator()(int i) const
2085  {
2086  return (points[3*i + coord] < mid) != dir;
2087  }
2088 };
2089 
2090 static void HilbertSort2D(int coord1, // major coordinate to sort points by
2091  bool dir1, // sort coord1 ascending/descending?
2092  bool dir2, // sort coord2 ascending/descending?
2093  const Array<double> &points, int *beg, int *end,
2094  double xmin, double ymin, double xmax, double ymax)
2095 {
2096  if (end - beg <= 1) { return; }
2097 
2098  double xmid = (xmin + xmax)*0.5;
2099  double ymid = (ymin + ymax)*0.5;
2100 
2101  int coord2 = (coord1 + 1) % 2; // the 'other' coordinate
2102 
2103  // sort (partition) points into four quadrants
2104  int *p0 = beg, *p4 = end;
2105  int *p2 = std::partition(p0, p4, HilbertCmp(coord1, dir1, points, xmid));
2106  int *p1 = std::partition(p0, p2, HilbertCmp(coord2, dir2, points, ymid));
2107  int *p3 = std::partition(p2, p4, HilbertCmp(coord2, !dir2, points, ymid));
2108 
2109  if (p1 != p4)
2110  {
2111  HilbertSort2D(coord2, dir2, dir1, points, p0, p1,
2112  ymin, xmin, ymid, xmid);
2113  }
2114  if (p1 != p0 || p2 != p4)
2115  {
2116  HilbertSort2D(coord1, dir1, dir2, points, p1, p2,
2117  xmin, ymid, xmid, ymax);
2118  }
2119  if (p2 != p0 || p3 != p4)
2120  {
2121  HilbertSort2D(coord1, dir1, dir2, points, p2, p3,
2122  xmid, ymid, xmax, ymax);
2123  }
2124  if (p3 != p0)
2125  {
2126  HilbertSort2D(coord2, !dir2, !dir1, points, p3, p4,
2127  ymid, xmax, ymin, xmid);
2128  }
2129 }
2130 
2131 static void HilbertSort3D(int coord1, bool dir1, bool dir2, bool dir3,
2132  const Array<double> &points, int *beg, int *end,
2133  double xmin, double ymin, double zmin,
2134  double xmax, double ymax, double zmax)
2135 {
2136  if (end - beg <= 1) { return; }
2137 
2138  double xmid = (xmin + xmax)*0.5;
2139  double ymid = (ymin + ymax)*0.5;
2140  double zmid = (zmin + zmax)*0.5;
2141 
2142  int coord2 = (coord1 + 1) % 3;
2143  int coord3 = (coord1 + 2) % 3;
2144 
2145  // sort (partition) points into eight octants
2146  int *p0 = beg, *p8 = end;
2147  int *p4 = std::partition(p0, p8, HilbertCmp(coord1, dir1, points, xmid));
2148  int *p2 = std::partition(p0, p4, HilbertCmp(coord2, dir2, points, ymid));
2149  int *p6 = std::partition(p4, p8, HilbertCmp(coord2, !dir2, points, ymid));
2150  int *p1 = std::partition(p0, p2, HilbertCmp(coord3, dir3, points, zmid));
2151  int *p3 = std::partition(p2, p4, HilbertCmp(coord3, !dir3, points, zmid));
2152  int *p5 = std::partition(p4, p6, HilbertCmp(coord3, dir3, points, zmid));
2153  int *p7 = std::partition(p6, p8, HilbertCmp(coord3, !dir3, points, zmid));
2154 
2155  if (p1 != p8)
2156  {
2157  HilbertSort3D(coord3, dir3, dir1, dir2, points, p0, p1,
2158  zmin, xmin, ymin, zmid, xmid, ymid);
2159  }
2160  if (p1 != p0 || p2 != p8)
2161  {
2162  HilbertSort3D(coord2, dir2, dir3, dir1, points, p1, p2,
2163  ymin, zmid, xmin, ymid, zmax, xmid);
2164  }
2165  if (p2 != p0 || p3 != p8)
2166  {
2167  HilbertSort3D(coord2, dir2, dir3, dir1, points, p2, p3,
2168  ymid, zmid, xmin, ymax, zmax, xmid);
2169  }
2170  if (p3 != p0 || p4 != p8)
2171  {
2172  HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p3, p4,
2173  xmin, ymax, zmid, xmid, ymid, zmin);
2174  }
2175  if (p4 != p0 || p5 != p8)
2176  {
2177  HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p4, p5,
2178  xmid, ymax, zmid, xmax, ymid, zmin);
2179  }
2180  if (p5 != p0 || p6 != p8)
2181  {
2182  HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p5, p6,
2183  ymax, zmid, xmax, ymid, zmax, xmid);
2184  }
2185  if (p6 != p0 || p7 != p8)
2186  {
2187  HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p6, p7,
2188  ymid, zmid, xmax, ymin, zmax, xmid);
2189  }
2190  if (p7 != p0)
2191  {
2192  HilbertSort3D(coord3, !dir3, !dir1, dir2, points, p7, p8,
2193  zmid, xmax, ymin, zmin, xmid, ymid);
2194  }
2195 }
2196 
2197 void Mesh::GetHilbertElementOrdering(Array<int> &ordering)
2198 {
2199  MFEM_VERIFY(spaceDim <= 3, "");
2200 
2201  Vector min, max, center;
2202  GetBoundingBox(min, max);
2203 
2204  Array<int> indices(GetNE());
2205  Array<double> points(3*GetNE());
2206 
2207  if (spaceDim < 3) { points = 0.0; }
2208 
2209  // calculate element centers
2210  for (int i = 0; i < GetNE(); i++)
2211  {
2212  GetElementCenter(i, center);
2213  for (int j = 0; j < spaceDim; j++)
2214  {
2215  points[3*i + j] = center(j);
2216  }
2217  indices[i] = i;
2218  }
2219 
2220  if (spaceDim == 1)
2221  {
2222  indices.Sort([&](int a, int b)
2223  { return points[3*a] < points[3*b]; });
2224  }
2225  else if (spaceDim == 2)
2226  {
2227  // recursively partition the points in 2D
2228  HilbertSort2D(0, false, false,
2229  points, indices.begin(), indices.end(),
2230  min(0), min(1), max(0), max(1));
2231  }
2232  else
2233  {
2234  // recursively partition the points in 3D
2235  HilbertSort3D(0, false, false, false,
2236  points, indices.begin(), indices.end(),
2237  min(0), min(1), min(2), max(0), max(1), max(2));
2238  }
2239 
2240  // return ordering in the format required by ReorderElements
2241  ordering.SetSize(GetNE());
2242  for (int i = 0; i < GetNE(); i++)
2243  {
2244  ordering[indices[i]] = i;
2245  }
2246 }
2247 
2248 
2249 void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
2250 {
2251  if (NURBSext)
2252  {
2253  MFEM_WARNING("element reordering of NURBS meshes is not supported.");
2254  return;
2255  }
2256  if (ncmesh)
2257  {
2258  MFEM_WARNING("element reordering of non-conforming meshes is not"
2259  " supported.");
2260  return;
2261  }
2262  MFEM_VERIFY(ordering.Size() == GetNE(), "invalid reordering array.")
2263 
2264  // Data members that need to be updated:
2265 
2266  // - elements - reorder of the pointers and the vertex ids if reordering
2267  // the vertices
2268  // - vertices - if reordering the vertices
2269  // - boundary - update the vertex ids, if reordering the vertices
2270  // - faces - regenerate
2271  // - faces_info - regenerate
2272 
2273  // Deleted by DeleteTables():
2274  // - el_to_edge - rebuild in 2D and 3D only
2275  // - el_to_face - rebuild in 3D only
2276  // - bel_to_edge - rebuild in 3D only
2277  // - el_to_el - no need to rebuild
2278  // - face_edge - no need to rebuild
2279  // - edge_vertex - no need to rebuild
2280  // - geom_factors - no need to rebuild
2281 
2282  // - be_to_edge - 2D only
2283  // - be_to_face - 3D only
2284 
2285  // - Nodes
2286 
2287  // Save the locations of the Nodes so we can rebuild them later
2288  Array<Vector*> old_elem_node_vals;
2289  FiniteElementSpace *nodes_fes = NULL;
2290  if (Nodes)
2291  {
2292  old_elem_node_vals.SetSize(GetNE());
2293  nodes_fes = Nodes->FESpace();
2294  Array<int> old_dofs;
2295  Vector vals;
2296  for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2297  {
2298  nodes_fes->GetElementVDofs(old_elid, old_dofs);
2299  Nodes->GetSubVector(old_dofs, vals);
2300  old_elem_node_vals[old_elid] = new Vector(vals);
2301  }
2302  }
2303 
2304  // Get the newly ordered elements
2305  Array<Element *> new_elements(GetNE());
2306  for (int old_elid = 0; old_elid < ordering.Size(); ++old_elid)
2307  {
2308  int new_elid = ordering[old_elid];
2309  new_elements[new_elid] = elements[old_elid];
2310  }
2311  mfem::Swap(elements, new_elements);
2312  new_elements.DeleteAll();
2313 
2314  if (reorder_vertices)
2315  {
2316  // Get the new vertex ordering permutation vectors and fill the new
2317  // vertices
2318  Array<int> vertex_ordering(GetNV());
2319  vertex_ordering = -1;
2320  Array<Vertex> new_vertices(GetNV());
2321  int new_vertex_ind = 0;
2322  for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2323  {
2324  int *elem_vert = elements[new_elid]->GetVertices();
2325  int nv = elements[new_elid]->GetNVertices();
2326  for (int vi = 0; vi < nv; ++vi)
2327  {
2328  int old_vertex_ind = elem_vert[vi];
2329  if (vertex_ordering[old_vertex_ind] == -1)
2330  {
2331  vertex_ordering[old_vertex_ind] = new_vertex_ind;
2332  new_vertices[new_vertex_ind] = vertices[old_vertex_ind];
2333  new_vertex_ind++;
2334  }
2335  }
2336  }
2337  mfem::Swap(vertices, new_vertices);
2338  new_vertices.DeleteAll();
2339 
2340  // Replace the vertex ids in the elements with the reordered vertex
2341  // numbers
2342  for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
2343  {
2344  int *elem_vert = elements[new_elid]->GetVertices();
2345  int nv = elements[new_elid]->GetNVertices();
2346  for (int vi = 0; vi < nv; ++vi)
2347  {
2348  elem_vert[vi] = vertex_ordering[elem_vert[vi]];
2349  }
2350  }
2351 
2352  // Replace the vertex ids in the boundary with reordered vertex numbers
2353  for (int belid = 0; belid < GetNBE(); ++belid)
2354  {
2355  int *be_vert = boundary[belid]->GetVertices();
2356  int nv = boundary[belid]->GetNVertices();
2357  for (int vi = 0; vi < nv; ++vi)
2358  {
2359  be_vert[vi] = vertex_ordering[be_vert[vi]];
2360  }
2361  }
2362  }
2363 
2364  // Destroy tables that need to be rebuild
2365  DeleteTables();
2366 
2367  if (Dim > 1)
2368  {
2369  // generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
2370  el_to_edge = new Table;
2371  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2372  }
2373  if (Dim > 2)
2374  {
2375  // generate el_to_face, be_to_face
2376  GetElementToFaceTable();
2377  }
2378  // Update faces and faces_info
2379  GenerateFaces();
2380 
2381  // Build the nodes from the saved locations if they were around before
2382  if (Nodes)
2383  {
2384  // To force FE space update, we need to increase 'sequence':
2385  sequence++;
2386  last_operation = Mesh::NONE;
2387  nodes_fes->Update(false); // want_transform = false
2388  Nodes->Update(); // just needed to update Nodes->sequence
2389  Array<int> new_dofs;
2390  for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2391  {
2392  int new_elid = ordering[old_elid];
2393  nodes_fes->GetElementVDofs(new_elid, new_dofs);
2394  Nodes->SetSubVector(new_dofs, *(old_elem_node_vals[old_elid]));
2395  delete old_elem_node_vals[old_elid];
2396  }
2397  }
2398 }
2399 
2400 
2401 void Mesh::MarkForRefinement()
2402 {
2403  if (meshgen & 1)
2404  {
2405  if (Dim == 2)
2406  {
2407  MarkTriMeshForRefinement();
2408  }
2409  else if (Dim == 3)
2410  {
2411  DSTable v_to_v(NumOfVertices);
2412  GetVertexToVertexTable(v_to_v);
2413  MarkTetMeshForRefinement(v_to_v);
2414  }
2415  }
2416 }
2417 
2418 void Mesh::MarkTriMeshForRefinement()
2419 {
2420  // Mark the longest triangle edge by rotating the indices so that
2421  // vertex 0 - vertex 1 is the longest edge in the triangle.
2422  DenseMatrix pmat;
2423  for (int i = 0; i < NumOfElements; i++)
2424  {
2425  if (elements[i]->GetType() == Element::TRIANGLE)
2426  {
2427  GetPointMatrix(i, pmat);
2428  static_cast<Triangle*>(elements[i])->MarkEdge(pmat);
2429  }
2430  }
2431 }
2432 
2433 void Mesh::GetEdgeOrdering(DSTable &v_to_v, Array<int> &order)
2434 {
2435  NumOfEdges = v_to_v.NumberOfEntries();
2436  order.SetSize(NumOfEdges);
2437  Array<Pair<double, int> > length_idx(NumOfEdges);
2438 
2439  for (int i = 0; i < NumOfVertices; i++)
2440  {
2441  for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
2442  {
2443  int j = it.Index();
2444  length_idx[j].one = GetLength(i, it.Column());
2445  length_idx[j].two = j;
2446  }
2447  }
2448 
2449  // Sort by increasing edge-length.
2450  length_idx.Sort();
2451 
2452  for (int i = 0; i < NumOfEdges; i++)
2453  {
2454  order[length_idx[i].two] = i;
2455  }
2456 }
2457 
2458 void Mesh::MarkTetMeshForRefinement(DSTable &v_to_v)
2459 {
2460  // Mark the longest tetrahedral edge by rotating the indices so that
2461  // vertex 0 - vertex 1 is the longest edge in the element.
2462  Array<int> order;
2463  GetEdgeOrdering(v_to_v, order);
2464 
2465  for (int i = 0; i < NumOfElements; i++)
2466  {
2467  if (elements[i]->GetType() == Element::TETRAHEDRON)
2468  {
2469  elements[i]->MarkEdge(v_to_v, order);
2470  }
2471  }
2472  for (int i = 0; i < NumOfBdrElements; i++)
2473  {
2474  if (boundary[i]->GetType() == Element::TRIANGLE)
2475  {
2476  boundary[i]->MarkEdge(v_to_v, order);
2477  }
2478  }
2479 }
2480 
2481 void Mesh::PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
2482 {
2483  if (*old_v_to_v && *old_elem_vert)
2484  {
2485  return;
2486  }
2487 
2488  FiniteElementSpace *fes = Nodes->FESpace();
2489 
2490  if (*old_v_to_v == NULL)
2491  {
2492  bool need_v_to_v = false;
2493  Array<int> dofs;
2494  for (int i = 0; i < GetNEdges(); i++)
2495  {
2496  // Since edge indices may change, we need to permute edge interior dofs
2497  // any time an edge index changes and there is at least one dof on that
2498  // edge.
2499  fes->GetEdgeInteriorDofs(i, dofs);
2500  if (dofs.Size() > 0)
2501  {
2502  need_v_to_v = true;
2503  break;
2504  }
2505  }
2506  if (need_v_to_v)
2507  {
2508  *old_v_to_v = new DSTable(NumOfVertices);
2509  GetVertexToVertexTable(*(*old_v_to_v));
2510  }
2511  }
2512  if (*old_elem_vert == NULL)
2513  {
2514  bool need_elem_vert = false;
2515  Array<int> dofs;
2516  for (int i = 0; i < GetNE(); i++)
2517  {
2518  // Since element indices do not change, we need to permute element
2519  // interior dofs only when there are at least 2 interior dofs in an
2520  // element (assuming the nodal dofs are non-directional).
2521  fes->GetElementInteriorDofs(i, dofs);
2522  if (dofs.Size() > 1)
2523  {
2524  need_elem_vert = true;
2525  break;
2526  }
2527  }
2528  if (need_elem_vert)
2529  {
2530  *old_elem_vert = new Table;
2531  (*old_elem_vert)->MakeI(GetNE());
2532  for (int i = 0; i < GetNE(); i++)
2533  {
2534  (*old_elem_vert)->AddColumnsInRow(i, elements[i]->GetNVertices());
2535  }
2536  (*old_elem_vert)->MakeJ();
2537  for (int i = 0; i < GetNE(); i++)
2538  {
2539  (*old_elem_vert)->AddConnections(i, elements[i]->GetVertices(),
2540  elements[i]->GetNVertices());
2541  }
2542  (*old_elem_vert)->ShiftUpI();
2543  }
2544  }
2545 }
2546 
2547 void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
2548 {
2549  FiniteElementSpace *fes = Nodes->FESpace();
2550  const FiniteElementCollection *fec = fes->FEColl();
2551  Array<int> old_dofs, new_dofs;
2552 
2553  // assuming that all edges have the same number of dofs
2554  if (NumOfEdges) { fes->GetEdgeInteriorDofs(0, old_dofs); }
2555  const int num_edge_dofs = old_dofs.Size();
2556 
2557  // Save the original nodes
2558  const Vector onodes = *Nodes;
2559 
2560  // vertex dofs do not need to be moved
2561  fes->GetVertexDofs(0, old_dofs);
2562  int offset = NumOfVertices * old_dofs.Size();
2563 
2564  // edge dofs:
2565  // edge enumeration may be different but edge orientation is the same
2566  if (num_edge_dofs > 0)
2567  {
2568  DSTable new_v_to_v(NumOfVertices);
2569  GetVertexToVertexTable(new_v_to_v);
2570 
2571  for (int i = 0; i < NumOfVertices; i++)
2572  {
2573  for (DSTable::RowIterator it(new_v_to_v, i); !it; ++it)
2574  {
2575  const int old_i = (*old_v_to_v)(i, it.Column());
2576  const int new_i = it.Index();
2577  if (new_i == old_i) { continue; }
2578 
2579  old_dofs.SetSize(num_edge_dofs);
2580  new_dofs.SetSize(num_edge_dofs);
2581  for (int j = 0; j < num_edge_dofs; j++)
2582  {
2583  old_dofs[j] = offset + old_i * num_edge_dofs + j;
2584  new_dofs[j] = offset + new_i * num_edge_dofs + j;
2585  }
2586  fes->DofsToVDofs(old_dofs);
2587  fes->DofsToVDofs(new_dofs);
2588  for (int j = 0; j < old_dofs.Size(); j++)
2589  {
2590  (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2591  }
2592  }
2593  }
2594  offset += NumOfEdges * num_edge_dofs;
2595  }
2596 
2597  // face dofs:
2598  // both enumeration and orientation of the faces may be different
2599  if (fes->GetNFDofs() > 0)
2600  {
2601  // generate the old face-vertex table using the unmodified 'faces'
2602  Table old_face_vertex;
2603  old_face_vertex.MakeI(NumOfFaces);
2604  for (int i = 0; i < NumOfFaces; i++)
2605  {
2606  old_face_vertex.AddColumnsInRow(i, faces[i]->GetNVertices());
2607  }
2608  old_face_vertex.MakeJ();
2609  for (int i = 0; i < NumOfFaces; i++)
2610  old_face_vertex.AddConnections(i, faces[i]->GetVertices(),
2611  faces[i]->GetNVertices());
2612  old_face_vertex.ShiftUpI();
2613 
2614  // update 'el_to_face', 'be_to_face', 'faces', and 'faces_info'
2615  STable3D *faces_tbl = GetElementToFaceTable(1);
2616  GenerateFaces();
2617 
2618  // compute the new face dof offsets
2619  Array<int> new_fdofs(NumOfFaces+1);
2620  new_fdofs[0] = 0;
2621  for (int i = 0; i < NumOfFaces; i++) // i = old face index
2622  {
2623  const int *old_v = old_face_vertex.GetRow(i);
2624  int new_i; // new face index
2625  switch (old_face_vertex.RowSize(i))
2626  {
2627  case 3:
2628  new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
2629  break;
2630  case 4:
2631  default:
2632  new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
2633  break;
2634  }
2635  fes->GetFaceInteriorDofs(i, old_dofs);
2636  new_fdofs[new_i+1] = old_dofs.Size();
2637  }
2638  new_fdofs.PartialSum();
2639 
2640  // loop over the old face numbers
2641  for (int i = 0; i < NumOfFaces; i++)
2642  {
2643  const int *old_v = old_face_vertex.GetRow(i), *new_v;
2644  const int *dof_ord;
2645  int new_i, new_or;
2646  switch (old_face_vertex.RowSize(i))
2647  {
2648  case 3:
2649  new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
2650  new_v = faces[new_i]->GetVertices();
2651  new_or = GetTriOrientation(old_v, new_v);
2652  dof_ord = fec->DofOrderForOrientation(Geometry::TRIANGLE, new_or);
2653  break;
2654  case 4:
2655  default:
2656  new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
2657  new_v = faces[new_i]->GetVertices();
2658  new_or = GetQuadOrientation(old_v, new_v);
2659  dof_ord = fec->DofOrderForOrientation(Geometry::SQUARE, new_or);
2660  break;
2661  }
2662 
2663  fes->GetFaceInteriorDofs(i, old_dofs);
2664  new_dofs.SetSize(old_dofs.Size());
2665  for (int j = 0; j < old_dofs.Size(); j++)
2666  {
2667  // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
2668  const int old_j = dof_ord[j];
2669  new_dofs[old_j] = offset + new_fdofs[new_i] + j;
2670  }
2671  fes->DofsToVDofs(old_dofs);
2672  fes->DofsToVDofs(new_dofs);
2673  for (int j = 0; j < old_dofs.Size(); j++)
2674  {
2675  (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2676  }
2677  }
2678 
2679  offset += fes->GetNFDofs();
2680  delete faces_tbl;
2681  }
2682 
2683  // element dofs:
2684  // element orientation may be different
2685  if (old_elem_vert) // have elements with 2 or more dofs
2686  {
2687  // matters when the 'fec' is
2688  // (this code is executed only for triangles/tets)
2689  // - Pk on triangles, k >= 4
2690  // - Qk on quads, k >= 3
2691  // - Pk on tets, k >= 5
2692  // - Qk on hexes, k >= 3
2693  // - DG spaces
2694  // - ...
2695 
2696  // loop over all elements
2697  for (int i = 0; i < GetNE(); i++)
2698  {
2699  const int *old_v = old_elem_vert->GetRow(i);
2700  const int *new_v = elements[i]->GetVertices();
2701  const int *dof_ord;
2702  int new_or;
2703  const Geometry::Type geom = elements[i]->GetGeometryType();
2704  switch (geom)
2705  {
2706  case Geometry::SEGMENT:
2707  new_or = (old_v[0] == new_v[0]) ? +1 : -1;
2708  break;
2709  case Geometry::TRIANGLE:
2710  new_or = GetTriOrientation(old_v, new_v);
2711  break;
2712  case Geometry::SQUARE:
2713  new_or = GetQuadOrientation(old_v, new_v);
2714  break;
2715  case Geometry::TETRAHEDRON:
2716  new_or = GetTetOrientation(old_v, new_v);
2717  break;
2718  default:
2719  new_or = 0;
2720  MFEM_ABORT(Geometry::Name[geom] << " elements (" << fec->Name()
2721  << " FE collection) are not supported yet!");
2722  break;
2723  }
2724  dof_ord = fec->DofOrderForOrientation(geom, new_or);
2725  MFEM_VERIFY(dof_ord != NULL,
2726  "FE collection '" << fec->Name()
2727  << "' does not define reordering for "
2728  << Geometry::Name[geom] << " elements!");
2729  fes->GetElementInteriorDofs(i, old_dofs);
2730  new_dofs.SetSize(old_dofs.Size());
2731  for (int j = 0; j < new_dofs.Size(); j++)
2732  {
2733  // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
2734  const int old_j = dof_ord[j];
2735  new_dofs[old_j] = offset + j;
2736  }
2737  offset += new_dofs.Size();
2738  fes->DofsToVDofs(old_dofs);
2739  fes->DofsToVDofs(new_dofs);
2740  for (int j = 0; j < old_dofs.Size(); j++)
2741  {
2742  (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2743  }
2744  }
2745  }
2746 
2747  // Update Tables, faces, etc
2748  if (Dim > 2)
2749  {
2750  if (fes->GetNFDofs() == 0)
2751  {
2752  // needed for FE spaces that have face dofs, even if
2753  // the 'Nodes' do not have face dofs.
2754  GetElementToFaceTable();
2755  GenerateFaces();
2756  }
2757  CheckBdrElementOrientation();
2758  }
2759  if (el_to_edge)
2760  {
2761  // update 'el_to_edge', 'be_to_edge' (2D), 'bel_to_edge' (3D)
2762  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2763  if (Dim == 2)
2764  {
2765  // update 'faces' and 'faces_info'
2766  GenerateFaces();
2767  CheckBdrElementOrientation();
2768  }
2769  }
2770  // To force FE space update, we need to increase 'sequence':
2771  sequence++;
2772  last_operation = Mesh::NONE;
2773  fes->Update(false); // want_transform = false
2774  Nodes->Update(); // just needed to update Nodes->sequence
2775 }
2776 
2777 void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
2778 {
2779  FinalizeCheck();
2780  CheckElementOrientation(fix_orientation);
2781 
2782  if (NumOfBdrElements == 0)
2783  {
2784  GetElementToFaceTable();
2785  GenerateFaces();
2786  GenerateBoundaryElements();
2787  }
2788 
2789  if (refine)
2790  {
2791  DSTable v_to_v(NumOfVertices);
2792  GetVertexToVertexTable(v_to_v);
2793  MarkTetMeshForRefinement(v_to_v);
2794  }
2795 
2796  GetElementToFaceTable();
2797  GenerateFaces();
2798 
2799  CheckBdrElementOrientation();
2800 
2801  if (generate_edges == 1)
2802  {
2803  el_to_edge = new Table;
2804  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2805  }
2806  else
2807  {
2808  el_to_edge = NULL; // Not really necessary -- InitTables was called
2809  bel_to_edge = NULL;
2810  NumOfEdges = 0;
2811  }
2812 
2813  SetAttributes();
2814 
2815  SetMeshGen();
2816 }
2817 
2818 void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
2819  bool fix_orientation)
2820 {
2821  FinalizeCheck();
2822  CheckElementOrientation(fix_orientation);
2823 
2824  if (NumOfBdrElements == 0)
2825  {
2826  GetElementToFaceTable();
2827  GenerateFaces();
2828  GenerateBoundaryElements();
2829  }
2830 
2831  GetElementToFaceTable();
2832  GenerateFaces();
2833 
2834  CheckBdrElementOrientation();
2835 
2836  if (generate_edges == 1)
2837  {
2838  el_to_edge = new Table;
2839  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2840  }
2841  else
2842  {
2843  el_to_edge = NULL; // Not really necessary -- InitTables was called
2844  bel_to_edge = NULL;
2845  NumOfEdges = 0;
2846  }
2847 
2848  SetAttributes();
2849 
2850  SetMeshGen();
2851 }
2852 
2853 void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
2854 {
2855  FinalizeCheck();
2856  CheckElementOrientation(fix_orientation);
2857 
2858  GetElementToFaceTable();
2859  GenerateFaces();
2860 
2861  if (NumOfBdrElements == 0)
2862  {
2863  GenerateBoundaryElements();
2864  }
2865 
2866  CheckBdrElementOrientation();
2867 
2868  if (generate_edges)
2869  {
2870  el_to_edge = new Table;
2871  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2872  }
2873  else
2874  {
2875  NumOfEdges = 0;
2876  }
2877 
2878  SetAttributes();
2879 
2880  SetMeshGen();
2881 }
2882 
2883 void Mesh::FinalizeMesh(int refine, bool fix_orientation)
2884 {
2885  FinalizeTopology();
2886 
2887  Finalize(refine, fix_orientation);
2888 }
2889 
2890 void Mesh::FinalizeTopology(bool generate_bdr)
2891 {
2892  // Requirements: the following should be defined:
2893  // 1) Dim
2894  // 2) NumOfElements, elements
2895  // 3) NumOfBdrElements, boundary
2896  // 4) NumOfVertices
2897  // Optional:
2898  // 2) ncmesh may be defined
2899  // 3) el_to_edge may be allocated (it will be re-computed)
2900 
2901  FinalizeCheck();
2902  bool generate_edges = true;
2903 
2904  if (spaceDim == 0) { spaceDim = Dim; }
2905  if (ncmesh) { ncmesh->spaceDim = spaceDim; }
2906 
2907  // if the user defined any hanging nodes (see AddVertexParent),
2908  // we're initializing a non-conforming mesh
2909  if (tmp_vertex_parents.Size())
2910  {
2911  MFEM_VERIFY(ncmesh == NULL, "");
2912  ncmesh = new NCMesh(this);
2913 
2914  // we need to recreate the Mesh because NCMesh reorders the vertices
2915  // (see NCMesh::UpdateVertices())
2916  InitFromNCMesh(*ncmesh);
2917  ncmesh->OnMeshUpdated(this);
2918  GenerateNCFaceInfo();
2919 
2920  SetAttributes();
2921 
2922  tmp_vertex_parents.DeleteAll();
2923  return;
2924  }
2925 
2926  // set the mesh type: 'meshgen', ...
2927  SetMeshGen();
2928 
2929  // generate the faces
2930  if (Dim > 2)
2931  {
2932  GetElementToFaceTable();
2933  GenerateFaces();
2934  if (NumOfBdrElements == 0 && generate_bdr)
2935  {
2936  GenerateBoundaryElements();
2937  GetElementToFaceTable(); // update be_to_face
2938  }
2939  }
2940  else
2941  {
2942  NumOfFaces = 0;
2943  }
2944 
2945  // generate edges if requested
2946  if (Dim > 1 && generate_edges)
2947  {
2948  // el_to_edge may already be allocated (P2 VTK meshes)
2949  if (!el_to_edge) { el_to_edge = new Table; }
2950  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2951  if (Dim == 2)
2952  {
2953  GenerateFaces(); // 'Faces' in 2D refers to the edges
2954  if (NumOfBdrElements == 0 && generate_bdr)
2955  {
2956  GenerateBoundaryElements();
2957  }
2958  }
2959  }
2960  else
2961  {
2962  NumOfEdges = 0;
2963  }
2964 
2965  if (Dim == 1)
2966  {
2967  GenerateFaces();
2968  }
2969 
2970  if (ncmesh)
2971  {
2972  // tell NCMesh the numbering of edges/faces
2973  ncmesh->OnMeshUpdated(this);
2974 
2975  // update faces_info with NC relations
2976  GenerateNCFaceInfo();
2977  }
2978 
2979  // generate the arrays 'attributes' and 'bdr_attributes'
2980  SetAttributes();
2981 }
2982 
2983 void Mesh::Finalize(bool refine, bool fix_orientation)
2984 {
2985  if (NURBSext || ncmesh)
2986  {
2987  MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
2988  MFEM_ASSERT(CheckBdrElementOrientation() == 0, "");
2989  return;
2990  }
2991 
2992  // Requirements:
2993  // 1) FinalizeTopology() or equivalent was called
2994  // 2) if (Nodes == NULL), vertices must be defined
2995  // 3) if (Nodes != NULL), Nodes must be defined
2996 
2997  const bool check_orientation = true; // for regular elements, not boundary
2998  const bool curved = (Nodes != NULL);
2999  const bool may_change_topology =
3000  ( refine && (Dim > 1 && (meshgen & 1)) ) ||
3001  ( check_orientation && fix_orientation &&
3002  (Dim == 2 || (Dim == 3 && (meshgen & 1))) );
3003 
3004  DSTable *old_v_to_v = NULL;
3005  Table *old_elem_vert = NULL;
3006 
3007  if (curved && may_change_topology)
3008  {
3009  PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
3010  }
3011 
3012  if (check_orientation)
3013  {
3014  // check and optionally fix element orientation
3015  CheckElementOrientation(fix_orientation);
3016  }
3017  if (refine)
3018  {
3019  MarkForRefinement(); // may change topology!
3020  }
3021 
3022  if (may_change_topology)
3023  {
3024  if (curved)
3025  {
3026  DoNodeReorder(old_v_to_v, old_elem_vert); // updates the mesh topology
3027  delete old_elem_vert;
3028  delete old_v_to_v;
3029  }
3030  else
3031  {
3032  FinalizeTopology(); // Re-computes some data unnecessarily.
3033  }
3034 
3035  // TODO: maybe introduce Mesh::NODE_REORDER operation and FESpace::
3036  // NodeReorderMatrix and do Nodes->Update() instead of DoNodeReorder?
3037  }
3038 
3039  // check and fix boundary element orientation
3040  CheckBdrElementOrientation();
3041 
3042 #ifdef MFEM_DEBUG
3043  // For non-orientable surfaces/manifolds, the check below will fail, so we
3044  // only perform it when Dim == spaceDim.
3045  if (Dim >= 2 && Dim == spaceDim)
3046  {
3047  const int num_faces = GetNumFaces();
3048  for (int i = 0; i < num_faces; i++)
3049  {
3050  MFEM_VERIFY(faces_info[i].Elem2No < 0 ||
3051  faces_info[i].Elem2Inf%2 != 0, "Invalid mesh topology."
3052  " Interior face with incompatible orientations.");
3053  }
3054  }
3055 #endif
3056 }
3057 
3058 void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
3059  double sx, double sy, double sz, bool sfc_ordering)
3060 {
3061  int x, y, z;
3062 
3063  int NVert, NElem, NBdrElem;
3064 
3065  NVert = (nx+1) * (ny+1) * (nz+1);
3066  NElem = nx * ny * nz;
3067  NBdrElem = 2*(nx*ny+nx*nz+ny*nz);
3068  if (type == Element::TETRAHEDRON)
3069  {
3070  NElem *= 6;
3071  NBdrElem *= 2;
3072  }
3073  else if (type == Element::WEDGE)
3074  {
3075  NElem *= 2;
3076  NBdrElem += 2*nx*ny;
3077  }
3078  else if (type == Element::PYRAMID)
3079  {
3080  NElem *= 6;
3081  NVert += nx * ny * nz;
3082  }
3083 
3084  InitMesh(3, 3, NVert, NElem, NBdrElem);
3085 
3086  double coord[3];
3087  int ind[9];
3088 
3089  // Sets vertices and the corresponding coordinates
3090  for (z = 0; z <= nz; z++)
3091  {
3092  coord[2] = ((double) z / nz) * sz;
3093  for (y = 0; y <= ny; y++)
3094  {
3095  coord[1] = ((double) y / ny) * sy;
3096  for (x = 0; x <= nx; x++)
3097  {
3098  coord[0] = ((double) x / nx) * sx;
3099  AddVertex(coord);
3100  }
3101  }
3102  }
3103  if (type == Element::PYRAMID)
3104  {
3105  for (z = 0; z < nz; z++)
3106  {
3107  coord[2] = (((double) z + 0.5) / nz) * sz;
3108  for (y = 0; y < ny; y++)
3109  {
3110  coord[1] = (((double) y + 0.5 ) / ny) * sy;
3111  for (x = 0; x < nx; x++)
3112  {
3113  coord[0] = (((double) x + 0.5 ) / nx) * sx;
3114  AddVertex(coord);
3115  }
3116  }
3117  }
3118  }
3119 
3120 #define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
3121 #define VTXP(XC, YC, ZC) ((nx+1)*(ny+1)*(nz+1)+(XC)+((YC)+(ZC)*ny)*nx)
3122 
3123  // Sets elements and the corresponding indices of vertices
3124  if (sfc_ordering && type == Element::HEXAHEDRON)
3125  {
3126  Array<int> sfc;
3127  NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
3128  MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
3129 
3130  for (int k = 0; k < nx*ny*nz; k++)
3131  {
3132  x = sfc[3*k + 0];
3133  y = sfc[3*k + 1];
3134  z = sfc[3*k + 2];
3135 
3136  // *INDENT-OFF*
3137  ind[0] = VTX(x , y , z );
3138  ind[1] = VTX(x+1, y , z );
3139  ind[2] = VTX(x+1, y+1, z );
3140  ind[3] = VTX(x , y+1, z );
3141  ind[4] = VTX(x , y , z+1);
3142  ind[5] = VTX(x+1, y , z+1);
3143  ind[6] = VTX(x+1, y+1, z+1);
3144  ind[7] = VTX(x , y+1, z+1);
3145  // *INDENT-ON*
3146 
3147  AddHex(ind, 1);
3148  }
3149  }
3150  else
3151  {
3152  for (z = 0; z < nz; z++)
3153  {
3154  for (y = 0; y < ny; y++)
3155  {
3156  for (x = 0; x < nx; x++)
3157  {
3158  // *INDENT-OFF*
3159  ind[0] = VTX(x , y , z );
3160  ind[1] = VTX(x+1, y , z );
3161  ind[2] = VTX(x+1, y+1, z );
3162  ind[3] = VTX(x , y+1, z );
3163  ind[4] = VTX(x , y , z+1);
3164  ind[5] = VTX(x+1, y , z+1);
3165  ind[6] = VTX(x+1, y+1, z+1);
3166  ind[7] = VTX( x, y+1, z+1);
3167  // *INDENT-ON*
3168  if (type == Element::TETRAHEDRON)
3169  {
3170  AddHexAsTets(ind, 1);
3171  }
3172  else if (type == Element::WEDGE)
3173  {
3174  AddHexAsWedges(ind, 1);
3175  }
3176  else if (type == Element::PYRAMID)
3177  {
3178  ind[8] = VTXP(x, y, z);
3179  AddHexAsPyramids(ind, 1);
3180  }
3181  else
3182  {
3183  AddHex(ind, 1);
3184  }
3185  }
3186  }
3187  }
3188  }
3189 
3190  // Sets boundary elements and the corresponding indices of vertices
3191  // bottom, bdr. attribute 1
3192  for (y = 0; y < ny; y++)
3193  {
3194  for (x = 0; x < nx; x++)
3195  {
3196  // *INDENT-OFF*
3197  ind[0] = VTX(x , y , 0);
3198  ind[1] = VTX(x , y+1, 0);
3199  ind[2] = VTX(x+1, y+1, 0);
3200  ind[3] = VTX(x+1, y , 0);
3201  // *INDENT-ON*
3202  if (type == Element::TETRAHEDRON)
3203  {
3204  AddBdrQuadAsTriangles(ind, 1);
3205  }
3206  else if (type == Element::WEDGE)
3207  {
3208  AddBdrQuadAsTriangles(ind, 1);
3209  }
3210  else
3211  {
3212  AddBdrQuad(ind, 1);
3213  }
3214  }
3215  }
3216  // top, bdr. attribute 6
3217  for (y = 0; y < ny; y++)
3218  {
3219  for (x = 0; x < nx; x++)
3220  {
3221  // *INDENT-OFF*
3222  ind[0] = VTX(x , y , nz);
3223  ind[1] = VTX(x+1, y , nz);
3224  ind[2] = VTX(x+1, y+1, nz);
3225  ind[3] = VTX(x , y+1, nz);
3226  // *INDENT-ON*
3227  if (type == Element::TETRAHEDRON)
3228  {
3229  AddBdrQuadAsTriangles(ind, 6);
3230  }
3231  else if (type == Element::WEDGE)
3232  {
3233  AddBdrQuadAsTriangles(ind, 6);
3234  }
3235  else
3236  {
3237  AddBdrQuad(ind, 6);
3238  }
3239  }
3240  }
3241  // left, bdr. attribute 5
3242  for (z = 0; z < nz; z++)
3243  {
3244  for (y = 0; y < ny; y++)
3245  {
3246  // *INDENT-OFF*
3247  ind[0] = VTX(0 , y , z );
3248  ind[1] = VTX(0 , y , z+1);
3249  ind[2] = VTX(0 , y+1, z+1);
3250  ind[3] = VTX(0 , y+1, z );
3251  // *INDENT-ON*
3252  if (type == Element::TETRAHEDRON)
3253  {
3254  AddBdrQuadAsTriangles(ind, 5);
3255  }
3256  else
3257  {
3258  AddBdrQuad(ind, 5);
3259  }
3260  }
3261  }
3262  // right, bdr. attribute 3
3263  for (z = 0; z < nz; z++)
3264  {
3265  for (y = 0; y < ny; y++)
3266  {
3267  // *INDENT-OFF*
3268  ind[0] = VTX(nx, y , z );
3269  ind[1] = VTX(nx, y+1, z );
3270  ind[2] = VTX(nx, y+1, z+1);
3271  ind[3] = VTX(nx, y , z+1);
3272  // *INDENT-ON*
3273  if (type == Element::TETRAHEDRON)
3274  {
3275  AddBdrQuadAsTriangles(ind, 3);
3276  }
3277  else
3278  {
3279  AddBdrQuad(ind, 3);
3280  }
3281  }
3282  }
3283  // front, bdr. attribute 2
3284  for (x = 0; x < nx; x++)
3285  {
3286  for (z = 0; z < nz; z++)
3287  {
3288  // *INDENT-OFF*
3289  ind[0] = VTX(x , 0, z );
3290  ind[1] = VTX(x+1, 0, z );
3291  ind[2] = VTX(x+1, 0, z+1);
3292  ind[3] = VTX(x , 0, z+1);
3293  // *INDENT-ON*
3294  if (type == Element::TETRAHEDRON)
3295  {
3296  AddBdrQuadAsTriangles(ind, 2);
3297  }
3298  else
3299  {
3300  AddBdrQuad(ind, 2);
3301  }
3302  }
3303  }
3304  // back, bdr. attribute 4
3305  for (x = 0; x < nx; x++)
3306  {
3307  for (z = 0; z < nz; z++)
3308  {
3309  // *INDENT-OFF*
3310  ind[0] = VTX(x , ny, z );
3311  ind[1] = VTX(x , ny, z+1);
3312  ind[2] = VTX(x+1, ny, z+1);
3313  ind[3] = VTX(x+1, ny, z );
3314  // *INDENT-ON*
3315  if (type == Element::TETRAHEDRON)
3316  {
3317  AddBdrQuadAsTriangles(ind, 4);
3318  }
3319  else
3320  {
3321  AddBdrQuad(ind, 4);
3322  }
3323  }
3324  }
3325 
3326 #undef VTX
3327 
3328 #if 0
3329  ofstream test_stream("debug.mesh");
3330  Print(test_stream);
3331  test_stream.close();
3332 #endif
3333 
3334  FinalizeTopology();
3335 
3336  // Finalize(...) can be called after this method, if needed
3337 }
3338 
3339 void Mesh::Make2D(int nx, int ny, Element::Type type,
3340  double sx, double sy,
3341  bool generate_edges, bool sfc_ordering)
3342 {
3343  int i, j, k;
3344 
3345  SetEmpty();
3346 
3347  Dim = spaceDim = 2;
3348 
3349  // Creates quadrilateral mesh
3350  if (type == Element::QUADRILATERAL)
3351  {
3352  NumOfVertices = (nx+1) * (ny+1);
3353  NumOfElements = nx * ny;
3354  NumOfBdrElements = 2 * nx + 2 * ny;
3355 
3356  vertices.SetSize(NumOfVertices);
3357  elements.SetSize(NumOfElements);
3358  boundary.SetSize(NumOfBdrElements);
3359 
3360  double cx, cy;
3361  int ind[4];
3362 
3363  // Sets vertices and the corresponding coordinates
3364  k = 0;
3365  for (j = 0; j < ny+1; j++)
3366  {
3367  cy = ((double) j / ny) * sy;
3368  for (i = 0; i < nx+1; i++)
3369  {
3370  cx = ((double) i / nx) * sx;
3371  vertices[k](0) = cx;
3372  vertices[k](1) = cy;
3373  k++;
3374  }
3375  }
3376 
3377  // Sets elements and the corresponding indices of vertices
3378  if (sfc_ordering)
3379  {
3380  Array<int> sfc;
3381  NCMesh::GridSfcOrdering2D(nx, ny, sfc);
3382  MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
3383 
3384  for (k = 0; k < nx*ny; k++)
3385  {
3386  i = sfc[2*k + 0];
3387  j = sfc[2*k + 1];
3388  ind[0] = i + j*(nx+1);
3389  ind[1] = i + 1 +j*(nx+1);
3390  ind[2] = i + 1 + (j+1)*(nx+1);
3391  ind[3] = i + (j+1)*(nx+1);
3392  elements[k] = new Quadrilateral(ind);
3393  }
3394  }
3395  else
3396  {
3397  k = 0;
3398  for (j = 0; j < ny; j++)
3399  {
3400  for (i = 0; i < nx; i++)
3401  {
3402  ind[0] = i + j*(nx+1);
3403  ind[1] = i + 1 +j*(nx+1);
3404  ind[2] = i + 1 + (j+1)*(nx+1);
3405  ind[3] = i + (j+1)*(nx+1);
3406  elements[k] = new Quadrilateral(ind);
3407  k++;
3408  }
3409  }
3410  }
3411 
3412  // Sets boundary elements and the corresponding indices of vertices
3413  int m = (nx+1)*ny;
3414  for (i = 0; i < nx; i++)
3415  {
3416  boundary[i] = new Segment(i, i+1, 1);
3417  boundary[nx+i] = new Segment(m+i+1, m+i, 3);
3418  }
3419  m = nx+1;
3420  for (j = 0; j < ny; j++)
3421  {
3422  boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
3423  boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
3424  }
3425  }
3426  // Creates triangular mesh
3427  else if (type == Element::TRIANGLE)
3428  {
3429  NumOfVertices = (nx+1) * (ny+1);
3430  NumOfElements = 2 * nx * ny;
3431  NumOfBdrElements = 2 * nx + 2 * ny;
3432 
3433  vertices.SetSize(NumOfVertices);
3434  elements.SetSize(NumOfElements);
3435  boundary.SetSize(NumOfBdrElements);
3436 
3437  double cx, cy;
3438  int ind[3];
3439 
3440  // Sets vertices and the corresponding coordinates
3441  k = 0;
3442  for (j = 0; j < ny+1; j++)
3443  {
3444  cy = ((double) j / ny) * sy;
3445  for (i = 0; i < nx+1; i++)
3446  {
3447  cx = ((double) i / nx) * sx;
3448  vertices[k](0) = cx;
3449  vertices[k](1) = cy;
3450  k++;
3451  }
3452  }
3453 
3454  // Sets the elements and the corresponding indices of vertices
3455  k = 0;
3456  for (j = 0; j < ny; j++)
3457  {
3458  for (i = 0; i < nx; i++)
3459  {
3460  ind[0] = i + j*(nx+1);
3461  ind[1] = i + 1 + (j+1)*(nx+1);
3462  ind[2] = i + (j+1)*(nx+1);
3463  elements[k] = new Triangle(ind);
3464  k++;
3465  ind[1] = i + 1 + j*(nx+1);
3466  ind[2] = i + 1 + (j+1)*(nx+1);
3467  elements[k] = new Triangle(ind);
3468  k++;
3469  }
3470  }
3471 
3472  // Sets boundary elements and the corresponding indices of vertices
3473  int m = (nx+1)*ny;
3474  for (i = 0; i < nx; i++)
3475  {
3476  boundary[i] = new Segment(i, i+1, 1);
3477  boundary[nx+i] = new Segment(m+i+1, m+i, 3);
3478  }
3479  m = nx+1;
3480  for (j = 0; j < ny; j++)
3481  {
3482  boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
3483  boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
3484  }
3485 
3486  // MarkTriMeshForRefinement(); // done in Finalize(...)
3487  }
3488  else
3489  {
3490  MFEM_ABORT("Unsupported element type.");
3491  }
3492 
3493  SetMeshGen();
3494  CheckElementOrientation();
3495 
3496  if (generate_edges == 1)
3497  {
3498  el_to_edge = new Table;
3499  NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
3500  GenerateFaces();
3501  CheckBdrElementOrientation();
3502  }
3503  else
3504  {
3505  NumOfEdges = 0;
3506  }
3507 
3508  NumOfFaces = 0;
3509 
3510  attributes.Append(1);
3511  bdr_attributes.Append(1); bdr_attributes.Append(2);
3512  bdr_attributes.Append(3); bdr_attributes.Append(4);
3513 
3514  // Finalize(...) can be called after this method, if needed
3515 }
3516 
3517 void Mesh::Make1D(int n, double sx)
3518 {
3519  int j, ind[1];
3520 
3521  SetEmpty();
3522 
3523  Dim = 1;
3524  spaceDim = 1;
3525 
3526  NumOfVertices = n + 1;
3527  NumOfElements = n;
3528  NumOfBdrElements = 2;
3529  vertices.SetSize(NumOfVertices);
3530  elements.SetSize(NumOfElements);
3531  boundary.SetSize(NumOfBdrElements);
3532 
3533  // Sets vertices and the corresponding coordinates
3534  for (j = 0; j < n+1; j++)
3535  {
3536  vertices[j](0) = ((double) j / n) * sx;
3537  }
3538 
3539  // Sets elements and the corresponding indices of vertices
3540  for (j = 0; j < n; j++)
3541  {
3542  elements[j] = new Segment(j, j+1, 1);
3543  }
3544 
3545  // Sets the boundary elements
3546  ind[0] = 0;
3547  boundary[0] = new Point(ind, 1);
3548  ind[0] = n;
3549  boundary[1] = new Point(ind, 2);
3550 
3551  NumOfEdges = 0;
3552  NumOfFaces = 0;
3553 
3554  SetMeshGen();
3555  GenerateFaces();
3556 
3557  attributes.Append(1);
3558  bdr_attributes.Append(1); bdr_attributes.Append(2);
3559 }
3560 
3561 Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
3562 {
3563  Dim = mesh.Dim;
3564  spaceDim = mesh.spaceDim;
3565 
3566  NumOfVertices = mesh.NumOfVertices;
3567  NumOfElements = mesh.NumOfElements;
3568  NumOfBdrElements = mesh.NumOfBdrElements;
3569  NumOfEdges = mesh.NumOfEdges;
3570  NumOfFaces = mesh.NumOfFaces;
3571  nbInteriorFaces = mesh.nbInteriorFaces;
3572  nbBoundaryFaces = mesh.nbBoundaryFaces;
3573 
3574  meshgen = mesh.meshgen;
3575  mesh_geoms = mesh.mesh_geoms;
3576 
3577  // Create the new Mesh instance without a record of its refinement history
3578  sequence = 0;
3579  last_operation = Mesh::NONE;
3580 
3581  // Duplicate the elements
3582  elements.SetSize(NumOfElements);
3583  for (int i = 0; i < NumOfElements; i++)
3584  {
3585  elements[i] = mesh.elements[i]->Duplicate(this);
3586  }
3587 
3588  // Copy the vertices
3589  mesh.vertices.Copy(vertices);
3590 
3591  // Duplicate the boundary
3592  boundary.SetSize(NumOfBdrElements);
3593  for (int i = 0; i < NumOfBdrElements; i++)
3594  {
3595  boundary[i] = mesh.boundary[i]->Duplicate(this);
3596  }
3597 
3598  // Copy the element-to-face Table, el_to_face
3599  el_to_face = (mesh.el_to_face) ? new Table(*mesh.el_to_face) : NULL;
3600 
3601  // Copy the boundary-to-face Array, be_to_face.
3602  mesh.be_to_face.Copy(be_to_face);
3603 
3604  // Copy the element-to-edge Table, el_to_edge
3605  el_to_edge = (mesh.el_to_edge) ? new Table(*mesh.el_to_edge) : NULL;
3606 
3607  // Copy the boundary-to-edge Table, bel_to_edge (3D)
3608  bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
3609 
3610  // Copy the boundary-to-edge Array, be_to_edge (2D)
3611  mesh.be_to_edge.Copy(be_to_edge);
3612 
3613  // Duplicate the faces and faces_info.
3614  faces.SetSize(mesh.faces.Size());
3615  for (int i = 0; i < faces.Size(); i++)
3616  {
3617  Element *face = mesh.faces[i]; // in 1D the faces are NULL
3618  faces[i] = (face) ? face->Duplicate(this) : NULL;
3619  }
3620  mesh.faces_info.Copy(faces_info);
3621  mesh.nc_faces_info.Copy(nc_faces_info);
3622 
3623  // Do NOT copy the element-to-element Table, el_to_el
3624  el_to_el = NULL;
3625 
3626  // Do NOT copy the face-to-edge Table, face_edge
3627  face_edge = NULL;
3628 
3629  // Copy the edge-to-vertex Table, edge_vertex
3630  edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
3631 
3632  // Copy the attributes and bdr_attributes
3633  mesh.attributes.Copy(attributes);
3634  mesh.bdr_attributes.Copy(bdr_attributes);
3635 
3636  // Deep copy the NURBSExtension.
3637 #ifdef MFEM_USE_MPI
3638  ParNURBSExtension *pNURBSext =
3639  dynamic_cast<ParNURBSExtension *>(mesh.NURBSext);
3640  if (pNURBSext)
3641  {
3642  NURBSext = new ParNURBSExtension(*pNURBSext);
3643  }
3644  else
3645 #endif
3646  {
3647  NURBSext = mesh.NURBSext ? new NURBSExtension(*mesh.NURBSext) : NULL;
3648  }
3649 
3650  // Deep copy the NCMesh.
3651 #ifdef MFEM_USE_MPI
3652  if (dynamic_cast<const ParMesh*>(&mesh))
3653  {
3654  ncmesh = NULL; // skip; will be done in ParMesh copy ctor
3655  }
3656  else
3657 #endif
3658  {
3659  ncmesh = mesh.ncmesh ? new NCMesh(*mesh.ncmesh) : NULL;
3660  }
3661 
3662  // Duplicate the Nodes, including the FiniteElementCollection and the
3663  // FiniteElementSpace
3664  if (mesh.Nodes && copy_nodes)
3665  {
3666  FiniteElementSpace *fes = mesh.Nodes->FESpace();
3667  const FiniteElementCollection *fec = fes->FEColl();
3668  FiniteElementCollection *fec_copy =
3669  FiniteElementCollection::New(fec->Name());
3670  FiniteElementSpace *fes_copy =
3671  new FiniteElementSpace(*fes, this, fec_copy);
3672  Nodes = new GridFunction(fes_copy);
3673  Nodes->MakeOwner(fec_copy);
3674  *Nodes = *mesh.Nodes;
3675  own_nodes = 1;
3676  }
3677  else
3678  {
3679  Nodes = mesh.Nodes;
3680  own_nodes = 0;
3681  }
3682 }
3683 
3684 Mesh::Mesh(Mesh &&mesh) : Mesh()
3685 {
3686  Swap(mesh, true);
3687 }
3688 
3690 {
3691  Swap(mesh, true);
3692  return *this;
3693 }
3694 
3695 Mesh Mesh::LoadFromFile(const char *filename, int generate_edges, int refine,
3696  bool fix_orientation)
3697 {
3698  Mesh mesh;
3699  named_ifgzstream imesh(filename);
3700  if (!imesh) { MFEM_ABORT("Mesh file not found: " << filename << '\n'); }
3701  else { mesh.Load(imesh, generate_edges, refine, fix_orientation); }
3702  return mesh;
3703 }
3704 
3705 Mesh Mesh::MakeCartesian1D(int n, double sx)
3706 {
3707  Mesh mesh;
3708  mesh.Make1D(n, sx);
3709  // mesh.Finalize(); not needed in this case
3710  return mesh;
3711 }
3712 
3714  int nx, int ny, Element::Type type, bool generate_edges,
3715  double sx, double sy, bool sfc_ordering)
3716 {
3717  Mesh mesh;
3718  mesh.Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
3719  mesh.Finalize(true); // refine = true
3720  return mesh;
3721 }
3722 
3724  int nx, int ny, int nz, Element::Type type,
3725  double sx, double sy, double sz, bool sfc_ordering)
3726 {
3727  Mesh mesh;
3728  mesh.Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
3729  mesh.Finalize(true); // refine = true
3730  return mesh;
3731 }
3732 
3733 Mesh Mesh::MakeRefined(Mesh &orig_mesh, int ref_factor, int ref_type)
3734 {
3735  Mesh mesh;
3736  Array<int> ref_factors(orig_mesh.GetNE());
3737  ref_factors = ref_factor;
3738  mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
3739  return mesh;
3740 }
3741 
3742 Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
3743  int ref_type)
3744 {
3745  Mesh mesh;
3746  mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
3747  return mesh;
3748 }
3749 
3750 Mesh::Mesh(const char *filename, int generate_edges, int refine,
3751  bool fix_orientation)
3752 {
3753  // Initialization as in the default constructor
3754  SetEmpty();
3755 
3756  named_ifgzstream imesh(filename);
3757  if (!imesh)
3758  {
3759  // Abort with an error message.
3760  MFEM_ABORT("Mesh file not found: " << filename << '\n');
3761  }
3762  else
3763  {
3764  Load(imesh, generate_edges, refine, fix_orientation);
3765  }
3766 }
3767 
3768 Mesh::Mesh(std::istream &input, int generate_edges, int refine,
3769  bool fix_orientation)
3770 {
3771  SetEmpty();
3772  Load(input, generate_edges, refine, fix_orientation);
3773 }
3774 
3775 void Mesh::ChangeVertexDataOwnership(double *vertex_data, int len_vertex_data,
3776  bool zerocopy)
3777 {
3778  // A dimension of 3 is now required since we use mfem::Vertex objects as PODs
3779  // and these object have a hardcoded double[3] entry
3780  MFEM_VERIFY(len_vertex_data >= NumOfVertices * 3,
3781  "Not enough vertices in external array : "
3782  "len_vertex_data = "<< len_vertex_data << ", "
3783  "NumOfVertices * 3 = " << NumOfVertices * 3);
3784  // Allow multiple calls to this method with the same vertex_data
3785  if (vertex_data == (double *)(vertices.GetData()))
3786  {
3787  MFEM_ASSERT(!vertices.OwnsData(), "invalid ownership");
3788  return;
3789  }
3790  if (!zerocopy)
3791  {
3792  memcpy(vertex_data, vertices.GetData(),
3793  NumOfVertices * 3 * sizeof(double));
3794  }
3795  // Vertex is POD double[3]
3796  vertices.MakeRef(reinterpret_cast<Vertex*>(vertex_data), NumOfVertices);
3797 }
3798 
3799 Mesh::Mesh(double *vertices_, int num_vertices,
3800  int *element_indices, Geometry::Type element_type,
3801  int *element_attributes, int num_elements,
3802  int *boundary_indices, Geometry::Type boundary_type,
3803  int *boundary_attributes, int num_boundary_elements,
3804  int dimension, int space_dimension)
3805 {
3806  if (space_dimension == -1)
3807  {
3808  space_dimension = dimension;
3809  }
3810 
3811  InitMesh(dimension, space_dimension, /*num_vertices*/ 0, num_elements,
3812  num_boundary_elements);
3813 
3814  int element_index_stride = Geometry::NumVerts[element_type];
3815  int boundary_index_stride = num_boundary_elements > 0 ?
3816  Geometry::NumVerts[boundary_type] : 0;
3817 
3818  // assuming Vertex is POD
3819  vertices.MakeRef(reinterpret_cast<Vertex*>(vertices_), num_vertices);
3820  NumOfVertices = num_vertices;
3821 
3822  for (int i = 0; i < num_elements; i++)
3823  {
3824  elements[i] = NewElement(element_type);
3825  elements[i]->SetVertices(element_indices + i * element_index_stride);
3826  elements[i]->SetAttribute(element_attributes[i]);
3827  }
3828  NumOfElements = num_elements;
3829 
3830  for (int i = 0; i < num_boundary_elements; i++)
3831  {
3832  boundary[i] = NewElement(boundary_type);
3833  boundary[i]->SetVertices(boundary_indices + i * boundary_index_stride);
3834  boundary[i]->SetAttribute(boundary_attributes[i]);
3835  }
3836  NumOfBdrElements = num_boundary_elements;
3837 
3838  FinalizeTopology();
3839 }
3840 
3842 {
3843  switch (geom)
3844  {
3845  case Geometry::POINT: return (new Point);
3846  case Geometry::SEGMENT: return (new Segment);
3847  case Geometry::TRIANGLE: return (new Triangle);
3848  case Geometry::SQUARE: return (new Quadrilateral);
3849  case Geometry::TETRAHEDRON:
3850 #ifdef MFEM_USE_MEMALLOC
3851  return TetMemory.Alloc();
3852 #else
3853  return (new Tetrahedron);
3854 #endif
3855  case Geometry::CUBE: return (new Hexahedron);
3856  case Geometry::PRISM: return (new Wedge);
3857  case Geometry::PYRAMID: return (new Pyramid);
3858  default:
3859  MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
3860  }
3861 
3862  return NULL;
3863 }
3864 
3866 {
3867  int geom, nv, *v;
3868  Element *el;
3869 
3870  input >> geom;
3871  el = NewElement(geom);
3872  MFEM_VERIFY(el, "Unsupported element type: " << geom);
3873  nv = el->GetNVertices();
3874  v = el->GetVertices();
3875  for (int i = 0; i < nv; i++)
3876  {
3877  input >> v[i];
3878  }
3879 
3880  return el;
3881 }
3882 
3883 void Mesh::PrintElementWithoutAttr(const Element *el, std::ostream &os)
3884 {
3885  os << el->GetGeometryType();
3886  const int nv = el->GetNVertices();
3887  const int *v = el->GetVertices();
3888  for (int j = 0; j < nv; j++)
3889  {
3890  os << ' ' << v[j];
3891  }
3892  os << '\n';
3893 }
3894 
3895 Element *Mesh::ReadElement(std::istream &input)
3896 {
3897  int attr;
3898  Element *el;
3899 
3900  input >> attr;
3901  el = ReadElementWithoutAttr(input);
3902  el->SetAttribute(attr);
3903 
3904  return el;
3905 }
3906 
3907 void Mesh::PrintElement(const Element *el, std::ostream &os)
3908 {
3909  os << el->GetAttribute() << ' ';
3910  PrintElementWithoutAttr(el, os);
3911 }
3912 
3914 {
3915  meshgen = mesh_geoms = 0;
3916  for (int i = 0; i < NumOfElements; i++)
3917  {
3918  const Element::Type type = GetElement(i)->GetType();
3919  switch (type)
3920  {
3921  case Element::TETRAHEDRON:
3923  case Element::TRIANGLE:
3924  mesh_geoms |= (1 << Geometry::TRIANGLE);
3925  case Element::SEGMENT:
3926  mesh_geoms |= (1 << Geometry::SEGMENT);
3927  case Element::POINT:
3928  mesh_geoms |= (1 << Geometry::POINT);
3929  meshgen |= 1;
3930  break;
3931 
3932  case Element::HEXAHEDRON:
3933  mesh_geoms |= (1 << Geometry::CUBE);
3935  mesh_geoms |= (1 << Geometry::SQUARE);
3936  mesh_geoms |= (1 << Geometry::SEGMENT);
3937  mesh_geoms |= (1 << Geometry::POINT);
3938  meshgen |= 2;
3939  break;
3940 
3941  case Element::WEDGE:
3942  mesh_geoms |= (1 << Geometry::PRISM);
3943  mesh_geoms |= (1 << Geometry::SQUARE);
3944  mesh_geoms |= (1 << Geometry::TRIANGLE);
3945  mesh_geoms |= (1 << Geometry::SEGMENT);
3946  mesh_geoms |= (1 << Geometry::POINT);
3947  meshgen |= 4;
3948  break;
3949 
3950  case Element::PYRAMID:
3951  mesh_geoms |= (1 << Geometry::PYRAMID);
3952  mesh_geoms |= (1 << Geometry::SQUARE);
3953  mesh_geoms |= (1 << Geometry::TRIANGLE);
3954  mesh_geoms |= (1 << Geometry::SEGMENT);
3955  mesh_geoms |= (1 << Geometry::POINT);
3956  meshgen |= 8;
3957  break;
3958 
3959  default:
3960  MFEM_ABORT("invalid element type: " << type);
3961  break;
3962  }
3963  }
3964 }
3965 
3966 void Mesh::Loader(std::istream &input, int generate_edges,
3967  std::string parse_tag)
3968 {
3969  int curved = 0, read_gf = 1;
3970  bool finalize_topo = true;
3971 
3972  if (!input)
3973  {
3974  MFEM_ABORT("Input stream is not open");
3975  }
3976 
3977  Clear();
3978 
3979  string mesh_type;
3980  input >> ws;
3981  getline(input, mesh_type);
3982  filter_dos(mesh_type);
3983 
3984  // MFEM's conforming mesh formats
3985  int mfem_version = 0;
3986  if (mesh_type == "MFEM mesh v1.0") { mfem_version = 10; } // serial
3987  else if (mesh_type == "MFEM mesh v1.2") { mfem_version = 12; } // parallel
3988 
3989  // MFEM nonconforming mesh format
3990  // (NOTE: previous v1.1 is now under this branch for backward compatibility)
3991  int mfem_nc_version = 0;
3992  if (mesh_type == "MFEM NC mesh v1.0") { mfem_nc_version = 10; }
3993  else if (mesh_type == "MFEM mesh v1.1") { mfem_nc_version = 1 /*legacy*/; }
3994 
3995  if (mfem_version)
3996  {
3997  // Formats mfem_v12 and newer have a tag indicating the end of the mesh
3998  // section in the stream. A user provided parse tag can also be provided
3999  // via the arguments. For example, if this is called from parallel mesh
4000  // object, it can indicate to read until parallel mesh section begins.
4001  if (mfem_version == 12 && parse_tag.empty())
4002  {
4003  parse_tag = "mfem_mesh_end";
4004  }
4005  ReadMFEMMesh(input, mfem_version, curved);
4006  }
4007  else if (mfem_nc_version)
4008  {
4009  MFEM_ASSERT(ncmesh == NULL, "internal error");
4010  int is_nc = 1;
4011 
4012 #ifdef MFEM_USE_MPI
4013  ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
4014  if (pmesh)
4015  {
4016  MFEM_VERIFY(mfem_nc_version >= 10,
4017  "Legacy nonconforming format (MFEM mesh v1.1) cannot be "
4018  "used to load a parallel nonconforming mesh, sorry.");
4019 
4020  ncmesh = new ParNCMesh(pmesh->GetComm(),
4021  input, mfem_nc_version, curved, is_nc);
4022  }
4023  else
4024 #endif
4025  {
4026  ncmesh = new NCMesh(input, mfem_nc_version, curved, is_nc);
4027  }
4029 
4030  if (!is_nc)
4031  {
4032  // special case for backward compatibility with MFEM <=4.2:
4033  // if the "vertex_parents" section is missing in the v1.1 format,
4034  // the mesh is treated as conforming
4035  delete ncmesh;
4036  ncmesh = NULL;
4037  }
4038  }
4039  else if (mesh_type == "linemesh") // 1D mesh
4040  {
4041  ReadLineMesh(input);
4042  }
4043  else if (mesh_type == "areamesh2" || mesh_type == "curved_areamesh2")
4044  {
4045  if (mesh_type == "curved_areamesh2")
4046  {
4047  curved = 1;
4048  }
4049  ReadNetgen2DMesh(input, curved);
4050  }
4051  else if (mesh_type == "NETGEN" || mesh_type == "NETGEN_Neutral_Format")
4052  {
4053  ReadNetgen3DMesh(input);
4054  }
4055  else if (mesh_type == "TrueGrid")
4056  {
4057  ReadTrueGridMesh(input);
4058  }
4059  else if (mesh_type.rfind("# vtk DataFile Version") == 0)
4060  {
4061  int major_vtk_version = mesh_type[mesh_type.length()-3] - '0';
4062  // int minor_vtk_version = mesh_type[mesh_type.length()-1] - '0';
4063  MFEM_VERIFY(major_vtk_version >= 2 && major_vtk_version <= 4,
4064  "Unsupported VTK format");
4065  ReadVTKMesh(input, curved, read_gf, finalize_topo);
4066  }
4067  else if (mesh_type.rfind("<VTKFile ") == 0 || mesh_type.rfind("<?xml") == 0)
4068  {
4069  ReadXML_VTKMesh(input, curved, read_gf, finalize_topo, mesh_type);
4070  }
4071  else if (mesh_type == "MFEM NURBS mesh v1.0")
4072  {
4073  ReadNURBSMesh(input, curved, read_gf);
4074  }
4075  else if (mesh_type == "MFEM INLINE mesh v1.0")
4076  {
4077  ReadInlineMesh(input, generate_edges);
4078  return; // done with inline mesh construction
4079  }
4080  else if (mesh_type == "$MeshFormat") // Gmsh
4081  {
4082  ReadGmshMesh(input, curved, read_gf);
4083  }
4084  else if
4085  ((mesh_type.size() > 2 &&
4086  mesh_type[0] == 'C' && mesh_type[1] == 'D' && mesh_type[2] == 'F') ||
4087  (mesh_type.size() > 3 &&
4088  mesh_type[1] == 'H' && mesh_type[2] == 'D' && mesh_type[3] == 'F'))
4089  {
4090  named_ifgzstream *mesh_input = dynamic_cast<named_ifgzstream *>(&input);
4091  if (mesh_input)
4092  {
4093 #ifdef MFEM_USE_NETCDF
4094  ReadCubit(mesh_input->filename.c_str(), curved, read_gf);
4095 #else
4096  MFEM_ABORT("NetCDF support requires configuration with"
4097  " MFEM_USE_NETCDF=YES");
4098  return;
4099 #endif
4100  }
4101  else
4102  {
4103  MFEM_ABORT("Can not determine Cubit mesh filename!"
4104  " Use mfem::named_ifgzstream for input.");
4105  return;
4106  }
4107  }
4108  else
4109  {
4110  MFEM_ABORT("Unknown input mesh format: " << mesh_type);
4111  return;
4112  }
4113 
4114  // at this point the following should be defined:
4115  // 1) Dim
4116  // 2) NumOfElements, elements
4117  // 3) NumOfBdrElements, boundary
4118  // 4) NumOfVertices, with allocated space in vertices
4119  // 5) curved
4120  // 5a) if curved == 0, vertices must be defined
4121  // 5b) if curved != 0 and read_gf != 0,
4122  // 'input' must point to a GridFunction
4123  // 5c) if curved != 0 and read_gf == 0,
4124  // vertices and Nodes must be defined
4125  // optional:
4126  // 1) el_to_edge may be allocated (as in the case of P2 VTK meshes)
4127  // 2) ncmesh may be allocated
4128 
4129  // FinalizeTopology() will:
4130  // - assume that generate_edges is true
4131  // - assume that refine is false
4132  // - does not check the orientation of regular and boundary elements
4133  if (finalize_topo)
4134  {
4135  // don't generate any boundary elements, especially in parallel
4136  bool generate_bdr = false;
4137 
4138  FinalizeTopology(generate_bdr);
4139  }
4140 
4141  if (curved && read_gf)
4142  {
4143  Nodes = new GridFunction(this, input);
4144 
4145  own_nodes = 1;
4146  spaceDim = Nodes->VectorDim();
4147  if (ncmesh) { ncmesh->spaceDim = spaceDim; }
4148 
4149  // Set vertex coordinates from the 'Nodes'
4151  }
4152 
4153  // If a parse tag was supplied, keep reading the stream until the tag is
4154  // encountered.
4155  if (mfem_version == 12)
4156  {
4157  string line;
4158  do
4159  {
4160  skip_comment_lines(input, '#');
4161  MFEM_VERIFY(input.good(), "Required mesh-end tag not found");
4162  getline(input, line);
4163  filter_dos(line);
4164  // mfem v1.2 may not have parse_tag in it, e.g. if trying to read a
4165  // serial mfem v1.2 mesh as parallel with "mfem_serial_mesh_end" as
4166  // parse_tag. That's why, regardless of parse_tag, we stop reading if
4167  // we find "mfem_mesh_end" which is required by mfem v1.2 format.
4168  if (line == "mfem_mesh_end") { break; }
4169  }
4170  while (line != parse_tag);
4171  }
4172  else if (mfem_nc_version >= 10)
4173  {
4174  string ident;
4175  skip_comment_lines(input, '#');
4176  input >> ident;
4177  MFEM_VERIFY(ident == "mfem_mesh_end",
4178  "invalid mesh: end of file tag not found");
4179  }
4180 
4181  // Finalize(...) should be called after this, if needed.
4182 }
4183 
4184 Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
4185 {
4186  int i, j, ie, ib, iv, *v, nv;
4187  Element *el;
4188  Mesh *m;
4189 
4190  SetEmpty();
4191 
4192  Dim = mesh_array[0]->Dimension();
4193  spaceDim = mesh_array[0]->SpaceDimension();
4194 
4195  if (mesh_array[0]->NURBSext)
4196  {
4197  // assuming the pieces form a partition of a NURBS mesh
4198  NURBSext = new NURBSExtension(mesh_array, num_pieces);
4199 
4202 
4204 
4205  // NumOfBdrElements = NURBSext->GetNBE();
4206  // NURBSext->GetBdrElementTopo(boundary);
4207 
4208  Array<int> lvert_vert, lelem_elem;
4209 
4210  // Here, for visualization purposes, we copy the boundary elements from
4211  // the individual pieces which include the interior boundaries. This
4212  // creates 'boundary' array that is different from the one generated by
4213  // the NURBSExtension which, in particular, makes the boundary-dof table
4214  // invalid. This, in turn, causes GetBdrElementTransformation to not
4215  // function properly.
4216  NumOfBdrElements = 0;
4217  for (i = 0; i < num_pieces; i++)
4218  {
4219  NumOfBdrElements += mesh_array[i]->GetNBE();
4220  }
4221  boundary.SetSize(NumOfBdrElements);
4222  vertices.SetSize(NumOfVertices);
4223  ib = 0;
4224  for (i = 0; i < num_pieces; i++)
4225  {
4226  m = mesh_array[i];
4227  m->NURBSext->GetVertexLocalToGlobal(lvert_vert);
4228  m->NURBSext->GetElementLocalToGlobal(lelem_elem);
4229  // copy the element attributes
4230  for (j = 0; j < m->GetNE(); j++)
4231  {
4232  elements[lelem_elem[j]]->SetAttribute(m->GetAttribute(j));
4233  }
4234  // copy the boundary
4235  for (j = 0; j < m->GetNBE(); j++)
4236  {
4237  el = m->GetBdrElement(j)->Duplicate(this);
4238  v = el->GetVertices();
4239  nv = el->GetNVertices();
4240  for (int k = 0; k < nv; k++)
4241  {
4242  v[k] = lvert_vert[v[k]];
4243  }
4244  boundary[ib++] = el;
4245  }
4246  // copy the vertices
4247  for (j = 0; j < m->GetNV(); j++)
4248  {
4249  vertices[lvert_vert[j]].SetCoords(m->SpaceDimension(),
4250  m->GetVertex(j));
4251  }
4252  }
4253  }
4254  else // not a NURBS mesh
4255  {
4256  NumOfElements = 0;
4257  NumOfBdrElements = 0;
4258  NumOfVertices = 0;
4259  for (i = 0; i < num_pieces; i++)
4260  {
4261  m = mesh_array[i];
4262  NumOfElements += m->GetNE();
4263  NumOfBdrElements += m->GetNBE();
4264  NumOfVertices += m->GetNV();
4265  }
4266  elements.SetSize(NumOfElements);
4267  boundary.SetSize(NumOfBdrElements);
4268  vertices.SetSize(NumOfVertices);
4269  ie = ib = iv = 0;
4270  for (i = 0; i < num_pieces; i++)
4271  {
4272  m = mesh_array[i];
4273  // copy the elements
4274  for (j = 0; j < m->GetNE(); j++)
4275  {
4276  el = m->GetElement(j)->Duplicate(this);
4277  v = el->GetVertices();
4278  nv = el->GetNVertices();
4279  for (int k = 0; k < nv; k++)
4280  {
4281  v[k] += iv;
4282  }
4283  elements[ie++] = el;
4284  }
4285  // copy the boundary elements
4286  for (j = 0; j < m->GetNBE(); j++)
4287  {
4288  el = m->GetBdrElement(j)->Duplicate(this);
4289  v = el->GetVertices();
4290  nv = el->GetNVertices();
4291  for (int k = 0; k < nv; k++)
4292  {
4293  v[k] += iv;
4294  }
4295  boundary[ib++] = el;
4296  }
4297  // copy the vertices
4298  for (j = 0; j < m->GetNV(); j++)
4299  {
4300  vertices[iv++].SetCoords(m->SpaceDimension(), m->GetVertex(j));
4301  }
4302  }
4303  }
4304 
4305  FinalizeTopology();
4306 
4307  // copy the nodes (curvilinear meshes)
4308  GridFunction *g = mesh_array[0]->GetNodes();
4309  if (g)
4310  {
4311  Array<GridFunction *> gf_array(num_pieces);
4312  for (i = 0; i < num_pieces; i++)
4313  {
4314  gf_array[i] = mesh_array[i]->GetNodes();
4315  }
4316  Nodes = new GridFunction(this, gf_array, num_pieces);
4317  own_nodes = 1;
4318  }
4319 
4320 #ifdef MFEM_DEBUG
4321  CheckElementOrientation(false);
4323 #endif
4324 }
4325 
4326 Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
4327 {
4328  Array<int> ref_factors(orig_mesh->GetNE());
4329  ref_factors = ref_factor;
4330  MakeRefined_(*orig_mesh, ref_factors, ref_type);
4331 }
4332 
4333 void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> ref_factors,
4334  int ref_type)
4335 {
4336  SetEmpty();
4337  Dim = orig_mesh.Dimension();
4338  spaceDim = orig_mesh.SpaceDimension();
4339 
4340  int orig_ne = orig_mesh.GetNE();
4341  MFEM_VERIFY(ref_factors.Size() == orig_ne && orig_ne > 0,
4342  "Number of refinement factors must equal number of elements")
4343  MFEM_VERIFY(ref_factors.Min() >= 1, "Refinement factor must be >= 1");
4344  const int q_type = BasisType::GetQuadrature1D(ref_type);
4345  MFEM_VERIFY(Quadrature1D::CheckClosed(q_type) != Quadrature1D::Invalid,
4346  "Invalid refinement type. Must use closed basis type.");
4347 
4348  int min_ref = ref_factors.Min();
4349  int max_ref = ref_factors.Max();
4350 
4351  bool var_order = (min_ref != max_ref);
4352 
4353  // variable order space can only be constructed over an NC mesh
4354  if (var_order) { orig_mesh.EnsureNCMesh(true); }
4355 
4356  // Construct a scalar H1 FE space of order ref_factor and use its dofs as
4357  // the indices of the new, refined vertices.
4358  H1_FECollection rfec(min_ref, Dim, ref_type);
4359  FiniteElementSpace rfes(&orig_mesh, &rfec);
4360 
4361  if (var_order)
4362  {
4363  rfes.SetRelaxedHpConformity(false);
4364  for (int i = 0; i < orig_ne; i++)
4365  {
4366  rfes.SetElementOrder(i, ref_factors[i]);
4367  }
4368  rfes.Update(false);
4369  }
4370 
4371  // Set the number of vertices, set the actual coordinates later
4372  NumOfVertices = rfes.GetNDofs();
4373  vertices.SetSize(NumOfVertices);
4374 
4375  Array<int> rdofs;
4376  DenseMatrix phys_pts;
4377 
4378  GeometryRefiner refiner;
4379  refiner.SetType(q_type);
4380 
4381  // Add refined elements and set vertex coordinates
4382  for (int el = 0; el < orig_ne; el++)
4383  {
4384  Geometry::Type geom = orig_mesh.GetElementGeometry(el);
4385  int attrib = orig_mesh.GetAttribute(el);
4386  int nvert = Geometry::NumVerts[geom];
4387  RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el]);
4388 
4389  rfes.GetElementDofs(el, rdofs);
4390  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
4391  const FiniteElement *rfe = rfes.GetFE(el);
4392  orig_mesh.GetElementTransformation(el)->Transform(rfe->GetNodes(),
4393  phys_pts);
4394  const int *c2h_map = rfec.GetDofMap(geom, ref_factors[el]);
4395  for (int i = 0; i < phys_pts.Width(); i++)
4396  {
4397  vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
4398  }
4399  for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
4400  {
4401  Element *elem = NewElement(geom);
4402  elem->SetAttribute(attrib);
4403  int *v = elem->GetVertices();
4404  for (int k = 0; k < nvert; k++)
4405  {
4406  int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
4407  v[k] = rdofs[c2h_map[cid]];
4408  }
4409  AddElement(elem);
4410  }
4411  }
4412 
4413  if (Dim > 2)
4414  {
4415  GetElementToFaceTable(false);
4416  GenerateFaces();
4417  }
4418 
4419  // Add refined boundary elements
4420  for (int el = 0; el < orig_mesh.GetNBE(); el++)
4421  {
4422  int i, info;
4423  orig_mesh.GetBdrElementAdjacentElement(el, i, info);
4424  Geometry::Type geom = orig_mesh.GetBdrElementBaseGeometry(el);
4425  int attrib = orig_mesh.GetBdrAttribute(el);
4426  int nvert = Geometry::NumVerts[geom];
4427  RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[i]);
4428 
4429  rfes.GetBdrElementDofs(el, rdofs);
4430  MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
4431  const int *c2h_map = rfec.GetDofMap(geom, ref_factors[i]);
4432  for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
4433  {
4434  Element *elem = NewElement(geom);
4435  elem->SetAttribute(attrib);
4436  int *v = elem->GetVertices();
4437  for (int k = 0; k < nvert; k++)
4438  {
4439  int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
4440  v[k] = rdofs[c2h_map[cid]];
4441  }
4442  AddBdrElement(elem);
4443  }
4444  }
4445  FinalizeTopology(false);
4446  sequence = orig_mesh.GetSequence() + 1;
4448 
4449  // Set up the nodes of the new mesh (if the original mesh has nodes). The new
4450  // mesh is always straight-sided (i.e. degree 1 finite element space), but
4451  // the nodes are required for e.g. periodic meshes.
4452  if (orig_mesh.GetNodes())
4453  {
4454  bool discont = orig_mesh.GetNodalFESpace()->IsDGSpace();
4455  Ordering::Type dof_ordering = orig_mesh.GetNodalFESpace()->GetOrdering();
4456  Mesh::SetCurvature(1, discont, spaceDim, dof_ordering);
4457  FiniteElementSpace *nodal_fes = Nodes->FESpace();
4458  const FiniteElementCollection *nodal_fec = nodal_fes->FEColl();
4459  H1_FECollection vertex_fec(1, Dim);
4460  Array<int> dofs;
4461  int el_counter = 0;
4462  for (int iel = 0; iel < orig_ne; iel++)
4463  {
4464  Geometry::Type geom = orig_mesh.GetElementBaseGeometry(iel);
4465  int nvert = Geometry::NumVerts[geom];
4466  RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[iel]);
4467  rfes.GetElementDofs(iel, rdofs);
4468  const FiniteElement *rfe = rfes.GetFE(iel);
4469  orig_mesh.GetElementTransformation(iel)->Transform(rfe->GetNodes(),
4470  phys_pts);
4471  const int *node_map = NULL;
4472  const H1_FECollection *h1_fec =
4473  dynamic_cast<const H1_FECollection *>(nodal_fec);
4474  if (h1_fec != NULL) { node_map = h1_fec->GetDofMap(geom); }
4475  const int *vertex_map = vertex_fec.GetDofMap(geom);
4476  const int *c2h_map = rfec.GetDofMap(geom, ref_factors[iel]);
4477  for (int jel = 0; jel < RG.RefGeoms.Size()/nvert; jel++)
4478  {
4479  nodal_fes->GetElementVDofs(el_counter++, dofs);
4480  for (int iv_lex=0; iv_lex<nvert; ++iv_lex)
4481  {
4482  // convert from lexicographic to vertex index
4483  int iv = vertex_map[iv_lex];
4484  // index of vertex of current element in phys_pts matrix
4485  int pt_idx = c2h_map[RG.RefGeoms[iv+nvert*jel]];
4486  // index of current vertex into DOF array
4487  int node_idx = node_map ? node_map[iv_lex] : iv_lex;
4488  for (int d=0; d<spaceDim; ++d)
4489  {
4490  (*Nodes)[dofs[node_idx + d*nvert]] = phys_pts(d,pt_idx);
4491  }
4492  }
4493  }
4494  }
4495  }
4496 
4497  // Setup the data for the coarse-fine refinement transformations
4498  CoarseFineTr.embeddings.SetSize(GetNE());
4499  // First, compute total number of point matrices that we need per geometry
4500  // and the offsets into that array
4501  using GeomRef = std::pair<Geometry::Type, int>;
4502  std::map<GeomRef, int> point_matrices_offsets;
4503  int n_point_matrices[Geometry::NumGeom] = {}; // initialize to zero
4504  for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
4505  {
4506  Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
4507  // Have we seen this pair of (goemetry, refinement level) before?
4508  GeomRef id(geom, ref_factors[el_coarse]);
4509  if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
4510  {
4511  RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el_coarse]);
4512  int nvert = Geometry::NumVerts[geom];
4513  int nref_el = RG.RefGeoms.Size()/nvert;
4514  // If not, then store the offset and add to the size required
4515  point_matrices_offsets[id] = n_point_matrices[geom];
4516  n_point_matrices[geom] += nref_el;
4517  }
4518  }
4519 
4520  // Set up the sizes
4521  for (int geom = 0; geom < Geometry::NumGeom; ++geom)
4522  {
4523  int nmatrices = n_point_matrices[geom];
4524  int nvert = Geometry::NumVerts[geom];
4525  CoarseFineTr.point_matrices[geom].SetSize(Dim, nvert, nmatrices);
4526  }
4527 
4528  // Compute the point matrices and embeddings
4529  int el_fine = 0;
4530  for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
4531  {
4532  Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
4533  int ref = ref_factors[el_coarse];
4534  int offset = point_matrices_offsets[GeomRef(geom, ref)];
4535  int nvert = Geometry::NumVerts[geom];
4536  RefinedGeometry &RG = *refiner.Refine(geom, ref);
4537  for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
4538  {
4539  DenseMatrix &Pj = CoarseFineTr.point_matrices[geom](offset + j);
4540  for (int k = 0; k < nvert; k++)
4541  {
4542  int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
4543  const IntegrationPoint &ip = RG.RefPts[cid];
4544  ip.Get(Pj.GetColumn(k), Dim);
4545  }
4546 
4547  Embedding &emb = CoarseFineTr.embeddings[el_fine];
4548  emb.parent = el_coarse;
4549  emb.matrix = offset + j;
4550  ++el_fine;
4551  }
4552  }
4553 
4554  MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
4555 
4556  // The check below is disabled because is fails for parallel meshes with
4557  // interior "boundary" element that, when such "boundary" element is between
4558  // two elements on different processors.
4559  // MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
4560 }
4561 
4562 Mesh Mesh::MakeSimplicial(const Mesh &orig_mesh)
4563 {
4564  Mesh mesh;
4565  mesh.MakeSimplicial_(orig_mesh, NULL);
4566  return mesh;
4567 }
4568 
4569 void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
4570 {
4571  MFEM_VERIFY(const_cast<Mesh&>(orig_mesh).CheckElementOrientation(false) == 0,
4572  "Mesh::MakeSimplicial requires a properly oriented input mesh");
4573  MFEM_VERIFY(orig_mesh.Conforming(),
4574  "Mesh::MakeSimplicial does not support non-conforming meshes.")
4575 
4576  int dim = orig_mesh.Dimension();
4577  int sdim = orig_mesh.SpaceDimension();
4578 
4579  if (dim == 1)
4580  {
4581  Mesh copy(orig_mesh);
4582  Swap(copy, true);
4583  return;
4584  }
4585 
4586  int nv = orig_mesh.GetNV();
4587  int ne = orig_mesh.GetNE();
4588  int nbe = orig_mesh.GetNBE();
4589 
4590  static int num_subdivisions[Geometry::NUM_GEOMETRIES];
4591  num_subdivisions[Geometry::POINT] = 1;
4592  num_subdivisions[Geometry::SEGMENT] = 1;
4593  num_subdivisions[Geometry::TRIANGLE] = 1;
4594  num_subdivisions[Geometry::TETRAHEDRON] = 1;
4595  num_subdivisions[Geometry::SQUARE] = 2;
4596  num_subdivisions[Geometry::PRISM] = 3;
4597  num_subdivisions[Geometry::CUBE] = 6;
4598  // NOTE: some hexes may be subdivided into only 5 tets, so this is an
4599  // estimate only. The actual number of created tets may be less, so the
4600  // elements array will need to be shrunk after mesh creation.
4601  int new_ne = 0, new_nbe = 0;
4602  for (int i=0; i<ne; ++i)
4603  {
4604  new_ne += num_subdivisions[orig_mesh.GetElementBaseGeometry(i)];
4605  }
4606  for (int i=0; i<nbe; ++i)
4607  {
4608  new_nbe += num_subdivisions[orig_mesh.GetBdrElementBaseGeometry(i)];
4609  }
4610 
4611  InitMesh(dim, sdim, nv, new_ne, new_nbe);
4612 
4613  // Vertices of the new mesh are same as the original mesh
4614  NumOfVertices = nv;
4615  for (int i=0; i<nv; ++i)
4616  {
4617  vertices[i].SetCoords(dim, orig_mesh.vertices[i]());
4618  }
4619 
4620  // We need a global vertex numbering to identify which diagonals to split
4621  // (quad faces are split using the diagonal originating from the smallest
4622  // global vertex number). Use the supplied global numbering, if it is
4623  // non-NULL, otherwise use the local numbering.
4624  Array<int> vglobal_id;
4625  if (vglobal == NULL)
4626  {
4627  vglobal_id.SetSize(nv);
4628  for (int i=0; i<nv; ++i) { vglobal_id[i] = i; }
4629  vglobal = vglobal_id.GetData();
4630  }
4631 
4632  constexpr int nv_tri = 3, nv_quad = 4, nv_tet = 4, nv_prism = 6, nv_hex = 8;
4633  constexpr int quad_ntris = 2, prism_ntets = 3;
4634  static const int quad_trimap[2][nv_tri*quad_ntris] =
4635  {
4636  {
4637  0, 0,
4638  1, 2,
4639  2, 3
4640  },{
4641  0, 1,
4642  1, 2,
4643  3, 3
4644  }
4645  };
4646  static const int prism_rot[nv_prism*nv_prism] =
4647  {
4648  0, 1, 2, 3, 4, 5,
4649  1, 2, 0, 4, 5, 3,
4650  2, 0, 1, 5, 3, 4,
4651  3, 5, 4, 0, 2, 1,
4652  4, 3, 5, 1, 0, 2,
4653  5, 4, 3, 2, 1, 0
4654  };
4655  static const int prism_f[nv_quad] = {1, 2, 5, 4};
4656  static const int prism_tetmaps[2][nv_prism*prism_ntets] =
4657  {
4658  {
4659  0, 0, 0,
4660  1, 1, 4,
4661  2, 5, 5,
4662  5, 4, 3
4663  },{
4664  0, 0, 0,
4665  1, 4, 4,
4666  2, 2, 5,
4667  4, 5, 3
4668  }
4669  };
4670  static const int hex_rot[nv_hex*nv_hex] =
4671  {
4672  0, 1, 2, 3, 4, 5, 6, 7,
4673  1, 0, 4, 5, 2, 3, 7, 6,
4674  2, 1, 5, 6, 3, 0, 4, 7,
4675  3, 0, 1, 2, 7, 4, 5, 6,
4676  4, 0, 3, 7, 5, 1, 2, 6,
4677  5, 1, 0, 4, 6, 2, 3, 7,
4678  6, 2, 1, 5, 7, 3, 0, 4,
4679  7, 3, 2, 6, 4, 0, 1, 5
4680  };
4681  static const int hex_f0[nv_quad] = {1, 2, 6, 5};
4682  static const int hex_f1[nv_quad] = {2, 3, 7, 6};
4683  static const int hex_f2[nv_quad] = {4, 5, 6, 7};
4684  static const int num_rot[8] = {0, 1, 2, 0, 0, 2, 1, 0};
4685  static const int hex_tetmap0[nv_tet*5] =
4686  {
4687  0, 0, 0, 0, 2,
4688  1, 2, 2, 5, 7,
4689  2, 7, 3, 7, 5,
4690  5, 5, 7, 4, 6
4691  };
4692  static const int hex_tetmap1[nv_tet*6] =
4693  {
4694  0, 0, 1, 0, 0, 1,
4695  5, 1, 6, 7, 7, 7,
4696  7, 7, 7, 2, 1, 6,
4697  4, 5, 5, 3, 2, 2
4698  };
4699  static const int hex_tetmap2[nv_tet*6] =
4700  {
4701  0, 0, 0, 0, 0, 0,
4702  4, 3, 7, 1, 3, 6,
4703  5, 7, 4, 2, 6, 5,
4704  6, 6, 6, 5, 2, 2
4705  };
4706  static const int hex_tetmap3[nv_tet*6] =
4707  {
4708  0, 0, 0, 0, 1, 1,
4709  2, 3, 7, 5, 5, 6,
4710  3, 7, 4, 6, 6, 2,
4711  6, 6, 6, 4, 0, 0
4712  };
4713  static const int *hex_tetmaps[4] =
4714  {
4715  hex_tetmap0, hex_tetmap1, hex_tetmap2, hex_tetmap3
4716  };
4717 
4718  auto find_min = [](const int*a, int n) { return std::min_element(a,a+n)-a; };
4719 
4720  for (int i=0; i<ne; ++i)
4721  {
4722  const int *v = orig_mesh.elements[i]->GetVertices();
4723  const int attrib = orig_mesh.GetAttribute(i);
4724  const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(i);
4725 
4726  if (num_subdivisions[orig_geom] == 1)
4727  {
4728  // (num_subdivisions[orig_geom] == 1) implies that the element does
4729  // not need to be further split (it is either a segment, triangle,
4730  // or tetrahedron), and so it is left unchanged.
4731  Element *e = NewElement(orig_geom);
4732  e->SetAttribute(attrib);
4733  e->SetVertices(v);
4734  AddElement(e);
4735  }
4736  else if (orig_geom == Geometry::SQUARE)
4737  {
4738  for (int itri=0; itri<quad_ntris; ++itri)
4739  {
4741  e->SetAttribute(attrib);
4742  int *v2 = e->GetVertices();
4743  for (int iv=0; iv<nv_tri; ++iv)
4744  {
4745  v2[iv] = v[quad_trimap[0][itri + iv*quad_ntris]];
4746  }
4747  AddElement(e);
4748  }
4749  }
4750  else if (orig_geom == Geometry::PRISM)
4751  {
4752  int vg[nv_prism];
4753  for (int iv=0; iv<nv_prism; ++iv) { vg[iv] = vglobal[v[iv]]; }
4754  // Rotate the vertices of the prism so that the smallest vertex index
4755  // is in the first place
4756  int irot = find_min(vg, nv_prism);
4757  for (int iv=0; iv<nv_prism; ++iv)
4758  {
4759  int jv = prism_rot[iv + irot*nv_prism];
4760  vg[iv] = v[jv];
4761  }
4762  // Two cases according to which diagonal splits third quad face
4763  int q[nv_quad];
4764  for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[prism_f[iv]]]; }
4765  int j = find_min(q, nv_quad);
4766  const int *tetmap = (j == 0 || j == 2) ? prism_tetmaps[0] : prism_tetmaps[1];
4767  for (int itet=0; itet<prism_ntets; ++itet)
4768  {
4770  e->SetAttribute(attrib);
4771  int *v2 = e->GetVertices();
4772  for (int iv=0; iv<nv_tet; ++iv)
4773  {
4774  v2[iv] = vg[tetmap[itet + iv*prism_ntets]];
4775  }
4776  AddElement(e);
4777  }
4778  }
4779  else if (orig_geom == Geometry::CUBE)
4780  {
4781  int vg[nv_hex];
4782  for (int iv=0; iv<nv_hex; ++iv) { vg[iv] = vglobal[v[iv]]; }
4783 
4784  // Rotate the vertices of the hex so that the smallest vertex index is
4785  // in the first place
4786  int irot = find_min(vg, nv_hex);
4787  for (int iv=0; iv<nv_hex; ++iv)
4788  {
4789  int jv = hex_rot[iv + irot*nv_hex];
4790  vg[iv] = v[jv];
4791  }
4792 
4793  int q[nv_quad];
4794  // Bitmask is three binary digits, each digit is 1 if the diagonal of
4795  // the corresponding face goes through the 7th vertex, and 0 if not.
4796  int bitmask = 0;
4797  int j;
4798  // First quad face
4799  for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f0[iv]]]; }
4800  j = find_min(q, nv_quad);
4801  if (j == 0 || j == 2) { bitmask += 4; }
4802  // Second quad face
4803  for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f1[iv]]]; }
4804  j = find_min(q, nv_quad);
4805  if (j == 1 || j == 3) { bitmask += 2; }
4806  // Third quad face
4807  for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f2[iv]]]; }
4808  j = find_min(q, nv_quad);
4809  if (j == 0 || j == 2) { bitmask += 1; }
4810 
4811  // Apply rotations
4812  int nrot = num_rot[bitmask];
4813  for (int k=0; k<nrot; ++k)
4814  {
4815  int vtemp;
4816  vtemp = vg[1];
4817  vg[1] = vg[4];
4818  vg[4] = vg[3];
4819  vg[3] = vtemp;
4820  vtemp = vg[5];
4821  vg[5] = vg[7];
4822  vg[7] = vg[2];
4823  vg[2] = vtemp;
4824  }
4825 
4826  // Sum up nonzero bits in bitmask
4827  int ndiags = ((bitmask&4) >> 2) + ((bitmask&2) >> 1) + (bitmask&1);
4828  int ntets = (ndiags == 0) ? 5 : 6;
4829  const int *tetmap = hex_tetmaps[ndiags];
4830  for (int itet=0; itet<ntets; ++itet)
4831  {
4833  e->SetAttribute(attrib);
4834  int *v2 = e->GetVertices();
4835  for (int iv=0; iv<nv_tet; ++iv)
4836  {
4837  v2[iv] = vg[tetmap[itet + iv*ntets]];
4838  }
4839  AddElement(e);
4840  }
4841  }
4842  }
4843  // In 3D, shrink the element array because some hexes have only 5 tets
4844  if (dim == 3) { elements.SetSize(NumOfElements); }
4845 
4846  for (int i=0; i<nbe; ++i)
4847  {
4848  const int *v = orig_mesh.boundary[i]->GetVertices();
4849  const int attrib = orig_mesh.GetBdrAttribute(i);
4850  const Geometry::Type orig_geom = orig_mesh.GetBdrElementBaseGeometry(i);
4851  if (num_subdivisions[orig_geom] == 1)
4852  {
4853  Element *be = NewElement(orig_geom);
4854  be->SetAttribute(attrib);
4855  be->SetVertices(v);
4856  AddBdrElement(be);
4857  }
4858  else if (orig_geom == Geometry::SQUARE)
4859  {
4860  int vg[nv_quad];
4861  for (int iv=0; iv<nv_quad; ++iv) { vg[iv] = vglobal[v[iv]]; }
4862  // Split quad according the smallest (global) vertex
4863  int iv_min = find_min(vg, nv_quad);
4864  int isplit = (iv_min == 0 || iv_min == 2) ? 0 : 1;
4865  for (int itri=0; itri<quad_ntris; ++itri)
4866  {
4868  be->SetAttribute(attrib);
4869  int *v2 = be->GetVertices();
4870  for (int iv=0; iv<nv_tri; ++iv)
4871  {
4872  v2[iv] = v[quad_trimap[isplit][itri + iv*quad_ntris]];
4873  }
4874  AddBdrElement(be);
4875  }
4876  }
4877  else
4878  {
4879  MFEM_ABORT("Unreachable");
4880  }
4881  }
4882 
4883  FinalizeTopology(false);
4884  sequence = orig_mesh.GetSequence();
4885  last_operation = orig_mesh.last_operation;
4886 
4887  MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
4888  MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
4889 }
4890 
4891 Mesh Mesh::MakePeriodic(const Mesh &orig_mesh, const std::vector<int> &v2v)
4892 {
4893  Mesh periodic_mesh(orig_mesh, true); // Make a copy of the original mesh
4894  const FiniteElementSpace *nodal_fes = orig_mesh.GetNodalFESpace();
4895  int nodal_order = nodal_fes ? nodal_fes->GetMaxElementOrder() : 1;
4896  periodic_mesh.SetCurvature(nodal_order, true);
4897 
4898  // renumber element vertices
4899  for (int i = 0; i < periodic_mesh.GetNE(); i++)
4900  {
4901  Element *el = periodic_mesh.GetElement(i);
4902  int *v = el->GetVertices();
4903  int nv = el->GetNVertices();
4904  for (int j = 0; j < nv; j++)
4905  {
4906  v[j] = v2v[v[j]];
4907  }
4908  }
4909  // renumber boundary element vertices
4910  for (int i = 0; i < periodic_mesh.GetNBE(); i++)
4911  {
4912  Element *el = periodic_mesh.GetBdrElement(i);
4913  int *v = el->GetVertices();
4914  int nv = el->GetNVertices();
4915  for (int j = 0; j < nv; j++)
4916  {
4917  v[j] = v2v[v[j]];
4918  }
4919  }
4920 
4921  periodic_mesh.RemoveUnusedVertices();
4922  return periodic_mesh;
4923 }
4924 
4926  const std::vector<Vector> &translations, double tol) const
4927 {
4928  int sdim = SpaceDimension();
4929 
4930  Vector coord(sdim), at(sdim), dx(sdim);
4931  Vector xMax(sdim), xMin(sdim), xDiff(sdim);
4932  xMax = xMin = xDiff = 0.0;
4933 
4934  // Get a list of all vertices on the boundary
4935  set<int> bdr_v;
4936  for (int be = 0; be < GetNBE(); be++)
4937  {
4938  Array<int> dofs;
4939  GetBdrElementVertices(be,dofs);
4940 
4941  for (int i = 0; i < dofs.Size(); i++)
4942  {
4943  bdr_v.insert(dofs[i]);
4944 
4945  coord = GetVertex(dofs[i]);
4946  for (int j = 0; j < sdim; j++)
4947  {
4948  xMax[j] = max(xMax[j], coord[j]);
4949  xMin[j] = min(xMin[j], coord[j]);
4950  }
4951  }
4952  }
4953  add(xMax, -1.0, xMin, xDiff);
4954  double dia = xDiff.Norml2(); // compute mesh diameter
4955 
4956  // We now identify coincident vertices. Several originally distinct vertices
4957  // may become coincident under the periodic mapping. One of these vertices
4958  // will be identified as the "primary" vertex, and all other coincident
4959  // vertices will be considered as "replicas".
4960 
4961  // replica2primary[v] is the index of the primary vertex of replica `v`
4962  map<int, int> replica2primary;
4963  // primary2replicas[v] is a set of indices of replicas of primary vertex `v`
4964  map<int, set<int>> primary2replicas;
4965 
4966  // We begin with the assumption that all vertices are primary, and that there
4967  // are no replicas.
4968  for (int v : bdr_v) { primary2replicas[v]; }
4969 
4970  // Make `r` and all of `r`'s replicas be replicas of `p`. Delete `r` from the
4971  // list of primary vertices.
4972  auto make_replica = [&replica2primary, &primary2replicas](int r, int p)
4973  {
4974  if (r == p) { return; }
4975  primary2replicas[p].insert(r);
4976  replica2primary[r] = p;
4977  for (int s : primary2replicas[r])
4978  {
4979  primary2replicas[p].insert(s);
4980  replica2primary[s] = p;
4981  }
4982  primary2replicas.erase(r);
4983  };
4984 
4985  for (unsigned int i = 0; i < translations.size(); i++)
4986  {
4987  for (int vi : bdr_v)
4988  {
4989  coord = GetVertex(vi);
4990  add(coord, translations[i], at);
4991 
4992  for (int vj : bdr_v)
4993  {
4994  coord = GetVertex(vj);
4995  add(at, -1.0, coord, dx);
4996  if (dx.Norml2() > dia*tol) { continue; }
4997 
4998  // The two vertices vi and vj are coincident.
4999 
5000  // Are vertices `vi` and `vj` already primary?
5001  bool pi = primary2replicas.find(vi) != primary2replicas.end();
5002  bool pj = primary2replicas.find(vj) != primary2replicas.end();
5003 
5004  if (pi && pj)
5005  {
5006  // Both vertices are currently primary
5007  // Demote `vj` to be a replica of `vi`
5008  make_replica(vj, vi);
5009  }
5010  else if (pi && !pj)
5011  {
5012  // `vi` is primary and `vj` is a replica
5013  int owner_of_vj = replica2primary[vj];
5014  // Make `vi` and its replicas be replicas of `vj`'s owner
5015  make_replica(vi, owner_of_vj);
5016  }
5017  else if (!pi && pj)
5018  {
5019  // `vi` is currently a replica and `vj` is currently primary
5020  // Make `vj` and its replicas be replicas of `vi`'s owner
5021  int owner_of_vi = replica2primary[vi];
5022  make_replica(vj, owner_of_vi);
5023  }
5024  else
5025  {
5026  // Both vertices are currently replicas
5027  // Make `vj`'s owner and all of its owner's replicas be replicas
5028  // of `vi`'s owner
5029  int owner_of_vi = replica2primary[vi];
5030  int owner_of_vj = replica2primary[vj];
5031  make_replica(owner_of_vj, owner_of_vi);
5032  }
5033  break;
5034  }
5035  }
5036  }
5037 
5038  std::vector<int> v2v(GetNV());
5039  for (size_t i = 0; i < v2v.size(); i++)
5040  {
5041  v2v[i] = i;
5042  }
5043  for (auto &&r2p : replica2primary)
5044  {
5045  v2v[r2p.first] = r2p.second;
5046  }
5047  return v2v;
5048 }
5049 
5051 {
5052  if (NURBSext == NULL)
5053  {
5054  mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
5055  }
5056 
5057  if (kv.Size() != NURBSext->GetNKV())
5058  {
5059  mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
5060  }
5061 
5063 
5064  NURBSext->KnotInsert(kv);
5065 
5066  last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
5067  sequence++;
5068 
5069  UpdateNURBS();
5070 }
5071 
5073 {
5074  if (NURBSext == NULL)
5075  {
5076  mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
5077  }
5078 
5079  if (kv.Size() != NURBSext->GetNKV())
5080  {
5081  mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
5082  }
5083 
5085 
5086  NURBSext->KnotInsert(kv);
5087 
5088  last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
5089  sequence++;
5090 
5091  UpdateNURBS();
5092 }
5093 
5095 {
5096  // do not check for NURBSext since this method is protected
5098 
5100 
5101  last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
5102  sequence++;
5103 
5104  UpdateNURBS();
5105 }
5106 
5107 void Mesh::DegreeElevate(int rel_degree, int degree)
5108 {
5109  if (NURBSext == NULL)
5110  {
5111  mfem_error("Mesh::DegreeElevate : Not a NURBS mesh!");
5112  }
5113 
5115 
5116  NURBSext->DegreeElevate(rel_degree, degree);
5117 
5118  last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
5119  sequence++;
5120 
5121  UpdateNURBS();
5122 }
5123 
5125 {
5126  ResetLazyData();
5127 
5129 
5130  Dim = NURBSext->Dimension();
5131  spaceDim = Dim;
5132 
5133  if (NumOfElements != NURBSext->GetNE())
5134  {
5135  for (int i = 0; i < elements.Size(); i++)
5136  {
5137  FreeElement(elements[i]);
5138  }
5141  }
5142 
5143  if (NumOfBdrElements != NURBSext->GetNBE())
5144  {
5145  for (int i = 0; i < boundary.Size(); i++)
5146  {
5147  FreeElement(boundary[i]);
5148  }
5151  }
5152 
5153  Nodes->FESpace()->Update();
5154  Nodes->Update();
5156 
5157  if (NumOfVertices != NURBSext->GetNV())
5158  {
5160  vertices.SetSize(NumOfVertices);
5161  int vd = Nodes->VectorDim();
5162  for (int i = 0; i < vd; i++)
5163  {
5164  Vector vert_val;
5165  Nodes->GetNodalValues(vert_val, i+1);
5166  for (int j = 0; j < NumOfVertices; j++)
5167  {
5168  vertices[j](i) = vert_val(j);
5169  }
5170  }
5171  }
5172 
5173  if (el_to_edge)
5174  {
5176  if (Dim == 2)
5177  {
5178  GenerateFaces();
5179  }
5180  }
5181 
5182  if (el_to_face)
5183  {
5185  GenerateFaces();
5186  }
5187 }
5188 
5189 void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
5190 {
5191  SetEmpty();
5192 
5193  // Read MFEM NURBS mesh v1.0 format
5194  string ident;
5195 
5196  skip_comment_lines(input, '#');
5197 
5198  input >> ident; // 'dimension'
5199  input >> Dim;
5200  spaceDim = Dim;
5201 
5202  skip_comment_lines(input, '#');
5203 
5204  input >> ident; // 'elements'
5205  input >> NumOfElements;
5206  elements.SetSize(NumOfElements);
5207  for (int j = 0; j < NumOfElements; j++)
5208  {
5209  elements[j] = ReadElement(input);
5210  }
5211 
5212  skip_comment_lines(input, '#');
5213 
5214  input >> ident; // 'boundary'
5215  input >> NumOfBdrElements;
5216  boundary.SetSize(NumOfBdrElements);
5217  for (int j = 0; j < NumOfBdrElements; j++)
5218  {
5219  boundary[j] = ReadElement(input);
5220  }
5221 
5222  skip_comment_lines(input, '#');
5223 
5224  input >> ident; // 'edges'
5225  input >> NumOfEdges;
5226  edge_vertex = new Table(NumOfEdges, 2);
5227  edge_to_knot.SetSize(NumOfEdges);
5228  for (int j = 0; j < NumOfEdges; j++)
5229  {
5230  int *v = edge_vertex->GetRow(j);
5231  input >> edge_to_knot[j] >> v[0] >> v[1];
5232  if (v[0] > v[1])
5233  {
5234  edge_to_knot[j] = -1 - edge_to_knot[j];
5235  }
5236  }
5237 
5238  skip_comment_lines(input, '#');
5239 
5240  input >> ident; // 'vertices'
5241  input >> NumOfVertices;
5242  vertices.SetSize(0);
5243 
5244  FinalizeTopology();
5245  CheckBdrElementOrientation(); // check and fix boundary element orientation
5246 }
5247 
5249 {
5250  if (p.Size() >= v.Size())
5251  {
5252  for (int d = 0; d < v.Size(); d++)
5253  {
5254  v(d) = p(d);
5255  }
5256  }
5257  else
5258  {
5259  int d;
5260  for (d = 0; d < p.Size(); d++)
5261  {
5262  v(d) = p(d);
5263  }
5264  for ( ; d < v.Size(); d++)
5265  {
5266  v(d) = 0.0;
5267  }
5268  }
5269 }
5270 
5271 void Mesh::GetNodes(GridFunction &nodes) const
5272 {
5273  if (Nodes == NULL || Nodes->FESpace() != nodes.FESpace())
5274  {
5275  const int newSpaceDim = nodes.FESpace()->GetVDim();
5277  nodes.ProjectCoefficient(xyz);
5278  }
5279  else
5280  {
5281  nodes = *Nodes;
5282  }
5283 }
5284 
5286 {
5287  GridFunction *nodes = new GridFunction(nfes);
5288  SetNodalGridFunction(nodes, true);
5289 }
5290 
5292 {
5293  if (Nodes)
5294  {
5296  if (dynamic_cast<const H1_FECollection*>(fec)
5297  || dynamic_cast<const L2_FECollection*>(fec))
5298  {
5299  return;
5300  }
5301  else // Mesh using a legacy FE_Collection
5302  {
5303  const int order = GetNodalFESpace()->GetElementOrder(0);
5304  if (NURBSext)
5305  {
5306 #ifndef MFEM_USE_MPI
5307  const bool warn = true;
5308 #else
5309  ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
5310  const bool warn = !pmesh || pmesh->GetMyRank() == 0;
5311 #endif
5312  if (warn)
5313  {
5314  MFEM_WARNING("converting NURBS mesh to order " << order <<
5315  " H1-continuous mesh!\n "
5316  "If this is the desired behavior, you can silence"
5317  " this warning by converting\n "
5318  "the NURBS mesh to high-order mesh in advance by"
5319  " calling the method\n "
5320  "Mesh::SetCurvature().");
5321  }
5322  }
5323  SetCurvature(order, false, -1, Ordering::byVDIM);
5324  }
5325  }
5326  else // First order H1 mesh
5327  {
5328  SetCurvature(1, false, -1, Ordering::byVDIM);
5329  }
5330 }
5331 
5332 void Mesh::SetNodalGridFunction(GridFunction *nodes, bool make_owner)
5333 {
5334  GetNodes(*nodes);
5335  NewNodes(*nodes, make_owner);
5336 }
5337 
5339 {
5340  return ((Nodes) ? Nodes->FESpace() : NULL);
5341 }
5342 
5343 void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
5344 {
5345  space_dim = (space_dim == -1) ? spaceDim : space_dim;
5347  if (discont)
5348  {
5349  const int type = 1; // Gauss-Lobatto points
5350  nfec = new L2_FECollection(order, Dim, type);
5351  }
5352  else
5353  {
5354  nfec = new H1_FECollection(order, Dim);
5355  }
5356  FiniteElementSpace* nfes = new FiniteElementSpace(this, nfec, space_dim,
5357  ordering);
5358  SetNodalFESpace(nfes);
5359  Nodes->MakeOwner(nfec);
5360 }
5361 
5363 {
5364  MFEM_ASSERT(nodes != NULL, "");
5365  for (int i = 0; i < spaceDim; i++)
5366  {
5367  Vector vert_val;
5368  nodes->GetNodalValues(vert_val, i+1);
5369  for (int j = 0; j < NumOfVertices; j++)
5370  {
5371  vertices[j](i) = vert_val(j);
5372  }
5373  }
5374 }
5375 
5377 {
5378  switch (Dim)
5379  {
5380  case 1: return GetNV();
5381  case 2: return GetNEdges();
5382  case 3: return GetNFaces();
5383  }
5384  return 0;
5385 }
5386 
5388 {
5389  return faces_info.Size();
5390 }
5391 
5393 {
5394  const bool isInt = type==FaceType::Interior;
5395  int &nf = isInt ? nbInteriorFaces : nbBoundaryFaces;
5396  if (nf<0)
5397  {
5398  nf = 0;
5399  for (int f = 0; f < GetNumFacesWithGhost(); ++f)
5400  {
5402  if ( face.IsOfFaceType(type) )
5403  {
5404  if (face.IsNonconformingCoarse())
5405  {
5406  // We don't count nonconforming coarse faces.
5407  continue;
5408  }
5409  nf++;
5410  }
5411  }
5412  }
5413  return nf;
5414 }
5415 
5416 #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
5417 static const char *fixed_or_not[] = { "fixed", "NOT FIXED" };
5418 #endif
5419 
5421 {
5422  int i, j, k, wo = 0, fo = 0;
5423  double *v[4];
5424 
5425  if (Dim == 2 && spaceDim == 2)
5426  {
5427  DenseMatrix J(2, 2);
5428 
5429  for (i = 0; i < NumOfElements; i++)
5430  {
5431  int *vi = elements[i]->GetVertices();
5432  if (Nodes == NULL)
5433  {
5434  for (j = 0; j < 3; j++)
5435  {
5436  v[j] = vertices[vi[j]]();
5437  }
5438  for (j = 0; j < 2; j++)
5439  for (k = 0; k < 2; k++)
5440  {
5441  J(j, k) = v[j+1][k] - v[0][k];
5442  }
5443  }
5444  else
5445  {
5446  // only check the Jacobian at the center of the element
5447  GetElementJacobian(i, J);
5448  }
5449  if (J.Det() < 0.0)
5450  {
5451  if (fix_it)
5452  {
5453  switch (GetElementType(i))
5454  {
5455  case Element::TRIANGLE:
5456  mfem::Swap(vi[0], vi[1]);
5457  break;
5459  mfem::Swap(vi[1], vi[3]);
5460  break;
5461  default:
5462  MFEM_ABORT("Invalid 2D element type \""
5463  << GetElementType(i) << "\"");
5464  break;
5465  }
5466  fo++;
5467  }
5468  wo++;
5469  }
5470  }
5471  }
5472 
5473  if (Dim == 3)
5474  {
5475  DenseMatrix J(3, 3);
5476 
5477  for (i = 0; i < NumOfElements; i++)
5478  {
5479  int *vi = elements[i]->GetVertices();
5480  switch (GetElementType(i))
5481  {
5482  case Element::TETRAHEDRON:
5483  if (Nodes == NULL)
5484  {
5485  for (j = 0; j < 4; j++)
5486  {
5487  v[j] = vertices[vi[j]]();
5488  }
5489  for (j = 0; j < 3; j++)
5490  for (k = 0; k < 3; k++)
5491  {
5492  J(j, k) = v[j+1][k] - v[0][k];
5493  }
5494  }
5495  else
5496  {
5497  // only check the Jacobian at the center of the element
5498  GetElementJacobian(i, J);
5499  }
5500  if (J.Det() < 0.0)
5501  {
5502  wo++;
5503  if (fix_it)
5504  {
5505  mfem::Swap(vi[0], vi[1]);
5506  fo++;
5507  }
5508  }
5509  break;
5510 
5511  case Element::WEDGE:
5512  // only check the Jacobian at the center of the element
5513  GetElementJacobian(i, J);
5514  if (J.Det() < 0.0)
5515  {
5516  wo++;
5517  if (fix_it)
5518  {
5519  // how?
5520  }
5521  }
5522  break;
5523 
5524  case Element::PYRAMID:
5525  // only check the Jacobian at the center of the element
5526  GetElementJacobian(i, J);
5527  if (J.Det() < 0.0)
5528  {
5529  wo++;
5530  if (fix_it)
5531  {
5532  // how?
5533  }
5534  }
5535  break;
5536 
5537  case Element::HEXAHEDRON:
5538  // only check the Jacobian at the center of the element
5539  GetElementJacobian(i, J);
5540  if (J.Det() < 0.0)
5541  {
5542  wo++;
5543  if (fix_it)
5544  {
5545  // how?
5546  }
5547  }
5548  break;
5549 
5550  default:
5551  MFEM_ABORT("Invalid 3D element type \""
5552  << GetElementType(i) << "\"");
5553  break;
5554  }
5555  }
5556  }
5557 #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
5558  if (wo > 0)
5559  {
5560  mfem::out << "Elements with wrong orientation: " << wo << " / "
5561  << NumOfElements << " (" << fixed_or_not[(wo == fo) ? 0 : 1]
5562  << ")" << endl;
5563  }
5564 #endif
5565  return wo;
5566 }
5567 
5568 int Mesh::GetTriOrientation(const int *base, const int *test)
5569 {
5570  // Static method.
5571  // This function computes the index 'j' of the permutation that transforms
5572  // test into base: test[tri_orientation[j][i]]=base[i].
5573  // tri_orientation = Geometry::Constants<Geometry::TRIANGLE>::Orient
5574  int orient;
5575 
5576  if (test[0] == base[0])
5577  if (test[1] == base[1])
5578  {
5579  orient = 0; // (0, 1, 2)
5580  }
5581  else
5582  {
5583  orient = 5; // (0, 2, 1)
5584  }
5585  else if (test[0] == base[1])
5586  if (test[1] == base[0])
5587  {
5588  orient = 1; // (1, 0, 2)
5589  }
5590  else
5591  {
5592  orient = 2; // (1, 2, 0)
5593  }
5594  else // test[0] == base[2]
5595  if (test[1] == base[0])
5596  {
5597  orient = 4; // (2, 0, 1)
5598  }
5599  else
5600  {
5601  orient = 3; // (2, 1, 0)
5602  }
5603 
5604 #ifdef MFEM_DEBUG
5605  const int *aor = tri_t::Orient[orient];
5606  for (int j = 0; j < 3; j++)
5607  if (test[aor[j]] != base[j])
5608  {
5609  mfem_error("Mesh::GetTriOrientation(...)");
5610  }
5611 #endif
5612 
5613  return orient;
5614 }
5615 
5616 int Mesh::GetQuadOrientation(const int *base, const int *test)
5617 {
5618  int i;
5619 
5620  for (i = 0; i < 4; i++)
5621  if (test[i] == base[0])
5622  {
5623  break;
5624  }
5625 
5626 #ifdef MFEM_DEBUG
5627  int orient;
5628  if (test[(i+1)%4] == base[1])
5629  {
5630  orient = 2*i;
5631  }
5632  else
5633  {
5634  orient = 2*i+1;
5635  }
5636  const int *aor = quad_t::Orient[orient];
5637  for (int j = 0; j < 4; j++)
5638  if (test[aor[j]] != base[j])
5639  {
5640  mfem::err << "Mesh::GetQuadOrientation(...)" << endl;
5641  mfem::err << " base = [";
5642  for (int k = 0; k < 4; k++)
5643  {
5644  mfem::err << " " << base[k];
5645  }
5646  mfem::err << " ]\n test = [";
5647  for (int k = 0; k < 4; k++)
5648  {
5649  mfem::err << " " << test[k];
5650  }
5651  mfem::err << " ]" << endl;
5652  mfem_error();
5653  }
5654 #endif
5655 
5656  if (test[(i+1)%4] == base[1])
5657  {
5658  return 2*i;
5659  }
5660 
5661  return 2*i+1;
5662 }
5663 
5664 int Mesh::GetTetOrientation(const int *base, const int *test)
5665 {
5666  // Static method.
5667  // This function computes the index 'j' of the permutation that transforms
5668  // test into base: test[tet_orientation[j][i]]=base[i].
5669  // tet_orientation = Geometry::Constants<Geometry::TETRAHEDRON>::Orient
5670  int orient;
5671 
5672  if (test[0] == base[0])
5673  if (test[1] == base[1])
5674  if (test[2] == base[2])
5675  {
5676  orient = 0; // (0, 1, 2, 3)
5677  }
5678  else
5679  {
5680  orient = 1; // (0, 1, 3, 2)
5681  }
5682  else if (test[2] == base[1])
5683  if (test[3] == base[2])
5684  {
5685  orient = 2; // (0, 2, 3, 1)
5686  }
5687  else
5688  {
5689  orient = 3; // (0, 2, 1, 3)
5690  }
5691  else // test[3] == base[1]
5692  if (test[1] == base[2])
5693  {
5694  orient = 4; // (0, 3, 1, 2)
5695  }
5696  else
5697  {
5698  orient = 5; // (0, 3, 2, 1)
5699  }
5700  else if (test[1] == base[0])
5701  if (test[2] == base[1])
5702  if (test[0] == base[2])
5703  {
5704  orient = 6; // (1, 2, 0, 3)
5705  }
5706  else
5707  {
5708  orient = 7; // (1, 2, 3, 0)
5709  }
5710  else if (test[3] == base[1])
5711  if (test[2] == base[2])
5712  {
5713  orient = 8; // (1, 3, 2, 0)
5714  }
5715  else
5716  {
5717  orient = 9; // (1, 3, 0, 2)
5718  }
5719  else // test[0] == base[1]
5720  if (test[3] == base[2])
5721  {
5722  orient = 10; // (1, 0, 3, 2)
5723  }
5724  else
5725  {
5726  orient = 11; // (1, 0, 2, 3)
5727  }
5728  else if (test[2] == base[0])
5729  if (test[3] == base[1])
5730  if (test[0] == base[2])
5731  {
5732  orient = 12; // (2, 3, 0, 1)
5733  }
5734  else
5735  {
5736  orient = 13; // (2, 3, 1, 0)
5737  }
5738  else if (test[0] == base[1])
5739  if (test[1] == base[2])
5740  {
5741  orient = 14; // (2, 0, 1, 3)
5742  }
5743  else
5744  {
5745  orient = 15; // (2, 0, 3, 1)
5746  }
5747  else // test[1] == base[1]
5748  if (test[3] == base[2])
5749  {
5750  orient = 16; // (2, 1, 3, 0)
5751  }
5752  else
5753  {
5754  orient = 17; // (2, 1, 0, 3)
5755  }
5756  else // (test[3] == base[0])
5757  if (test[0] == base[1])
5758  if (test[2] == base[2])
5759  {
5760  orient = 18; // (3, 0, 2, 1)
5761  }
5762  else
5763  {
5764  orient = 19; // (3, 0, 1, 2)
5765  }
5766  else if (test[1] == base[1])
5767  if (test[0] == base[2])
5768  {
5769  orient = 20; // (3, 1, 0, 2)
5770  }
5771  else
5772  {
5773  orient = 21; // (3, 1, 2, 0)
5774  }
5775  else // test[2] == base[1]
5776  if (test[1] == base[2])
5777  {
5778  orient = 22; // (3, 2, 1, 0)
5779  }
5780  else
5781  {
5782  orient = 23; // (3, 2, 0, 1)
5783  }
5784 
5785 #ifdef MFEM_DEBUG
5786  const int *aor = tet_t::Orient[orient];
5787  for (int j = 0; j < 4; j++)
5788  if (test[aor[j]] != base[j])
5789  {
5790  mfem_error("Mesh::GetTetOrientation(...)");
5791  }
5792 #endif
5793 
5794  return orient;
5795 }
5796 
5798 {
5799  int wo = 0; // count wrong orientations
5800 
5801  if (Dim == 2)
5802  {
5803  if (el_to_edge == NULL) // edges were not generated
5804  {
5805  el_to_edge = new Table;
5807  GenerateFaces(); // 'Faces' in 2D refers to the edges
5808  }
5809  for (int i = 0; i < NumOfBdrElements; i++)
5810  {
5811  if (faces_info[be_to_edge[i]].Elem2No < 0) // boundary face
5812  {
5813  int *bv = boundary[i]->GetVertices();
5814  int *fv = faces[be_to_edge[i]]->GetVertices();
5815  if (bv[0] != fv[0])
5816  {
5817  if (fix_it)
5818  {
5819  mfem::Swap<int>(bv[0], bv[1]);
5820  }
5821  wo++;
5822  }
5823  }
5824  }
5825  }
5826 
5827  if (Dim == 3)
5828  {
5829  for (int i = 0; i < NumOfBdrElements; i++)
5830  {
5831  const int fi = be_to_face[i];
5832 
5833  if (faces_info[fi].Elem2No >= 0) { continue; }
5834 
5835  // boundary face
5836  int *bv = boundary[i]->GetVertices();
5837  // Make sure the 'faces' are generated:
5838  MFEM_ASSERT(fi < faces.Size(), "internal error");
5839  const int *fv = faces[fi]->GetVertices();
5840  int orientation; // orientation of the bdr. elem. w.r.t. the
5841  // corresponding face element (that's the base)
5842  const Element::Type bdr_type = GetBdrElementType(i);
5843  switch (bdr_type)
5844  {
5845  case Element::TRIANGLE:
5846  {
5847  orientation = GetTriOrientation(fv, bv);
5848  break;
5849  }
5851  {
5852  orientation = GetQuadOrientation(fv, bv);
5853  break;
5854  }
5855  default:
5856  MFEM_ABORT("Invalid 2D boundary element type \""
5857  << bdr_type << "\"");
5858  orientation = 0; // suppress a warning
5859  break;
5860  }
5861 
5862  if (orientation % 2 == 0) { continue; }
5863  wo++;
5864  if (!fix_it) { continue; }
5865 
5866  switch (bdr_type)
5867  {
5868  case Element::TRIANGLE:
5869  {
5870  // swap vertices 0 and 1 so that we don't change the marked edge:
5871  // (0,1,2) -> (1,0,2)
5872  mfem::Swap<int>(bv[0], bv[1]);
5873  if (bel_to_edge)
5874  {
5875  int *be = bel_to_edge->GetRow(i);
5876  mfem::Swap<int>(be[1], be[2]);
5877  }
5878  break;
5879  }
5881  {
5882  mfem::Swap<int>(bv[0], bv[2]);
5883  if (bel_to_edge)
5884  {
5885  int *be = bel_to_edge->GetRow(i);
5886  mfem::Swap<int>(be[0], be[1]);
5887  mfem::Swap<int>(be[2], be[3]);
5888  }
5889  break;
5890  }
5891  default: // unreachable
5892  break;
5893  }
5894  }
5895  }
5896  // #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
5897 #ifdef MFEM_DEBUG
5898  if (wo > 0)
5899  {
5900  mfem::out << "Boundary elements with wrong orientation: " << wo << " / "
5901  << NumOfBdrElements << " (" << fixed_or_not[fix_it ? 0 : 1]
5902  << ")" << endl;
5903  }
5904 #endif
5905  return wo;
5906 }
5907 
5909 {
5910  MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
5911  int num_geoms = 0;
5912  for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
5913  {
5914  if (HasGeometry(Geometry::Type(g))) { num_geoms++; }
5915  }
5916  return num_geoms;
5917 }
5918 
5920 {
5921  MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
5922  el_geoms.SetSize(0);
5923  for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
5924  {
5925  if (HasGeometry(Geometry::Type(g)))
5926  {
5927  el_geoms.Append(Geometry::Type(g));
5928  }
5929  }
5930 }
5931 
5932 void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
5933 {
5934  if (el_to_edge)
5935  {
5936  el_to_edge->GetRow(i, edges);
5937  }
5938  else
5939  {
5940  mfem_error("Mesh::GetElementEdges(...) element to edge table "
5941  "is not generated.");
5942  }
5943 
5944  const int *v = elements[i]->GetVertices();
5945  const int ne = elements[i]->GetNEdges();
5946  cor.SetSize(ne);
5947  for (int j = 0; j < ne; j++)
5948  {
5949  const int *e = elements[i]->GetEdgeVertices(j);
5950  cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
5951  }
5952 }
5953 
5954 void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
5955 {
5956  if (Dim == 2)
5957  {
5958  edges.SetSize(1);
5959  cor.SetSize(1);
5960  edges[0] = be_to_edge[i];
5961  const int *v = boundary[i]->GetVertices();
5962  cor[0] = (v[0] < v[1]) ? (1) : (-1);
5963  }
5964  else if (Dim == 3)
5965  {
5966  if (bel_to_edge)
5967  {
5968  bel_to_edge->GetRow(i, edges);
5969  }
5970  else
5971  {
5972  mfem_error("Mesh::GetBdrElementEdges(...)");
5973  }
5974 
5975  const int *v = boundary[i]->GetVertices();
5976  const int ne = boundary[i]->GetNEdges();
5977  cor.SetSize(ne);
5978  for (int j = 0; j < ne; j++)
5979  {
5980  const int *e = boundary[i]->GetEdgeVertices(j);
5981  cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
5982  }
5983  }
5984 }
5985 
5986 void Mesh::GetFaceEdges(int i, Array<int> &edges, Array<int> &o) const
5987 {
5988  if (Dim == 2)
5989  {
5990  edges.SetSize(1);
5991  edges[0] = i;
5992  o.SetSize(1);
5993  const int *v = faces[i]->GetVertices();
5994  o[0] = (v[0] < v[1]) ? (1) : (-1);
5995  }
5996 
5997  if (Dim != 3)
5998  {
5999  return;
6000  }
6001 
6002  GetFaceEdgeTable(); // generate face_edge Table (if not generated)
6003 
6004  face_edge->GetRow(i, edges);
6005 
6006  const int *v = faces[i]->GetVertices();
6007  const int ne = faces[i]->GetNEdges();
6008  o.SetSize(ne);
6009  for (int j = 0; j < ne; j++)
6010  {
6011  const int *e = faces[i]->GetEdgeVertices(j);
6012  o[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
6013  }
6014 }
6015 
6016 void Mesh::GetEdgeVertices(int i, Array<int> &vert) const
6017 {
6018  // the two vertices are sorted: vert[0] < vert[1]
6019  // this is consistent with the global edge orientation
6020  // generate edge_vertex Table (if not generated)
6021  if (!edge_vertex) { GetEdgeVertexTable(); }
6022  edge_vertex->GetRow(i, vert);
6023 }
6024 
6026 {
6027  if (face_edge)
6028  {
6029  return face_edge;
6030  }
6031 
6032  if (Dim != 3)
6033  {
6034  return NULL;
6035  }
6036 
6037 #ifdef MFEM_DEBUG
6038  if (faces.Size() != NumOfFaces)
6039  {
6040  mfem_error("Mesh::GetFaceEdgeTable : faces were not generated!");
6041  }
6042 #endif
6043 
6044  DSTable v_to_v(NumOfVertices);
6045  GetVertexToVertexTable(v_to_v);
6046 
6047  face_edge = new Table;
6049 
6050  return (face_edge);
6051 }
6052 
6054 {
6055  if (edge_vertex)
6056  {
6057  return edge_vertex;
6058  }
6059 
6060  DSTable v_to_v(NumOfVertices);
6061  GetVertexToVertexTable(v_to_v);
6062 
6063  int nedges = v_to_v.NumberOfEntries();
6064  edge_vertex = new Table(nedges, 2);
6065  for (int i = 0; i < NumOfVertices; i++)
6066  {
6067  for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
6068  {
6069  int j = it.Index();
6070  edge_vertex->Push(j, i);
6071  edge_vertex->Push(j, it.Column());
6072  }
6073  }
6074  edge_vertex->Finalize();
6075 
6076  return edge_vertex;
6077 }
6078 
6080 {
6081  int i, j, nv, *v;
6082 
6083  Table *vert_elem = new Table;
6084 
6085  vert_elem->MakeI(NumOfVertices);
6086 
6087  for (i = 0; i < NumOfElements; i++)
6088  {
6089  nv = elements[i]->GetNVertices();
6090  v = elements[i]->GetVertices();
6091  for (j = 0; j < nv; j++)
6092  {
6093  vert_elem->AddAColumnInRow(v[j]);
6094  }
6095  }
6096 
6097  vert_elem->MakeJ();
6098 
6099  for (i = 0; i < NumOfElements; i++)
6100  {
6101  nv = elements[i]->GetNVertices();
6102  v = elements[i]->GetVertices();
6103  for (j = 0; j < nv; j++)
6104  {
6105  vert_elem->AddConnection(v[j], i);
6106  }
6107  }
6108 
6109  vert_elem->ShiftUpI();
6110 
6111  return vert_elem;
6112 }
6113 
6115 {
6116  Table *face_elem = new Table;
6117 
6118  face_elem->MakeI(faces_info.Size());
6119 
6120  for (int i = 0; i < faces_info.Size(); i++)
6121  {
6122  if (faces_info[i].Elem2No >= 0)
6123  {
6124  face_elem->AddColumnsInRow(i, 2);
6125  }
6126  else
6127  {
6128  face_elem->AddAColumnInRow(i);
6129  }
6130  }
6131 
6132  face_elem->MakeJ();
6133 
6134  for (int i = 0; i < faces_info.Size(); i++)
6135  {
6136  face_elem->AddConnection(i, faces_info[i].Elem1No);
6137  if (faces_info[i].Elem2No >= 0)
6138  {
6139  face_elem->AddConnection(i, faces_info[i].Elem2No);
6140  }
6141  }
6142 
6143  face_elem->ShiftUpI();
6144 
6145  return face_elem;
6146 }
6147 
6148 void Mesh::GetElementFaces(int i, Array<int> &el_faces, Array<int> &ori) const
6149 {
6150  MFEM_VERIFY(el_to_face != NULL, "el_to_face not generated");
6151 
6152  el_to_face->GetRow(i, el_faces);
6153 
6154  int n = el_faces.Size();
6155  ori.SetSize(n);
6156 
6157  for (int j = 0; j < n; j++)
6158  {
6159  if (faces_info[el_faces[j]].Elem1No == i)
6160  {
6161  ori[j] = faces_info[el_faces[j]].Elem1Inf % 64;
6162  }
6163  else
6164  {
6165  MFEM_ASSERT(faces_info[el_faces[j]].Elem2No == i, "internal error");
6166  ori[j] = faces_info[el_faces[j]].Elem2Inf % 64;
6167  }
6168  }
6169 }
6170 
6171 void Mesh::GetBdrElementFace(int i, int *f, int *o) const
6172 {
6173  const int *bv, *fv;
6174 
6175  *f = be_to_face[i];
6176  bv = boundary[i]->GetVertices();
6177  fv = faces[be_to_face[i]]->GetVertices();
6178 
6179  // find the orientation of the bdr. elem. w.r.t.
6180  // the corresponding face element (that's the base)
6181  switch (GetBdrElementType(i))
6182  {
6183  case Element::TRIANGLE:
6184  *o = GetTriOrientation(fv, bv);
6185  break;
6187  *o = GetQuadOrientation(fv, bv);
6188  break;
6189  default:
6190  MFEM_ABORT("invalid geometry");
6191  }
6192 }
6193 
6195 {
6196  switch (Dim)
6197  {
6198  case 1: return boundary[i]->GetVertices()[0];
6199  case 2: return be_to_edge[i];
6200  case 3: return be_to_face[i];
6201  default: MFEM_ABORT("invalid dimension!");
6202  }
6203  return -1;
6204 }
6205 
6206 void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
6207 {
6208  int fid = GetBdrElementEdgeIndex(bdr_el);
6209 
6210  const FaceInfo &fi = faces_info[fid];
6211  MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
6212 
6213  const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
6214  const int *bv = boundary[bdr_el]->GetVertices();
6215  int ori;
6216  switch (GetBdrElementGeometry(bdr_el))
6217  {
6218  case Geometry::POINT: ori = 0; break;
6219  case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
6220  case Geometry::TRIANGLE: ori = GetTriOrientation(fv, bv); break;
6221  case Geometry::SQUARE: ori = GetQuadOrientation(fv, bv); break;
6222  default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
6223  }
6224  el = fi.Elem1No;
6225  info = fi.Elem1Inf + ori;
6226 }
6227 
6228 void Mesh::GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const
6229 {
6230  int fid = GetBdrElementEdgeIndex(bdr_el);
6231 
6232  const FaceInfo &fi = faces_info[fid];
6233  MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
6234 
6235  const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
6236  const int *bv = boundary[bdr_el]->GetVertices();
6237  int ori;
6238  switch (GetBdrElementGeometry(bdr_el))
6239  {
6240  case Geometry::POINT: ori = 0; break;
6241  case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
6242  case Geometry::TRIANGLE: ori = GetTriOrientation(bv, fv); break;
6243  case Geometry::SQUARE: ori = GetQuadOrientation(bv, fv); break;
6244  default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
6245  }
6246  el = fi.Elem1No;
6247  info = fi.Elem1Inf + ori;
6248 }
6249 
6251 {
6252  return elements[i]->GetType();
6253 }
6254 
6256 {
6257  return boundary[i]->GetType();
6258 }
6259 
6260 void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
6261 {
6262  int k, j, nv;
6263  const int *v;
6264 
6265  v = elements[i]->GetVertices();
6266  nv = elements[i]->GetNVertices();
6267 
6268  pointmat.SetSize(spaceDim, nv);
6269  for (k = 0; k < spaceDim; k++)
6270  {
6271  for (j = 0; j < nv; j++)
6272  {
6273  pointmat(k, j) = vertices[v[j]](k);
6274  }
6275  }
6276 }
6277 
6278 void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
6279 {
6280  int k, j, nv;
6281  const int *v;
6282 
6283  v = boundary[i]->GetVertices();
6284  nv = boundary[i]->GetNVertices();
6285 
6286  pointmat.SetSize(spaceDim, nv);
6287  for (k = 0; k < spaceDim; k++)
6288  for (j = 0; j < nv; j++)
6289  {
6290  pointmat(k, j) = vertices[v[j]](k);
6291  }
6292 }
6293 
6294 double Mesh::GetLength(int i, int j) const
6295 {
6296  const double *vi = vertices[i]();
6297  const double *vj = vertices[j]();
6298  double length = 0.;
6299 
6300  for (int k = 0; k < spaceDim; k++)
6301  {
6302  length += (vi[k]-vj[k])*(vi[k]-vj[k]);
6303  }
6304 
6305  return sqrt(length);
6306 }
6307 
6308 // static method
6310  const DSTable &v_to_v, Table &el_to_edge)
6311 {
6312  el_to_edge.MakeI(elem_array.Size());
6313  for (int i = 0; i < elem_array.Size(); i++)
6314  {
6315  el_to_edge.AddColumnsInRow(i, elem_array[i]->GetNEdges());
6316  }
6317  el_to_edge.MakeJ();
6318  for (int i = 0; i < elem_array.Size(); i++)
6319  {
6320  const int *v = elem_array[i]->GetVertices();
6321  const int ne = elem_array[i]->GetNEdges();
6322  for (int j = 0; j < ne; j++)
6323  {
6324  const int *e = elem_array[i]->GetEdgeVertices(j);
6325  el_to_edge.AddConnection(i, v_to_v(v[e[0]], v[e[1]]));
6326  }
6327  }
6328  el_to_edge.ShiftUpI();
6329 }
6330 
6332 {
6333  if (edge_vertex)
6334  {
6335  for (int i = 0; i < edge_vertex->Size(); i++)
6336  {
6337  const int *v = edge_vertex->GetRow(i);
6338  v_to_v.Push(v[0], v[1]);
6339  }
6340  }
6341  else
6342  {
6343  for (int i = 0; i < NumOfElements; i++)
6344  {
6345  const int *v = elements[i]->GetVertices();
6346  const int ne = elements[i]->GetNEdges();
6347  for (int j = 0; j < ne; j++)
6348  {
6349  const int *e = elements[i]->GetEdgeVertices(j);
6350  v_to_v.Push(v[e[0]], v[e[1]]);
6351  }
6352  }
6353  }
6354 }
6355 
6357 {
6358  int i, NumberOfEdges;
6359 
6360  DSTable v_to_v(NumOfVertices);
6361  GetVertexToVertexTable(v_to_v);
6362 
6363  NumberOfEdges = v_to_v.NumberOfEntries();
6364 
6365  // Fill the element to edge table
6366  GetElementArrayEdgeTable(elements, v_to_v, e_to_f);
6367 
6368  if (Dim == 2)
6369  {
6370  // Initialize the indices for the boundary elements.
6371  be_to_f.SetSize(NumOfBdrElements);
6372  for (i = 0; i < NumOfBdrElements; i++)
6373  {
6374  const int *v = boundary[i]->GetVertices();
6375  be_to_f[i] = v_to_v(v[0], v[1]);
6376  }
6377  }
6378  else if (Dim == 3)
6379  {
6380  if (bel_to_edge == NULL)
6381  {
6382  bel_to_edge = new Table;
6383  }
6385  }
6386  else
6387  {
6388  mfem_error("1D GetElementToEdgeTable is not yet implemented.");
6389  }
6390 
6391  // Return the number of edges
6392  return NumberOfEdges;
6393 }
6394 
6396 {
6397  if (el_to_el)
6398  {
6399  return *el_to_el;
6400  }
6401 
6402  // Note that, for ParNCMeshes, faces_info will contain also the ghost faces
6403  MFEM_ASSERT(faces_info.Size() >= GetNumFaces(), "faces were not generated!");
6404 
6405  Array<Connection> conn;
6406  conn.Reserve(2*faces_info.Size());
6407 
6408  for (int i = 0; i < faces_info.Size(); i++)
6409  {
6410  const FaceInfo &fi = faces_info[i];
6411  if (fi.Elem2No >= 0)
6412  {
6413  conn.Append(Connection(fi.Elem1No, fi.Elem2No));
6414  conn.Append(Connection(fi.Elem2No, fi.Elem1No));
6415  }
6416  else if (fi.Elem2Inf >= 0)
6417  {
6418  int nbr_elem_idx = NumOfElements - 1 - fi.Elem2No;
6419  conn.Append(Connection(fi.Elem1No, nbr_elem_idx));
6420  conn.Append(Connection(nbr_elem_idx, fi.Elem1No));
6421  }
6422  }
6423 
6424  conn.Sort();
6425  conn.Unique();
6426  el_to_el = new Table(NumOfElements, conn);
6427 
6428  return *el_to_el;
6429 }
6430 
6432 {
6433  if (el_to_face == NULL)
6434  {
6435  mfem_error("Mesh::ElementToFaceTable()");
6436  }
6437  return *el_to_face;
6438 }
6439 
6441 {
6442  if (el_to_edge == NULL)
6443  {
6444  mfem_error("Mesh::ElementToEdgeTable()");
6445  }
6446  return *el_to_edge;
6447 }
6448 
6449 void Mesh::AddPointFaceElement(int lf, int gf, int el)
6450 {
6451  if (faces_info[gf].Elem1No == -1) // this will be elem1
6452  {
6453  // faces[gf] = new Point(&gf);
6454  faces_info[gf].Elem1No = el;
6455  faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
6456  faces_info[gf].Elem2No = -1; // in case there's no other side
6457  faces_info[gf].Elem2Inf = -1; // face is not shared
6458  }
6459  else // this will be elem2
6460  {
6461  /* WARNING: Without the following check the mesh faces_info data structure
6462  may contain unreliable data. Normally, the order in which elements are
6463  processed could swap which elements appear as Elem1No and Elem2No. In
6464  branched meshes, where more than two elements can meet at a given node,
6465  the indices stored in Elem1No and Elem2No will be the first and last,
6466  respectively, elements found which touch a given node. This can lead to
6467  inconsistencies in any algorithms which rely on this data structure. To
6468  properly support branched meshes this data structure should be extended
6469  to support multiple elements per face. */
6470  /*
6471  MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
6472  "Interior point found connecting 1D elements "
6473  << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
6474