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