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