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