MFEM  v4.4.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
mesh_readers.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "mesh_headers.hpp"
13 #include "../fem/fem.hpp"
14 #include "../general/binaryio.hpp"
15 #include "../general/text.hpp"
16 #include "../general/tinyxml2.h"
17 #include "gmsh.hpp"
18 
19 #include <iostream>
20 #include <cstdio>
21 #include <vector>
22 #include <algorithm>
23 
24 #ifdef MFEM_USE_NETCDF
25 #include "netcdf.h"
26 #endif
27 
28 #ifdef MFEM_USE_ZLIB
29 #include <zlib.h>
30 #endif
31 
32 using namespace std;
33 
34 namespace mfem
35 {
36 
37 bool Mesh::remove_unused_vertices = true;
38 
39 void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
40 {
41  // Read MFEM mesh v1.0 or v1.2 format
42  MFEM_VERIFY(version == 10 || version == 12,
43  "unknown MFEM mesh version");
44 
45  string ident;
46 
47  // read lines beginning with '#' (comments)
48  skip_comment_lines(input, '#');
49  input >> ident; // 'dimension'
50 
51  MFEM_VERIFY(ident == "dimension", "invalid mesh file");
52  input >> Dim;
53 
54  skip_comment_lines(input, '#');
55  input >> ident; // 'elements'
56 
57  MFEM_VERIFY(ident == "elements", "invalid mesh file");
58  input >> NumOfElements;
59  elements.SetSize(NumOfElements);
60  for (int j = 0; j < NumOfElements; j++)
61  {
62  elements[j] = ReadElement(input);
63  }
64 
65  skip_comment_lines(input, '#');
66  input >> ident; // 'boundary'
67 
68  MFEM_VERIFY(ident == "boundary", "invalid mesh file");
69  input >> NumOfBdrElements;
70  boundary.SetSize(NumOfBdrElements);
71  for (int j = 0; j < NumOfBdrElements; j++)
72  {
73  boundary[j] = ReadElement(input);
74  }
75 
76  skip_comment_lines(input, '#');
77  input >> ident; // 'vertices'
78 
79  MFEM_VERIFY(ident == "vertices", "invalid mesh file");
80  input >> NumOfVertices;
81  vertices.SetSize(NumOfVertices);
82 
83  input >> ws >> ident;
84  if (ident != "nodes")
85  {
86  // read the vertices
87  spaceDim = atoi(ident.c_str());
88  for (int j = 0; j < NumOfVertices; j++)
89  {
90  for (int i = 0; i < spaceDim; i++)
91  {
92  input >> vertices[j](i);
93  }
94  }
95  }
96  else
97  {
98  // prepare to read the nodes
99  input >> ws;
100  curved = 1;
101  }
102 
103  // When visualizing solutions on non-conforming grids, PETSc
104  // may dump additional vertices
105  if (remove_unused_vertices) { RemoveUnusedVertices(); }
106 }
107 
108 void Mesh::ReadLineMesh(std::istream &input)
109 {
110  int j,p1,p2,a;
111 
112  Dim = 1;
113 
114  input >> NumOfVertices;
115  vertices.SetSize(NumOfVertices);
116  // Sets vertices and the corresponding coordinates
117  for (j = 0; j < NumOfVertices; j++)
118  {
119  input >> vertices[j](0);
120  }
121 
122  input >> NumOfElements;
123  elements.SetSize(NumOfElements);
124  // Sets elements and the corresponding indices of vertices
125  for (j = 0; j < NumOfElements; j++)
126  {
127  input >> a >> p1 >> p2;
128  elements[j] = new Segment(p1-1, p2-1, a);
129  }
130 
131  int ind[1];
132  input >> NumOfBdrElements;
133  boundary.SetSize(NumOfBdrElements);
134  for (j = 0; j < NumOfBdrElements; j++)
135  {
136  input >> a >> ind[0];
137  ind[0]--;
138  boundary[j] = new Point(ind,a);
139  }
140 }
141 
142 void Mesh::ReadNetgen2DMesh(std::istream &input, int &curved)
143 {
144  int ints[32], attr, n;
145 
146  // Read planar mesh in Netgen format.
147  Dim = 2;
148 
149  // Read the boundary elements.
150  input >> NumOfBdrElements;
151  boundary.SetSize(NumOfBdrElements);
152  for (int i = 0; i < NumOfBdrElements; i++)
153  {
154  input >> attr
155  >> ints[0] >> ints[1];
156  ints[0]--; ints[1]--;
157  boundary[i] = new Segment(ints, attr);
158  }
159 
160  // Read the elements.
161  input >> NumOfElements;
162  elements.SetSize(NumOfElements);
163  for (int i = 0; i < NumOfElements; i++)
164  {
165  input >> attr >> n;
166  for (int j = 0; j < n; j++)
167  {
168  input >> ints[j];
169  ints[j]--;
170  }
171  switch (n)
172  {
173  case 2:
174  elements[i] = new Segment(ints, attr);
175  break;
176  case 3:
177  elements[i] = new Triangle(ints, attr);
178  break;
179  case 4:
180  elements[i] = new Quadrilateral(ints, attr);
181  break;
182  }
183  }
184 
185  if (!curved)
186  {
187  // Read the vertices.
188  input >> NumOfVertices;
189  vertices.SetSize(NumOfVertices);
190  for (int i = 0; i < NumOfVertices; i++)
191  for (int j = 0; j < Dim; j++)
192  {
193  input >> vertices[i](j);
194  }
195  }
196  else
197  {
198  input >> NumOfVertices;
199  vertices.SetSize(NumOfVertices);
200  input >> ws;
201  }
202 }
203 
204 void Mesh::ReadNetgen3DMesh(std::istream &input)
205 {
206  int ints[32], attr;
207 
208  // Read a Netgen format mesh of tetrahedra.
209  Dim = 3;
210 
211  // Read the vertices
212  input >> NumOfVertices;
213 
214  vertices.SetSize(NumOfVertices);
215  for (int i = 0; i < NumOfVertices; i++)
216  for (int j = 0; j < Dim; j++)
217  {
218  input >> vertices[i](j);
219  }
220 
221  // Read the elements
222  input >> NumOfElements;
223  elements.SetSize(NumOfElements);
224  for (int i = 0; i < NumOfElements; i++)
225  {
226  input >> attr;
227  for (int j = 0; j < 4; j++)
228  {
229  input >> ints[j];
230  ints[j]--;
231  }
232 #ifdef MFEM_USE_MEMALLOC
233  Tetrahedron *tet;
234  tet = TetMemory.Alloc();
235  tet->SetVertices(ints);
236  tet->SetAttribute(attr);
237  elements[i] = tet;
238 #else
239  elements[i] = new Tetrahedron(ints, attr);
240 #endif
241  }
242 
243  // Read the boundary information.
244  input >> NumOfBdrElements;
245  boundary.SetSize(NumOfBdrElements);
246  for (int i = 0; i < NumOfBdrElements; i++)
247  {
248  input >> attr;
249  for (int j = 0; j < 3; j++)
250  {
251  input >> ints[j];
252  ints[j]--;
253  }
254  boundary[i] = new Triangle(ints, attr);
255  }
256 }
257 
258 void Mesh::ReadTrueGridMesh(std::istream &input)
259 {
260  int i, j, ints[32], attr;
261  const int buflen = 1024;
262  char buf[buflen];
263 
264  // TODO: find the actual dimension
265  Dim = 3;
266 
267  if (Dim == 2)
268  {
269  int vari;
270  double varf;
271 
272  input >> vari >> NumOfVertices >> vari >> vari >> NumOfElements;
273  input.getline(buf, buflen);
274  input.getline(buf, buflen);
275  input >> vari;
276  input.getline(buf, buflen);
277  input.getline(buf, buflen);
278  input.getline(buf, buflen);
279 
280  // Read the vertices.
281  vertices.SetSize(NumOfVertices);
282  for (i = 0; i < NumOfVertices; i++)
283  {
284  input >> vari >> varf >> vertices[i](0) >> vertices[i](1);
285  input.getline(buf, buflen);
286  }
287 
288  // Read the elements.
289  elements.SetSize(NumOfElements);
290  for (i = 0; i < NumOfElements; i++)
291  {
292  input >> vari >> attr;
293  for (j = 0; j < 4; j++)
294  {
295  input >> ints[j];
296  ints[j]--;
297  }
298  input.getline(buf, buflen);
299  input.getline(buf, buflen);
300  elements[i] = new Quadrilateral(ints, attr);
301  }
302  }
303  else if (Dim == 3)
304  {
305  int vari;
306  double varf;
307  input >> vari >> NumOfVertices >> NumOfElements;
308  input.getline(buf, buflen);
309  input.getline(buf, buflen);
310  input >> vari >> vari >> NumOfBdrElements;
311  input.getline(buf, buflen);
312  input.getline(buf, buflen);
313  input.getline(buf, buflen);
314  // Read the vertices.
315  vertices.SetSize(NumOfVertices);
316  for (i = 0; i < NumOfVertices; i++)
317  {
318  input >> vari >> varf >> vertices[i](0) >> vertices[i](1)
319  >> vertices[i](2);
320  input.getline(buf, buflen);
321  }
322  // Read the elements.
323  elements.SetSize(NumOfElements);
324  for (i = 0; i < NumOfElements; i++)
325  {
326  input >> vari >> attr;
327  for (j = 0; j < 8; j++)
328  {
329  input >> ints[j];
330  ints[j]--;
331  }
332  input.getline(buf, buflen);
333  elements[i] = new Hexahedron(ints, attr);
334  }
335  // Read the boundary elements.
336  boundary.SetSize(NumOfBdrElements);
337  for (i = 0; i < NumOfBdrElements; i++)
338  {
339  input >> attr;
340  for (j = 0; j < 4; j++)
341  {
342  input >> ints[j];
343  ints[j]--;
344  }
345  input.getline(buf, buflen);
346  boundary[i] = new Quadrilateral(ints, attr);
347  }
348  }
349 }
350 
351 // see Tetrahedron::edges
352 const int Mesh::vtk_quadratic_tet[10] =
353 { 0, 1, 2, 3, 4, 7, 5, 6, 8, 9 };
354 
355 // see Pyramid::edges & Mesh::GenerateFaces
356 // https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
357 const int Mesh::vtk_quadratic_pyramid[13] =
358 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
359 
360 // see Wedge::edges & Mesh::GenerateFaces
361 // https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
362 const int Mesh::vtk_quadratic_wedge[18] =
363 { 0, 2, 1, 3, 5, 4, 8, 7, 6, 11, 10, 9, 12, 14, 13, 17, 16, 15};
364 
365 // see Hexahedron::edges & Mesh::GenerateFaces
366 const int Mesh::vtk_quadratic_hex[27] =
367 {
368  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
369  24, 22, 21, 23, 20, 25, 26
370 };
371 
372 void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
373  const Array<int> &cell_offsets,
374  const Array<int> &cell_types,
375  const Array<int> &cell_attributes,
376  int &curved, int &read_gf, bool &finalize_topo)
377 {
378  int np = points.Size()/3;
379  Dim = -1;
380  NumOfElements = cell_types.Size();
381  elements.SetSize(NumOfElements);
382 
383  int order = -1;
384  bool legacy_elem = false, lagrange_elem = false;
385 
386  for (int i = 0; i < NumOfElements; i++)
387  {
388  int j = (i > 0) ? cell_offsets[i-1] : 0;
389  int ct = cell_types[i];
390  Geometry::Type geom = VTKGeometry::GetMFEMGeometry(ct);
391  elements[i] = NewElement(geom);
392  if (cell_attributes.Size() > 0)
393  {
394  elements[i]->SetAttribute(cell_attributes[i]);
395  }
396  // VTK ordering of vertices is the same as MFEM ordering of vertices
397  // for all element types *except* prisms, which require a permutation
398  if (geom == Geometry::PRISM && ct != VTKGeometry::LAGRANGE_PRISM)
399  {
400  int prism_vertices[6];
401  for (int k=0; k<6; ++k)
402  {
403  prism_vertices[k] = cell_data[j+VTKGeometry::PrismMap[k]];
404  }
405  elements[i]->SetVertices(prism_vertices);
406  }
407  else
408  {
409  elements[i]->SetVertices(&cell_data[j]);
410  }
411 
412  int elem_dim = Geometry::Dimension[geom];
413  int elem_order = VTKGeometry::GetOrder(ct, cell_offsets[i] - j);
414 
415  if (VTKGeometry::IsLagrange(ct)) { lagrange_elem = true; }
416  else { legacy_elem = true; }
417 
418  MFEM_VERIFY(Dim == -1 || Dim == elem_dim,
419  "Elements with different dimensions are not supported");
420  MFEM_VERIFY(order == -1 || order == elem_order,
421  "Elements with different orders are not supported");
422  MFEM_VERIFY(legacy_elem != lagrange_elem,
423  "Mixing of legacy and Lagrange cell types is not supported");
424  Dim = elem_dim;
425  order = elem_order;
426  }
427 
428  // determine spaceDim based on min/max differences detected each dimension
429  spaceDim = 0;
430  if (np > 0)
431  {
432  double min_value, max_value;
433  for (int d = 3; d > 0; --d)
434  {
435  min_value = max_value = points(3*0 + d-1);
436  for (int i = 1; i < np; i++)
437  {
438  min_value = std::min(min_value, points(3*i + d-1));
439  max_value = std::max(max_value, points(3*i + d-1));
440  if (min_value != max_value)
441  {
442  spaceDim = d;
443  break;
444  }
445  }
446  if (spaceDim > 0) { break; }
447  }
448  }
449 
450  if (order == 1 && !lagrange_elem)
451  {
452  NumOfVertices = np;
453  vertices.SetSize(np);
454  for (int i = 0; i < np; i++)
455  {
456  vertices[i](0) = points(3*i+0);
457  vertices[i](1) = points(3*i+1);
458  vertices[i](2) = points(3*i+2);
459  }
460  // No boundary is defined in a VTK mesh
461  NumOfBdrElements = 0;
462  FinalizeTopology();
463  CheckElementOrientation(true);
464  }
465  else
466  {
467  // The following section of code is shared for legacy quadratic and the
468  // Lagrange high order elements
469  curved = 1;
470 
471  // generate new enumeration for the vertices
472  Array<int> pts_dof(np);
473  pts_dof = -1;
474  // mark vertex points
475  for (int i = 0; i < NumOfElements; i++)
476  {
477  int *v = elements[i]->GetVertices();
478  int nv = elements[i]->GetNVertices();
479  for (int j = 0; j < nv; j++)
480  {
481  if (pts_dof[v[j]] == -1) { pts_dof[v[j]] = 0; }
482  }
483  }
484 
485  // The following loop reorders pts_dofs so vertices are visited in
486  // canonical order
487 
488  // Keep the original ordering of the vertices
489  NumOfVertices = 0;
490  for (int i = 0; i < np; i++)
491  {
492  if (pts_dof[i] != -1)
493  {
494  pts_dof[i] = NumOfVertices++;
495  }
496  }
497  // update the element vertices
498  for (int i = 0; i < NumOfElements; i++)
499  {
500  int *v = elements[i]->GetVertices();
501  int nv = elements[i]->GetNVertices();
502  for (int j = 0; j < nv; j++)
503  {
504  v[j] = pts_dof[v[j]];
505  }
506  }
507  // Define the 'vertices' from the 'points' through the 'pts_dof' map
508  vertices.SetSize(NumOfVertices);
509  for (int i = 0; i < np; i++)
510  {
511  int j = pts_dof[i];
512  if (j != -1)
513  {
514  vertices[j](0) = points(3*i+0);
515  vertices[j](1) = points(3*i+1);
516  vertices[j](2) = points(3*i+2);
517  }
518  }
519 
520  // No boundary is defined in a VTK mesh
521  NumOfBdrElements = 0;
522 
523  // Generate faces and edges so that we can define FE space on the mesh
524  FinalizeTopology();
525 
527  FiniteElementSpace *fes;
528  if (legacy_elem)
529  {
530  // Define quadratic FE space
531  fec = new QuadraticFECollection;
532  fes = new FiniteElementSpace(this, fec, spaceDim);
533  Nodes = new GridFunction(fes);
534  Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
535  own_nodes = 1;
536 
537  // Map vtk points to edge/face/element dofs
538  Array<int> dofs;
539  for (int i = 0; i < NumOfElements; i++)
540  {
541  fes->GetElementDofs(i, dofs);
542  const int *vtk_mfem;
543  switch (elements[i]->GetGeometryType())
544  {
545  case Geometry::TRIANGLE:
546  case Geometry::SQUARE:
547  vtk_mfem = vtk_quadratic_hex; break; // identity map
548  case Geometry::TETRAHEDRON:
549  vtk_mfem = vtk_quadratic_tet; break;
550  case Geometry::CUBE:
551  vtk_mfem = vtk_quadratic_hex; break;
552  case Geometry::PRISM:
553  vtk_mfem = vtk_quadratic_wedge; break;
554  case Geometry::PYRAMID:
555  vtk_mfem = vtk_quadratic_pyramid; break;
556  default:
557  vtk_mfem = NULL; // suppress a warning
558  break;
559  }
560 
561  int offset = (i == 0) ? 0 : cell_offsets[i-1];
562  for (int j = 0; j < dofs.Size(); j++)
563  {
564  if (pts_dof[cell_data[offset+j]] == -1)
565  {
566  pts_dof[cell_data[offset+j]] = dofs[vtk_mfem[j]];
567  }
568  else
569  {
570  if (pts_dof[cell_data[offset+j]] != dofs[vtk_mfem[j]])
571  {
572  MFEM_ABORT("VTK mesh: inconsistent quadratic mesh!");
573  }
574  }
575  }
576  }
577  }
578  else
579  {
580  // Define H1 FE space
581  fec = new H1_FECollection(order,Dim,BasisType::ClosedUniform);
582  fes = new FiniteElementSpace(this, fec, spaceDim);
583  Nodes = new GridFunction(fes);
584  Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
585  own_nodes = 1;
586  Array<int> dofs;
587 
588  std::map<Geometry::Type,Array<int>> vtk_inv_maps;
589  std::map<Geometry::Type,const Array<int>*> lex_orderings;
590 
591  int i, n;
592  for (n = i = 0; i < NumOfElements; i++)
593  {
594  Geometry::Type geom = GetElementBaseGeometry(i);
595  fes->GetElementDofs(i, dofs);
596 
597  Array<int> &vtk_inv_map = vtk_inv_maps[geom];
598  if (vtk_inv_map.Size() == 0)
599  {
600  Array<int> vtk_map;
601  CreateVTKElementConnectivity(vtk_map, geom, order);
602  vtk_inv_map.SetSize(vtk_map.Size());
603  for (int j=0; j<vtk_map.Size(); ++j)
604  {
605  vtk_inv_map[vtk_map[j]] = j;
606  }
607  }
608  const Array<int> *&lex_ordering = lex_orderings[geom];
609  if (!lex_ordering)
610  {
611  const FiniteElement *fe = fes->GetFE(i);
612  const NodalFiniteElement *nodal_fe =
613  dynamic_cast<const NodalFiniteElement*>(fe);
614  MFEM_ASSERT(nodal_fe != NULL, "Unsupported element type");
615  lex_ordering = &nodal_fe->GetLexicographicOrdering();
616  }
617 
618  for (int lex_idx = 0; lex_idx < dofs.Size(); lex_idx++)
619  {
620  int mfem_idx = (*lex_ordering)[lex_idx];
621  int vtk_idx = vtk_inv_map[lex_idx];
622  int pt_idx = cell_data[n + vtk_idx];
623  if (pts_dof[pt_idx] == -1)
624  {
625  pts_dof[pt_idx] = dofs[mfem_idx];
626  }
627  else
628  {
629  if (pts_dof[pt_idx] != dofs[mfem_idx])
630  {
631  MFEM_ABORT("VTK mesh: inconsistent Lagrange mesh!");
632  }
633  }
634  }
635  n += dofs.Size();
636  }
637  }
638  // Define the 'Nodes' from the 'points' through the 'pts_dof' map
639  Array<int> dofs;
640  for (int i = 0; i < np; i++)
641  {
642  dofs.SetSize(1);
643  if (pts_dof[i] != -1)
644  {
645  dofs[0] = pts_dof[i];
646  fes->DofsToVDofs(dofs);
647  for (int d = 0; d < dofs.Size(); d++)
648  {
649  (*Nodes)(dofs[d]) = points(3*i+d);
650  }
651  }
652  }
653  read_gf = 0;
654  }
655 }
656 
657 namespace vtk_xml
658 {
659 
660 using namespace tinyxml2;
661 
662 /// Return false if either string is NULL or if the strings differ, return true
663 /// if the strings are the same.
664 bool StringCompare(const char *s1, const char *s2)
665 {
666  if (s1 == NULL || s2 == NULL) { return false; }
667  return strcmp(s1, s2) == 0;
668 }
669 
670 /// Abstract base class for reading continguous arrays of (potentially
671 /// compressed, potentially base-64 encoded) binary data from a buffer into a
672 /// destination array. The types of the source and destination arrays may be
673 /// different (e.g. read data of type uint8_t into destination array of
674 /// uint32_t), which is handled by the templated derived class @a BufferReader.
675 struct BufferReaderBase
676 {
677  enum HeaderType { UINT32_HEADER, UINT64_HEADER };
678  virtual void ReadBinary(const char *buf, void *dest, int n) const = 0;
679  virtual void ReadBase64(const char *txt, void *dest, int n) const = 0;
680  virtual ~BufferReaderBase() { }
681 };
682 
683 /// Read an array of source data stored as (potentially compressed, potentially
684 /// base-64 encoded) into a destination array. The types of the elements in the
685 /// source array are given by template parameter @a F ("from") and the types of
686 /// the elements of the destination array are given by @a T ("to"). The binary
687 /// data has a header, which is one integer if the data is uncompressed, and is
688 /// four integers if the data is compressed. The integers may either by uint32_t
689 /// or uint64_t, according to the @a header_type. If the data is compressed and
690 /// base-64 encoded, then the header is encoded separately from the data. If the
691 /// data is uncompressed and base-64 encoded, then the header and data are
692 /// encoded together.
693 template <typename T, typename F>
694 struct BufferReader : BufferReaderBase
695 {
696  bool compressed;
697  HeaderType header_type;
698  BufferReader(bool compressed_, HeaderType header_type_)
699  : compressed(compressed_), header_type(header_type_) { }
700 
701  /// Return the number of bytes of each header entry.
702  size_t HeaderEntrySize() const
703  {
704  return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t);
705  }
706 
707  /// Return the value of the header entry pointer to by @a header_buf. The
708  /// value is stored as either uint32_t or uint64_t, according to the @a
709  /// header_type, and is returned as uint64_t.
710  uint64_t ReadHeaderEntry(const char *header_buf) const
711  {
712  return (header_type == UINT64_HEADER) ? bin_io::read<uint64_t>(header_buf)
713  : bin_io::read<uint32_t>(header_buf);
714  }
715 
716  /// Return the number of bytes in the header. The header consists of one
717  /// integer if the data is uncompressed, and @a N + 3 integers if the data is
718  /// compressed, where @a N is the number of blocks. The integers are either
719  /// 32 or 64 bytes depending on the value of @a header_type. The number of
720  /// blocks is determined by reading the first integer (of type @a
721  /// header_type) pointed to by @a header_buf.
722  int NumHeaderBytes(const char *header_buf) const
723  {
724  if (!compressed) { return HeaderEntrySize(); }
725  return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize();
726  }
727 
728  /// Read @a n elements of type @a F from the source buffer @a buf into the
729  /// (pre-allocated) destination array of elements of type @a T stored in
730  /// the buffer @a dest_void. The header is stored @b separately from the
731  /// rest of the data, in the buffer @a header_buf. The data buffer @a buf
732  /// does @b not contain a header.
733  void ReadBinaryWithHeader(const char *header_buf, const char *buf,
734  void *dest_void, int n) const
735  {
736  std::vector<char> uncompressed_data;
737  T *dest = static_cast<T*>(dest_void);
738 
739  if (compressed)
740  {
741 #ifdef MFEM_USE_ZLIB
742  // The header has format (where header_t is uint32_t or uint64_t):
743  // header_t number_of_blocks;
744  // header_t uncompressed_block_size;
745  // header_t uncompressed_last_block_size;
746  // header_t compressed_size[number_of_blocks];
747  int header_entry_size = HeaderEntrySize();
748  int nblocks = ReadHeaderEntry(header_buf);
749  header_buf += header_entry_size;
750  std::vector<int> header(nblocks + 2);
751  for (int i=0; i<nblocks+2; ++i)
752  {
753  header[i] = ReadHeaderEntry(header_buf);
754  header_buf += header_entry_size;
755  }
756  uncompressed_data.resize((nblocks-1)*header[0] + header[1]);
757  Bytef *dest_ptr = (Bytef *)uncompressed_data.data();
758  Bytef *dest_start = dest_ptr;
759  const Bytef *source_ptr = (const Bytef *)buf;
760  for (int i=0; i<nblocks; ++i)
761  {
762  uLongf source_len = header[i+2];
763  uLong dest_len = (i == nblocks-1) ? header[1] : header[0];
764  int res = uncompress(dest_ptr, &dest_len, source_ptr, source_len);
765  MFEM_VERIFY(res == Z_OK, "Error uncompressing");
766  dest_ptr += dest_len;
767  source_ptr += source_len;
768  }
769  MFEM_VERIFY(int(sizeof(F)*n) == (dest_ptr - dest_start),
770  "AppendedData: wrong data size");
771  buf = uncompressed_data.data();
772 #else
773  MFEM_ABORT("MFEM must be compiled with zlib enabled to uncompress.")
774 #endif
775  }
776  else
777  {
778  // Each "data block" is preceded by a header that is either UInt32 or
779  // UInt64. The rest of the data follows.
780  uint64_t data_size;
781  if (header_type == UINT32_HEADER)
782  {
783  uint32_t *data_size_32 = (uint32_t *)header_buf;
784  data_size = *data_size_32;
785  }
786  else
787  {
788  uint64_t *data_size_64 = (uint64_t *)header_buf;
789  data_size = *data_size_64;
790  }
791  MFEM_VERIFY(sizeof(F)*n == data_size, "AppendedData: wrong data size");
792  }
793 
794  if (std::is_same<T, F>::value)
795  {
796  // Special case: no type conversions necessary, so can just memcpy
797  memcpy(dest, buf, sizeof(T)*n);
798  }
799  else
800  {
801  for (int i=0; i<n; ++i)
802  {
803  // Read binary data as type F, place in array as type T
804  dest[i] = bin_io::read<F>(buf + i*sizeof(F));
805  }
806  }
807  }
808 
809  /// Read @a n elements of type @a F from source buffer @a buf into
810  /// (pre-allocated) array of elements of type @a T stored in destination
811  /// buffer @a dest. The input buffer contains both the header and the data.
812  void ReadBinary(const char *buf, void *dest, int n) const override
813  {
814  ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n);
815  }
816 
817  /// Read @a n elements of type @a F from base-64 encoded source buffer into
818  /// (pre-allocated) array of elements of type @a T stored in destination
819  /// buffer @a dest. The base-64-encoded data is given by the null-terminated
820  /// string @a txt, which contains both the header and the data.
821  void ReadBase64(const char *txt, void *dest, int n) const override
822  {
823  // Skip whitespace
824  while (*txt)
825  {
826  if (*txt != ' ' && *txt != '\n') { break; }
827  ++txt;
828  }
829  if (compressed)
830  {
831  // Decode the first entry of the header, which we need to determine
832  // how long the rest of the header is.
833  std::vector<char> nblocks_buf;
834  int nblocks_b64 = bin_io::NumBase64Chars(HeaderEntrySize());
835  bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf);
836  std::vector<char> data, header;
837  // Compute number of characters needed to encode header in base 64,
838  // then round to nearest multiple of 4 to take padding into account.
839  int header_b64 = bin_io::NumBase64Chars(NumHeaderBytes(nblocks_buf.data()));
840  // If data is compressed, header is encoded separately
841  bin_io::DecodeBase64(txt, header_b64, header);
842  bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data);
843  ReadBinaryWithHeader(header.data(), data.data(), dest, n);
844  }
845  else
846  {
847  std::vector<char> data;
848  bin_io::DecodeBase64(txt, strlen(txt), data);
849  ReadBinary(data.data(), dest, n);
850  }
851  }
852 };
853 
854 /// Class to read data from VTK's @a DataArary elements. Each @a DataArray can
855 /// contain inline ASCII data, inline base-64-encoded data (potentially
856 /// compressed), or reference "appended data", which may be raw or base-64, and
857 /// may be compressed or uncompressed.
858 struct XMLDataReader
859 {
860  const char *appended_data, *byte_order, *compressor;
861  enum AppendedDataEncoding { RAW, BASE64 };
862  map<string,BufferReaderBase*> type_map;
863  AppendedDataEncoding encoding;
864 
865  /// Create the data reader, where @a vtk is the @a VTKFile XML element, and
866  /// @a vtu is the child @a UnstructuredGrid XML element. This will determine
867  /// the header type (32 or 64 bit integers) and whether compression is
868  /// enabled or not. The appended data will be loaded.
869  XMLDataReader(const XMLElement *vtk, const XMLElement *vtu)
870  {
871  // Determine whether binary data header is 32 or 64 bit integer
872  BufferReaderBase::HeaderType htype;
873  if (StringCompare(vtk->Attribute("header_type"), "UInt64"))
874  {
875  htype = BufferReaderBase::UINT64_HEADER;
876  }
877  else
878  {
879  htype = BufferReaderBase::UINT32_HEADER;
880  }
881 
882  // Get the byte order of the file (will check if we encounter binary data)
883  byte_order = vtk->Attribute("byte_order");
884 
885  // Get the compressor. We will check that MFEM can handle the compression
886  // if we encounter binary data.
887  compressor = vtk->Attribute("compressor");
888  bool compressed = (compressor != NULL);
889 
890  // Find the appended data.
891  appended_data = NULL;
892  for (const XMLElement *xml_elem = vtu->NextSiblingElement();
893  xml_elem != NULL;
894  xml_elem = xml_elem->NextSiblingElement())
895  {
896  if (StringCompare(xml_elem->Name(), "AppendedData"))
897  {
898  const char *encoding_str = xml_elem->Attribute("encoding");
899  if (StringCompare(encoding_str, "raw"))
900  {
901  appended_data = xml_elem->GetAppendedData();
902  encoding = RAW;
903  }
904  else if (StringCompare(encoding_str, "base64"))
905  {
906  appended_data = xml_elem->GetText();
907  encoding = BASE64;
908  }
909  MFEM_VERIFY(appended_data != NULL, "Invalid AppendedData");
910  // Appended data follows first underscore
911  bool found_leading_underscore = false;
912  while (*appended_data)
913  {
914  ++appended_data;
915  if (*appended_data == '_')
916  {
917  found_leading_underscore = true;
918  ++appended_data;
919  break;
920  }
921  }
922  MFEM_VERIFY(found_leading_underscore, "Invalid AppendedData");
923  break;
924  }
925  }
926 
927  type_map["Int8"] = new BufferReader<int, int8_t>(compressed, htype);
928  type_map["Int16"] = new BufferReader<int, int16_t>(compressed, htype);
929  type_map["Int32"] = new BufferReader<int, int32_t>(compressed, htype);
930  type_map["Int64"] = new BufferReader<int, int64_t>(compressed, htype);
931  type_map["UInt8"] = new BufferReader<int, uint8_t>(compressed, htype);
932  type_map["UInt16"] = new BufferReader<int, uint16_t>(compressed, htype);
933  type_map["UInt32"] = new BufferReader<int, uint32_t>(compressed, htype);
934  type_map["UInt64"] = new BufferReader<int, uint64_t>(compressed, htype);
935  type_map["Float32"] = new BufferReader<double, float>(compressed, htype);
936  type_map["Float64"] = new BufferReader<double, double>(compressed, htype);
937  }
938 
939  /// Read the @a DataArray XML element given by @a xml_elem into
940  /// (pre-allocated) destination array @a dest, where @a dest stores @a n
941  /// elements of type @a T.
942  template <typename T>
943  void Read(const XMLElement *xml_elem, T *dest, int n)
944  {
945  static const char *erstr = "Error reading XML DataArray";
946  MFEM_VERIFY(StringCompare(xml_elem->Name(), "DataArray"), erstr);
947  const char *format = xml_elem->Attribute("format");
948  if (StringCompare(format, "ascii"))
949  {
950  const char *txt = xml_elem->GetText();
951  MFEM_VERIFY(txt != NULL, erstr);
952  std::istringstream data_stream(txt);
953  for (int i=0; i<n; ++i) { data_stream >> dest[i]; }
954  }
955  else if (StringCompare(format, "appended"))
956  {
957  VerifyBinaryOptions();
958  int offset = xml_elem->IntAttribute("offset");
959  const char *type = xml_elem->Attribute("type");
960  MFEM_VERIFY(type != NULL, erstr);
961  BufferReaderBase *reader = type_map[type];
962  MFEM_VERIFY(reader != NULL, erstr);
963  MFEM_VERIFY(appended_data != NULL, "No AppendedData found");
964  if (encoding == RAW)
965  {
966  reader->ReadBinary(appended_data + offset, dest, n);
967  }
968  else
969  {
970  reader->ReadBase64(appended_data + offset, dest, n);
971  }
972  }
973  else if (StringCompare(format, "binary"))
974  {
975  VerifyBinaryOptions();
976  const char *txt = xml_elem->GetText();
977  MFEM_VERIFY(txt != NULL, erstr);
978  const char *type = xml_elem->Attribute("type");
979  if (type == NULL) { MFEM_ABORT(erstr); }
980  BufferReaderBase *reader = type_map[type];
981  if (reader == NULL) { MFEM_ABORT(erstr); }
982  reader->ReadBase64(txt, dest, n);
983  }
984  else
985  {
986  MFEM_ABORT("Invalid XML VTK DataArray format");
987  }
988  }
989 
990  /// Check that the byte order of the file is the same as the native byte
991  /// order that we're running with. We don't currently support converting
992  /// between byte orders. The byte order is only verified if we encounter
993  /// binary data.
994  void VerifyByteOrder() const
995  {
996  // Can't handle reading big endian from little endian or vice versa
997  if (byte_order && !StringCompare(byte_order, VTKByteOrder()))
998  {
999  MFEM_ABORT("Converting between different byte orders is unsupported.");
1000  }
1001  }
1002 
1003  /// Check that the compressor is compatible (MFEM currently only supports
1004  /// zlib compression). If MFEM is not compiled with zlib, then we cannot
1005  /// read binary data with compression.
1006  void VerifyCompressor() const
1007  {
1008  if (compressor && !StringCompare(compressor, "vtkZLibDataCompressor"))
1009  {
1010  MFEM_ABORT("Unsupported compressor. Only zlib is supported.")
1011  }
1012 #ifndef MFEM_USE_ZLIB
1013  MFEM_VERIFY(compressor == NULL, "MFEM must be compiled with zlib enabled "
1014  "to support reading compressed data.");
1015 #endif
1016  }
1017 
1018  /// Verify that the binary data is stored with compatible options (i.e.
1019  /// native byte order and compatible compression).
1020  void VerifyBinaryOptions() const
1021  {
1022  VerifyByteOrder();
1023  VerifyCompressor();
1024  }
1025 
1026  ~XMLDataReader()
1027  {
1028  for (auto &x : type_map) { delete x.second; }
1029  }
1030 };
1031 
1032 } // namespace vtk_xml
1033 
1034 void Mesh::ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf,
1035  bool &finalize_topo, const std::string &xml_prefix)
1036 {
1037  using namespace vtk_xml;
1038 
1039  static const char *erstr = "XML parsing error";
1040 
1041  // Create buffer beginning with xml_prefix, then read the rest of the stream
1042  std::vector<char> buf(xml_prefix.begin(), xml_prefix.end());
1043  std::istreambuf_iterator<char> eos;
1044  buf.insert(buf.end(), std::istreambuf_iterator<char>(input), eos);
1045  buf.push_back('\0'); // null-terminate buffer
1046 
1047  XMLDocument xml;
1048  xml.Parse(buf.data(), buf.size());
1049  if (xml.ErrorID() != XML_SUCCESS)
1050  {
1051  MFEM_ABORT("Error parsing XML VTK file.\n" << xml.ErrorStr());
1052  }
1053 
1054  const XMLElement *vtkfile = xml.FirstChildElement();
1055  MFEM_VERIFY(vtkfile, erstr);
1056  MFEM_VERIFY(StringCompare(vtkfile->Name(), "VTKFile"), erstr);
1057  const XMLElement *vtu = vtkfile->FirstChildElement();
1058  MFEM_VERIFY(vtu, erstr);
1059  MFEM_VERIFY(StringCompare(vtu->Name(), "UnstructuredGrid"), erstr);
1060 
1061  XMLDataReader data_reader(vtkfile, vtu);
1062 
1063  // Count the number of points and cells
1064  const XMLElement *piece = vtu->FirstChildElement();
1065  MFEM_VERIFY(StringCompare(piece->Name(), "Piece"), erstr);
1066  MFEM_VERIFY(piece->NextSiblingElement() == NULL,
1067  "XML VTK meshes with more than one Piece are not supported");
1068  int npts = piece->IntAttribute("NumberOfPoints");
1069  int ncells = piece->IntAttribute("NumberOfCells");
1070 
1071  // Read the points
1072  Vector points(3*npts);
1073  const XMLElement *pts_xml;
1074  for (pts_xml = piece->FirstChildElement();
1075  pts_xml != NULL;
1076  pts_xml = pts_xml->NextSiblingElement())
1077  {
1078  if (StringCompare(pts_xml->Name(), "Points"))
1079  {
1080  const XMLElement *pts_data = pts_xml->FirstChildElement();
1081  MFEM_VERIFY(pts_data->IntAttribute("NumberOfComponents") == 3,
1082  "XML VTK Points DataArray must have 3 components");
1083  data_reader.Read(pts_data, points.GetData(), points.Size());
1084  break;
1085  }
1086  }
1087  if (pts_xml == NULL) { MFEM_ABORT(erstr); }
1088 
1089  // Read the cells
1090  Array<int> cell_data, cell_offsets(ncells), cell_types(ncells);
1091  const XMLElement *cells_xml;
1092  for (cells_xml = piece->FirstChildElement();
1093  cells_xml != NULL;
1094  cells_xml = cells_xml->NextSiblingElement())
1095  {
1096  if (StringCompare(cells_xml->Name(), "Cells"))
1097  {
1098  const XMLElement *cell_data_xml = NULL;
1099  for (const XMLElement *data_xml = cells_xml->FirstChildElement();
1100  data_xml != NULL;
1101  data_xml = data_xml->NextSiblingElement())
1102  {
1103  const char *data_name = data_xml->Attribute("Name");
1104  if (StringCompare(data_name, "offsets"))
1105  {
1106  data_reader.Read(data_xml, cell_offsets.GetData(), ncells);
1107  }
1108  else if (StringCompare(data_name, "types"))
1109  {
1110  data_reader.Read(data_xml, cell_types.GetData(), ncells);
1111  }
1112  else if (StringCompare(data_name, "connectivity"))
1113  {
1114  // Have to read the connectivity after the offsets, because we
1115  // don't know how many points to read until we have the offsets
1116  // (size of connectivity array is equal to the last offset), so
1117  // store the XML element pointer and read this data later.
1118  cell_data_xml = data_xml;
1119  }
1120  }
1121  MFEM_VERIFY(cell_data_xml != NULL, erstr);
1122  int cell_data_size = cell_offsets.Last();
1123  cell_data.SetSize(cell_data_size);
1124  data_reader.Read(cell_data_xml, cell_data.GetData(), cell_data_size);
1125  break;
1126  }
1127  }
1128  if (cells_xml == NULL) { MFEM_ABORT(erstr); }
1129 
1130  // Read the element attributes, which are stored as CellData named "material"
1131  Array<int> cell_attributes;
1132  for (const XMLElement *cell_data_xml = piece->FirstChildElement();
1133  cell_data_xml != NULL;
1134  cell_data_xml = cell_data_xml->NextSiblingElement())
1135  {
1136  if (StringCompare(cell_data_xml->Name(), "CellData")
1137  && StringCompare(cell_data_xml->Attribute("Scalars"), "material"))
1138  {
1139  const XMLElement *data_xml = cell_data_xml->FirstChildElement();
1140  if (data_xml != NULL && StringCompare(data_xml->Name(), "DataArray"))
1141  {
1142  cell_attributes.SetSize(ncells);
1143  data_reader.Read(data_xml, cell_attributes.GetData(), ncells);
1144  }
1145  }
1146  }
1147 
1148  CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1149  curved, read_gf, finalize_topo);
1150 }
1151 
1152 void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
1153  bool &finalize_topo)
1154 {
1155  // VTK resources:
1156  // * https://www.vtk.org/doc/nightly/html/vtkCellType_8h_source.html
1157  // * https://www.vtk.org/doc/nightly/html/classvtkCell.html
1158  // * https://lorensen.github.io/VTKExamples/site/VTKFileFormats
1159  // * https://www.kitware.com/products/books/VTKUsersGuide.pdf
1160 
1161  string buff;
1162  getline(input, buff); // comment line
1163  getline(input, buff);
1164  filter_dos(buff);
1165  if (buff != "ASCII")
1166  {
1167  MFEM_ABORT("VTK mesh is not in ASCII format!");
1168  return;
1169  }
1170  do
1171  {
1172  getline(input, buff);
1173  filter_dos(buff);
1174  if (!input.good()) { MFEM_ABORT("VTK mesh is not UNSTRUCTURED_GRID!"); }
1175  }
1176  while (buff != "DATASET UNSTRUCTURED_GRID");
1177 
1178  // Read the points, skipping optional sections such as the FIELD data from
1179  // VisIt's VTK export (or from Mesh::PrintVTK with field_data==1).
1180  do
1181  {
1182  input >> buff;
1183  if (!input.good())
1184  {
1185  MFEM_ABORT("VTK mesh does not have POINTS data!");
1186  }
1187  }
1188  while (buff != "POINTS");
1189 
1190  Vector points;
1191  int np;
1192  input >> np >> ws;
1193  getline(input, buff); // "double"
1194  points.Load(input, 3*np);
1195 
1196  //skip metadata
1197  // Looks like:
1198  // METADATA
1199  //INFORMATION 2
1200  //NAME L2_NORM_RANGE LOCATION vtkDataArray
1201  //DATA 2 0 5.19615
1202  //NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray
1203  //DATA 2 0 5.19615
1204  do
1205  {
1206  input >> buff;
1207  if (!input.good())
1208  {
1209  MFEM_ABORT("VTK mesh does not have CELLS data!");
1210  }
1211  }
1212  while (buff != "CELLS");
1213 
1214  // Read the cells
1215  Array<int> cell_data, cell_offsets;
1216  if (buff == "CELLS")
1217  {
1218  int ncells, n;
1219  input >> ncells >> n >> ws;
1220  cell_offsets.SetSize(ncells);
1221  cell_data.SetSize(n - ncells);
1222  int offset = 0;
1223  for (int i=0; i<ncells; ++i)
1224  {
1225  int nv;
1226  input >> nv;
1227  cell_offsets[i] = offset + nv;
1228  for (int j=0; j<nv; ++j)
1229  {
1230  input >> cell_data[offset + j];
1231  }
1232  offset += nv;
1233  }
1234  }
1235 
1236  // Read the cell types
1237  input >> ws >> buff;
1238  Array<int> cell_types;
1239  int ncells;
1240  MFEM_VERIFY(buff == "CELL_TYPES", "CELL_TYPES not provided in VTK mesh.")
1241  input >> ncells;
1242  cell_types.Load(ncells, input);
1243 
1244  while ((input.good()) && (buff != "CELL_DATA"))
1245  {
1246  input >> buff;
1247  }
1248  getline(input, buff); // finish the line
1249 
1250  // Read the cell materials
1251  // bool found_material = false;
1252  Array<int> cell_attributes;
1253  while ((input.good()))
1254  {
1255  getline(input, buff);
1256  if (buff.rfind("POINT_DATA") == 0)
1257  {
1258  break; // We have entered the POINT_DATA block. Quit.
1259  }
1260  else if (buff.rfind("SCALARS material") == 0)
1261  {
1262  getline(input, buff); // LOOKUP_TABLE default
1263  if (buff.rfind("LOOKUP_TABLE default") != 0)
1264  {
1265  MFEM_ABORT("Invalid LOOKUP_TABLE for material array in VTK file.");
1266  }
1267  cell_attributes.Load(ncells, input);
1268  // found_material = true;
1269  break;
1270  }
1271  }
1272 
1273  // if (!found_material)
1274  // {
1275  // MFEM_WARNING("Material array not found in VTK file. "
1276  // "Assuming uniform material composition.");
1277  // }
1278 
1279  CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1280  curved, read_gf, finalize_topo);
1281 } // end ReadVTKMesh
1282 
1283 void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf)
1284 {
1285  NURBSext = new NURBSExtension(input);
1286 
1287  Dim = NURBSext->Dimension();
1288  NumOfVertices = NURBSext->GetNV();
1289  NumOfElements = NURBSext->GetNE();
1290  NumOfBdrElements = NURBSext->GetNBE();
1291 
1292  NURBSext->GetElementTopo(elements);
1293  NURBSext->GetBdrElementTopo(boundary);
1294 
1295  vertices.SetSize(NumOfVertices);
1296  curved = 1;
1297  if (NURBSext->HavePatches())
1298  {
1299  NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
1300  FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
1301  Ordering::byVDIM);
1302  Nodes = new GridFunction(fes);
1303  Nodes->MakeOwner(fec);
1304  NURBSext->SetCoordsFromPatches(*Nodes);
1305  own_nodes = 1;
1306  read_gf = 0;
1307  spaceDim = Nodes->VectorDim();
1308  for (int i = 0; i < spaceDim; i++)
1309  {
1310  Vector vert_val;
1311  Nodes->GetNodalValues(vert_val, i+1);
1312  for (int j = 0; j < NumOfVertices; j++)
1313  {
1314  vertices[j](i) = vert_val(j);
1315  }
1316  }
1317  }
1318  else
1319  {
1320  read_gf = 1;
1321  }
1322 }
1323 
1324 void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
1325 {
1326  // Initialize to negative numbers so that we know if they've been set. We're
1327  // using Element::POINT as our flag, since we're not going to make a 0D mesh,
1328  // ever.
1329  int nx = -1;
1330  int ny = -1;
1331  int nz = -1;
1332  double sx = -1.0;
1333  double sy = -1.0;
1334  double sz = -1.0;
1335  Element::Type type = Element::POINT;
1336 
1337  while (true)
1338  {
1339  skip_comment_lines(input, '#');
1340  // Break out if we reached the end of the file after gobbling up the
1341  // whitespace and comments after the last keyword.
1342  if (!input.good())
1343  {
1344  break;
1345  }
1346 
1347  // Read the next keyword
1348  std::string name;
1349  input >> name;
1350  input >> std::ws;
1351  // Make sure there's an equal sign
1352  MFEM_VERIFY(input.get() == '=',
1353  "Inline mesh expected '=' after keyword " << name);
1354  input >> std::ws;
1355 
1356  if (name == "nx")
1357  {
1358  input >> nx;
1359  }
1360  else if (name == "ny")
1361  {
1362  input >> ny;
1363  }
1364  else if (name == "nz")
1365  {
1366  input >> nz;
1367  }
1368  else if (name == "sx")
1369  {
1370  input >> sx;
1371  }
1372  else if (name == "sy")
1373  {
1374  input >> sy;
1375  }
1376  else if (name == "sz")
1377  {
1378  input >> sz;
1379  }
1380  else if (name == "type")
1381  {
1382  std::string eltype;
1383  input >> eltype;
1384  if (eltype == "segment")
1385  {
1386  type = Element::SEGMENT;
1387  }
1388  else if (eltype == "quad")
1389  {
1390  type = Element::QUADRILATERAL;
1391  }
1392  else if (eltype == "tri")
1393  {
1394  type = Element::TRIANGLE;
1395  }
1396  else if (eltype == "hex")
1397  {
1398  type = Element::HEXAHEDRON;
1399  }
1400  else if (eltype == "wedge")
1401  {
1402  type = Element::WEDGE;
1403  }
1404  else if (eltype == "pyramid")
1405  {
1406  type = Element::PYRAMID;
1407  }
1408  else if (eltype == "tet")
1409  {
1410  type = Element::TETRAHEDRON;
1411  }
1412  else
1413  {
1414  MFEM_ABORT("unrecognized element type (read '" << eltype
1415  << "') in inline mesh format. "
1416  "Allowed: segment, tri, quad, tet, hex, wedge");
1417  }
1418  }
1419  else
1420  {
1421  MFEM_ABORT("unrecognized keyword (" << name
1422  << ") in inline mesh format. "
1423  "Allowed: nx, ny, nz, type, sx, sy, sz");
1424  }
1425 
1426  input >> std::ws;
1427  // Allow an optional semi-colon at the end of each line.
1428  if (input.peek() == ';')
1429  {
1430  input.get();
1431  }
1432 
1433  // Done reading file
1434  if (!input)
1435  {
1436  break;
1437  }
1438  }
1439 
1440  // Now make the mesh.
1441  if (type == Element::SEGMENT)
1442  {
1443  MFEM_VERIFY(nx > 0 && sx > 0.0,
1444  "invalid 1D inline mesh format, all values must be "
1445  "positive\n"
1446  << " nx = " << nx << "\n"
1447  << " sx = " << sx << "\n");
1448  Make1D(nx, sx);
1449  }
1450  else if (type == Element::TRIANGLE || type == Element::QUADRILATERAL)
1451  {
1452  MFEM_VERIFY(nx > 0 && ny > 0 && sx > 0.0 && sy > 0.0,
1453  "invalid 2D inline mesh format, all values must be "
1454  "positive\n"
1455  << " nx = " << nx << "\n"
1456  << " ny = " << ny << "\n"
1457  << " sx = " << sx << "\n"
1458  << " sy = " << sy << "\n");
1459  Make2D(nx, ny, type, sx, sy, generate_edges, true);
1460  }
1461  else if (type == Element::TETRAHEDRON || type == Element::WEDGE ||
1462  type == Element::HEXAHEDRON || type == Element::PYRAMID)
1463  {
1464  MFEM_VERIFY(nx > 0 && ny > 0 && nz > 0 &&
1465  sx > 0.0 && sy > 0.0 && sz > 0.0,
1466  "invalid 3D inline mesh format, all values must be "
1467  "positive\n"
1468  << " nx = " << nx << "\n"
1469  << " ny = " << ny << "\n"
1470  << " nz = " << nz << "\n"
1471  << " sx = " << sx << "\n"
1472  << " sy = " << sy << "\n"
1473  << " sz = " << sz << "\n");
1474  Make3D(nx, ny, nz, type, sx, sy, sz, true);
1475  // TODO: maybe have an option in the file to control ordering?
1476  }
1477  else
1478  {
1479  MFEM_ABORT("For inline mesh, must specify an element type ="
1480  " [segment, tri, quad, tet, hex, wedge]");
1481  }
1482 }
1483 
1484 void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
1485 {
1486  string buff;
1487  double version;
1488  int binary, dsize;
1489  input >> version >> binary >> dsize;
1490  if (version < 2.2)
1491  {
1492  MFEM_ABORT("Gmsh file version < 2.2");
1493  }
1494  if (dsize != sizeof(double))
1495  {
1496  MFEM_ABORT("Gmsh file : dsize != sizeof(double)");
1497  }
1498  getline(input, buff);
1499  // There is a number 1 in binary format
1500  if (binary)
1501  {
1502  int one;
1503  input.read(reinterpret_cast<char*>(&one), sizeof(one));
1504  if (one != 1)
1505  {
1506  MFEM_ABORT("Gmsh file : wrong binary format");
1507  }
1508  }
1509 
1510  // A map between a serial number of the vertex and its number in the file
1511  // (there may be gaps in the numbering, and also Gmsh enumerates vertices
1512  // starting from 1, not 0)
1513  map<int, int> vertices_map;
1514 
1515  // Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the
1516  // mesh element dimension (Dim) and the dimension of the space in which the
1517  // mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and
1518  // spaceDim = 2, while a 2D surface mesh in 3D has Dim = 2 but spaceDim = 3.
1519  // Below we set spaceDim by measuring the mesh bounding box and checking for
1520  // a lower dimensional subspace. The assumption is that the mesh is at least
1521  // 2D if the y-dimension of the box is non-trivial and 3D if the z-dimension
1522  // is non-trivial. Note that with these assumptions a 2D mesh parallel to the
1523  // yz plane will be considered a surface mesh embedded in 3D whereas the same
1524  // 2D mesh parallel to the xy plane will be considered a 2D mesh.
1525  double bb_tol = 1e-14;
1526  double bb_min[3];
1527  double bb_max[3];
1528 
1529  // Mesh order
1530  int mesh_order = 1;
1531 
1532  // Mesh type
1533  bool periodic = false;
1534 
1535  // Vector field to store uniformly spaced Gmsh high order mesh coords
1536  GridFunction Nodes_gf;
1537 
1538  // Read the lines of the mesh file. If we face specific keyword, we'll treat
1539  // the section.
1540  while (input >> buff)
1541  {
1542  if (buff == "$Nodes") // reading mesh vertices
1543  {
1544  input >> NumOfVertices;
1545  getline(input, buff);
1546  vertices.SetSize(NumOfVertices);
1547  int serial_number;
1548  const int gmsh_dim = 3; // Gmsh always outputs 3 coordinates
1549  double coord[gmsh_dim];
1550  for (int ver = 0; ver < NumOfVertices; ++ver)
1551  {
1552  if (binary)
1553  {
1554  input.read(reinterpret_cast<char*>(&serial_number), sizeof(int));
1555  input.read(reinterpret_cast<char*>(coord), gmsh_dim*sizeof(double));
1556  }
1557  else // ASCII
1558  {
1559  input >> serial_number;
1560  for (int ci = 0; ci < gmsh_dim; ++ci)
1561  {
1562  input >> coord[ci];
1563  }
1564  }
1565  vertices[ver] = Vertex(coord, gmsh_dim);
1566  vertices_map[serial_number] = ver;
1567 
1568  for (int ci = 0; ci < gmsh_dim; ++ci)
1569  {
1570  bb_min[ci] = (ver == 0) ? coord[ci] :
1571  std::min(bb_min[ci], coord[ci]);
1572  bb_max[ci] = (ver == 0) ? coord[ci] :
1573  std::max(bb_max[ci], coord[ci]);
1574  }
1575  }
1576  double bb_size = std::max(bb_max[0] - bb_min[0],
1577  std::max(bb_max[1] - bb_min[1],
1578  bb_max[2] - bb_min[2]));
1579  spaceDim = 1;
1580  if (bb_max[1] - bb_min[1] > bb_size * bb_tol)
1581  {
1582  spaceDim++;
1583  }
1584  if (bb_max[2] - bb_min[2] > bb_size * bb_tol)
1585  {
1586  spaceDim++;
1587  }
1588 
1589  if (static_cast<int>(vertices_map.size()) != NumOfVertices)
1590  {
1591  MFEM_ABORT("Gmsh file : vertices indices are not unique");
1592  }
1593  } // section '$Nodes'
1594  else if (buff == "$Elements") // reading mesh elements
1595  {
1596  int num_of_all_elements;
1597  input >> num_of_all_elements;
1598  // = NumOfElements + NumOfBdrElements + (maybe, PhysicalPoints)
1599  getline(input, buff);
1600 
1601  int serial_number; // serial number of an element
1602  int type_of_element; // ID describing a type of a mesh element
1603  int n_tags; // number of different tags describing an element
1604  int phys_domain; // element's attribute
1605  int elem_domain; // another element's attribute (rarely used)
1606  int n_partitions; // number of partitions where an element takes place
1607 
1608  // number of nodes for each type of Gmsh elements, type is the index of
1609  // the array + 1
1610  int nodes_of_gmsh_element[] =
1611  {
1612  2, // 2-node line.
1613  3, // 3-node triangle.
1614  4, // 4-node quadrangle.
1615  4, // 4-node tetrahedron.
1616  8, // 8-node hexahedron.
1617  6, // 6-node prism.
1618  5, // 5-node pyramid.
1619  3, /* 3-node second order line (2 nodes associated with the
1620  vertices and 1 with the edge). */
1621  6, /* 6-node second order triangle (3 nodes associated with the
1622  vertices and 3 with the edges). */
1623  9, /* 9-node second order quadrangle (4 nodes associated with the
1624  vertices, 4 with the edges and 1 with the face). */
1625  10, /* 10-node second order tetrahedron (4 nodes associated with
1626  the vertices and 6 with the edges). */
1627  27, /* 27-node second order hexahedron (8 nodes associated with the
1628  vertices, 12 with the edges, 6 with the faces and 1 with the
1629  volume). */
1630  18, /* 18-node second order prism (6 nodes associated with the
1631  vertices, 9 with the edges and 3 with the quadrangular
1632  faces). */
1633  14, /* 14-node second order pyramid (5 nodes associated with the
1634  vertices, 8 with the edges and 1 with the quadrangular
1635  face). */
1636  1, // 1-node point.
1637  8, /* 8-node second order quadrangle (4 nodes associated with the
1638  vertices and 4 with the edges). */
1639  20, /* 20-node second order hexahedron (8 nodes associated with the
1640  vertices and 12 with the edges). */
1641  15, /* 15-node second order prism (6 nodes associated with the
1642  vertices and 9 with the edges). */
1643  13, /* 13-node second order pyramid (5 nodes associated with the
1644  vertices and 8 with the edges). */
1645  9, /* 9-node third order incomplete triangle (3 nodes associated
1646  with the vertices, 6 with the edges) */
1647  10, /* 10-node third order triangle (3 nodes associated with the
1648  vertices, 6 with the edges, 1 with the face) */
1649  12, /* 12-node fourth order incomplete triangle (3 nodes associated
1650  with the vertices, 9 with the edges) */
1651  15, /* 15-node fourth order triangle (3 nodes associated with the
1652  vertices, 9 with the edges, 3 with the face) */
1653  15, /* 15-node fifth order incomplete triangle (3 nodes associated
1654  with the vertices, 12 with the edges) */
1655  21, /* 21-node fifth order complete triangle (3 nodes associated
1656  with the vertices, 12 with the edges, 6 with the face) */
1657  4, /* 4-node third order edge (2 nodes associated with the
1658  vertices, 2 internal to the edge) */
1659  5, /* 5-node fourth order edge (2 nodes associated with the
1660  vertices, 3 internal to the edge) */
1661  6, /* 6-node fifth order edge (2 nodes associated with the
1662  vertices, 4 internal to the edge) */
1663  20, /* 20-node third order tetrahedron (4 nodes associated with the
1664  vertices, 12 with the edges, 4 with the faces) */
1665  35, /* 35-node fourth order tetrahedron (4 nodes associated with
1666  the vertices, 18 with the edges, 12 with the faces, and 1
1667  with the volume) */
1668  56, /* 56-node fifth order tetrahedron (4 nodes associated with the
1669  vertices, 24 with the edges, 24 with the faces, and 4 with
1670  the volume) */
1671  -1,-1, /* unsupported tetrahedral types */
1672  -1,-1, /* unsupported polygonal and polyhedral types */
1673  16, /* 16-node third order quadrilateral (4 nodes associated with
1674  the vertices, 8 with the edges, 4 wth the face) */
1675  25, /* 25-node fourth order quadrilateral (4 nodes associated with
1676  the vertices, 12 with the edges, 9 wth the face) */
1677  36, /* 36-node fifth order quadrilateral (4 nodes associated with
1678  the vertices, 16 with the edges, 16 wth the face) */
1679  -1,-1,-1, /* unsupported quadrilateral types */
1680  28, /* 28-node sixth order complete triangle (3 nodes associated
1681  with the vertices, 15 with the edges, 10 with the face) */
1682  36, /* 36-node seventh order complete triangle (3 nodes associated
1683  with the vertices, 18 with the edges, 15 with the face) */
1684  45, /* 45-node eighth order complete triangle (3 nodes associated
1685  with the vertices, 21 with the edges, 21 with the face) */
1686  55, /* 55-node ninth order complete triangle (3 nodes associated
1687  with the vertices, 24 with the edges, 28 with the face) */
1688  66, /* 66-node tenth order complete triangle (3 nodes associated
1689  with the vertices, 27 with the edges, 36 with the face) */
1690  49, /* 49-node sixth order quadrilateral (4 nodes associated with
1691  the vertices, 20 with the edges, 25 wth the face) */
1692  64, /* 64-node seventh order quadrilateral (4 nodes associated with
1693  the vertices, 24 with the edges, 36 wth the face) */
1694  81, /* 81-node eighth order quadrilateral (4 nodes associated with
1695  the vertices, 28 with the edges, 49 wth the face) */
1696  100, /* 100-node ninth order quadrilateral (4 nodes associated with
1697  the vertices, 32 with the edges, 64 wth the face) */
1698  121, /* 121-node tenth order quadrilateral (4 nodes associated with
1699  the vertices, 36 with the edges, 81 wth the face) */
1700  -1,-1,-1,-1,-1, /* unsupported triangular types */
1701  -1,-1,-1,-1,-1, /* unsupported quadrilateral types */
1702  7, /* 7-node sixth order edge (2 nodes associated with the
1703  vertices, 5 internal to the edge) */
1704  8, /* 8-node seventh order edge (2 nodes associated with the
1705  vertices, 6 internal to the edge) */
1706  9, /* 9-node eighth order edge (2 nodes associated with the
1707  vertices, 7 internal to the edge) */
1708  10, /* 10-node ninth order edge (2 nodes associated with the
1709  vertices, 8 internal to the edge) */
1710  11, /* 11-node tenth order edge (2 nodes associated with the
1711  vertices, 9 internal to the edge) */
1712  -1, /* unsupported linear types */
1713  -1,-1,-1, /* unsupported types */
1714  84, /* 84-node sixth order tetrahedron (4 nodes associated with the
1715  vertices, 30 with the edges, 40 with the faces, and 10 with
1716  the volume) */
1717  120, /* 120-node seventh order tetrahedron (4 nodes associated with
1718  the vertices, 36 with the edges, 60 with the faces, and 20
1719  with the volume) */
1720  165, /* 165-node eighth order tetrahedron (4 nodes associated with
1721  the vertices, 42 with the edges, 84 with the faces, and 35
1722  with the volume) */
1723  220, /* 220-node ninth order tetrahedron (4 nodes associated with
1724  the vertices, 48 with the edges, 112 with the faces, and 56
1725  with the volume) */
1726  286, /* 286-node tenth order tetrahedron (4 nodes associated with
1727  the vertices, 54 with the edges, 144 with the faces, and 84
1728  with the volume) */
1729  -1,-1,-1, /* undefined types */
1730  -1,-1,-1,-1,-1, /* unsupported tetrahedral types */
1731  -1,-1,-1,-1,-1,-1, /* unsupported types */
1732  40, /* 40-node third order prism (6 nodes associated with the
1733  vertices, 18 with the edges, 14 with the faces, and 2 with
1734  the volume) */
1735  75, /* 75-node fourth order prism (6 nodes associated with the
1736  vertices, 27 with the edges, 33 with the faces, and 9 with
1737  the volume) */
1738  64, /* 64-node third order hexahedron (8 nodes associated with the
1739  vertices, 24 with the edges, 24 with the faces and 8 with
1740  the volume).*/
1741  125, /* 125-node fourth order hexahedron (8 nodes associated with
1742  the vertices, 36 with the edges, 54 with the faces and 27
1743  with the volume).*/
1744  216, /* 216-node fifth order hexahedron (8 nodes associated with the
1745  vertices, 48 with the edges, 96 with the faces and 64 with
1746  the volume).*/
1747  343, /* 343-node sixth order hexahedron (8 nodes associated with the
1748  vertices, 60 with the edges, 150 with the faces and 125 with
1749  the volume).*/
1750  512, /* 512-node seventh order hexahedron (8 nodes associated with
1751  the vertices, 72 with the edges, 216 with the faces and 216
1752  with the volume).*/
1753  729, /* 729-node eighth order hexahedron (8 nodes associated with
1754  the vertices, 84 with the edges, 294 with the faces and 343
1755  with the volume).*/
1756  1000,/* 1000-node ninth order hexahedron (8 nodes associated with
1757  the vertices, 96 with the edges, 384 with the faces and 512
1758  with the volume).*/
1759  -1,-1,-1,-1,-1,-1,-1, /* unsupported hexahedron types */
1760  126, /* 126-node fifth order prism (6 nodes associated with the
1761  vertices, 36 with the edges, 60 with the faces, and 24 with
1762  the volume) */
1763  196, /* 196-node sixth order prism (6 nodes associated with the
1764  vertices, 45 with the edges, 95 with the faces, and 50 with
1765  the volume) */
1766  288, /* 288-node seventh order prism (6 nodes associated with the
1767  vertices, 54 with the edges, 138 with the faces, and 90 with
1768  the volume) */
1769  405, /* 405-node eighth order prism (6 nodes associated with the
1770  vertices, 63 with the edges, 189 with the faces, and 147
1771  with the volume) */
1772  550, /* 550-node ninth order prism (6 nodes associated with the
1773  vertices, 72 with the edges, 248 with the faces, and 224
1774  with the volume) */
1775  -1,-1,-1,-1,-1,-1,-1, /* unsupported prism types */
1776  30, /* 30-node third order pyramid (5 nodes associated with the
1777  vertices, 16 with the edges and 8 with the faces, and 1 with
1778  the volume). */
1779  55, /* 55-node fourth order pyramid (5 nodes associated with the
1780  vertices, 24 with the edges and 21 with the faces, and 5
1781  with the volume). */
1782  91, /* 91-node fifth order pyramid (5 nodes associated with the
1783  vertices, 32 with the edges and 40 with the faces, and 14
1784  with the volume). */
1785  140, /* 140-node sixth order pyramid (5 nodes associated with the
1786  vertices, 40 with the edges and 65 with the faces, and 30
1787  with the volume). */
1788  204, /* 204-node seventh order pyramid (5 nodes associated with the
1789  vertices, 48 with the edges and 96 with the faces, and 55
1790  with the volume). */
1791  285, /* 285-node eighth order pyramid (5 nodes associated with the
1792  vertices, 56 with the edges and 133 with the faces, and 91
1793  with the volume). */
1794  385 /* 385-node ninth order pyramid (5 nodes associated with the
1795  vertices, 64 with the edges and 176 with the faces, and 140
1796  with the volume). */
1797  };
1798 
1799  /** The following mappings convert the Gmsh node orderings for high
1800  order elements to MFEM's L2 degree of freedom ordering. To support
1801  more options examine Gmsh's ordering and read off the indices in
1802  MFEM's order. For example 2nd order Gmsh quadrilaterals use the
1803  following ordering:
1804 
1805  3--6--2
1806  | | |
1807  7 8 5
1808  | | |
1809  0--4--1
1810 
1811  (from https://gmsh.info/doc/texinfo/gmsh.html#Node-ordering)
1812 
1813  Whereas MFEM uses a tensor product ordering with the horizontal
1814  axis cycling fastest so we would read off:
1815 
1816  0 4 1 7 8 5 3 6 2
1817 
1818  This corresponds to the quad9 mapping below.
1819  */
1820  int lin3[] = {0,2,1}; // 2nd order segment
1821  int lin4[] = {0,2,3,1}; // 3rd order segment
1822  int tri6[] = {0,3,1,5,4,2}; // 2nd order triangle
1823  int tri10[] = {0,3,4,1,8,9,5,7,6,2}; // 3rd order triangle
1824  int quad9[] = {0,4,1,7,8,5,3,6,2}; // 2nd order quadrilateral
1825  int quad16[] = {0,4,5,1,11,12,13,6, // 3rd order quadrilateral
1826  10,15,14,7,3,9,8,2
1827  };
1828  int tet10[] {0,4,1,6,5,2,7,9,8,3}; // 2nd order tetrahedron
1829  int tet20[] = {0,4,5,1,9,16,6,8,7,2, // 3rd order tetrahedron
1830  11,17,15,18,19,13,10,14,12,3
1831  };
1832  int hex27[] {0,8,1,9,20,11,3,13,2, // 2nd order hexahedron
1833  10,21,12,22,26,23,15,24,14,
1834  4,16,5,17,25,18,7,19,6
1835  };
1836  int hex64[] {0,8,9,1,10,32,35,14, // 3rd order hexahedron
1837  11,33,34,15,3,19,18,2,
1838  12,36,37,16,40,56,57,44,
1839  43,59,58,45,22,49,48,20,
1840  13,39,38,17,41,60,61,47,
1841  42,63,62,46,23,50,51,21,
1842  4,24,25,5,26,52,53,28,
1843  27,55,54,29,7,31,30,6
1844  };
1845 
1846  int wdg18[] = {0,6,1,7,9,2,8,15,10, // 2nd order wedge/prism
1847  16,17,11,3,12,4,13,14,5
1848  };
1849  int wdg40[] = {0,6,7,1,8,24,12,9,13,2, // 3rd order wedge/prism
1850  10,26,27,14,30,38,34,33,35,16,
1851  11,29,28,15,31,39,37,32,36,17,
1852  3,18,19,4,20,25,22,21,23,5
1853  };
1854 
1855  int pyr14[] = {0,5,1,6,13,8,3, // 2nd order pyramid
1856  10,2,7,9,12,11,4
1857  };
1858  int pyr30[] = {0,5,6,1,7,25,28,11,8,26, // 3rd order pyramid
1859  27,12,3,16,15,2,9,21,13,22,
1860  29,23,19,24,17,10,14,20,18,4
1861  };
1862 
1863  vector<Element*> elements_0D, elements_1D, elements_2D, elements_3D;
1864  elements_0D.reserve(num_of_all_elements);
1865  elements_1D.reserve(num_of_all_elements);
1866  elements_2D.reserve(num_of_all_elements);
1867  elements_3D.reserve(num_of_all_elements);
1868 
1869  // Temporary storage for high order vertices, if present
1870  vector<Array<int>*> ho_verts_1D, ho_verts_2D, ho_verts_3D;
1871  ho_verts_1D.reserve(num_of_all_elements);
1872  ho_verts_2D.reserve(num_of_all_elements);
1873  ho_verts_3D.reserve(num_of_all_elements);
1874 
1875  // Temporary storage for order of elements
1876  vector<int> ho_el_order_1D, ho_el_order_2D, ho_el_order_3D;
1877  ho_el_order_1D.reserve(num_of_all_elements);
1878  ho_el_order_2D.reserve(num_of_all_elements);
1879  ho_el_order_3D.reserve(num_of_all_elements);
1880 
1881  // Vertex order mappings
1882  Array<int*> ho_lin(11); ho_lin = NULL;
1883  Array<int*> ho_tri(11); ho_tri = NULL;
1884  Array<int*> ho_sqr(11); ho_sqr = NULL;
1885  Array<int*> ho_tet(11); ho_tet = NULL;
1886  Array<int*> ho_hex(10); ho_hex = NULL;
1887  Array<int*> ho_wdg(10); ho_wdg = NULL;
1888  Array<int*> ho_pyr(10); ho_pyr = NULL;
1889 
1890  // Use predefined arrays at lowest orders (for efficiency)
1891  ho_lin[2] = lin3; ho_lin[3] = lin4;
1892  ho_tri[2] = tri6; ho_tri[3] = tri10;
1893  ho_sqr[2] = quad9; ho_sqr[3] = quad16;
1894  ho_tet[2] = tet10; ho_tet[3] = tet20;
1895  ho_hex[2] = hex27; ho_hex[3] = hex64;
1896  ho_wdg[2] = wdg18; ho_wdg[3] = wdg40;
1897  ho_pyr[2] = pyr14; ho_pyr[3] = pyr30;
1898 
1899  bool has_nonpositive_phys_domain = false;
1900  bool has_positive_phys_domain = false;
1901 
1902  if (binary)
1903  {
1904  int n_elem_part = 0; // partial sum of elements that are read
1905  const int header_size = 3;
1906  // header consists of 3 numbers: type of the element, number of
1907  // elements of this type, and number of tags
1908  int header[header_size];
1909  int n_elem_one_type; // number of elements of a specific type
1910 
1911  while (n_elem_part < num_of_all_elements)
1912  {
1913  input.read(reinterpret_cast<char*>(header),
1914  header_size*sizeof(int));
1915  type_of_element = header[0];
1916  n_elem_one_type = header[1];
1917  n_tags = header[2];
1918 
1919  n_elem_part += n_elem_one_type;
1920 
1921  const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1];
1922  vector<int> data(1+n_tags+n_elem_nodes);
1923  for (int el = 0; el < n_elem_one_type; ++el)
1924  {
1925  input.read(reinterpret_cast<char*>(&data[0]),
1926  data.size()*sizeof(int));
1927  int dd = 0; // index for data array
1928  serial_number = data[dd++];
1929  // physical domain - the most important value (to distinguish
1930  // materials with different properties)
1931  phys_domain = (n_tags > 0) ? data[dd++] : 1;
1932  // elementary domain - to distinguish different geometrical
1933  // domains (typically, it's used rarely)
1934  elem_domain = (n_tags > 1) ? data[dd++] : 0;
1935  // the number of tags is bigger than 2 if there are some
1936  // partitions (domain decompositions)
1937  n_partitions = (n_tags > 2) ? data[dd++] : 0;
1938  // we currently just skip the partitions if they exist, and go
1939  // directly to vertices describing the mesh element
1940  vector<int> vert_indices(n_elem_nodes);
1941  for (int vi = 0; vi < n_elem_nodes; ++vi)
1942  {
1943  map<int, int>::const_iterator it =
1944  vertices_map.find(data[1+n_tags+vi]);
1945  if (it == vertices_map.end())
1946  {
1947  MFEM_ABORT("Gmsh file : vertex index doesn't exist");
1948  }
1949  vert_indices[vi] = it->second;
1950  }
1951 
1952  // Non-positive attributes are not allowed in MFEM. However,
1953  // by default, Gmsh sets the physical domain of all elements
1954  // to zero. In the case that all elements have physical domain
1955  // zero, we will given them attribute 1. If only some elements
1956  // have physical domain zero, we will throw an error.
1957  if (phys_domain <= 0)
1958  {
1959  has_nonpositive_phys_domain = true;
1960  phys_domain = 1;
1961  }
1962  else
1963  {
1964  has_positive_phys_domain = true;
1965  }
1966 
1967  // initialize the mesh element
1968  int el_order = 11;
1969  switch (type_of_element)
1970  {
1971  case 1: // 2-node line
1972  case 8: // 3-node line (2nd order)
1973  case 26: // 4-node line (3rd order)
1974  case 27: // 5-node line (4th order)
1975  case 28: // 6-node line (5th order)
1976  case 62: // 7-node line (6th order)
1977  case 63: // 8-node line (7th order)
1978  case 64: // 9-node line (8th order)
1979  case 65: // 10-node line (9th order)
1980  case 66: // 11-node line (10th order)
1981  {
1982  elements_1D.push_back(
1983  new Segment(&vert_indices[0], phys_domain));
1984  if (type_of_element != 1)
1985  {
1986  el_order = n_elem_nodes - 1;
1987  Array<int> * hov = new Array<int>;
1988  hov->Append(&vert_indices[0], n_elem_nodes);
1989  ho_verts_1D.push_back(hov);
1990  ho_el_order_1D.push_back(el_order);
1991  }
1992  break;
1993  }
1994  case 2: el_order--; // 3-node triangle
1995  case 9: el_order--; // 6-node triangle (2nd order)
1996  case 21: el_order--; // 10-node triangle (3rd order)
1997  case 23: el_order--; // 15-node triangle (4th order)
1998  case 25: el_order--; // 21-node triangle (5th order)
1999  case 42: el_order--; // 28-node triangle (6th order)
2000  case 43: el_order--; // 36-node triangle (7th order)
2001  case 44: el_order--; // 45-node triangle (8th order)
2002  case 45: el_order--; // 55-node triangle (9th order)
2003  case 46: el_order--; // 66-node triangle (10th order)
2004  {
2005  elements_2D.push_back(
2006  new Triangle(&vert_indices[0], phys_domain));
2007  if (el_order > 1)
2008  {
2009  Array<int> * hov = new Array<int>;
2010  hov->Append(&vert_indices[0], n_elem_nodes);
2011  ho_verts_2D.push_back(hov);
2012  ho_el_order_2D.push_back(el_order);
2013  }
2014  break;
2015  }
2016  case 3: el_order--; // 4-node quadrangle
2017  case 10: el_order--; // 9-node quadrangle (2nd order)
2018  case 36: el_order--; // 16-node quadrangle (3rd order)
2019  case 37: el_order--; // 25-node quadrangle (4th order)
2020  case 38: el_order--; // 36-node quadrangle (5th order)
2021  case 47: el_order--; // 49-node quadrangle (6th order)
2022  case 48: el_order--; // 64-node quadrangle (7th order)
2023  case 49: el_order--; // 81-node quadrangle (8th order)
2024  case 50: el_order--; // 100-node quadrangle (9th order)
2025  case 51: el_order--; // 121-node quadrangle (10th order)
2026  {
2027  elements_2D.push_back(
2028  new Quadrilateral(&vert_indices[0], phys_domain));
2029  if (el_order > 1)
2030  {
2031  Array<int> * hov = new Array<int>;
2032  hov->Append(&vert_indices[0], n_elem_nodes);
2033  ho_verts_2D.push_back(hov);
2034  ho_el_order_2D.push_back(el_order);
2035  }
2036  break;
2037  }
2038  case 4: el_order--; // 4-node tetrahedron
2039  case 11: el_order--; // 10-node tetrahedron (2nd order)
2040  case 29: el_order--; // 20-node tetrahedron (3rd order)
2041  case 30: el_order--; // 35-node tetrahedron (4th order)
2042  case 31: el_order--; // 56-node tetrahedron (5th order)
2043  case 71: el_order--; // 84-node tetrahedron (6th order)
2044  case 72: el_order--; // 120-node tetrahedron (7th order)
2045  case 73: el_order--; // 165-node tetrahedron (8th order)
2046  case 74: el_order--; // 220-node tetrahedron (9th order)
2047  case 75: el_order--; // 286-node tetrahedron (10th order)
2048  {
2049 #ifdef MFEM_USE_MEMALLOC
2050  elements_3D.push_back(TetMemory.Alloc());
2051  elements_3D.back()->SetVertices(&vert_indices[0]);
2052  elements_3D.back()->SetAttribute(phys_domain);
2053 #else
2054  elements_3D.push_back(
2055  new Tetrahedron(&vert_indices[0], phys_domain));
2056 #endif
2057  if (el_order > 1)
2058  {
2059  Array<int> * hov = new Array<int>;
2060  hov->Append(&vert_indices[0], n_elem_nodes);
2061  ho_verts_3D.push_back(hov);
2062  ho_el_order_3D.push_back(el_order);
2063  }
2064  break;
2065  }
2066  case 5: el_order--; // 8-node hexahedron
2067  case 12: el_order--; // 27-node hexahedron (2nd order)
2068  case 92: el_order--; // 64-node hexahedron (3rd order)
2069  case 93: el_order--; // 125-node hexahedron (4th order)
2070  case 94: el_order--; // 216-node hexahedron (5th order)
2071  case 95: el_order--; // 343-node hexahedron (6th order)
2072  case 96: el_order--; // 512-node hexahedron (7th order)
2073  case 97: el_order--; // 729-node hexahedron (8th order)
2074  case 98: el_order--; // 1000-node hexahedron (9th order)
2075  {
2076  el_order--; // Gmsh does not define an order 10 hex
2077  elements_3D.push_back(
2078  new Hexahedron(&vert_indices[0], phys_domain));
2079  if (el_order > 1)
2080  {
2081  Array<int> * hov = new Array<int>;
2082  hov->Append(&vert_indices[0], n_elem_nodes);
2083  ho_verts_3D.push_back(hov);
2084  ho_el_order_3D.push_back(el_order);
2085  }
2086  break;
2087  }
2088  case 6: el_order--; // 6-node wedge
2089  case 13: el_order--; // 18-node wedge (2nd order)
2090  case 90: el_order--; // 40-node wedge (3rd order)
2091  case 91: el_order--; // 75-node wedge (4th order)
2092  case 106: el_order--; // 126-node wedge (5th order)
2093  case 107: el_order--; // 196-node wedge (6th order)
2094  case 108: el_order--; // 288-node wedge (7th order)
2095  case 109: el_order--; // 405-node wedge (8th order)
2096  case 110: el_order--; // 550-node wedge (9th order)
2097  {
2098  el_order--; // Gmsh does not define an order 10 wedge
2099  elements_3D.push_back(
2100  new Wedge(&vert_indices[0], phys_domain));
2101  if (el_order > 1)
2102  {
2103  Array<int> * hov = new Array<int>;
2104  hov->Append(&vert_indices[0], n_elem_nodes);
2105  ho_verts_3D.push_back(hov);
2106  ho_el_order_3D.push_back(el_order);
2107  }
2108  break;
2109  }
2110  /*
2111  // MFEM does not support pyramids yet
2112  case 7: el_order--; // 5-node pyramid
2113  case 14: el_order--; // 14-node pyramid (2nd order)
2114  case 118: el_order--; // 30-node pyramid (3rd order)
2115  case 119: el_order--; // 55-node pyramid (4th order)
2116  case 120: el_order--; // 91-node pyramid (5th order)
2117  case 121: el_order--; // 140-node pyramid (6th order)
2118  case 122: el_order--; // 204-node pyramid (7th order)
2119  case 123: el_order--; // 285-node pyramid (8th order)
2120  case 124: el_order--; // 385-node pyramid (9th order)
2121  {
2122  el_order--; // Gmsh does not define an order 10 pyr
2123  elements_3D.push_back(
2124  new Pyramid(&vert_indices[0], phys_domain));
2125  if (el_order > 1)
2126  {
2127  Array<int> * hov = new Array<int>;
2128  hov->Append(&vert_indices[0], n_elem_nodes);
2129  ho_verts_3D.push_back(hov);
2130  ho_el_order_3D.push_back(el_order);
2131  }
2132  break;
2133  }
2134  */
2135  case 15: // 1-node point
2136  {
2137  elements_0D.push_back(
2138  new Point(&vert_indices[0], phys_domain));
2139  break;
2140  }
2141  default: // any other element
2142  MFEM_WARNING("Unsupported Gmsh element type.");
2143  break;
2144 
2145  } // switch (type_of_element)
2146  } // el (elements of one type)
2147  } // all elements
2148  } // if binary
2149  else // ASCII
2150  {
2151  for (int el = 0; el < num_of_all_elements; ++el)
2152  {
2153  input >> serial_number >> type_of_element >> n_tags;
2154  vector<int> data(n_tags);
2155  for (int i = 0; i < n_tags; ++i) { input >> data[i]; }
2156  // physical domain - the most important value (to distinguish
2157  // materials with different properties)
2158  phys_domain = (n_tags > 0) ? data[0] : 1;
2159  // elementary domain - to distinguish different geometrical
2160  // domains (typically, it's used rarely)
2161  elem_domain = (n_tags > 1) ? data[1] : 0;
2162  // the number of tags is bigger than 2 if there are some
2163  // partitions (domain decompositions)
2164  n_partitions = (n_tags > 2) ? data[2] : 0;
2165  // we currently just skip the partitions if they exist, and go
2166  // directly to vertices describing the mesh element
2167  const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1];
2168  vector<int> vert_indices(n_elem_nodes);
2169  int index;
2170  for (int vi = 0; vi < n_elem_nodes; ++vi)
2171  {
2172  input >> index;
2173  map<int, int>::const_iterator it = vertices_map.find(index);
2174  if (it == vertices_map.end())
2175  {
2176  MFEM_ABORT("Gmsh file : vertex index doesn't exist");
2177  }
2178  vert_indices[vi] = it->second;
2179  }
2180 
2181  // Non-positive attributes are not allowed in MFEM. However,
2182  // by default, Gmsh sets the physical domain of all elements
2183  // to zero. In the case that all elements have physical domain
2184  // zero, we will given them attribute 1. If only some elements
2185  // have physical domain zero, we will throw an error.
2186  if (phys_domain <= 0)
2187  {
2188  has_nonpositive_phys_domain = true;
2189  phys_domain = 1;
2190  }
2191  else
2192  {
2193  has_positive_phys_domain = true;
2194  }
2195 
2196  // initialize the mesh element
2197  int el_order = 11;
2198  switch (type_of_element)
2199  {
2200  case 1: // 2-node line
2201  case 8: // 3-node line (2nd order)
2202  case 26: // 4-node line (3rd order)
2203  case 27: // 5-node line (4th order)
2204  case 28: // 6-node line (5th order)
2205  case 62: // 7-node line (6th order)
2206  case 63: // 8-node line (7th order)
2207  case 64: // 9-node line (8th order)
2208  case 65: // 10-node line (9th order)
2209  case 66: // 11-node line (10th order)
2210  {
2211  elements_1D.push_back(
2212  new Segment(&vert_indices[0], phys_domain));
2213  if (type_of_element != 1)
2214  {
2215  Array<int> * hov = new Array<int>;
2216  hov->Append(&vert_indices[0], n_elem_nodes);
2217  ho_verts_1D.push_back(hov);
2218  el_order = n_elem_nodes - 1;
2219  ho_el_order_1D.push_back(el_order);
2220  }
2221  break;
2222  }
2223  case 2: el_order--; // 3-node triangle
2224  case 9: el_order--; // 6-node triangle (2nd order)
2225  case 21: el_order--; // 10-node triangle (3rd order)
2226  case 23: el_order--; // 15-node triangle (4th order)
2227  case 25: el_order--; // 21-node triangle (5th order)
2228  case 42: el_order--; // 28-node triangle (6th order)
2229  case 43: el_order--; // 36-node triangle (7th order)
2230  case 44: el_order--; // 45-node triangle (8th order)
2231  case 45: el_order--; // 55-node triangle (9th order)
2232  case 46: el_order--; // 66-node triangle (10th order)
2233  {
2234  elements_2D.push_back(
2235  new Triangle(&vert_indices[0], phys_domain));
2236  if (el_order > 1)
2237  {
2238  Array<int> * hov = new Array<int>;
2239  hov->Append(&vert_indices[0], n_elem_nodes);
2240  ho_verts_2D.push_back(hov);
2241  ho_el_order_2D.push_back(el_order);
2242  }
2243  break;
2244  }
2245  case 3: el_order--; // 4-node quadrangle
2246  case 10: el_order--; // 9-node quadrangle (2nd order)
2247  case 36: el_order--; // 16-node quadrangle (3rd order)
2248  case 37: el_order--; // 25-node quadrangle (4th order)
2249  case 38: el_order--; // 36-node quadrangle (5th order)
2250  case 47: el_order--; // 49-node quadrangle (6th order)
2251  case 48: el_order--; // 64-node quadrangle (7th order)
2252  case 49: el_order--; // 81-node quadrangle (8th order)
2253  case 50: el_order--; // 100-node quadrangle (9th order)
2254  case 51: el_order--; // 121-node quadrangle (10th order)
2255  {
2256  elements_2D.push_back(
2257  new Quadrilateral(&vert_indices[0], phys_domain));
2258  if (el_order > 1)
2259  {
2260  Array<int> * hov = new Array<int>;
2261  hov->Append(&vert_indices[0], n_elem_nodes);
2262  ho_verts_2D.push_back(hov);
2263  ho_el_order_2D.push_back(el_order);
2264  }
2265  break;
2266  }
2267  case 4: el_order--; // 4-node tetrahedron
2268  case 11: el_order--; // 10-node tetrahedron (2nd order)
2269  case 29: el_order--; // 20-node tetrahedron (3rd order)
2270  case 30: el_order--; // 35-node tetrahedron (4th order)
2271  case 31: el_order--; // 56-node tetrahedron (5th order)
2272  case 71: el_order--; // 84-node tetrahedron (6th order)
2273  case 72: el_order--; // 120-node tetrahedron (7th order)
2274  case 73: el_order--; // 165-node tetrahedron (8th order)
2275  case 74: el_order--; // 220-node tetrahedron (9th order)
2276  case 75: el_order--; // 286-node tetrahedron (10th order)
2277  {
2278 #ifdef MFEM_USE_MEMALLOC
2279  elements_3D.push_back(TetMemory.Alloc());
2280  elements_3D.back()->SetVertices(&vert_indices[0]);
2281  elements_3D.back()->SetAttribute(phys_domain);
2282 #else
2283  elements_3D.push_back(
2284  new Tetrahedron(&vert_indices[0], phys_domain));
2285 #endif
2286  if (el_order > 1)
2287  {
2288  Array<int> * hov = new Array<int>;
2289  hov->Append(&vert_indices[0], n_elem_nodes);
2290  ho_verts_3D.push_back(hov);
2291  ho_el_order_3D.push_back(el_order);
2292  }
2293  break;
2294  }
2295  case 5: el_order--; // 8-node hexahedron
2296  case 12: el_order--; // 27-node hexahedron (2nd order)
2297  case 92: el_order--; // 64-node hexahedron (3rd order)
2298  case 93: el_order--; // 125-node hexahedron (4th order)
2299  case 94: el_order--; // 216-node hexahedron (5th order)
2300  case 95: el_order--; // 343-node hexahedron (6th order)
2301  case 96: el_order--; // 512-node hexahedron (7th order)
2302  case 97: el_order--; // 729-node hexahedron (8th order)
2303  case 98: el_order--; // 1000-node hexahedron (9th order)
2304  {
2305  el_order--;
2306  elements_3D.push_back(
2307  new Hexahedron(&vert_indices[0], phys_domain));
2308  if (el_order > 1)
2309  {
2310  Array<int> * hov = new Array<int>;
2311  hov->Append(&vert_indices[0], n_elem_nodes);
2312  ho_verts_3D.push_back(hov);
2313  ho_el_order_3D.push_back(el_order);
2314  }
2315  break;
2316  }
2317  case 6: el_order--; // 6-node wedge
2318  case 13: el_order--; // 18-node wedge (2nd order)
2319  case 90: el_order--; // 40-node wedge (3rd order)
2320  case 91: el_order--; // 75-node wedge (4th order)
2321  case 106: el_order--; // 126-node wedge (5th order)
2322  case 107: el_order--; // 196-node wedge (6th order)
2323  case 108: el_order--; // 288-node wedge (7th order)
2324  case 109: el_order--; // 405-node wedge (8th order)
2325  case 110: el_order--; // 550-node wedge (9th order)
2326  {
2327  el_order--;
2328  elements_3D.push_back(
2329  new Wedge(&vert_indices[0], phys_domain));
2330  if (el_order > 1)
2331  {
2332  Array<int> * hov = new Array<int>;
2333  hov->Append(&vert_indices[0], n_elem_nodes);
2334  ho_verts_3D.push_back(hov);
2335  ho_el_order_3D.push_back(el_order);
2336  }
2337  break;
2338  }
2339  /*
2340  // MFEM does not support pyramids yet
2341  case 7: el_order--; // 5-node pyramid
2342  case 14: el_order--; // 14-node pyramid (2nd order)
2343  case 118: el_order--; // 30-node pyramid (3rd order)
2344  case 119: el_order--; // 55-node pyramid (4th order)
2345  case 120: el_order--; // 91-node pyramid (5th order)
2346  case 121: el_order--; // 140-node pyramid (6th order)
2347  case 122: el_order--; // 204-node pyramid (7th order)
2348  case 123: el_order--; // 285-node pyramid (8th order)
2349  case 124: el_order--; // 385-node pyramid (9th order)
2350  {
2351  el_order--;
2352  elements_3D.push_back(
2353  new Pyramid(&vert_indices[0], phys_domain));
2354  if (el_order > 1)
2355  {
2356  Array<int> * hov = new Array<int>;
2357  hov->Append(&vert_indices[0], n_elem_nodes);
2358  ho_verts_3D.push_back(hov);
2359  ho_el_order_3D.push_back(el_order);
2360  }
2361  break;
2362  }
2363  */
2364  case 15: // 1-node point
2365  {
2366  elements_0D.push_back(
2367  new Point(&vert_indices[0], phys_domain));
2368  break;
2369  }
2370  default: // any other element
2371  MFEM_WARNING("Unsupported Gmsh element type.");
2372  break;
2373 
2374  } // switch (type_of_element)
2375  } // el (all elements)
2376  } // if ASCII
2377 
2378  if (has_positive_phys_domain && has_nonpositive_phys_domain)
2379  {
2380  MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n"
2381  "By default Gmsh sets element tags (attributes)"
2382  " to '0' but MFEM requires that they be"
2383  " positive integers.\n"
2384  "Use \"Physical Curve\", \"Physical Surface\","
2385  " or \"Physical Volume\" to set tags/attributes"
2386  " for all curves, surfaces, or volumes in your"
2387  " Gmsh geometry to values which are >= 1.");
2388  }
2389  else if (has_nonpositive_phys_domain)
2390  {
2391  mfem::out << "\nGmsh reader: all element attributes were zero.\n"
2392  << "MFEM only supports positive element attributes.\n"
2393  << "Setting element attributes to 1.\n\n";
2394  }
2395 
2396  if (!elements_3D.empty())
2397  {
2398  Dim = 3;
2399  NumOfElements = elements_3D.size();
2400  elements.SetSize(NumOfElements);
2401  for (int el = 0; el < NumOfElements; ++el)
2402  {
2403  elements[el] = elements_3D[el];
2404  }
2405  NumOfBdrElements = elements_2D.size();
2406  boundary.SetSize(NumOfBdrElements);
2407  for (int el = 0; el < NumOfBdrElements; ++el)
2408  {
2409  boundary[el] = elements_2D[el];
2410  }
2411  for (size_t el = 0; el < ho_el_order_3D.size(); el++)
2412  {
2413  mesh_order = max(mesh_order, ho_el_order_3D[el]);
2414  }
2415  // discard other elements
2416  for (size_t el = 0; el < elements_1D.size(); ++el)
2417  {
2418  delete elements_1D[el];
2419  }
2420  for (size_t el = 0; el < elements_0D.size(); ++el)
2421  {
2422  delete elements_0D[el];
2423  }
2424  }
2425  else if (!elements_2D.empty())
2426  {
2427  Dim = 2;
2428  NumOfElements = elements_2D.size();
2429  elements.SetSize(NumOfElements);
2430  for (int el = 0; el < NumOfElements; ++el)
2431  {
2432  elements[el] = elements_2D[el];
2433  }
2434  NumOfBdrElements = elements_1D.size();
2435  boundary.SetSize(NumOfBdrElements);
2436  for (int el = 0; el < NumOfBdrElements; ++el)
2437  {
2438  boundary[el] = elements_1D[el];
2439  }
2440  for (size_t el = 0; el < ho_el_order_2D.size(); el++)
2441  {
2442  mesh_order = max(mesh_order, ho_el_order_2D[el]);
2443  }
2444  // discard other elements
2445  for (size_t el = 0; el < elements_0D.size(); ++el)
2446  {
2447  delete elements_0D[el];
2448  }
2449  }
2450  else if (!elements_1D.empty())
2451  {
2452  Dim = 1;
2453  NumOfElements = elements_1D.size();
2454  elements.SetSize(NumOfElements);
2455  for (int el = 0; el < NumOfElements; ++el)
2456  {
2457  elements[el] = elements_1D[el];
2458  }
2459  NumOfBdrElements = elements_0D.size();
2460  boundary.SetSize(NumOfBdrElements);
2461  for (int el = 0; el < NumOfBdrElements; ++el)
2462  {
2463  boundary[el] = elements_0D[el];
2464  }
2465  for (size_t el = 0; el < ho_el_order_1D.size(); el++)
2466  {
2467  mesh_order = max(mesh_order, ho_el_order_1D[el]);
2468  }
2469  }
2470  else
2471  {
2472  MFEM_ABORT("Gmsh file : no elements found");
2473  return;
2474  }
2475 
2476  if (mesh_order > 1)
2477  {
2478  curved = 1;
2479  read_gf = 0;
2480 
2481  // initialize mesh_geoms so we can create Nodes FE space below
2482  this->SetMeshGen();
2483 
2484  // Generate faces and edges so that we can define
2485  // FE space on the mesh
2486  this->FinalizeTopology();
2487 
2488  // Construct GridFunction for uniformly spaced high order coords
2490  FiniteElementSpace* nfes;
2491  nfec = new L2_FECollection(mesh_order, Dim,
2492  BasisType::ClosedUniform);
2493  nfes = new FiniteElementSpace(this, nfec, spaceDim,
2494  Ordering::byVDIM);
2495  Nodes_gf.SetSpace(nfes);
2496  Nodes_gf.MakeOwner(nfec);
2497 
2498  int o = 0;
2499  int el_order = 1;
2500  for (int el = 0; el < NumOfElements; el++)
2501  {
2502  const int * vm = NULL;
2503  Array<int> * ho_verts = NULL;
2504  switch (GetElementType(el))
2505  {
2506  case Element::SEGMENT:
2507  ho_verts = ho_verts_1D[el];
2508  el_order = ho_el_order_1D[el];
2509  if (!ho_lin[el_order])
2510  {
2511  ho_lin[el_order] = new int[ho_verts->Size()];
2512  GmshHOSegmentMapping(el_order, ho_lin[el_order]);
2513  }
2514  vm = ho_lin[el_order];
2515  break;
2516  case Element::TRIANGLE:
2517  ho_verts = ho_verts_2D[el];
2518  el_order = ho_el_order_2D[el];
2519  if (!ho_tri[el_order])
2520  {
2521  ho_tri[el_order] = new int[ho_verts->Size()];
2522  GmshHOTriangleMapping(el_order, ho_tri[el_order]);
2523  }
2524  vm = ho_tri[el_order];
2525  break;
2526  case Element::QUADRILATERAL:
2527  ho_verts = ho_verts_2D[el];
2528  el_order = ho_el_order_2D[el];
2529  if (!ho_sqr[el_order])
2530  {
2531  ho_sqr[el_order] = new int[ho_verts->Size()];
2532  GmshHOQuadrilateralMapping(el_order, ho_sqr[el_order]);
2533  }
2534  vm = ho_sqr[el_order];
2535  break;
2536  case Element::TETRAHEDRON:
2537  ho_verts = ho_verts_3D[el];
2538  el_order = ho_el_order_3D[el];
2539  if (!ho_tet[el_order])
2540  {
2541  ho_tet[el_order] = new int[ho_verts->Size()];
2542  GmshHOTetrahedronMapping(el_order, ho_tet[el_order]);
2543  }
2544  vm = ho_tet[el_order];
2545  break;
2546  case Element::HEXAHEDRON:
2547  ho_verts = ho_verts_3D[el];
2548  el_order = ho_el_order_3D[el];
2549  if (!ho_hex[el_order])
2550  {
2551  ho_hex[el_order] = new int[ho_verts->Size()];
2552  GmshHOHexahedronMapping(el_order, ho_hex[el_order]);
2553  }
2554  vm = ho_hex[el_order];
2555  break;
2556  case Element::WEDGE:
2557  ho_verts = ho_verts_3D[el];
2558  el_order = ho_el_order_3D[el];
2559  if (!ho_wdg[el_order])
2560  {
2561  ho_wdg[el_order] = new int[ho_verts->Size()];
2562  GmshHOWedgeMapping(el_order, ho_wdg[el_order]);
2563  }
2564  vm = ho_wdg[el_order];
2565  break;
2566  // case Element::PYRAMID:
2567  // ho_verts = ho_verts_3D[el];
2568  // el_order = ho_el_order_3D[el];
2569  // if (ho_pyr[el_order])
2570  // {
2571  // ho_pyr[el_order] = new int[ho_verts->Size()];
2572  // GmshHOPyramidMapping(el_order, ho_pyr[el_order]);
2573  // }
2574  // vm = ho_pyr[el_order];
2575  // break;
2576  default: // Any other element type
2577  MFEM_WARNING("Unsupported Gmsh element type.");
2578  break;
2579  }
2580  int nv = (ho_verts) ? ho_verts->Size() : 0;
2581 
2582  for (int v = 0; v<nv; v++)
2583  {
2584  double * c = GetVertex((*ho_verts)[vm[v]]);
2585  for (int d=0; d<spaceDim; d++)
2586  {
2587  Nodes_gf(spaceDim * (o + v) + d) = c[d];
2588  }
2589  }
2590  o += nv;
2591  }
2592  }
2593 
2594  // Delete any high order element to vertex connectivity
2595  for (size_t el=0; el<ho_verts_1D.size(); el++)
2596  {
2597  delete ho_verts_1D[el];
2598  }
2599  for (size_t el=0; el<ho_verts_2D.size(); el++)
2600  {
2601  delete ho_verts_2D[el];
2602  }
2603  for (size_t el=0; el<ho_verts_3D.size(); el++)
2604  {
2605  delete ho_verts_3D[el];
2606  }
2607 
2608  // Delete dynamically allocated high vertex order mappings
2609  for (int ord=4; ord<ho_lin.Size(); ord++)
2610  {
2611  if (ho_lin[ord] != NULL) { delete [] ho_lin[ord]; }
2612  }
2613  for (int ord=4; ord<ho_tri.Size(); ord++)
2614  {
2615  if (ho_tri[ord] != NULL) { delete [] ho_tri[ord]; }
2616  }
2617  for (int ord=4; ord<ho_sqr.Size(); ord++)
2618  {
2619  if (ho_sqr[ord] != NULL) { delete [] ho_sqr[ord]; }
2620  }
2621  for (int ord=4; ord<ho_tet.Size(); ord++)
2622  {
2623  if (ho_tet[ord] != NULL) { delete [] ho_tet[ord]; }
2624  }
2625  for (int ord=4; ord<ho_hex.Size(); ord++)
2626  {
2627  if (ho_hex[ord] != NULL) { delete [] ho_hex[ord]; }
2628  }
2629  for (int ord=4; ord<ho_wdg.Size(); ord++)
2630  {
2631  if (ho_wdg[ord] != NULL) { delete [] ho_wdg[ord]; }
2632  }
2633  for (int ord=4; ord<ho_pyr.Size(); ord++)
2634  {
2635  if (ho_pyr[ord] != NULL) { delete [] ho_pyr[ord]; }
2636  }
2637 
2638  // Suppress warnings (MFEM_CONTRACT_VAR does not work here with nvcc):
2639  ++n_partitions;
2640  ++elem_domain;
2641 
2642  } // section '$Elements'
2643  else if (buff == "$Periodic") // Reading master/slave node pairs
2644  {
2645  curved = 1;
2646  read_gf = 0;
2647  periodic = true;
2648 
2649  Array<int> v2v(NumOfVertices);
2650  for (int i = 0; i < v2v.Size(); i++)
2651  {
2652  v2v[i] = i;
2653  }
2654  int num_per_ent;
2655  int num_nodes;
2656  input >> num_per_ent;
2657  getline(input, buff); // Read end-of-line
2658  for (int i = 0; i < num_per_ent; i++)
2659  {
2660  getline(input, buff); // Read and ignore entity dimension and tags
2661  getline(input, buff); // If affine mapping exist, read and ignore
2662  if (!strncmp(buff.c_str(), "Affine", 6))
2663  {
2664  input >> num_nodes;
2665  }
2666  else
2667  {
2668  num_nodes = atoi(buff.c_str());
2669  }
2670  for (int j=0; j<num_nodes; j++)
2671  {
2672  int slave, master;
2673  input >> slave >> master;
2674  v2v[slave - 1] = master - 1;
2675  }
2676  getline(input, buff); // Read end-of-line
2677  }
2678 
2679  // Follow existing long chains of slave->master in v2v array.
2680  // Upon completion of this loop, each v2v[slave] will point to a true
2681  // master vertex. This algorithm is useful for periodicity defined in
2682  // multiple directions.
2683  for (int slave = 0; slave < v2v.Size(); slave++)
2684  {
2685  int master = v2v[slave];
2686  if (master != slave)
2687  {
2688  // This loop will end if it finds a circular dependency.
2689  while (v2v[master] != master && master != slave)
2690  {
2691  master = v2v[master];
2692  }
2693  if (master == slave)
2694  {
2695  // if master and slave are the same vertex, circular dependency
2696  // exists. We need to fix the problem, we choose slave.
2697  v2v[slave] = slave;
2698  }
2699  else
2700  {
2701  // the long chain has ended on the true master vertex.
2702  v2v[slave] = master;
2703  }
2704  }
2705  }
2706 
2707  // Convert nodes to discontinuous GridFunction (if they aren't already)
2708  if (mesh_order == 1)
2709  {
2710  this->FinalizeTopology();
2711  this->SetMeshGen();
2712  this->SetCurvature(1, true, spaceDim, Ordering::byVDIM);
2713  }
2714 
2715  // Replace "slave" vertex indices in the element connectivity
2716  // with their corresponding "master" vertex indices.
2717  for (int i = 0; i < this->GetNE(); i++)
2718  {
2719  Element *el = this->GetElement(i);
2720  int *v = el->GetVertices();
2721  int nv = el->GetNVertices();
2722  for (int j = 0; j < nv; j++)
2723  {
2724  v[j] = v2v[v[j]];
2725  }
2726  }
2727  // Replace "slave" vertex indices in the boundary element connectivity
2728  // with their corresponding "master" vertex indices.
2729  for (int i = 0; i < this->GetNBE(); i++)
2730  {
2731  Element *el = this->GetBdrElement(i);
2732  int *v = el->GetVertices();
2733  int nv = el->GetNVertices();
2734  for (int j = 0; j < nv; j++)
2735  {
2736  v[j] = v2v[v[j]];
2737  }
2738  }
2739  }
2740  } // we reach the end of the file
2741 
2742  this->RemoveUnusedVertices();
2743  if (periodic)
2744  {
2745  this->RemoveInternalBoundaries();
2746  }
2747  this->FinalizeTopology();
2748 
2749  // If a high order coordinate field was created project it onto the mesh
2750  if (mesh_order > 1)
2751  {
2752  SetCurvature(mesh_order, periodic, spaceDim, Ordering::byVDIM);
2753 
2754  VectorGridFunctionCoefficient NodesCoef(&Nodes_gf);
2755  Nodes->ProjectCoefficient(NodesCoef);
2756  }
2757 }
2758 
2759 
2760 #ifdef MFEM_USE_NETCDF
2761 void Mesh::ReadCubit(const char *filename, int &curved, int &read_gf)
2762 {
2763  read_gf = 0;
2764 
2765  // curved set to zero will change if mesh is indeed curved
2766  curved = 0;
2767 
2768  const int sideMapTri3[3][2] =
2769  {
2770  {1,2},
2771  {2,3},
2772  {3,1},
2773  };
2774 
2775  const int sideMapQuad4[4][2] =
2776  {
2777  {1,2},
2778  {2,3},
2779  {3,4},
2780  {4,1},
2781  };
2782 
2783  const int sideMapTri6[3][3] =
2784  {
2785  {1,2,4},
2786  {2,3,5},
2787  {3,1,6},
2788  };
2789 
2790  const int sideMapQuad9[4][3] =
2791  {
2792  {1,2,5},
2793  {2,3,6},
2794  {3,4,7},
2795  {4,1,8},
2796  };
2797 
2798  const int sideMapTet4[4][3] =
2799  {
2800  {1,2,4},
2801  {2,3,4},
2802  {1,4,3},
2803  {1,3,2}
2804  };
2805 
2806  const int sideMapTet10[4][6] =
2807  {
2808  {1,2,4,5,9,8},
2809  {2,3,4,6,10,9},
2810  {1,4,3,8,10,7},
2811  {1,3,2,7,6,5}
2812  };
2813 
2814  const int sideMapHex8[6][4] =
2815  {
2816  {1,2,6,5},
2817  {2,3,7,6},
2818  {4,3,7,8},
2819  {1,4,8,5},
2820  {1,4,3,2},
2821  {5,8,7,6}
2822  };
2823 
2824  const int sideMapHex27[6][9] =
2825  {
2826  {1,2,6,5,9,14,17,13,26},
2827  {2,3,7,6,10,15,18,14,25},
2828  {4,3,7,8,11,15,19,16,27},
2829  {1,4,8,5,12,16,20,13,24},
2830  {1,4,3,2,12,11,10,9,22},
2831  {5,8,7,6,20,19,18,17,23}
2832  };
2833 
2834 
2835  // 1,2,3,4,5,6,7,8,9,10
2836  const int mfemToGenesisTet10[10] = {1,2,3,4,5,7,8,6,9,10};
2837 
2838  // 1,2,3,4,5,6,7,8,9,10,11,
2839  const int mfemToGenesisHex27[27] = {1,2,3,4,5,6,7,8,9,10,11,
2840  // 12,13,14,15,16,17,18,19
2841  12,17,18,19,20,13,14,15,
2842  // 20,21,22,23,24,25,26,27
2843  16,22,26,25,27,24,23,21
2844  };
2845 
2846  const int mfemToGenesisTri6[6] = {1,2,3,4,5,6};
2847  const int mfemToGenesisQuad9[9] = {1,2,3,4,5,6,7,8,9};
2848 
2849 
2850  // error handling.
2851  int retval;
2852 
2853  // dummy string
2854  char str_dummy[256];
2855 
2856  char temp_str[256];
2857  int temp_id;
2858 
2859  // open the file.
2860  int ncid;
2861  if ((retval = nc_open(filename, NC_NOWRITE, &ncid)))
2862  {
2863  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
2864  }
2865 
2866  // read important dimensions
2867 
2868  int id;
2869  size_t num_dim=0, num_nodes=0, num_elem=0, num_el_blk=0, num_side_sets=0;
2870 
2871  if ((retval = nc_inq_dimid(ncid, "num_dim", &id)) ||
2872  (retval = nc_inq_dim(ncid, id, str_dummy, &num_dim)) ||
2873 
2874  (retval = nc_inq_dimid(ncid, "num_nodes", &id)) ||
2875  (retval = nc_inq_dim(ncid, id, str_dummy, &num_nodes)) ||
2876 
2877  (retval = nc_inq_dimid(ncid, "num_elem", &id)) ||
2878  (retval = nc_inq_dim(ncid, id, str_dummy, &num_elem)) ||
2879 
2880  (retval = nc_inq_dimid(ncid, "num_el_blk", &id)) ||
2881  (retval = nc_inq_dim(ncid, id, str_dummy, &num_el_blk)))
2882  {
2883  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
2884  }
2885  if ((retval = nc_inq_dimid(ncid, "num_side_sets", &id)) ||
2886  (retval = nc_inq_dim(ncid, id, str_dummy, &num_side_sets)))
2887  {
2888  num_side_sets = 0;
2889  }
2890 
2891  Dim = num_dim;
2892 
2893  // create arrays for element blocks
2894  size_t *num_el_in_blk = new size_t[num_el_blk];
2895  size_t num_node_per_el;
2896 
2897  int previous_num_node_per_el = 0;
2898  for (int i = 0; i < (int) num_el_blk; i++)
2899  {
2900  sprintf(temp_str, "num_el_in_blk%d", i+1);
2901  if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) ||
2902  (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_el_in_blk[i])))
2903  {
2904  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
2905  }
2906 
2907  sprintf(temp_str, "num_nod_per_el%d", i+1);
2908  if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) ||
2909  (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_node_per_el)))
2910  {
2911  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
2912  }
2913 
2914  // check for different element types in each block
2915  // which is not currently supported
2916  if (i != 0)
2917  {
2918  if ((int) num_node_per_el != previous_num_node_per_el)
2919  {
2920  MFEM_ABORT("Element blocks of different element types not supported");
2921  }
2922  }
2923  previous_num_node_per_el = num_node_per_el;
2924  }
2925 
2926  // Determine CUBIT element and face type
2927  enum CubitElementType
2928  {
2929  ELEMENT_TRI3,
2930  ELEMENT_TRI6,
2931  ELEMENT_QUAD4,
2932  ELEMENT_QUAD9,
2933  ELEMENT_TET4,
2934  ELEMENT_TET10,
2935  ELEMENT_HEX8,
2936  ELEMENT_HEX27
2937  };
2938 
2939  enum CubitFaceType
2940  {
2941  FACE_EDGE2,
2942  FACE_EDGE3,
2943  FACE_TRI3,
2944  FACE_TRI6,
2945  FACE_QUAD4,
2946  FACE_QUAD9
2947  };
2948 
2949  CubitElementType cubit_element_type = ELEMENT_TRI3; // suppress a warning
2950  CubitFaceType cubit_face_type = FACE_EDGE2; // suppress a warning
2951  int num_element_linear_nodes = 0; // initialize to suppress a warning
2952 
2953  if (num_dim == 2)
2954  {
2955  switch (num_node_per_el)
2956  {
2957  case (3) :
2958  {
2959  cubit_element_type = ELEMENT_TRI3;
2960  cubit_face_type = FACE_EDGE2;
2961  num_element_linear_nodes = 3;
2962  break;
2963  }
2964  case (6) :
2965  {
2966  cubit_element_type = ELEMENT_TRI6;
2967  cubit_face_type = FACE_EDGE3;
2968  num_element_linear_nodes = 3;
2969  break;
2970  }
2971  case (4) :
2972  {
2973  cubit_element_type = ELEMENT_QUAD4;
2974  cubit_face_type = FACE_EDGE2;
2975  num_element_linear_nodes = 4;
2976  break;
2977  }
2978  case (9) :
2979  {
2980  cubit_element_type = ELEMENT_QUAD9;
2981  cubit_face_type = FACE_EDGE3;
2982  num_element_linear_nodes = 4;
2983  break;
2984  }
2985  default :
2986  {
2987  MFEM_ABORT("Don't know what to do with a " << num_node_per_el <<
2988  " node 2D element\n");
2989  }
2990  }
2991  }
2992  else if (num_dim == 3)
2993  {
2994  switch (num_node_per_el)
2995  {
2996  case (4) :
2997  {
2998  cubit_element_type = ELEMENT_TET4;
2999  cubit_face_type = FACE_TRI3;
3000  num_element_linear_nodes = 4;
3001  break;
3002  }
3003  case (10) :
3004  {
3005  cubit_element_type = ELEMENT_TET10;
3006  cubit_face_type = FACE_TRI6;
3007  num_element_linear_nodes = 4;
3008  break;
3009  }
3010  case (8) :
3011  {
3012  cubit_element_type = ELEMENT_HEX8;
3013  cubit_face_type = FACE_QUAD4;
3014  num_element_linear_nodes = 8;
3015  break;
3016  }
3017  case (27) :
3018  {
3019  cubit_element_type = ELEMENT_HEX27;
3020  cubit_face_type = FACE_QUAD9;
3021  num_element_linear_nodes = 8;
3022  break;
3023  }
3024  default :
3025  {
3026  MFEM_ABORT("Don't know what to do with a " << num_node_per_el <<
3027  " node 3D element\n");
3028  }
3029  }
3030  }
3031  else
3032  {
3033  MFEM_ABORT("Invalid dimension: num_dim = " << num_dim);
3034  }
3035 
3036  // Determine order of elements
3037  int order = 0;
3038  if (cubit_element_type == ELEMENT_TRI3 || cubit_element_type == ELEMENT_QUAD4 ||
3039  cubit_element_type == ELEMENT_TET4 || cubit_element_type == ELEMENT_HEX8)
3040  {
3041  order = 1;
3042  }
3043  else if (cubit_element_type == ELEMENT_TRI6 ||
3044  cubit_element_type == ELEMENT_QUAD9 ||
3045  cubit_element_type == ELEMENT_TET10 || cubit_element_type == ELEMENT_HEX27)
3046  {
3047  order = 2;
3048  }
3049 
3050  // create array for number of sides in side sets
3051  size_t *num_side_in_ss = new size_t[num_side_sets];
3052  for (int i = 0; i < (int) num_side_sets; i++)
3053  {
3054  sprintf(temp_str, "num_side_ss%d", i+1);
3055  if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) ||
3056  (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_side_in_ss[i])))
3057  {
3058  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3059  }
3060  }
3061 
3062  // read the coordinates
3063  double *coordx = new double[num_nodes];
3064  double *coordy = new double[num_nodes];
3065  double *coordz = new double[num_nodes];
3066 
3067  if ((retval = nc_inq_varid(ncid, "coordx", &id)) ||
3068  (retval = nc_get_var_double(ncid, id, coordx)) ||
3069  (retval = nc_inq_varid(ncid, "coordy", &id)) ||
3070  (retval = nc_get_var_double(ncid, id, coordy)))
3071  {
3072  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3073  }
3074 
3075  if (num_dim == 3)
3076  {
3077  if ((retval = nc_inq_varid(ncid, "coordz", &id)) ||
3078  (retval = nc_get_var_double(ncid, id, coordz)))
3079  {
3080  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3081  }
3082  }
3083 
3084  // read the element blocks
3085  int **elem_blk = new int*[num_el_blk];
3086  for (int i = 0; i < (int) num_el_blk; i++)
3087  {
3088  elem_blk[i] = new int[num_el_in_blk[i] * num_node_per_el];
3089  sprintf(temp_str, "connect%d", i+1);
3090  if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) ||
3091  (retval = nc_get_var_int(ncid, temp_id, elem_blk[i])))
3092  {
3093  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3094  }
3095  }
3096  int *ebprop = new int[num_el_blk];
3097  if ((retval = nc_inq_varid(ncid, "eb_prop1", &id)) ||
3098  (retval = nc_get_var_int(ncid, id, ebprop)))
3099  {
3100  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3101  }
3102 
3103  // read the side sets, a side is is given by (element, face) pairs
3104 
3105  int **elem_ss = new int*[num_side_sets];
3106  int **side_ss = new int*[num_side_sets];
3107 
3108  for (int i = 0; i < (int) num_side_sets; i++)
3109  {
3110  elem_ss[i] = new int[num_side_in_ss[i]];
3111  side_ss[i] = new int[num_side_in_ss[i]];
3112 
3113  sprintf(temp_str, "elem_ss%d", i+1);
3114  if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) ||
3115  (retval = nc_get_var_int(ncid, temp_id, elem_ss[i])))
3116  {
3117  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3118  }
3119 
3120  sprintf(temp_str,"side_ss%d",i+1);
3121  if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) ||
3122  (retval = nc_get_var_int(ncid, temp_id, side_ss[i])))
3123  {
3124  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3125  }
3126  }
3127 
3128  int *ssprop = new int[num_side_sets];
3129  if ((num_side_sets > 0) &&
3130  ((retval = nc_inq_varid(ncid, "ss_prop1", &id)) ||
3131  (retval = nc_get_var_int(ncid, id, ssprop))))
3132  {
3133  MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval));
3134  }
3135 
3136  // convert (elem,side) pairs to 2D elements
3137 
3138 
3139  int num_face_nodes = 0;
3140  int num_face_linear_nodes = 0;
3141 
3142  switch (cubit_face_type)
3143  {
3144  case (FACE_EDGE2):
3145  {
3146  num_face_nodes = 2;
3147  num_face_linear_nodes = 2;
3148  break;
3149  }
3150  case (FACE_EDGE3):
3151  {
3152  num_face_nodes = 3;
3153  num_face_linear_nodes = 2;
3154  break;
3155  }
3156  case (FACE_TRI3):
3157  {
3158  num_face_nodes = 3;
3159  num_face_linear_nodes = 3;
3160  break;
3161  }
3162  case (FACE_TRI6):
3163  {
3164  num_face_nodes = 6;
3165  num_face_linear_nodes = 3;
3166  break;
3167  }
3168  case (FACE_QUAD4):
3169  {
3170  num_face_nodes = 4;
3171  num_face_linear_nodes = 4;
3172  break;
3173  }
3174  case (FACE_QUAD9):
3175  {
3176  num_face_nodes = 9;
3177  num_face_linear_nodes = 4;
3178  break;
3179  }
3180  }
3181 
3182  // given a global element number, determine the element block and local
3183  // element number
3184  int *start_of_block = new int[num_el_blk+1];
3185  start_of_block[0] = 0;
3186  for (int i = 1; i < (int) num_el_blk+1; i++)
3187  {
3188  start_of_block[i] = start_of_block[i-1] + num_el_in_blk[i-1];
3189  }
3190 
3191  int **ss_node_id = new int*[num_side_sets];
3192 
3193  for (int i = 0; i < (int) num_side_sets; i++)
3194  {
3195  ss_node_id[i] = new int[num_side_in_ss[i]*num_face_nodes];
3196  for (int j = 0; j < (int) num_side_in_ss[i]; j++)
3197  {
3198  int glob_ind = elem_ss[i][j]-1;
3199  int iblk = 0;
3200  int loc_ind;
3201  while (iblk < (int) num_el_blk && glob_ind >= start_of_block[iblk+1])
3202  {
3203  iblk++;
3204  }
3205  if (iblk >= (int) num_el_blk)
3206  {
3207  MFEM_ABORT("Sideset element does not exist");
3208  }
3209  loc_ind = glob_ind - start_of_block[iblk];
3210  int this_side = side_ss[i][j];
3211  int ielem = loc_ind*num_node_per_el;
3212 
3213  for (int k = 0; k < num_face_nodes; k++)
3214  {
3215  int inode;
3216  switch (cubit_element_type)
3217  {
3218  case (ELEMENT_TRI3):
3219  {
3220  inode = sideMapTri3[this_side-1][k];
3221  break;
3222  }
3223  case (ELEMENT_TRI6):
3224  {
3225  inode = sideMapTri6[this_side-1][k];
3226  break;
3227  }
3228  case (ELEMENT_QUAD4):
3229  {
3230  inode = sideMapQuad4[this_side-1][k];
3231  break;
3232  }
3233  case (ELEMENT_QUAD9):
3234  {
3235  inode = sideMapQuad9[this_side-1][k];
3236  break;
3237  }
3238  case (ELEMENT_TET4):
3239  {
3240  inode = sideMapTet4[this_side-1][k];
3241  break;
3242  }
3243  case (ELEMENT_TET10):
3244  {
3245  inode = sideMapTet10[this_side-1][k];
3246  break;
3247  }
3248  case (ELEMENT_HEX8):
3249  {
3250  inode = sideMapHex8[this_side-1][k];
3251  break;
3252  }
3253  case (ELEMENT_HEX27):
3254  {
3255  inode = sideMapHex27[this_side-1][k];
3256  break;
3257  }
3258  }
3259  ss_node_id[i][j*num_face_nodes+k] =
3260  elem_blk[iblk][ielem + inode - 1];
3261  }
3262  }
3263  }
3264 
3265  // we need another node ID mapping since MFEM needs contiguous vertex IDs
3266  std::vector<int> uniqueVertexID;
3267 
3268  for (int iblk = 0; iblk < (int) num_el_blk; iblk++)
3269  {
3270  for (int i = 0; i < (int) num_el_in_blk[iblk]; i++)
3271  {
3272  for (int j = 0; j < num_element_linear_nodes; j++)
3273  {
3274  uniqueVertexID.push_back(elem_blk[iblk][i*num_node_per_el + j]);
3275  }
3276  }
3277  }
3278  std::sort(uniqueVertexID.begin(), uniqueVertexID.end());
3279  std::vector<int>::iterator newEnd;
3280  newEnd = std::unique(uniqueVertexID.begin(), uniqueVertexID.end());
3281  uniqueVertexID.resize(std::distance(uniqueVertexID.begin(), newEnd));
3282 
3283  // OK at this point uniqueVertexID contains a list of all the nodes that are
3284  // actually used by the mesh, 1-based, and sorted. We need to invert this
3285  // list, the inverse is a map
3286 
3287  std::map<int,int> cubitToMFEMVertMap;
3288  for (int i = 0; i < (int) uniqueVertexID.size(); i++)
3289  {
3290  cubitToMFEMVertMap[uniqueVertexID[i]] = i+1;
3291  }
3292  MFEM_ASSERT(cubitToMFEMVertMap.size() == uniqueVertexID.size(),
3293  "This should never happen\n");
3294 
3295  // OK now load up the MFEM mesh structures
3296 
3297  // load up the vertices
3298 
3299  NumOfVertices = uniqueVertexID.size();
3300  vertices.SetSize(NumOfVertices);
3301  for (int i = 0; i < (int) uniqueVertexID.size(); i++)
3302  {
3303  vertices[i](0) = coordx[uniqueVertexID[i] - 1];
3304  vertices[i](1) = coordy[uniqueVertexID[i] - 1];
3305  if (Dim == 3)
3306  {
3307  vertices[i](2) = coordz[uniqueVertexID[i] - 1];
3308  }
3309  }
3310 
3311  NumOfElements = num_elem;
3312  elements.SetSize(num_elem);
3313  int elcount = 0;
3314  int renumberedVertID[8];
3315  for (int iblk = 0; iblk < (int) num_el_blk; iblk++)
3316  {
3317  int NumNodePerEl = num_node_per_el;
3318  for (int i = 0; i < (int) num_el_in_blk[iblk]; i++)
3319  {
3320  for (int j = 0; j < num_element_linear_nodes; j++)
3321  {
3322  renumberedVertID[j] =
3323  cubitToMFEMVertMap[elem_blk[iblk][i*NumNodePerEl+j]]-1;
3324  }
3325 
3326  switch (cubit_element_type)
3327  {
3328  case (ELEMENT_TRI3):
3329  case (ELEMENT_TRI6):
3330  {
3331  elements[elcount] = new Triangle(renumberedVertID,ebprop[iblk]);
3332  break;
3333  }
3334  case (ELEMENT_QUAD4):
3335  case (ELEMENT_QUAD9):
3336  {
3337  elements[elcount] = new Quadrilateral(renumberedVertID,ebprop[iblk]);
3338  break;
3339  }
3340  case (ELEMENT_TET4):
3341  case (ELEMENT_TET10):
3342  {
3343 #ifdef MFEM_USE_MEMALLOC
3344  elements[elcount] = TetMemory.Alloc();
3345  elements[elcount]->SetVertices(renumberedVertID);
3346  elements[elcount]->SetAttribute(ebprop[iblk]);
3347 #else
3348  elements[elcount] = new Tetrahedron(renumberedVertID,
3349  ebprop[iblk]);
3350 #endif
3351  break;
3352  }
3353  case (ELEMENT_HEX8):
3354  case (ELEMENT_HEX27):
3355  {
3356  elements[elcount] = new Hexahedron(renumberedVertID,ebprop[iblk]);
3357  break;
3358  }
3359  }
3360  elcount++;
3361  }
3362  }
3363 
3364  // load up the boundary elements
3365 
3366  NumOfBdrElements = 0;
3367  for (int iss = 0; iss < (int) num_side_sets; iss++)
3368  {
3369  NumOfBdrElements += num_side_in_ss[iss];
3370  }
3371  boundary.SetSize(NumOfBdrElements);
3372  int sidecount = 0;
3373  for (int iss = 0; iss < (int) num_side_sets; iss++)
3374  {
3375  for (int i = 0; i < (int) num_side_in_ss[iss]; i++)
3376  {
3377  for (int j = 0; j < num_face_linear_nodes; j++)
3378  {
3379  renumberedVertID[j] =
3380  cubitToMFEMVertMap[ss_node_id[iss][i*num_face_nodes+j]] - 1;
3381  }
3382  switch (cubit_face_type)
3383  {
3384  case (FACE_EDGE2):
3385  case (FACE_EDGE3):
3386  {
3387  boundary[sidecount] = new Segment(renumberedVertID,ssprop[iss]);
3388  break;
3389  }
3390  case (FACE_TRI3):
3391  case (FACE_TRI6):
3392  {
3393  boundary[sidecount] = new Triangle(renumberedVertID,ssprop[iss]);
3394  break;
3395  }
3396  case (FACE_QUAD4):
3397  case (FACE_QUAD9):
3398  {
3399  boundary[sidecount] = new Quadrilateral(renumberedVertID,ssprop[iss]);
3400  break;
3401  }
3402  }
3403  sidecount++;
3404  }
3405  }
3406 
3407  if (order == 2)
3408  {
3409  curved = 1;
3410  int *mymap = NULL;
3411 
3412  switch (cubit_element_type)
3413  {
3414  case (ELEMENT_TRI6):
3415  {
3416  mymap = (int *) mfemToGenesisTri6;
3417  break;
3418  }
3419  case (ELEMENT_QUAD9):
3420  {
3421  mymap = (int *) mfemToGenesisQuad9;
3422  break;
3423  }
3424  case (ELEMENT_TET10):
3425  {
3426  mymap = (int *) mfemToGenesisTet10;
3427  break;
3428  }
3429  case (ELEMENT_HEX27):
3430  {
3431  mymap = (int *) mfemToGenesisHex27;
3432  break;
3433  }
3434  case (ELEMENT_TRI3):
3435  case (ELEMENT_QUAD4):
3436  case (ELEMENT_TET4):
3437  case (ELEMENT_HEX8):
3438  {
3439  MFEM_ABORT("Something went wrong. Linear elements detected when order is 2.");
3440  break;
3441  }
3442  }
3443 
3444  FinalizeTopology();
3445 
3446  // Define quadratic FE space
3447  FiniteElementCollection *fec = new H1_FECollection(2,3);
3448  FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
3449  Ordering::byVDIM);
3450  Nodes = new GridFunction(fes);
3451  Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
3452  own_nodes = 1;
3453 
3454  // int nTotDofs = fes->GetNDofs();
3455  // int nTotVDofs = fes->GetVSize();
3456  // mfem::out << endl << "nTotDofs = " << nTotDofs << " nTotVDofs "
3457  // << nTotVDofs << endl << endl;
3458 
3459  for (int i = 0; i < NumOfElements; i++)
3460  {
3461  Array<int> dofs;
3462 
3463  fes->GetElementDofs(i, dofs);
3464  Array<int> vdofs;
3465  vdofs.SetSize(dofs.Size());
3466  for (int l = 0; l < dofs.Size(); l++) { vdofs[l] = dofs[l]; }
3467  fes->DofsToVDofs(vdofs);
3468  int iblk = 0;
3469  int loc_ind;
3470  while (iblk < (int) num_el_blk && i >= start_of_block[iblk+1]) { iblk++; }
3471  loc_ind = i - start_of_block[iblk];
3472  for (int j = 0; j < dofs.Size(); j++)
3473  {
3474  int point_id = elem_blk[iblk][loc_ind*num_node_per_el + mymap[j] - 1] - 1;
3475  (*Nodes)(vdofs[j]) = coordx[point_id];
3476  (*Nodes)(vdofs[j]+1) = coordy[point_id];
3477  if (Dim == 3)
3478  {
3479  (*Nodes)(vdofs[j]+2) = coordz[point_id];
3480  }
3481  }
3482  }
3483  }
3484 
3485  // clean up all netcdf stuff
3486 
3487  nc_close(ncid);
3488 
3489  for (int i = 0; i < (int) num_side_sets; i++)
3490  {
3491  delete [] elem_ss[i];
3492  delete [] side_ss[i];
3493  }
3494 
3495  delete [] elem_ss;
3496  delete [] side_ss;
3497  delete [] num_el_in_blk;
3498  delete [] num_side_in_ss;
3499  delete [] coordx;
3500  delete [] coordy;
3501  delete [] coordz;
3502 
3503  for (int i = 0; i < (int) num_el_blk; i++)
3504  {
3505  delete [] elem_blk[i];
3506  }
3507 
3508  delete [] elem_blk;
3509  delete [] start_of_block;
3510 
3511  for (int i = 0; i < (int) num_side_sets; i++)
3512  {
3513  delete [] ss_node_id[i];
3514  }
3515  delete [] ss_node_id;
3516  delete [] ebprop;
3517  delete [] ssprop;
3518 
3519 }
3520 #endif // #ifdef MFEM_USE_NETCDF
3521 
3522 } // namespace mfem
Abstract class for all finite elements.
Definition: fe_base.hpp:235
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition: fe_coll.hpp:602
Vector bb_max
Definition: ex9.cpp:67
Mesh * Make3D(int nsteps, double rstep, double aspect, int order, bool sfc)
Definition: polar-nc.cpp:370
int Size() const
Return the logical size of the array.
Definition: array.hpp:138
void Load(std::istream &in, int fmt=0)
Read an Array from the stream in using format fmt. The format fmt can be:
Definition: array.cpp:53
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:30
virtual void GetVertices(Array< int > &v) const =0
Returns element&#39;s vertices.
void GmshHOSegmentMapping(int order, int *map)
Generate Gmsh vertex mapping for a Segment.
Definition: gmsh.cpp:378
T * GetData()
Returns the data.
Definition: array.hpp:112
int Size() const
Returns the size of the vector.
Definition: vector.hpp:199
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec and fes.
Definition: gridfunc.hpp:124
Data type for vertex.
Definition: vertex.hpp:22
virtual void SetVertices(const int *ind)
Set the vertices according to the given input.
Data type quadrilateral element.
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:208
Data type Wedge element.
Definition: wedge.hpp:22
void skip_comment_lines(std::istream &is, const char comment_char)
Check if the stream starts with comment_char. If so skip it.
Definition: text.hpp:31
void DecodeBase64(const char *src, size_t len, std::vector< char > &buf)
Decode len base-64 encoded characters in the buffer src, and store the resulting decoded data in buf...
Definition: binaryio.cpp:74
Mesh * Make2D(int nsteps, double rstep, double phi, double aspect, int order, bool sfc)
Definition: polar-nc.cpp:49
Class for standard nodal finite elements.
Definition: fe_base.hpp:706
void Load(std::istream **in, int np, int *dim)
Reads a vector from multiple files.
Definition: vector.cpp:57
Data type hexahedron element.
Definition: hexahedron.hpp:22
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:751
Type
Constants for the classes derived from Element.
Definition: element.hpp:41
Data type triangle element.
Definition: triangle.hpp:23
size_t NumBase64Chars(size_t nbytes)
Return the number of characters needed to encode nbytes in base-64.
Definition: binaryio.cpp:103
const T * Read(const Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read access to mem with the mfem::Device&#39;s DeviceMemoryClass, if on_dev = true...
Definition: device.hpp:319
void GmshHOQuadrilateralMapping(int order, int *map)
Generate Gmsh vertex mapping for a Quadrilateral.
Definition: gmsh.cpp:403
virtual DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element &#39;elem&#39;.
Definition: fespace.cpp:2680
Data type tetrahedron element.
Definition: tetrahedron.hpp:22
Vector bb_min
Definition: ex9.cpp:67
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:88
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition: fe_coll.hpp:26
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:679
void GmshHOTriangleMapping(int order, int *map)
Generate Gmsh vertex mapping for a Triangle.
Definition: gmsh.cpp:388
void GmshHOTetrahedronMapping(int order, int *map)
Generate Gmsh vertex mapping for a Tetrahedron.
Definition: gmsh.cpp:417
const Array< int > & GetLexicographicOrdering() const
Get an Array&lt;int&gt; that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition: fe_base.hpp:780
double a
Definition: lissajous.cpp:41
bool StringCompare(const char *s1, const char *s2)
const char * VTKByteOrder()
Determine the byte order and return either &quot;BigEndian&quot; or &quot;LittleEndian&quot;.
Definition: vtk.cpp:590
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th element in t...
Definition: fespace.cpp:2783
int index(int i, int j, int nx, int ny)
Definition: life.cpp:237
virtual int GetNVertices() const =0
void SetAttribute(const int attr)
Set element&#39;s attribute.
Definition: element.hpp:58
Piecewise-(bi)quadratic continuous finite elements.
Definition: fe_coll.hpp:687
virtual void SetSpace(FiniteElementSpace *f)
Associate a new FiniteElementSpace with the GridFunction.
Definition: gridfunc.cpp:193
void filter_dos(std::string &line)
Check for, and remove, a trailing &#39;\r&#39; from and std::string.
Definition: text.hpp:45
Vector data type.
Definition: vector.hpp:60
Data type point element.
Definition: point.hpp:22
Vector coefficient defined by a vector GridFunction.
void GmshHOHexahedronMapping(int order, int *map)
Generate Gmsh vertex mapping for a Hexahedron.
Definition: gmsh.cpp:436
void CreateVTKElementConnectivity(Array< int > &con, Geometry::Type geom, int ref)
Create the VTK element connectivity array for a given element geometry and refinement level...
Definition: vtk.cpp:489
Arbitrary order H1-conforming (continuous) finite elements.
Definition: fe_coll.hpp:216
void GmshHOWedgeMapping(int order, int *map)
Generate Gmsh vertex mapping for a Wedge.
Definition: gmsh.cpp:453
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
Abstract data type element.
Definition: element.hpp:28
Data type line segment element.
Definition: segment.hpp:22
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:284
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Definition: fespace.cpp:215