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