MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh_readers.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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"
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#include <map>
24
25#ifdef MFEM_USE_NETCDF
26#include "netcdf.h"
27#endif
28
29#ifdef MFEM_USE_ZLIB
30#include <zlib.h>
31#endif
32
33using namespace std;
34
35namespace mfem
36{
37
39
40void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
41{
42 // Read MFEM mesh v1.0, v1.2, or v1.3 format
43 MFEM_VERIFY(version == 10 || version == 12 || version == 13,
44 "unknown MFEM mesh version");
45
46 string ident;
47
48 // read lines beginning with '#' (comments)
49 skip_comment_lines(input, '#');
50 input >> ident; // 'dimension'
51
52 MFEM_VERIFY(ident == "dimension", "invalid mesh file");
53 input >> Dim;
54
55 skip_comment_lines(input, '#');
56 input >> ident; // 'elements'
57
58 MFEM_VERIFY(ident == "elements", "invalid mesh file");
59 input >> NumOfElements;
60 elements.SetSize(NumOfElements);
61 for (int j = 0; j < NumOfElements; j++)
62 {
63 elements[j] = ReadElement(input);
64 }
65
66 if (version == 13)
67 {
68 skip_comment_lines(input, '#');
69 input >> ident; // 'attribute_sets'
70
71 MFEM_VERIFY(ident == "attribute_sets", "invalid mesh file");
72
76 }
77
78 skip_comment_lines(input, '#');
79 input >> ident; // 'boundary'
80
81 MFEM_VERIFY(ident == "boundary", "invalid mesh file");
82 input >> NumOfBdrElements;
84 for (int j = 0; j < NumOfBdrElements; j++)
85 {
86 boundary[j] = ReadElement(input);
87 }
88
89 if (version == 13)
90 {
91 skip_comment_lines(input, '#');
92 input >> ident; // 'bdr_attribute_sets'
93
94 MFEM_VERIFY(ident == "bdr_attribute_sets", "invalid mesh file");
95
99 }
100
101 skip_comment_lines(input, '#');
102 input >> ident; // 'vertices'
103
104 MFEM_VERIFY(ident == "vertices", "invalid mesh file");
105 input >> NumOfVertices;
106 vertices.SetSize(NumOfVertices);
107
108 input >> ws >> ident;
109 if (ident != "nodes")
110 {
111 // read the vertices
112 spaceDim = atoi(ident.c_str());
113 for (int j = 0; j < NumOfVertices; j++)
114 {
115 for (int i = 0; i < spaceDim; i++)
116 {
117 input >> vertices[j](i);
118 }
119 }
120 }
121 else
122 {
123 // prepare to read the nodes
124 input >> ws;
125 curved = 1;
126 }
127
128 // When visualizing solutions on non-conforming grids, PETSc
129 // may dump additional vertices
131}
132
133void Mesh::ReadLineMesh(std::istream &input)
134{
135 int j,p1,p2,a;
136
137 Dim = 1;
138
139 input >> NumOfVertices;
140 vertices.SetSize(NumOfVertices);
141 // Sets vertices and the corresponding coordinates
142 for (j = 0; j < NumOfVertices; j++)
143 {
144 input >> vertices[j](0);
145 }
146
147 input >> NumOfElements;
148 elements.SetSize(NumOfElements);
149 // Sets elements and the corresponding indices of vertices
150 for (j = 0; j < NumOfElements; j++)
151 {
152 input >> a >> p1 >> p2;
153 elements[j] = new Segment(p1-1, p2-1, a);
154 }
155
156 int ind[1];
157 input >> NumOfBdrElements;
158 boundary.SetSize(NumOfBdrElements);
159 for (j = 0; j < NumOfBdrElements; j++)
160 {
161 input >> a >> ind[0];
162 ind[0]--;
163 boundary[j] = new Point(ind,a);
164 }
165}
166
167void Mesh::ReadNetgen2DMesh(std::istream &input, int &curved)
168{
169 int ints[32], attr, n;
170
171 // Read planar mesh in Netgen format.
172 Dim = 2;
173
174 // Read the boundary elements.
175 input >> NumOfBdrElements;
176 boundary.SetSize(NumOfBdrElements);
177 for (int i = 0; i < NumOfBdrElements; i++)
178 {
179 input >> attr
180 >> ints[0] >> ints[1];
181 ints[0]--; ints[1]--;
182 boundary[i] = new Segment(ints, attr);
183 }
184
185 // Read the elements.
186 input >> NumOfElements;
187 elements.SetSize(NumOfElements);
188 for (int i = 0; i < NumOfElements; i++)
189 {
190 input >> attr >> n;
191 for (int j = 0; j < n; j++)
192 {
193 input >> ints[j];
194 ints[j]--;
195 }
196 switch (n)
197 {
198 case 2:
199 elements[i] = new Segment(ints, attr);
200 break;
201 case 3:
202 elements[i] = new Triangle(ints, attr);
203 break;
204 case 4:
205 elements[i] = new Quadrilateral(ints, attr);
206 break;
207 }
208 }
209
210 if (!curved)
211 {
212 // Read the vertices.
213 input >> NumOfVertices;
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 else
222 {
223 input >> NumOfVertices;
224 vertices.SetSize(NumOfVertices);
225 input >> ws;
226 }
227}
228
229void Mesh::ReadNetgen3DMesh(std::istream &input)
230{
231 int ints[32], attr;
232
233 // Read a Netgen format mesh of tetrahedra.
234 Dim = 3;
235
236 // Read the vertices
237 input >> NumOfVertices;
238
239 vertices.SetSize(NumOfVertices);
240 for (int i = 0; i < NumOfVertices; i++)
241 for (int j = 0; j < Dim; j++)
242 {
243 input >> vertices[i](j);
244 }
245
246 // Read the elements
247 input >> NumOfElements;
248 elements.SetSize(NumOfElements);
249 for (int i = 0; i < NumOfElements; i++)
250 {
251 input >> attr;
252 for (int j = 0; j < 4; j++)
253 {
254 input >> ints[j];
255 ints[j]--;
256 }
257#ifdef MFEM_USE_MEMALLOC
258 Tetrahedron *tet;
259 tet = TetMemory.Alloc();
260 tet->SetVertices(ints);
261 tet->SetAttribute(attr);
262 elements[i] = tet;
263#else
264 elements[i] = new Tetrahedron(ints, attr);
265#endif
266 }
267
268 // Read the boundary information.
269 input >> NumOfBdrElements;
270 boundary.SetSize(NumOfBdrElements);
271 for (int i = 0; i < NumOfBdrElements; i++)
272 {
273 input >> attr;
274 for (int j = 0; j < 3; j++)
275 {
276 input >> ints[j];
277 ints[j]--;
278 }
279 boundary[i] = new Triangle(ints, attr);
280 }
281}
282
283void Mesh::ReadTrueGridMesh(std::istream &input)
284{
285 int i, j, ints[32], attr;
286 const int buflen = 1024;
287 char buf[buflen];
288
289 // TODO: find the actual dimension
290 Dim = 3;
291
292 if (Dim == 2)
293 {
294 int vari;
295 real_t varf;
296
297 input >> vari >> NumOfVertices >> vari >> vari >> NumOfElements;
298 input.getline(buf, buflen);
299 input.getline(buf, buflen);
300 input >> vari;
301 input.getline(buf, buflen);
302 input.getline(buf, buflen);
303 input.getline(buf, buflen);
304
305 // Read the vertices.
306 vertices.SetSize(NumOfVertices);
307 for (i = 0; i < NumOfVertices; i++)
308 {
309 input >> vari >> varf >> vertices[i](0) >> vertices[i](1);
310 input.getline(buf, buflen);
311 }
312
313 // Read the elements.
314 elements.SetSize(NumOfElements);
315 for (i = 0; i < NumOfElements; i++)
316 {
317 input >> vari >> attr;
318 for (j = 0; j < 4; j++)
319 {
320 input >> ints[j];
321 ints[j]--;
322 }
323 input.getline(buf, buflen);
324 input.getline(buf, buflen);
325 elements[i] = new Quadrilateral(ints, attr);
326 }
327 }
328 else if (Dim == 3)
329 {
330 int vari;
331 real_t varf;
332 input >> vari >> NumOfVertices >> NumOfElements;
333 input.getline(buf, buflen);
334 input.getline(buf, buflen);
335 input >> vari >> vari >> NumOfBdrElements;
336 input.getline(buf, buflen);
337 input.getline(buf, buflen);
338 input.getline(buf, buflen);
339 // Read the vertices.
340 vertices.SetSize(NumOfVertices);
341 for (i = 0; i < NumOfVertices; i++)
342 {
343 input >> vari >> varf >> vertices[i](0) >> vertices[i](1)
344 >> vertices[i](2);
345 input.getline(buf, buflen);
346 }
347 // Read the elements.
348 elements.SetSize(NumOfElements);
349 for (i = 0; i < NumOfElements; i++)
350 {
351 input >> vari >> attr;
352 for (j = 0; j < 8; j++)
353 {
354 input >> ints[j];
355 ints[j]--;
356 }
357 input.getline(buf, buflen);
358 elements[i] = new Hexahedron(ints, attr);
359 }
360 // Read the boundary elements.
361 boundary.SetSize(NumOfBdrElements);
362 for (i = 0; i < NumOfBdrElements; i++)
363 {
364 input >> attr;
365 for (j = 0; j < 4; j++)
366 {
367 input >> ints[j];
368 ints[j]--;
369 }
370 input.getline(buf, buflen);
371 boundary[i] = new Quadrilateral(ints, attr);
372 }
373 }
374}
375
376// see Tetrahedron::edges
377const int Mesh::vtk_quadratic_tet[10] =
378{ 0, 1, 2, 3, 4, 7, 5, 6, 8, 9 };
379
380// see Pyramid::edges & Mesh::GenerateFaces
381// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
382const int Mesh::vtk_quadratic_pyramid[13] =
383{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
384
385// see Wedge::edges & Mesh::GenerateFaces
386// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
387const int Mesh::vtk_quadratic_wedge[18] =
388{ 0, 2, 1, 3, 5, 4, 8, 7, 6, 11, 10, 9, 12, 14, 13, 17, 16, 15};
389
390// see Hexahedron::edges & Mesh::GenerateFaces
391const int Mesh::vtk_quadratic_hex[27] =
392{
393 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
394 24, 22, 21, 23, 20, 25, 26
395};
396
397void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
398 const Array<int> &cell_offsets,
399 const Array<int> &cell_types,
400 const Array<int> &cell_attributes,
401 int &curved, int &read_gf, bool &finalize_topo)
402{
403 int np = points.Size()/3;
404 Dim = -1;
405 NumOfElements = cell_types.Size();
406 elements.SetSize(NumOfElements);
407
408 int order = -1;
409 bool legacy_elem = false, lagrange_elem = false;
410
411 for (int i = 0; i < NumOfElements; i++)
412 {
413 int j = (i > 0) ? cell_offsets[i-1] : 0;
414 int ct = cell_types[i];
416 elements[i] = NewElement(geom);
417 if (cell_attributes.Size() > 0)
418 {
419 elements[i]->SetAttribute(cell_attributes[i]);
420 }
421 // VTK ordering of vertices is the same as MFEM ordering of vertices
422 // for all element types *except* prisms, which require a permutation
423 if (geom == Geometry::PRISM && ct != VTKGeometry::LAGRANGE_PRISM)
424 {
425 int prism_vertices[6];
426 for (int k=0; k<6; ++k)
427 {
428 prism_vertices[k] = cell_data[j+VTKGeometry::PrismMap[k]];
429 }
430 elements[i]->SetVertices(prism_vertices);
431 }
432 else
433 {
434 elements[i]->SetVertices(&cell_data[j]);
435 }
436
437 int elem_dim = Geometry::Dimension[geom];
438 int elem_order = VTKGeometry::GetOrder(ct, cell_offsets[i] - j);
439
440 if (VTKGeometry::IsLagrange(ct)) { lagrange_elem = true; }
441 else { legacy_elem = true; }
442
443 MFEM_VERIFY(Dim == -1 || Dim == elem_dim,
444 "Elements with different dimensions are not supported");
445 MFEM_VERIFY(order == -1 || order == elem_order,
446 "Elements with different orders are not supported");
447 MFEM_VERIFY(legacy_elem != lagrange_elem,
448 "Mixing of legacy and Lagrange cell types is not supported");
449 Dim = elem_dim;
450 order = elem_order;
451 }
452
453 // determine spaceDim based on min/max differences detected each dimension
454 spaceDim = 0;
455 if (np > 0)
456 {
457 real_t min_value, max_value;
458 for (int d = 3; d > 0; --d)
459 {
460 min_value = max_value = points(3*0 + d-1);
461 for (int i = 1; i < np; i++)
462 {
463 min_value = std::min(min_value, points(3*i + d-1));
464 max_value = std::max(max_value, points(3*i + d-1));
465 if (min_value != max_value)
466 {
467 spaceDim = d;
468 break;
469 }
470 }
471 if (spaceDim > 0) { break; }
472 }
473 }
474
475 if (order == 1 && !lagrange_elem)
476 {
477 NumOfVertices = np;
478 vertices.SetSize(np);
479 for (int i = 0; i < np; i++)
480 {
481 vertices[i](0) = points(3*i+0);
482 vertices[i](1) = points(3*i+1);
483 vertices[i](2) = points(3*i+2);
484 }
485 // No boundary is defined in a VTK mesh
489 }
490 else
491 {
492 // The following section of code is shared for legacy quadratic and the
493 // Lagrange high order elements
494 curved = 1;
495
496 // generate new enumeration for the vertices
497 Array<int> pts_dof(np);
498 pts_dof = -1;
499 // mark vertex points
500 for (int i = 0; i < NumOfElements; i++)
501 {
502 int *v = elements[i]->GetVertices();
503 int nv = elements[i]->GetNVertices();
504 for (int j = 0; j < nv; j++)
505 {
506 if (pts_dof[v[j]] == -1) { pts_dof[v[j]] = 0; }
507 }
508 }
509
510 // The following loop reorders pts_dofs so vertices are visited in
511 // canonical order
512
513 // Keep the original ordering of the vertices
514 NumOfVertices = 0;
515 for (int i = 0; i < np; i++)
516 {
517 if (pts_dof[i] != -1)
518 {
519 pts_dof[i] = NumOfVertices++;
520 }
521 }
522 // update the element vertices
523 for (int i = 0; i < NumOfElements; i++)
524 {
525 int *v = elements[i]->GetVertices();
526 int nv = elements[i]->GetNVertices();
527 for (int j = 0; j < nv; j++)
528 {
529 v[j] = pts_dof[v[j]];
530 }
531 }
532 // Define the 'vertices' from the 'points' through the 'pts_dof' map
534 for (int i = 0; i < np; i++)
535 {
536 int j = pts_dof[i];
537 if (j != -1)
538 {
539 vertices[j](0) = points(3*i+0);
540 vertices[j](1) = points(3*i+1);
541 vertices[j](2) = points(3*i+2);
542 }
543 }
544
545 // No boundary is defined in a VTK mesh
547
548 // Generate faces and edges so that we can define FE space on the mesh
550
553 if (legacy_elem)
554 {
555 // Define quadratic FE space
556 fec = new QuadraticFECollection;
557 fes = new FiniteElementSpace(this, fec, spaceDim);
558 Nodes = new GridFunction(fes);
559 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
560 own_nodes = 1;
561
562 // Map vtk points to edge/face/element dofs
563 Array<int> dofs;
564 for (int i = 0; i < NumOfElements; i++)
565 {
566 fes->GetElementDofs(i, dofs);
567 const int *vtk_mfem;
568 switch (elements[i]->GetGeometryType())
569 {
571 case Geometry::SQUARE:
572 vtk_mfem = vtk_quadratic_hex; break; // identity map
574 vtk_mfem = vtk_quadratic_tet; break;
575 case Geometry::CUBE:
576 vtk_mfem = vtk_quadratic_hex; break;
577 case Geometry::PRISM:
578 vtk_mfem = vtk_quadratic_wedge; break;
580 vtk_mfem = vtk_quadratic_pyramid; break;
581 default:
582 vtk_mfem = NULL; // suppress a warning
583 break;
584 }
585
586 int offset = (i == 0) ? 0 : cell_offsets[i-1];
587 for (int j = 0; j < dofs.Size(); j++)
588 {
589 if (pts_dof[cell_data[offset+j]] == -1)
590 {
591 pts_dof[cell_data[offset+j]] = dofs[vtk_mfem[j]];
592 }
593 else
594 {
595 if (pts_dof[cell_data[offset+j]] != dofs[vtk_mfem[j]])
596 {
597 MFEM_ABORT("VTK mesh: inconsistent quadratic mesh!");
598 }
599 }
600 }
601 }
602 }
603 else
604 {
605 // Define H1 FE space
607 fes = new FiniteElementSpace(this, fec, spaceDim);
608 Nodes = new GridFunction(fes);
609 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
610 own_nodes = 1;
611 Array<int> dofs;
612
613 std::map<Geometry::Type,Array<int>> vtk_inv_maps;
614 std::map<Geometry::Type,const Array<int>*> lex_orderings;
615
616 int i, n;
617 for (n = i = 0; i < NumOfElements; i++)
618 {
620 fes->GetElementDofs(i, dofs);
621
622 Array<int> &vtk_inv_map = vtk_inv_maps[geom];
623 if (vtk_inv_map.Size() == 0)
624 {
625 Array<int> vtk_map;
626 CreateVTKElementConnectivity(vtk_map, geom, order);
627 vtk_inv_map.SetSize(vtk_map.Size());
628 for (int j=0; j<vtk_map.Size(); ++j)
629 {
630 vtk_inv_map[vtk_map[j]] = j;
631 }
632 }
633 const Array<int> *&lex_ordering = lex_orderings[geom];
634 if (!lex_ordering)
635 {
636 const FiniteElement *fe = fes->GetFE(i);
637 const NodalFiniteElement *nodal_fe =
638 dynamic_cast<const NodalFiniteElement*>(fe);
639 MFEM_ASSERT(nodal_fe != NULL, "Unsupported element type");
640 lex_ordering = &nodal_fe->GetLexicographicOrdering();
641 }
642
643 for (int lex_idx = 0; lex_idx < dofs.Size(); lex_idx++)
644 {
645 int mfem_idx = (*lex_ordering)[lex_idx];
646 int vtk_idx = vtk_inv_map[lex_idx];
647 int pt_idx = cell_data[n + vtk_idx];
648 if (pts_dof[pt_idx] == -1)
649 {
650 pts_dof[pt_idx] = dofs[mfem_idx];
651 }
652 else
653 {
654 if (pts_dof[pt_idx] != dofs[mfem_idx])
655 {
656 MFEM_ABORT("VTK mesh: inconsistent Lagrange mesh!");
657 }
658 }
659 }
660 n += dofs.Size();
661 }
662 }
663 // Define the 'Nodes' from the 'points' through the 'pts_dof' map
664 Array<int> dofs;
665 for (int i = 0; i < np; i++)
666 {
667 dofs.SetSize(1);
668 if (pts_dof[i] != -1)
669 {
670 dofs[0] = pts_dof[i];
671 fes->DofsToVDofs(dofs);
672 for (int d = 0; d < dofs.Size(); d++)
673 {
674 (*Nodes)(dofs[d]) = points(3*i+d);
675 }
676 }
677 }
678 read_gf = 0;
679 }
680}
681
682namespace vtk_xml
683{
684
685using namespace tinyxml2;
686
687/// Return false if either string is NULL or if the strings differ, return true
688/// if the strings are the same.
689bool StringCompare(const char *s1, const char *s2)
690{
691 if (s1 == NULL || s2 == NULL) { return false; }
692 return strcmp(s1, s2) == 0;
693}
694
695/// Abstract base class for reading contiguous arrays of (potentially
696/// compressed, potentially base-64 encoded) binary data from a buffer into a
697/// destination array. The types of the source and destination arrays may be
698/// different (e.g. read data of type uint8_t into destination array of
699/// uint32_t), which is handled by the templated derived class @a BufferReader.
700struct BufferReaderBase
701{
702 enum HeaderType { UINT32_HEADER, UINT64_HEADER };
703 virtual void ReadBinary(const char *buf, void *dest, int n) const = 0;
704 virtual void ReadBase64(const char *txt, void *dest, int n) const = 0;
705 virtual ~BufferReaderBase() { }
706};
707
708/// Read an array of source data stored as (potentially compressed, potentially
709/// base-64 encoded) into a destination array. The types of the elements in the
710/// source array are given by template parameter @a F ("from") and the types of
711/// the elements of the destination array are given by @a T ("to"). The binary
712/// data has a header, which is one integer if the data is uncompressed, and is
713/// four integers if the data is compressed. The integers may either by uint32_t
714/// or uint64_t, according to the @a header_type. If the data is compressed and
715/// base-64 encoded, then the header is encoded separately from the data. If the
716/// data is uncompressed and base-64 encoded, then the header and data are
717/// encoded together.
718template <typename T, typename F>
719struct BufferReader : BufferReaderBase
720{
721 bool compressed;
722 HeaderType header_type;
723 BufferReader(bool compressed_, HeaderType header_type_)
724 : compressed(compressed_), header_type(header_type_) { }
725
726 /// Return the number of bytes of each header entry.
727 size_t HeaderEntrySize() const
728 {
729 return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t);
730 }
731
732 /// Return the value of the header entry pointer to by @a header_buf. The
733 /// value is stored as either uint32_t or uint64_t, according to the @a
734 /// header_type, and is returned as uint64_t.
735 uint64_t ReadHeaderEntry(const char *header_buf) const
736 {
737 return (header_type == UINT64_HEADER) ? bin_io::read<uint64_t>(header_buf)
738 : bin_io::read<uint32_t>(header_buf);
739 }
740
741 /// Return the number of bytes in the header. The header consists of one
742 /// integer if the data is uncompressed, and @a N + 3 integers if the data is
743 /// compressed, where @a N is the number of blocks. The integers are either
744 /// 32 or 64 bytes depending on the value of @a header_type. The number of
745 /// blocks is determined by reading the first integer (of type @a
746 /// header_type) pointed to by @a header_buf.
747 int NumHeaderBytes(const char *header_buf) const
748 {
749 if (!compressed) { return static_cast<int>(HeaderEntrySize()); }
750 return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize();
751 }
752
753 /// Read @a n elements of type @a F from the source buffer @a buf into the
754 /// (pre-allocated) destination array of elements of type @a T stored in
755 /// the buffer @a dest_void. The header is stored @b separately from the
756 /// rest of the data, in the buffer @a header_buf. The data buffer @a buf
757 /// does @b not contain a header.
758 void ReadBinaryWithHeader(const char *header_buf, const char *buf,
759 void *dest_void, int n) const
760 {
761 std::vector<char> uncompressed_data;
762 T *dest = static_cast<T*>(dest_void);
763
764 if (compressed)
765 {
766#ifdef MFEM_USE_ZLIB
767 // The header has format (where header_t is uint32_t or uint64_t):
768 // header_t number_of_blocks;
769 // header_t uncompressed_block_size;
770 // header_t uncompressed_last_block_size;
771 // header_t compressed_size[number_of_blocks];
772 int header_entry_size = HeaderEntrySize();
773 int nblocks = ReadHeaderEntry(header_buf);
774 header_buf += header_entry_size;
775 std::vector<int> header(nblocks + 2);
776 for (int i=0; i<nblocks+2; ++i)
777 {
778 header[i] = ReadHeaderEntry(header_buf);
779 header_buf += header_entry_size;
780 }
781 uncompressed_data.resize((nblocks-1)*header[0] + header[1]);
782 Bytef *dest_ptr = (Bytef *)uncompressed_data.data();
783 Bytef *dest_start = dest_ptr;
784 const Bytef *source_ptr = (const Bytef *)buf;
785 for (int i=0; i<nblocks; ++i)
786 {
787 uLongf source_len = header[i+2];
788 uLong dest_len = (i == nblocks-1) ? header[1] : header[0];
789 int res = uncompress(dest_ptr, &dest_len, source_ptr, source_len);
790 MFEM_VERIFY(res == Z_OK, "Error uncompressing");
791 dest_ptr += dest_len;
792 source_ptr += source_len;
793 }
794 MFEM_VERIFY(int(sizeof(F)*n) == (dest_ptr - dest_start),
795 "AppendedData: wrong data size");
796 buf = uncompressed_data.data();
797#else
798 MFEM_ABORT("MFEM must be compiled with zlib enabled to uncompress.")
799#endif
800 }
801 else
802 {
803 // Each "data block" is preceded by a header that is either UInt32 or
804 // UInt64. The rest of the data follows.
805 uint64_t data_size;
806 if (header_type == UINT32_HEADER)
807 {
808 uint32_t *data_size_32 = (uint32_t *)header_buf;
809 data_size = *data_size_32;
810 }
811 else
812 {
813 uint64_t *data_size_64 = (uint64_t *)header_buf;
814 data_size = *data_size_64;
815 }
816 MFEM_VERIFY(sizeof(F)*n == data_size, "AppendedData: wrong data size");
817 }
818
819 if (std::is_same<T, F>::value)
820 {
821 // Special case: no type conversions necessary, so can just memcpy
822 memcpy(dest, buf, sizeof(T)*n);
823 }
824 else
825 {
826 for (int i=0; i<n; ++i)
827 {
828 // Read binary data as type F, place in array as type T
829 dest[i] = bin_io::read<F>(buf + i*sizeof(F));
830 }
831 }
832 }
833
834 /// Read @a n elements of type @a F from source buffer @a buf into
835 /// (pre-allocated) array of elements of type @a T stored in destination
836 /// buffer @a dest. The input buffer contains both the header and the data.
837 void ReadBinary(const char *buf, void *dest, int n) const override
838 {
839 ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n);
840 }
841
842 /// Read @a n elements of type @a F from base-64 encoded source buffer into
843 /// (pre-allocated) array of elements of type @a T stored in destination
844 /// buffer @a dest. The base-64-encoded data is given by the null-terminated
845 /// string @a txt, which contains both the header and the data.
846 void ReadBase64(const char *txt, void *dest, int n) const override
847 {
848 // Skip whitespace
849 while (*txt)
850 {
851 if (*txt != ' ' && *txt != '\n') { break; }
852 ++txt;
853 }
854 if (compressed)
855 {
856 // Decode the first entry of the header, which we need to determine
857 // how long the rest of the header is.
858 std::vector<char> nblocks_buf;
859 int nblocks_b64 = static_cast<int>(bin_io::NumBase64Chars(HeaderEntrySize()));
860 bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf);
861 std::vector<char> data, header;
862 // Compute number of characters needed to encode header in base 64,
863 // then round to nearest multiple of 4 to take padding into account.
864 int header_b64 = static_cast<int>(bin_io::NumBase64Chars(NumHeaderBytes(
865 nblocks_buf.data())));
866 // If data is compressed, header is encoded separately
867 bin_io::DecodeBase64(txt, header_b64, header);
868 bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data);
869 ReadBinaryWithHeader(header.data(), data.data(), dest, n);
870 }
871 else
872 {
873 std::vector<char> data;
874 bin_io::DecodeBase64(txt, strlen(txt), data);
875 ReadBinary(data.data(), dest, n);
876 }
877 }
878};
879
880/// Class to read data from VTK's @a DataArary elements. Each @a DataArray can
881/// contain inline ASCII data, inline base-64-encoded data (potentially
882/// compressed), or reference "appended data", which may be raw or base-64, and
883/// may be compressed or uncompressed.
884struct XMLDataReader
885{
886 const char *appended_data, *byte_order, *compressor;
887 enum AppendedDataEncoding { RAW, BASE64 };
888 map<string,BufferReaderBase*> type_map;
889 AppendedDataEncoding encoding;
890
891 /// Create the data reader, where @a vtk is the @a VTKFile XML element, and
892 /// @a vtu is the child @a UnstructuredGrid XML element. This will determine
893 /// the header type (32 or 64 bit integers) and whether compression is
894 /// enabled or not. The appended data will be loaded.
895 XMLDataReader(const XMLElement *vtk, const XMLElement *vtu)
896 {
897 // Determine whether binary data header is 32 or 64 bit integer
898 BufferReaderBase::HeaderType htype;
899 if (StringCompare(vtk->Attribute("header_type"), "UInt64"))
900 {
901 htype = BufferReaderBase::UINT64_HEADER;
902 }
903 else
904 {
905 htype = BufferReaderBase::UINT32_HEADER;
906 }
907
908 // Get the byte order of the file (will check if we encounter binary data)
909 byte_order = vtk->Attribute("byte_order");
910
911 // Get the compressor. We will check that MFEM can handle the compression
912 // if we encounter binary data.
913 compressor = vtk->Attribute("compressor");
914 bool compressed = (compressor != NULL);
915
916 // Find the appended data.
917 appended_data = NULL;
918 for (const XMLElement *xml_elem = vtu->NextSiblingElement();
919 xml_elem != NULL;
920 xml_elem = xml_elem->NextSiblingElement())
921 {
922 if (StringCompare(xml_elem->Name(), "AppendedData"))
923 {
924 const char *encoding_str = xml_elem->Attribute("encoding");
925 if (StringCompare(encoding_str, "raw"))
926 {
927 appended_data = xml_elem->GetAppendedData();
928 encoding = RAW;
929 }
930 else if (StringCompare(encoding_str, "base64"))
931 {
932 appended_data = xml_elem->GetText();
933 encoding = BASE64;
934 }
935 MFEM_VERIFY(appended_data != NULL, "Invalid AppendedData");
936 // Appended data follows first underscore
937 bool found_leading_underscore = false;
938 while (*appended_data)
939 {
940 ++appended_data;
941 if (*appended_data == '_')
942 {
943 found_leading_underscore = true;
944 ++appended_data;
945 break;
946 }
947 }
948 MFEM_VERIFY(found_leading_underscore, "Invalid AppendedData");
949 break;
950 }
951 }
952
953 type_map["Int8"] = new BufferReader<int, int8_t>(compressed, htype);
954 type_map["Int16"] = new BufferReader<int, int16_t>(compressed, htype);
955 type_map["Int32"] = new BufferReader<int, int32_t>(compressed, htype);
956 type_map["Int64"] = new BufferReader<int, int64_t>(compressed, htype);
957 type_map["UInt8"] = new BufferReader<int, uint8_t>(compressed, htype);
958 type_map["UInt16"] = new BufferReader<int, uint16_t>(compressed, htype);
959 type_map["UInt32"] = new BufferReader<int, uint32_t>(compressed, htype);
960 type_map["UInt64"] = new BufferReader<int, uint64_t>(compressed, htype);
961 type_map["Float32"] = new BufferReader<double, float>(compressed, htype);
962 type_map["Float64"] = new BufferReader<double, double>(compressed, htype);
963 }
964
965 /// Read the @a DataArray XML element given by @a xml_elem into
966 /// (pre-allocated) destination array @a dest, where @a dest stores @a n
967 /// elements of type @a T.
968 template <typename T>
969 void Read(const XMLElement *xml_elem, T *dest, int n)
970 {
971 static const char *erstr = "Error reading XML DataArray";
972 MFEM_VERIFY(StringCompare(xml_elem->Name(), "DataArray"), erstr);
973 const char *format = xml_elem->Attribute("format");
974 if (StringCompare(format, "ascii"))
975 {
976 const char *txt = xml_elem->GetText();
977 MFEM_VERIFY(txt != NULL, erstr);
978 std::istringstream data_stream(txt);
979 for (int i=0; i<n; ++i) { data_stream >> dest[i]; }
980 }
981 else if (StringCompare(format, "appended"))
982 {
983 VerifyBinaryOptions();
984 int offset = xml_elem->IntAttribute("offset");
985 const char *type = xml_elem->Attribute("type");
986 MFEM_VERIFY(type != NULL, erstr);
987 BufferReaderBase *reader = type_map[type];
988 MFEM_VERIFY(reader != NULL, erstr);
989 MFEM_VERIFY(appended_data != NULL, "No AppendedData found");
990 if (encoding == RAW)
991 {
992 reader->ReadBinary(appended_data + offset, dest, n);
993 }
994 else
995 {
996 reader->ReadBase64(appended_data + offset, dest, n);
997 }
998 }
999 else if (StringCompare(format, "binary"))
1000 {
1001 VerifyBinaryOptions();
1002 const char *txt = xml_elem->GetText();
1003 MFEM_VERIFY(txt != NULL, erstr);
1004 const char *type = xml_elem->Attribute("type");
1005 if (type == NULL) { MFEM_ABORT(erstr); }
1006 BufferReaderBase *reader = type_map[type];
1007 if (reader == NULL) { MFEM_ABORT(erstr); }
1008 reader->ReadBase64(txt, dest, n);
1009 }
1010 else
1011 {
1012 MFEM_ABORT("Invalid XML VTK DataArray format");
1013 }
1014 }
1015
1016 /// Check that the byte order of the file is the same as the native byte
1017 /// order that we're running with. We don't currently support converting
1018 /// between byte orders. The byte order is only verified if we encounter
1019 /// binary data.
1020 void VerifyByteOrder() const
1021 {
1022 // Can't handle reading big endian from little endian or vice versa
1023 if (byte_order && !StringCompare(byte_order, VTKByteOrder()))
1024 {
1025 MFEM_ABORT("Converting between different byte orders is unsupported.");
1026 }
1027 }
1028
1029 /// Check that the compressor is compatible (MFEM currently only supports
1030 /// zlib compression). If MFEM is not compiled with zlib, then we cannot
1031 /// read binary data with compression.
1032 void VerifyCompressor() const
1033 {
1034 if (compressor && !StringCompare(compressor, "vtkZLibDataCompressor"))
1035 {
1036 MFEM_ABORT("Unsupported compressor. Only zlib is supported.")
1037 }
1038#ifndef MFEM_USE_ZLIB
1039 MFEM_VERIFY(compressor == NULL, "MFEM must be compiled with zlib enabled "
1040 "to support reading compressed data.");
1041#endif
1042 }
1043
1044 /// Verify that the binary data is stored with compatible options (i.e.
1045 /// native byte order and compatible compression).
1046 void VerifyBinaryOptions() const
1047 {
1048 VerifyByteOrder();
1049 VerifyCompressor();
1050 }
1051
1052 ~XMLDataReader()
1053 {
1054 for (auto &x : type_map) { delete x.second; }
1055 }
1056};
1057
1058} // namespace vtk_xml
1059
1060void Mesh::ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf,
1061 bool &finalize_topo, const std::string &xml_prefix)
1062{
1063 using namespace vtk_xml;
1064
1065 static const char *erstr = "XML parsing error";
1066
1067 // Create buffer beginning with xml_prefix, then read the rest of the stream
1068 std::vector<char> buf(xml_prefix.begin(), xml_prefix.end());
1069 std::istreambuf_iterator<char> eos;
1070 buf.insert(buf.end(), std::istreambuf_iterator<char>(input), eos);
1071 buf.push_back('\0'); // null-terminate buffer
1072
1073 XMLDocument xml;
1074 xml.Parse(buf.data(), buf.size());
1075 if (xml.ErrorID() != XML_SUCCESS)
1076 {
1077 MFEM_ABORT("Error parsing XML VTK file.\n" << xml.ErrorStr());
1078 }
1079
1080 const XMLElement *vtkfile = xml.FirstChildElement();
1081 MFEM_VERIFY(vtkfile, erstr);
1082 MFEM_VERIFY(StringCompare(vtkfile->Name(), "VTKFile"), erstr);
1083 const XMLElement *vtu = vtkfile->FirstChildElement();
1084 MFEM_VERIFY(vtu, erstr);
1085 MFEM_VERIFY(StringCompare(vtu->Name(), "UnstructuredGrid"), erstr);
1086
1087 XMLDataReader data_reader(vtkfile, vtu);
1088
1089 // Count the number of points and cells
1090 const XMLElement *piece = vtu->FirstChildElement();
1091 MFEM_VERIFY(StringCompare(piece->Name(), "Piece"), erstr);
1092 MFEM_VERIFY(piece->NextSiblingElement() == NULL,
1093 "XML VTK meshes with more than one Piece are not supported");
1094 int npts = piece->IntAttribute("NumberOfPoints");
1095 int ncells = piece->IntAttribute("NumberOfCells");
1096
1097 // Read the points
1098 Vector points(3*npts);
1099 const XMLElement *pts_xml;
1100 for (pts_xml = piece->FirstChildElement();
1101 pts_xml != NULL;
1102 pts_xml = pts_xml->NextSiblingElement())
1103 {
1104 if (StringCompare(pts_xml->Name(), "Points"))
1105 {
1106 const XMLElement *pts_data = pts_xml->FirstChildElement();
1107 MFEM_VERIFY(pts_data->IntAttribute("NumberOfComponents") == 3,
1108 "XML VTK Points DataArray must have 3 components");
1109 data_reader.Read(pts_data, points.GetData(), points.Size());
1110 break;
1111 }
1112 }
1113 if (pts_xml == NULL) { MFEM_ABORT(erstr); }
1114
1115 // Read the cells
1116 Array<int> cell_data, cell_offsets(ncells), cell_types(ncells);
1117 const XMLElement *cells_xml;
1118 for (cells_xml = piece->FirstChildElement();
1119 cells_xml != NULL;
1120 cells_xml = cells_xml->NextSiblingElement())
1121 {
1122 if (StringCompare(cells_xml->Name(), "Cells"))
1123 {
1124 const XMLElement *cell_data_xml = NULL;
1125 for (const XMLElement *data_xml = cells_xml->FirstChildElement();
1126 data_xml != NULL;
1127 data_xml = data_xml->NextSiblingElement())
1128 {
1129 const char *data_name = data_xml->Attribute("Name");
1130 if (StringCompare(data_name, "offsets"))
1131 {
1132 data_reader.Read(data_xml, cell_offsets.GetData(), ncells);
1133 }
1134 else if (StringCompare(data_name, "types"))
1135 {
1136 data_reader.Read(data_xml, cell_types.GetData(), ncells);
1137 }
1138 else if (StringCompare(data_name, "connectivity"))
1139 {
1140 // Have to read the connectivity after the offsets, because we
1141 // don't know how many points to read until we have the offsets
1142 // (size of connectivity array is equal to the last offset), so
1143 // store the XML element pointer and read this data later.
1144 cell_data_xml = data_xml;
1145 }
1146 }
1147 MFEM_VERIFY(cell_data_xml != NULL, erstr);
1148 int cell_data_size = cell_offsets.Last();
1149 cell_data.SetSize(cell_data_size);
1150 data_reader.Read(cell_data_xml, cell_data.GetData(), cell_data_size);
1151 break;
1152 }
1153 }
1154 if (cells_xml == NULL) { MFEM_ABORT(erstr); }
1155
1156 // Read the element attributes, which are stored as CellData named either
1157 // "material" or "attribute". We prioritize "material" over "attribute" for
1158 // backwards compatibility.
1159 Array<int> cell_attributes;
1160 bool found_attributes = false;
1161 for (const XMLElement *cell_data_xml = piece->FirstChildElement();
1162 cell_data_xml != NULL;
1163 cell_data_xml = cell_data_xml->NextSiblingElement())
1164 {
1165 const bool is_cell_data =
1166 StringCompare(cell_data_xml->Name(), "CellData");
1167 const bool is_material =
1168 StringCompare(cell_data_xml->Attribute("Scalars"), "material");
1169 const bool is_attribute =
1170 StringCompare(cell_data_xml->Attribute("Scalars"), "attribute");
1171 if (is_cell_data && (is_material || (is_attribute && !found_attributes)))
1172 {
1173 found_attributes = true;
1174 const XMLElement *data_xml = cell_data_xml->FirstChildElement();
1175 if (data_xml != NULL && StringCompare(data_xml->Name(), "DataArray"))
1176 {
1177 cell_attributes.SetSize(ncells);
1178 data_reader.Read(data_xml, cell_attributes.GetData(), ncells);
1179 }
1180 }
1181 }
1182
1183 CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1184 curved, read_gf, finalize_topo);
1185}
1186
1187void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
1188 bool &finalize_topo)
1189{
1190 // VTK resources:
1191 // * https://www.vtk.org/doc/nightly/html/vtkCellType_8h_source.html
1192 // * https://www.vtk.org/doc/nightly/html/classvtkCell.html
1193 // * https://lorensen.github.io/VTKExamples/site/VTKFileFormats
1194 // * https://www.kitware.com/products/books/VTKUsersGuide.pdf
1195
1196 string buff;
1197 getline(input, buff); // comment line
1198 getline(input, buff);
1199 filter_dos(buff);
1200 if (buff != "ASCII")
1201 {
1202 MFEM_ABORT("VTK mesh is not in ASCII format!");
1203 return;
1204 }
1205 do
1206 {
1207 getline(input, buff);
1208 filter_dos(buff);
1209 if (!input.good()) { MFEM_ABORT("VTK mesh is not UNSTRUCTURED_GRID!"); }
1210 }
1211 while (buff != "DATASET UNSTRUCTURED_GRID");
1212
1213 // Read the points, skipping optional sections such as the FIELD data from
1214 // VisIt's VTK export (or from Mesh::PrintVTK with field_data==1).
1215 do
1216 {
1217 input >> buff;
1218 if (!input.good())
1219 {
1220 MFEM_ABORT("VTK mesh does not have POINTS data!");
1221 }
1222 }
1223 while (buff != "POINTS");
1224
1225 Vector points;
1226 int np;
1227 input >> np >> ws;
1228 getline(input, buff); // "double"
1229 points.Load(input, 3*np);
1230
1231 //skip metadata
1232 // Looks like:
1233 // METADATA
1234 //INFORMATION 2
1235 //NAME L2_NORM_RANGE LOCATION vtkDataArray
1236 //DATA 2 0 5.19615
1237 //NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray
1238 //DATA 2 0 5.19615
1239 do
1240 {
1241 input >> buff;
1242 if (!input.good())
1243 {
1244 MFEM_ABORT("VTK mesh does not have CELLS data!");
1245 }
1246 }
1247 while (buff != "CELLS");
1248
1249 // Read the cells
1250 Array<int> cell_data, cell_offsets;
1251 if (buff == "CELLS")
1252 {
1253 int ncells, n;
1254 input >> ncells >> n >> ws;
1255 cell_offsets.SetSize(ncells);
1256 cell_data.SetSize(n - ncells);
1257 int offset = 0;
1258 for (int i=0; i<ncells; ++i)
1259 {
1260 int nv;
1261 input >> nv;
1262 cell_offsets[i] = offset + nv;
1263 for (int j=0; j<nv; ++j)
1264 {
1265 input >> cell_data[offset + j];
1266 }
1267 offset += nv;
1268 }
1269 }
1270
1271 // Read the cell types
1272 input >> ws >> buff;
1273 Array<int> cell_types;
1274 int ncells;
1275 MFEM_VERIFY(buff == "CELL_TYPES", "CELL_TYPES not provided in VTK mesh.")
1276 input >> ncells;
1277 cell_types.Load(ncells, input);
1278
1279 while ((input.good()) && (buff != "CELL_DATA"))
1280 {
1281 input >> buff;
1282 }
1283 getline(input, buff); // finish the line
1284
1285 // Read the cell materials
1286 // bool found_material = false;
1287 Array<int> cell_attributes;
1288 bool found_attributes = false;
1289 while ((input.good()))
1290 {
1291 getline(input, buff);
1292 if (buff.rfind("POINT_DATA") == 0)
1293 {
1294 break; // We have entered the POINT_DATA block. Quit.
1295 }
1296 else if (buff.rfind("SCALARS material") == 0 ||
1297 (buff.rfind("SCALARS attribute") == 0 && !found_attributes))
1298 {
1299 found_attributes = true;
1300 getline(input, buff); // LOOKUP_TABLE default
1301 if (buff.rfind("LOOKUP_TABLE default") != 0)
1302 {
1303 MFEM_ABORT("Invalid LOOKUP_TABLE for material array in VTK file.");
1304 }
1305 cell_attributes.Load(ncells, input);
1306 // found_material = true;
1307 break;
1308 }
1309 }
1310
1311 // if (!found_material)
1312 // {
1313 // MFEM_WARNING("Material array not found in VTK file. "
1314 // "Assuming uniform material composition.");
1315 // }
1316
1317 CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1318 curved, read_gf, finalize_topo);
1319} // end ReadVTKMesh
1320
1321void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
1322 bool spacing)
1323{
1324 NURBSext = new NURBSExtension(input, spacing);
1325
1326 Dim = NURBSext->Dimension();
1330
1333
1334 vertices.SetSize(NumOfVertices);
1335 curved = 1;
1336 if (NURBSext->HavePatches())
1337 {
1339 FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
1341 Nodes = new GridFunction(fes);
1342 Nodes->MakeOwner(fec);
1344 own_nodes = 1;
1345 read_gf = 0;
1347 for (int i = 0; i < spaceDim; i++)
1348 {
1349 Vector vert_val;
1350 Nodes->GetNodalValues(vert_val, i+1);
1351 for (int j = 0; j < NumOfVertices; j++)
1352 {
1353 vertices[j](i) = vert_val(j);
1354 }
1355 }
1356 }
1357 else
1358 {
1359 read_gf = 1;
1360 }
1361}
1362
1363void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
1364{
1365 // Initialize to negative numbers so that we know if they've been set. We're
1366 // using Element::POINT as our flag, since we're not going to make a 0D mesh,
1367 // ever.
1368 int nx = -1;
1369 int ny = -1;
1370 int nz = -1;
1371 real_t sx = -1.0;
1372 real_t sy = -1.0;
1373 real_t sz = -1.0;
1375
1376 while (true)
1377 {
1378 skip_comment_lines(input, '#');
1379 // Break out if we reached the end of the file after gobbling up the
1380 // whitespace and comments after the last keyword.
1381 if (!input.good())
1382 {
1383 break;
1384 }
1385
1386 // Read the next keyword
1387 std::string name;
1388 input >> name;
1389 input >> std::ws;
1390 // Make sure there's an equal sign
1391 MFEM_VERIFY(input.get() == '=',
1392 "Inline mesh expected '=' after keyword " << name);
1393 input >> std::ws;
1394
1395 if (name == "nx")
1396 {
1397 input >> nx;
1398 }
1399 else if (name == "ny")
1400 {
1401 input >> ny;
1402 }
1403 else if (name == "nz")
1404 {
1405 input >> nz;
1406 }
1407 else if (name == "sx")
1408 {
1409 input >> sx;
1410 }
1411 else if (name == "sy")
1412 {
1413 input >> sy;
1414 }
1415 else if (name == "sz")
1416 {
1417 input >> sz;
1418 }
1419 else if (name == "type")
1420 {
1421 std::string eltype;
1422 input >> eltype;
1423 if (eltype == "segment")
1424 {
1425 type = Element::SEGMENT;
1426 }
1427 else if (eltype == "quad")
1428 {
1430 }
1431 else if (eltype == "tri")
1432 {
1433 type = Element::TRIANGLE;
1434 }
1435 else if (eltype == "hex")
1436 {
1437 type = Element::HEXAHEDRON;
1438 }
1439 else if (eltype == "wedge")
1440 {
1441 type = Element::WEDGE;
1442 }
1443 else if (eltype == "pyramid")
1444 {
1445 type = Element::PYRAMID;
1446 }
1447 else if (eltype == "tet")
1448 {
1449 type = Element::TETRAHEDRON;
1450 }
1451 else
1452 {
1453 MFEM_ABORT("unrecognized element type (read '" << eltype
1454 << "') in inline mesh format. "
1455 "Allowed: segment, tri, quad, tet, hex, wedge");
1456 }
1457 }
1458 else
1459 {
1460 MFEM_ABORT("unrecognized keyword (" << name
1461 << ") in inline mesh format. "
1462 "Allowed: nx, ny, nz, type, sx, sy, sz");
1463 }
1464
1465 input >> std::ws;
1466 // Allow an optional semi-colon at the end of each line.
1467 if (input.peek() == ';')
1468 {
1469 input.get();
1470 }
1471
1472 // Done reading file
1473 if (!input)
1474 {
1475 break;
1476 }
1477 }
1478
1479 // Now make the mesh.
1480 if (type == Element::SEGMENT)
1481 {
1482 MFEM_VERIFY(nx > 0 && sx > 0.0,
1483 "invalid 1D inline mesh format, all values must be "
1484 "positive\n"
1485 << " nx = " << nx << "\n"
1486 << " sx = " << sx << "\n");
1487 Make1D(nx, sx);
1488 }
1489 else if (type == Element::TRIANGLE || type == Element::QUADRILATERAL)
1490 {
1491 MFEM_VERIFY(nx > 0 && ny > 0 && sx > 0.0 && sy > 0.0,
1492 "invalid 2D inline mesh format, all values must be "
1493 "positive\n"
1494 << " nx = " << nx << "\n"
1495 << " ny = " << ny << "\n"
1496 << " sx = " << sx << "\n"
1497 << " sy = " << sy << "\n");
1498 Make2D(nx, ny, type, sx, sy, generate_edges, true);
1499 }
1500 else if (type == Element::TETRAHEDRON || type == Element::WEDGE ||
1501 type == Element::HEXAHEDRON || type == Element::PYRAMID)
1502 {
1503 MFEM_VERIFY(nx > 0 && ny > 0 && nz > 0 &&
1504 sx > 0.0 && sy > 0.0 && sz > 0.0,
1505 "invalid 3D inline mesh format, all values must be "
1506 "positive\n"
1507 << " nx = " << nx << "\n"
1508 << " ny = " << ny << "\n"
1509 << " nz = " << nz << "\n"
1510 << " sx = " << sx << "\n"
1511 << " sy = " << sy << "\n"
1512 << " sz = " << sz << "\n");
1513 Make3D(nx, ny, nz, type, sx, sy, sz, true);
1514 // TODO: maybe have an option in the file to control ordering?
1515 }
1516 else
1517 {
1518 MFEM_ABORT("For inline mesh, must specify an element type ="
1519 " [segment, tri, quad, tet, hex, wedge]");
1520 }
1521}
1522
1523void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
1524{
1525 string buff;
1526 real_t version;
1527 int binary, dsize;
1528 input >> version >> binary >> dsize;
1529 if (version < 2.2)
1530 {
1531 MFEM_ABORT("Gmsh file version < 2.2");
1532 }
1533 if (dsize != sizeof(double))
1534 {
1535 MFEM_ABORT("Gmsh file : dsize != sizeof(double)");
1536 }
1537 getline(input, buff);
1538 // There is a number 1 in binary format
1539 if (binary)
1540 {
1541 int one;
1542 input.read(reinterpret_cast<char*>(&one), sizeof(one));
1543 if (one != 1)
1544 {
1545 MFEM_ABORT("Gmsh file : wrong binary format");
1546 }
1547 }
1548
1549 // A map between a serial number of the vertex and its number in the file
1550 // (there may be gaps in the numbering, and also Gmsh enumerates vertices
1551 // starting from 1, not 0)
1552 map<int, int> vertices_map;
1553
1554 // A map containing names of physical curves, surfaces, and volumes.
1555 // The first index is the dimension of the physical manifold, the second
1556 // index is the element attribute number of the set, and the string is
1557 // the assigned name.
1558 map<int,map<int,std::string> > phys_names_by_dim;
1559
1560 // Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the
1561 // mesh element dimension (Dim) and the dimension of the space in which the
1562 // mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and
1563 // spaceDim = 2, while a 2D surface mesh in 3D has Dim = 2 but spaceDim = 3.
1564 // Below we set spaceDim by measuring the mesh bounding box and checking for
1565 // a lower dimensional subspace. The assumption is that the mesh is at least
1566 // 2D if the y-dimension of the box is non-trivial and 3D if the z-dimension
1567 // is non-trivial. Note that with these assumptions a 2D mesh parallel to the
1568 // yz plane will be considered a surface mesh embedded in 3D whereas the same
1569 // 2D mesh parallel to the xy plane will be considered a 2D mesh.
1570 real_t bb_tol = 1e-14;
1571 real_t bb_min[3];
1572 real_t bb_max[3];
1573
1574 // Mesh order
1575 int mesh_order = 1;
1576
1577 // Mesh type
1578 bool periodic = false;
1579
1580 // Vector field to store uniformly spaced Gmsh high order mesh coords
1581 GridFunction Nodes_gf;
1582
1583 // Read the lines of the mesh file. If we face specific keyword, we'll treat
1584 // the section.
1585 while (input >> buff)
1586 {
1587 if (buff == "$Nodes") // reading mesh vertices
1588 {
1589 input >> NumOfVertices;
1590 getline(input, buff);
1591 vertices.SetSize(NumOfVertices);
1592 int serial_number;
1593 const int gmsh_dim = 3; // Gmsh always outputs 3 coordinates
1594 real_t coord[gmsh_dim];
1595 for (int ver = 0; ver < NumOfVertices; ++ver)
1596 {
1597 if (binary)
1598 {
1599 input.read(reinterpret_cast<char*>(&serial_number), sizeof(int));
1600 input.read(reinterpret_cast<char*>(coord), gmsh_dim*sizeof(double));
1601 }
1602 else // ASCII
1603 {
1604 input >> serial_number;
1605 for (int ci = 0; ci < gmsh_dim; ++ci)
1606 {
1607 input >> coord[ci];
1608 }
1609 }
1610 vertices[ver] = Vertex(coord, gmsh_dim);
1611 vertices_map[serial_number] = ver;
1612
1613 for (int ci = 0; ci < gmsh_dim; ++ci)
1614 {
1615 bb_min[ci] = (ver == 0) ? coord[ci] :
1616 std::min(bb_min[ci], coord[ci]);
1617 bb_max[ci] = (ver == 0) ? coord[ci] :
1618 std::max(bb_max[ci], coord[ci]);
1619 }
1620 }
1621 real_t bb_size = std::max(bb_max[0] - bb_min[0],
1622 std::max(bb_max[1] - bb_min[1],
1623 bb_max[2] - bb_min[2]));
1624 spaceDim = 1;
1625 if (bb_max[1] - bb_min[1] > bb_size * bb_tol)
1626 {
1627 spaceDim++;
1628 }
1629 if (bb_max[2] - bb_min[2] > bb_size * bb_tol)
1630 {
1631 spaceDim++;
1632 }
1633
1634 if (static_cast<int>(vertices_map.size()) != NumOfVertices)
1635 {
1636 MFEM_ABORT("Gmsh file : vertices indices are not unique");
1637 }
1638 } // section '$Nodes'
1639 else if (buff == "$Elements") // reading mesh elements
1640 {
1641 int num_of_all_elements;
1642 input >> num_of_all_elements;
1643 // = NumOfElements + NumOfBdrElements + (maybe, PhysicalPoints)
1644 getline(input, buff);
1645
1646 int serial_number; // serial number of an element
1647 int type_of_element; // ID describing a type of a mesh element
1648 int n_tags; // number of different tags describing an element
1649 int phys_domain; // element's attribute
1650 int elem_domain; // another element's attribute (rarely used)
1651 int n_partitions; // number of partitions where an element takes place
1652
1653 // number of nodes for each type of Gmsh elements, type is the index of
1654 // the array + 1
1655 int nodes_of_gmsh_element[] =
1656 {
1657 2, // 2-node line.
1658 3, // 3-node triangle.
1659 4, // 4-node quadrangle.
1660 4, // 4-node tetrahedron.
1661 8, // 8-node hexahedron.
1662 6, // 6-node prism.
1663 5, // 5-node pyramid.
1664 3, /* 3-node second order line (2 nodes associated with the
1665 vertices and 1 with the edge). */
1666 6, /* 6-node second order triangle (3 nodes associated with the
1667 vertices and 3 with the edges). */
1668 9, /* 9-node second order quadrangle (4 nodes associated with the
1669 vertices, 4 with the edges and 1 with the face). */
1670 10, /* 10-node second order tetrahedron (4 nodes associated with
1671 the vertices and 6 with the edges). */
1672 27, /* 27-node second order hexahedron (8 nodes associated with the
1673 vertices, 12 with the edges, 6 with the faces and 1 with the
1674 volume). */
1675 18, /* 18-node second order prism (6 nodes associated with the
1676 vertices, 9 with the edges and 3 with the quadrangular
1677 faces). */
1678 14, /* 14-node second order pyramid (5 nodes associated with the
1679 vertices, 8 with the edges and 1 with the quadrangular
1680 face). */
1681 1, // 1-node point.
1682 8, /* 8-node second order quadrangle (4 nodes associated with the
1683 vertices and 4 with the edges). */
1684 20, /* 20-node second order hexahedron (8 nodes associated with the
1685 vertices and 12 with the edges). */
1686 15, /* 15-node second order prism (6 nodes associated with the
1687 vertices and 9 with the edges). */
1688 13, /* 13-node second order pyramid (5 nodes associated with the
1689 vertices and 8 with the edges). */
1690 9, /* 9-node third order incomplete triangle (3 nodes associated
1691 with the vertices, 6 with the edges) */
1692 10, /* 10-node third order triangle (3 nodes associated with the
1693 vertices, 6 with the edges, 1 with the face) */
1694 12, /* 12-node fourth order incomplete triangle (3 nodes associated
1695 with the vertices, 9 with the edges) */
1696 15, /* 15-node fourth order triangle (3 nodes associated with the
1697 vertices, 9 with the edges, 3 with the face) */
1698 15, /* 15-node fifth order incomplete triangle (3 nodes associated
1699 with the vertices, 12 with the edges) */
1700 21, /* 21-node fifth order complete triangle (3 nodes associated
1701 with the vertices, 12 with the edges, 6 with the face) */
1702 4, /* 4-node third order edge (2 nodes associated with the
1703 vertices, 2 internal to the edge) */
1704 5, /* 5-node fourth order edge (2 nodes associated with the
1705 vertices, 3 internal to the edge) */
1706 6, /* 6-node fifth order edge (2 nodes associated with the
1707 vertices, 4 internal to the edge) */
1708 20, /* 20-node third order tetrahedron (4 nodes associated with the
1709 vertices, 12 with the edges, 4 with the faces) */
1710 35, /* 35-node fourth order tetrahedron (4 nodes associated with
1711 the vertices, 18 with the edges, 12 with the faces, and 1
1712 with the volume) */
1713 56, /* 56-node fifth order tetrahedron (4 nodes associated with the
1714 vertices, 24 with the edges, 24 with the faces, and 4 with
1715 the volume) */
1716 -1,-1, /* unsupported tetrahedral types */
1717 -1,-1, /* unsupported polygonal and polyhedral types */
1718 16, /* 16-node third order quadrilateral (4 nodes associated with
1719 the vertices, 8 with the edges, 4 with the face) */
1720 25, /* 25-node fourth order quadrilateral (4 nodes associated with
1721 the vertices, 12 with the edges, 9 with the face) */
1722 36, /* 36-node fifth order quadrilateral (4 nodes associated with
1723 the vertices, 16 with the edges, 16 with the face) */
1724 -1,-1,-1, /* unsupported quadrilateral types */
1725 28, /* 28-node sixth order complete triangle (3 nodes associated
1726 with the vertices, 15 with the edges, 10 with the face) */
1727 36, /* 36-node seventh order complete triangle (3 nodes associated
1728 with the vertices, 18 with the edges, 15 with the face) */
1729 45, /* 45-node eighth order complete triangle (3 nodes associated
1730 with the vertices, 21 with the edges, 21 with the face) */
1731 55, /* 55-node ninth order complete triangle (3 nodes associated
1732 with the vertices, 24 with the edges, 28 with the face) */
1733 66, /* 66-node tenth order complete triangle (3 nodes associated
1734 with the vertices, 27 with the edges, 36 with the face) */
1735 49, /* 49-node sixth order quadrilateral (4 nodes associated with
1736 the vertices, 20 with the edges, 25 with the face) */
1737 64, /* 64-node seventh order quadrilateral (4 nodes associated with
1738 the vertices, 24 with the edges, 36 with the face) */
1739 81, /* 81-node eighth order quadrilateral (4 nodes associated with
1740 the vertices, 28 with the edges, 49 with the face) */
1741 100, /* 100-node ninth order quadrilateral (4 nodes associated with
1742 the vertices, 32 with the edges, 64 with the face) */
1743 121, /* 121-node tenth order quadrilateral (4 nodes associated with
1744 the vertices, 36 with the edges, 81 with the face) */
1745 -1,-1,-1,-1,-1, /* unsupported triangular types */
1746 -1,-1,-1,-1,-1, /* unsupported quadrilateral types */
1747 7, /* 7-node sixth order edge (2 nodes associated with the
1748 vertices, 5 internal to the edge) */
1749 8, /* 8-node seventh order edge (2 nodes associated with the
1750 vertices, 6 internal to the edge) */
1751 9, /* 9-node eighth order edge (2 nodes associated with the
1752 vertices, 7 internal to the edge) */
1753 10, /* 10-node ninth order edge (2 nodes associated with the
1754 vertices, 8 internal to the edge) */
1755 11, /* 11-node tenth order edge (2 nodes associated with the
1756 vertices, 9 internal to the edge) */
1757 -1, /* unsupported linear types */
1758 -1,-1,-1, /* unsupported types */
1759 84, /* 84-node sixth order tetrahedron (4 nodes associated with the
1760 vertices, 30 with the edges, 40 with the faces, and 10 with
1761 the volume) */
1762 120, /* 120-node seventh order tetrahedron (4 nodes associated with
1763 the vertices, 36 with the edges, 60 with the faces, and 20
1764 with the volume) */
1765 165, /* 165-node eighth order tetrahedron (4 nodes associated with
1766 the vertices, 42 with the edges, 84 with the faces, and 35
1767 with the volume) */
1768 220, /* 220-node ninth order tetrahedron (4 nodes associated with
1769 the vertices, 48 with the edges, 112 with the faces, and 56
1770 with the volume) */
1771 286, /* 286-node tenth order tetrahedron (4 nodes associated with
1772 the vertices, 54 with the edges, 144 with the faces, and 84
1773 with the volume) */
1774 -1,-1,-1, /* undefined types */
1775 -1,-1,-1,-1,-1, /* unsupported tetrahedral types */
1776 -1,-1,-1,-1,-1,-1, /* unsupported types */
1777 40, /* 40-node third order prism (6 nodes associated with the
1778 vertices, 18 with the edges, 14 with the faces, and 2 with
1779 the volume) */
1780 75, /* 75-node fourth order prism (6 nodes associated with the
1781 vertices, 27 with the edges, 33 with the faces, and 9 with
1782 the volume) */
1783 64, /* 64-node third order hexahedron (8 nodes associated with the
1784 vertices, 24 with the edges, 24 with the faces and 8 with
1785 the volume).*/
1786 125, /* 125-node fourth order hexahedron (8 nodes associated with
1787 the vertices, 36 with the edges, 54 with the faces and 27
1788 with the volume).*/
1789 216, /* 216-node fifth order hexahedron (8 nodes associated with the
1790 vertices, 48 with the edges, 96 with the faces and 64 with
1791 the volume).*/
1792 343, /* 343-node sixth order hexahedron (8 nodes associated with the
1793 vertices, 60 with the edges, 150 with the faces and 125 with
1794 the volume).*/
1795 512, /* 512-node seventh order hexahedron (8 nodes associated with
1796 the vertices, 72 with the edges, 216 with the faces and 216
1797 with the volume).*/
1798 729, /* 729-node eighth order hexahedron (8 nodes associated with
1799 the vertices, 84 with the edges, 294 with the faces and 343
1800 with the volume).*/
1801 1000,/* 1000-node ninth order hexahedron (8 nodes associated with
1802 the vertices, 96 with the edges, 384 with the faces and 512
1803 with the volume).*/
1804 -1,-1,-1,-1,-1,-1,-1, /* unsupported hexahedron types */
1805 126, /* 126-node fifth order prism (6 nodes associated with the
1806 vertices, 36 with the edges, 60 with the faces, and 24 with
1807 the volume) */
1808 196, /* 196-node sixth order prism (6 nodes associated with the
1809 vertices, 45 with the edges, 95 with the faces, and 50 with
1810 the volume) */
1811 288, /* 288-node seventh order prism (6 nodes associated with the
1812 vertices, 54 with the edges, 138 with the faces, and 90 with
1813 the volume) */
1814 405, /* 405-node eighth order prism (6 nodes associated with the
1815 vertices, 63 with the edges, 189 with the faces, and 147
1816 with the volume) */
1817 550, /* 550-node ninth order prism (6 nodes associated with the
1818 vertices, 72 with the edges, 248 with the faces, and 224
1819 with the volume) */
1820 -1,-1,-1,-1,-1,-1,-1, /* unsupported prism types */
1821 30, /* 30-node third order pyramid (5 nodes associated with the
1822 vertices, 16 with the edges and 8 with the faces, and 1 with
1823 the volume). */
1824 55, /* 55-node fourth order pyramid (5 nodes associated with the
1825 vertices, 24 with the edges and 21 with the faces, and 5
1826 with the volume). */
1827 91, /* 91-node fifth order pyramid (5 nodes associated with the
1828 vertices, 32 with the edges and 40 with the faces, and 14
1829 with the volume). */
1830 140, /* 140-node sixth order pyramid (5 nodes associated with the
1831 vertices, 40 with the edges and 65 with the faces, and 30
1832 with the volume). */
1833 204, /* 204-node seventh order pyramid (5 nodes associated with the
1834 vertices, 48 with the edges and 96 with the faces, and 55
1835 with the volume). */
1836 285, /* 285-node eighth order pyramid (5 nodes associated with the
1837 vertices, 56 with the edges and 133 with the faces, and 91
1838 with the volume). */
1839 385 /* 385-node ninth order pyramid (5 nodes associated with the
1840 vertices, 64 with the edges and 176 with the faces, and 140
1841 with the volume). */
1842 };
1843
1844 /** The following mappings convert the Gmsh node orderings for high
1845 order elements to MFEM's L2 degree of freedom ordering. To support
1846 more options examine Gmsh's ordering and read off the indices in
1847 MFEM's order. For example 2nd order Gmsh quadrilaterals use the
1848 following ordering:
1849
1850 3--6--2
1851 | | |
1852 7 8 5
1853 | | |
1854 0--4--1
1855
1856 (from https://gmsh.info/doc/texinfo/gmsh.html#Node-ordering)
1857
1858 Whereas MFEM uses a tensor product ordering with the horizontal
1859 axis cycling fastest so we would read off:
1860
1861 0 4 1 7 8 5 3 6 2
1862
1863 This corresponds to the quad9 mapping below.
1864 */
1865 int lin3[] = {0,2,1}; // 2nd order segment
1866 int lin4[] = {0,2,3,1}; // 3rd order segment
1867 int tri6[] = {0,3,1,5,4,2}; // 2nd order triangle
1868 int tri10[] = {0,3,4,1,8,9,5,7,6,2}; // 3rd order triangle
1869 int quad9[] = {0,4,1,7,8,5,3,6,2}; // 2nd order quadrilateral
1870 int quad16[] = {0,4,5,1,11,12,13,6, // 3rd order quadrilateral
1871 10,15,14,7,3,9,8,2
1872 };
1873 int tet10[] {0,4,1,6,5,2,7,9,8,3}; // 2nd order tetrahedron
1874 int tet20[] = {0,4,5,1,9,16,6,8,7,2, // 3rd order tetrahedron
1875 11,17,15,18,19,13,10,14,12,3
1876 };
1877 int hex27[] {0,8,1,9,20,11,3,13,2, // 2nd order hexahedron
1878 10,21,12,22,26,23,15,24,14,
1879 4,16,5,17,25,18,7,19,6
1880 };
1881 int hex64[] {0,8,9,1,10,32,35,14, // 3rd order hexahedron
1882 11,33,34,15,3,19,18,2,
1883 12,36,37,16,40,56,57,44,
1884 43,59,58,45,22,49,48,20,
1885 13,39,38,17,41,60,61,47,
1886 42,63,62,46,23,50,51,21,
1887 4,24,25,5,26,52,53,28,
1888 27,55,54,29,7,31,30,6
1889 };
1890
1891 int wdg18[] = {0,6,1,7,9,2,8,15,10, // 2nd order wedge/prism
1892 16,17,11,3,12,4,13,14,5
1893 };
1894 int wdg40[] = {0,6,7,1,8,24,12,9,13,2, // 3rd order wedge/prism
1895 10,26,27,14,30,38,34,33,35,16,
1896 11,29,28,15,31,39,37,32,36,17,
1897 3,18,19,4,20,25,22,21,23,5
1898 };
1899
1900 int pyr14[] = {0,5,1,6,13,8,3, // 2nd order pyramid
1901 10,2,7,9,12,11,4
1902 };
1903 int pyr30[] = {0,5,6,1,7,25,28,11,8,26, // 3rd order pyramid
1904 27,12,3,16,15,2,9,21,13,22,
1905 29,23,19,24,17,10,14,20,18,4
1906 };
1907
1908 vector<Element*> elements_0D, elements_1D, elements_2D, elements_3D;
1909 elements_0D.reserve(num_of_all_elements);
1910 elements_1D.reserve(num_of_all_elements);
1911 elements_2D.reserve(num_of_all_elements);
1912 elements_3D.reserve(num_of_all_elements);
1913
1914 // Temporary storage for high order vertices, if present
1915 vector<Array<int>*> ho_verts_1D, ho_verts_2D, ho_verts_3D;
1916 ho_verts_1D.reserve(num_of_all_elements);
1917 ho_verts_2D.reserve(num_of_all_elements);
1918 ho_verts_3D.reserve(num_of_all_elements);
1919
1920 // Temporary storage for order of elements
1921 vector<int> ho_el_order_1D, ho_el_order_2D, ho_el_order_3D;
1922 ho_el_order_1D.reserve(num_of_all_elements);
1923 ho_el_order_2D.reserve(num_of_all_elements);
1924 ho_el_order_3D.reserve(num_of_all_elements);
1925
1926 // Vertex order mappings
1927 Array<int*> ho_lin(11); ho_lin = NULL;
1928 Array<int*> ho_tri(11); ho_tri = NULL;
1929 Array<int*> ho_sqr(11); ho_sqr = NULL;
1930 Array<int*> ho_tet(11); ho_tet = NULL;
1931 Array<int*> ho_hex(10); ho_hex = NULL;
1932 Array<int*> ho_wdg(10); ho_wdg = NULL;
1933 Array<int*> ho_pyr(10); ho_pyr = NULL;
1934
1935 // Use predefined arrays at lowest orders (for efficiency)
1936 ho_lin[2] = lin3; ho_lin[3] = lin4;
1937 ho_tri[2] = tri6; ho_tri[3] = tri10;
1938 ho_sqr[2] = quad9; ho_sqr[3] = quad16;
1939 ho_tet[2] = tet10; ho_tet[3] = tet20;
1940 ho_hex[2] = hex27; ho_hex[3] = hex64;
1941 ho_wdg[2] = wdg18; ho_wdg[3] = wdg40;
1942 ho_pyr[2] = pyr14; ho_pyr[3] = pyr30;
1943
1944 bool has_nonpositive_phys_domain = false;
1945 bool has_positive_phys_domain = false;
1946
1947 if (binary)
1948 {
1949 int n_elem_part = 0; // partial sum of elements that are read
1950 const int header_size = 3;
1951 // header consists of 3 numbers: type of the element, number of
1952 // elements of this type, and number of tags
1953 int header[header_size];
1954 int n_elem_one_type; // number of elements of a specific type
1955
1956 while (n_elem_part < num_of_all_elements)
1957 {
1958 input.read(reinterpret_cast<char*>(header),
1959 header_size*sizeof(int));
1960 type_of_element = header[0];
1961 n_elem_one_type = header[1];
1962 n_tags = header[2];
1963
1964 n_elem_part += n_elem_one_type;
1965
1966 const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1];
1967 vector<int> data(1+n_tags+n_elem_nodes);
1968 for (int el = 0; el < n_elem_one_type; ++el)
1969 {
1970 input.read(reinterpret_cast<char*>(&data[0]),
1971 data.size()*sizeof(int));
1972 int dd = 0; // index for data array
1973 serial_number = data[dd++];
1974 // physical domain - the most important value (to distinguish
1975 // materials with different properties)
1976 phys_domain = (n_tags > 0) ? data[dd++] : 1;
1977 // elementary domain - to distinguish different geometrical
1978 // domains (typically, it's used rarely)
1979 elem_domain = (n_tags > 1) ? data[dd++] : 0;
1980 // the number of tags is bigger than 2 if there are some
1981 // partitions (domain decompositions)
1982 n_partitions = (n_tags > 2) ? data[dd++] : 0;
1983 // we currently just skip the partitions if they exist, and go
1984 // directly to vertices describing the mesh element
1985 vector<int> vert_indices(n_elem_nodes);
1986 for (int vi = 0; vi < n_elem_nodes; ++vi)
1987 {
1988 map<int, int>::const_iterator it =
1989 vertices_map.find(data[1+n_tags+vi]);
1990 if (it == vertices_map.end())
1991 {
1992 MFEM_ABORT("Gmsh file : vertex index doesn't exist");
1993 }
1994 vert_indices[vi] = it->second;
1995 }
1996
1997 // Non-positive attributes are not allowed in MFEM. However,
1998 // by default, Gmsh sets the physical domain of all elements
1999 // to zero. In the case that all elements have physical domain
2000 // zero, we will given them attribute 1. If only some elements
2001 // have physical domain zero, we will throw an error.
2002 if (phys_domain <= 0)
2003 {
2004 has_nonpositive_phys_domain = true;
2005 phys_domain = 1;
2006 }
2007 else
2008 {
2009 has_positive_phys_domain = true;
2010 }
2011
2012 // initialize the mesh element
2013 int el_order = 11;
2014 switch (type_of_element)
2015 {
2016 case 1: // 2-node line
2017 case 8: // 3-node line (2nd order)
2018 case 26: // 4-node line (3rd order)
2019 case 27: // 5-node line (4th order)
2020 case 28: // 6-node line (5th order)
2021 case 62: // 7-node line (6th order)
2022 case 63: // 8-node line (7th order)
2023 case 64: // 9-node line (8th order)
2024 case 65: // 10-node line (9th order)
2025 case 66: // 11-node line (10th order)
2026 {
2027 elements_1D.push_back(
2028 new Segment(&vert_indices[0], phys_domain));
2029 if (type_of_element != 1)
2030 {
2031 el_order = n_elem_nodes - 1;
2032 Array<int> * hov = new Array<int>;
2033 hov->Append(&vert_indices[0], n_elem_nodes);
2034 ho_verts_1D.push_back(hov);
2035 ho_el_order_1D.push_back(el_order);
2036 }
2037 break;
2038 }
2039 case 2: el_order--; // 3-node triangle
2040 case 9: el_order--; // 6-node triangle (2nd order)
2041 case 21: el_order--; // 10-node triangle (3rd order)
2042 case 23: el_order--; // 15-node triangle (4th order)
2043 case 25: el_order--; // 21-node triangle (5th order)
2044 case 42: el_order--; // 28-node triangle (6th order)
2045 case 43: el_order--; // 36-node triangle (7th order)
2046 case 44: el_order--; // 45-node triangle (8th order)
2047 case 45: el_order--; // 55-node triangle (9th order)
2048 case 46: el_order--; // 66-node triangle (10th order)
2049 {
2050 elements_2D.push_back(
2051 new Triangle(&vert_indices[0], phys_domain));
2052 if (el_order > 1)
2053 {
2054 Array<int> * hov = new Array<int>;
2055 hov->Append(&vert_indices[0], n_elem_nodes);
2056 ho_verts_2D.push_back(hov);
2057 ho_el_order_2D.push_back(el_order);
2058 }
2059 break;
2060 }
2061 case 3: el_order--; // 4-node quadrangle
2062 case 10: el_order--; // 9-node quadrangle (2nd order)
2063 case 36: el_order--; // 16-node quadrangle (3rd order)
2064 case 37: el_order--; // 25-node quadrangle (4th order)
2065 case 38: el_order--; // 36-node quadrangle (5th order)
2066 case 47: el_order--; // 49-node quadrangle (6th order)
2067 case 48: el_order--; // 64-node quadrangle (7th order)
2068 case 49: el_order--; // 81-node quadrangle (8th order)
2069 case 50: el_order--; // 100-node quadrangle (9th order)
2070 case 51: el_order--; // 121-node quadrangle (10th order)
2071 {
2072 elements_2D.push_back(
2073 new Quadrilateral(&vert_indices[0], phys_domain));
2074 if (el_order > 1)
2075 {
2076 Array<int> * hov = new Array<int>;
2077 hov->Append(&vert_indices[0], n_elem_nodes);
2078 ho_verts_2D.push_back(hov);
2079 ho_el_order_2D.push_back(el_order);
2080 }
2081 break;
2082 }
2083 case 4: el_order--; // 4-node tetrahedron
2084 case 11: el_order--; // 10-node tetrahedron (2nd order)
2085 case 29: el_order--; // 20-node tetrahedron (3rd order)
2086 case 30: el_order--; // 35-node tetrahedron (4th order)
2087 case 31: el_order--; // 56-node tetrahedron (5th order)
2088 case 71: el_order--; // 84-node tetrahedron (6th order)
2089 case 72: el_order--; // 120-node tetrahedron (7th order)
2090 case 73: el_order--; // 165-node tetrahedron (8th order)
2091 case 74: el_order--; // 220-node tetrahedron (9th order)
2092 case 75: el_order--; // 286-node tetrahedron (10th order)
2093 {
2094#ifdef MFEM_USE_MEMALLOC
2095 elements_3D.push_back(TetMemory.Alloc());
2096 elements_3D.back()->SetVertices(&vert_indices[0]);
2097 elements_3D.back()->SetAttribute(phys_domain);
2098#else
2099 elements_3D.push_back(
2100 new Tetrahedron(&vert_indices[0], phys_domain));
2101#endif
2102 if (el_order > 1)
2103 {
2104 Array<int> * hov = new Array<int>;
2105 hov->Append(&vert_indices[0], n_elem_nodes);
2106 ho_verts_3D.push_back(hov);
2107 ho_el_order_3D.push_back(el_order);
2108 }
2109 break;
2110 }
2111 case 5: el_order--; // 8-node hexahedron
2112 case 12: el_order--; // 27-node hexahedron (2nd order)
2113 case 92: el_order--; // 64-node hexahedron (3rd order)
2114 case 93: el_order--; // 125-node hexahedron (4th order)
2115 case 94: el_order--; // 216-node hexahedron (5th order)
2116 case 95: el_order--; // 343-node hexahedron (6th order)
2117 case 96: el_order--; // 512-node hexahedron (7th order)
2118 case 97: el_order--; // 729-node hexahedron (8th order)
2119 case 98: el_order--; // 1000-node hexahedron (9th order)
2120 {
2121 el_order--; // Gmsh does not define an order 10 hex
2122 elements_3D.push_back(
2123 new Hexahedron(&vert_indices[0], phys_domain));
2124 if (el_order > 1)
2125 {
2126 Array<int> * hov = new Array<int>;
2127 hov->Append(&vert_indices[0], n_elem_nodes);
2128 ho_verts_3D.push_back(hov);
2129 ho_el_order_3D.push_back(el_order);
2130 }
2131 break;
2132 }
2133 case 6: el_order--; // 6-node wedge
2134 case 13: el_order--; // 18-node wedge (2nd order)
2135 case 90: el_order--; // 40-node wedge (3rd order)
2136 case 91: el_order--; // 75-node wedge (4th order)
2137 case 106: el_order--; // 126-node wedge (5th order)
2138 case 107: el_order--; // 196-node wedge (6th order)
2139 case 108: el_order--; // 288-node wedge (7th order)
2140 case 109: el_order--; // 405-node wedge (8th order)
2141 case 110: el_order--; // 550-node wedge (9th order)
2142 {
2143 el_order--; // Gmsh does not define an order 10 wedge
2144 elements_3D.push_back(
2145 new Wedge(&vert_indices[0], phys_domain));
2146 if (el_order > 1)
2147 {
2148 Array<int> * hov = new Array<int>;
2149 hov->Append(&vert_indices[0], n_elem_nodes);
2150 ho_verts_3D.push_back(hov);
2151 ho_el_order_3D.push_back(el_order);
2152 }
2153 break;
2154 }
2155 case 7: el_order--; // 5-node pyramid
2156 case 14: el_order--; // 14-node pyramid (2nd order)
2157 case 118: el_order--; // 30-node pyramid (3rd order)
2158 case 119: el_order--; // 55-node pyramid (4th order)
2159 case 120: el_order--; // 91-node pyramid (5th order)
2160 case 121: el_order--; // 140-node pyramid (6th order)
2161 case 122: el_order--; // 204-node pyramid (7th order)
2162 case 123: el_order--; // 285-node pyramid (8th order)
2163 case 124: el_order--; // 385-node pyramid (9th order)
2164 {
2165 el_order--; // Gmsh does not define an order 10 pyr
2166 elements_3D.push_back(
2167 new Pyramid(&vert_indices[0], phys_domain));
2168 if (el_order > 1)
2169 {
2170 Array<int> * hov = new Array<int>;
2171 hov->Append(&vert_indices[0], n_elem_nodes);
2172 ho_verts_3D.push_back(hov);
2173 ho_el_order_3D.push_back(el_order);
2174 }
2175 break;
2176 }
2177 case 15: // 1-node point
2178 {
2179 elements_0D.push_back(
2180 new Point(&vert_indices[0], phys_domain));
2181 break;
2182 }
2183 default: // any other element
2184 MFEM_WARNING("Unsupported Gmsh element type.");
2185 break;
2186
2187 } // switch (type_of_element)
2188 } // el (elements of one type)
2189 } // all elements
2190 } // if binary
2191 else // ASCII
2192 {
2193 for (int el = 0; el < num_of_all_elements; ++el)
2194 {
2195 input >> serial_number >> type_of_element >> n_tags;
2196 vector<int> data(n_tags);
2197 for (int i = 0; i < n_tags; ++i) { input >> data[i]; }
2198 // physical domain - the most important value (to distinguish
2199 // materials with different properties)
2200 phys_domain = (n_tags > 0) ? data[0] : 1;
2201 // elementary domain - to distinguish different geometrical
2202 // domains (typically, it's used rarely)
2203 elem_domain = (n_tags > 1) ? data[1] : 0;
2204 // the number of tags is bigger than 2 if there are some
2205 // partitions (domain decompositions)
2206 n_partitions = (n_tags > 2) ? data[2] : 0;
2207 // we currently just skip the partitions if they exist, and go
2208 // directly to vertices describing the mesh element
2209 const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1];
2210 vector<int> vert_indices(n_elem_nodes);
2211 int index;
2212 for (int vi = 0; vi < n_elem_nodes; ++vi)
2213 {
2214 input >> index;
2215 map<int, int>::const_iterator it = vertices_map.find(index);
2216 if (it == vertices_map.end())
2217 {
2218 MFEM_ABORT("Gmsh file : vertex index doesn't exist");
2219 }
2220 vert_indices[vi] = it->second;
2221 }
2222
2223 // Non-positive attributes are not allowed in MFEM. However,
2224 // by default, Gmsh sets the physical domain of all elements
2225 // to zero. In the case that all elements have physical domain
2226 // zero, we will given them attribute 1. If only some elements
2227 // have physical domain zero, we will throw an error.
2228 if (phys_domain <= 0)
2229 {
2230 has_nonpositive_phys_domain = true;
2231 phys_domain = 1;
2232 }
2233 else
2234 {
2235 has_positive_phys_domain = true;
2236 }
2237
2238 // initialize the mesh element
2239 int el_order = 11;
2240 switch (type_of_element)
2241 {
2242 case 1: // 2-node line
2243 case 8: // 3-node line (2nd order)
2244 case 26: // 4-node line (3rd order)
2245 case 27: // 5-node line (4th order)
2246 case 28: // 6-node line (5th order)
2247 case 62: // 7-node line (6th order)
2248 case 63: // 8-node line (7th order)
2249 case 64: // 9-node line (8th order)
2250 case 65: // 10-node line (9th order)
2251 case 66: // 11-node line (10th order)
2252 {
2253 elements_1D.push_back(
2254 new Segment(&vert_indices[0], phys_domain));
2255 if (type_of_element != 1)
2256 {
2257 Array<int> * hov = new Array<int>;
2258 hov->Append(&vert_indices[0], n_elem_nodes);
2259 ho_verts_1D.push_back(hov);
2260 el_order = n_elem_nodes - 1;
2261 ho_el_order_1D.push_back(el_order);
2262 }
2263 break;
2264 }
2265 case 2: el_order--; // 3-node triangle
2266 case 9: el_order--; // 6-node triangle (2nd order)
2267 case 21: el_order--; // 10-node triangle (3rd order)
2268 case 23: el_order--; // 15-node triangle (4th order)
2269 case 25: el_order--; // 21-node triangle (5th order)
2270 case 42: el_order--; // 28-node triangle (6th order)
2271 case 43: el_order--; // 36-node triangle (7th order)
2272 case 44: el_order--; // 45-node triangle (8th order)
2273 case 45: el_order--; // 55-node triangle (9th order)
2274 case 46: el_order--; // 66-node triangle (10th order)
2275 {
2276 elements_2D.push_back(
2277 new Triangle(&vert_indices[0], phys_domain));
2278 if (el_order > 1)
2279 {
2280 Array<int> * hov = new Array<int>;
2281 hov->Append(&vert_indices[0], n_elem_nodes);
2282 ho_verts_2D.push_back(hov);
2283 ho_el_order_2D.push_back(el_order);
2284 }
2285 break;
2286 }
2287 case 3: el_order--; // 4-node quadrangle
2288 case 10: el_order--; // 9-node quadrangle (2nd order)
2289 case 36: el_order--; // 16-node quadrangle (3rd order)
2290 case 37: el_order--; // 25-node quadrangle (4th order)
2291 case 38: el_order--; // 36-node quadrangle (5th order)
2292 case 47: el_order--; // 49-node quadrangle (6th order)
2293 case 48: el_order--; // 64-node quadrangle (7th order)
2294 case 49: el_order--; // 81-node quadrangle (8th order)
2295 case 50: el_order--; // 100-node quadrangle (9th order)
2296 case 51: el_order--; // 121-node quadrangle (10th order)
2297 {
2298 elements_2D.push_back(
2299 new Quadrilateral(&vert_indices[0], phys_domain));
2300 if (el_order > 1)
2301 {
2302 Array<int> * hov = new Array<int>;
2303 hov->Append(&vert_indices[0], n_elem_nodes);
2304 ho_verts_2D.push_back(hov);
2305 ho_el_order_2D.push_back(el_order);
2306 }
2307 break;
2308 }
2309 case 4: el_order--; // 4-node tetrahedron
2310 case 11: el_order--; // 10-node tetrahedron (2nd order)
2311 case 29: el_order--; // 20-node tetrahedron (3rd order)
2312 case 30: el_order--; // 35-node tetrahedron (4th order)
2313 case 31: el_order--; // 56-node tetrahedron (5th order)
2314 case 71: el_order--; // 84-node tetrahedron (6th order)
2315 case 72: el_order--; // 120-node tetrahedron (7th order)
2316 case 73: el_order--; // 165-node tetrahedron (8th order)
2317 case 74: el_order--; // 220-node tetrahedron (9th order)
2318 case 75: el_order--; // 286-node tetrahedron (10th order)
2319 {
2320#ifdef MFEM_USE_MEMALLOC
2321 elements_3D.push_back(TetMemory.Alloc());
2322 elements_3D.back()->SetVertices(&vert_indices[0]);
2323 elements_3D.back()->SetAttribute(phys_domain);
2324#else
2325 elements_3D.push_back(
2326 new Tetrahedron(&vert_indices[0], phys_domain));
2327#endif
2328 if (el_order > 1)
2329 {
2330 Array<int> * hov = new Array<int>;
2331 hov->Append(&vert_indices[0], n_elem_nodes);
2332 ho_verts_3D.push_back(hov);
2333 ho_el_order_3D.push_back(el_order);
2334 }
2335 break;
2336 }
2337 case 5: el_order--; // 8-node hexahedron
2338 case 12: el_order--; // 27-node hexahedron (2nd order)
2339 case 92: el_order--; // 64-node hexahedron (3rd order)
2340 case 93: el_order--; // 125-node hexahedron (4th order)
2341 case 94: el_order--; // 216-node hexahedron (5th order)
2342 case 95: el_order--; // 343-node hexahedron (6th order)
2343 case 96: el_order--; // 512-node hexahedron (7th order)
2344 case 97: el_order--; // 729-node hexahedron (8th order)
2345 case 98: el_order--; // 1000-node hexahedron (9th order)
2346 {
2347 el_order--;
2348 elements_3D.push_back(
2349 new Hexahedron(&vert_indices[0], phys_domain));
2350 if (el_order > 1)
2351 {
2352 Array<int> * hov = new Array<int>;
2353 hov->Append(&vert_indices[0], n_elem_nodes);
2354 ho_verts_3D.push_back(hov);
2355 ho_el_order_3D.push_back(el_order);
2356 }
2357 break;
2358 }
2359 case 6: el_order--; // 6-node wedge
2360 case 13: el_order--; // 18-node wedge (2nd order)
2361 case 90: el_order--; // 40-node wedge (3rd order)
2362 case 91: el_order--; // 75-node wedge (4th order)
2363 case 106: el_order--; // 126-node wedge (5th order)
2364 case 107: el_order--; // 196-node wedge (6th order)
2365 case 108: el_order--; // 288-node wedge (7th order)
2366 case 109: el_order--; // 405-node wedge (8th order)
2367 case 110: el_order--; // 550-node wedge (9th order)
2368 {
2369 el_order--;
2370 elements_3D.push_back(
2371 new Wedge(&vert_indices[0], phys_domain));
2372 if (el_order > 1)
2373 {
2374 Array<int> * hov = new Array<int>;
2375 hov->Append(&vert_indices[0], n_elem_nodes);
2376 ho_verts_3D.push_back(hov);
2377 ho_el_order_3D.push_back(el_order);
2378 }
2379 break;
2380 }
2381 case 7: el_order--; // 5-node pyramid
2382 case 14: el_order--; // 14-node pyramid (2nd order)
2383 case 118: el_order--; // 30-node pyramid (3rd order)
2384 case 119: el_order--; // 55-node pyramid (4th order)
2385 case 120: el_order--; // 91-node pyramid (5th order)
2386 case 121: el_order--; // 140-node pyramid (6th order)
2387 case 122: el_order--; // 204-node pyramid (7th order)
2388 case 123: el_order--; // 285-node pyramid (8th order)
2389 case 124: el_order--; // 385-node pyramid (9th order)
2390 {
2391 el_order--;
2392 elements_3D.push_back(
2393 new Pyramid(&vert_indices[0], phys_domain));
2394 if (el_order > 1)
2395 {
2396 Array<int> * hov = new Array<int>;
2397 hov->Append(&vert_indices[0], n_elem_nodes);
2398 ho_verts_3D.push_back(hov);
2399 ho_el_order_3D.push_back(el_order);
2400 }
2401 break;
2402 }
2403 case 15: // 1-node point
2404 {
2405 elements_0D.push_back(
2406 new Point(&vert_indices[0], phys_domain));
2407 break;
2408 }
2409 default: // any other element
2410 MFEM_WARNING("Unsupported Gmsh element type.");
2411 break;
2412
2413 } // switch (type_of_element)
2414 } // el (all elements)
2415 } // if ASCII
2416
2417 if (has_positive_phys_domain && has_nonpositive_phys_domain)
2418 {
2419 MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n"
2420 "By default Gmsh sets element tags (attributes)"
2421 " to '0' but MFEM requires that they be"
2422 " positive integers.\n"
2423 "Use \"Physical Curve\", \"Physical Surface\","
2424 " or \"Physical Volume\" to set tags/attributes"
2425 " for all curves, surfaces, or volumes in your"
2426 " Gmsh geometry to values which are >= 1.");
2427 }
2428 else if (has_nonpositive_phys_domain)
2429 {
2430 mfem::out << "\nGmsh reader: all element attributes were zero.\n"
2431 << "MFEM only supports positive element attributes.\n"
2432 << "Setting element attributes to 1.\n\n";
2433 }
2434
2435 if (!elements_3D.empty())
2436 {
2437 Dim = 3;
2438 NumOfElements = static_cast<int>(elements_3D.size());
2439 elements.SetSize(NumOfElements);
2440 for (int el = 0; el < NumOfElements; ++el)
2441 {
2442 elements[el] = elements_3D[el];
2443 }
2444 NumOfBdrElements = static_cast<int>(elements_2D.size());
2445 boundary.SetSize(NumOfBdrElements);
2446 for (int el = 0; el < NumOfBdrElements; ++el)
2447 {
2448 boundary[el] = elements_2D[el];
2449 }
2450 for (size_t el = 0; el < ho_el_order_3D.size(); el++)
2451 {
2452 mesh_order = max(mesh_order, ho_el_order_3D[el]);
2453 }
2454 // discard other elements
2455 for (size_t el = 0; el < elements_1D.size(); ++el)
2456 {
2457 delete elements_1D[el];
2458 }
2459 for (size_t el = 0; el < elements_0D.size(); ++el)
2460 {
2461 delete elements_0D[el];
2462 }
2463 }
2464 else if (!elements_2D.empty())
2465 {
2466 Dim = 2;
2467 NumOfElements = static_cast<int>(elements_2D.size());
2468 elements.SetSize(NumOfElements);
2469 for (int el = 0; el < NumOfElements; ++el)
2470 {
2471 elements[el] = elements_2D[el];
2472 }
2473 NumOfBdrElements = static_cast<int>(elements_1D.size());
2474 boundary.SetSize(NumOfBdrElements);
2475 for (int el = 0; el < NumOfBdrElements; ++el)
2476 {
2477 boundary[el] = elements_1D[el];
2478 }
2479 for (size_t el = 0; el < ho_el_order_2D.size(); el++)
2480 {
2481 mesh_order = max(mesh_order, ho_el_order_2D[el]);
2482 }
2483 // discard other elements
2484 for (size_t el = 0; el < elements_0D.size(); ++el)
2485 {
2486 delete elements_0D[el];
2487 }
2488 }
2489 else if (!elements_1D.empty())
2490 {
2491 Dim = 1;
2492 NumOfElements = static_cast<int>(elements_1D.size());
2493 elements.SetSize(NumOfElements);
2494 for (int el = 0; el < NumOfElements; ++el)
2495 {
2496 elements[el] = elements_1D[el];
2497 }
2498 NumOfBdrElements = static_cast<int>(elements_0D.size());
2499 boundary.SetSize(NumOfBdrElements);
2500 for (int el = 0; el < NumOfBdrElements; ++el)
2501 {
2502 boundary[el] = elements_0D[el];
2503 }
2504 for (size_t el = 0; el < ho_el_order_1D.size(); el++)
2505 {
2506 mesh_order = max(mesh_order, ho_el_order_1D[el]);
2507 }
2508 }
2509 else
2510 {
2511 MFEM_ABORT("Gmsh file : no elements found");
2512 return;
2513 }
2514
2515 if (mesh_order > 1)
2516 {
2517 curved = 1;
2518 read_gf = 0;
2519
2520 // initialize mesh_geoms so we can create Nodes FE space below
2521 this->SetMeshGen();
2522
2523 // Generate faces and edges so that we can define
2524 // FE space on the mesh
2525 this->FinalizeTopology();
2526
2527 // Construct GridFunction for uniformly spaced high order coords
2529 FiniteElementSpace* nfes;
2530 nfec = new L2_FECollection(mesh_order, Dim,
2532 nfes = new FiniteElementSpace(this, nfec, spaceDim,
2534 Nodes_gf.SetSpace(nfes);
2535 Nodes_gf.MakeOwner(nfec);
2536
2537 int o = 0;
2538 int el_order = 1;
2539 for (int el = 0; el < NumOfElements; el++)
2540 {
2541 const int * vm = NULL;
2542 Array<int> * ho_verts = NULL;
2543 switch (GetElementType(el))
2544 {
2545 case Element::SEGMENT:
2546 ho_verts = ho_verts_1D[el];
2547 el_order = ho_el_order_1D[el];
2548 if (!ho_lin[el_order])
2549 {
2550 ho_lin[el_order] = new int[ho_verts->Size()];
2551 GmshHOSegmentMapping(el_order, ho_lin[el_order]);
2552 }
2553 vm = ho_lin[el_order];
2554 break;
2555 case Element::TRIANGLE:
2556 ho_verts = ho_verts_2D[el];
2557 el_order = ho_el_order_2D[el];
2558 if (!ho_tri[el_order])
2559 {
2560 ho_tri[el_order] = new int[ho_verts->Size()];
2561 GmshHOTriangleMapping(el_order, ho_tri[el_order]);
2562 }
2563 vm = ho_tri[el_order];
2564 break;
2566 ho_verts = ho_verts_2D[el];
2567 el_order = ho_el_order_2D[el];
2568 if (!ho_sqr[el_order])
2569 {
2570 ho_sqr[el_order] = new int[ho_verts->Size()];
2571 GmshHOQuadrilateralMapping(el_order, ho_sqr[el_order]);
2572 }
2573 vm = ho_sqr[el_order];
2574 break;
2576 ho_verts = ho_verts_3D[el];
2577 el_order = ho_el_order_3D[el];
2578 if (!ho_tet[el_order])
2579 {
2580 ho_tet[el_order] = new int[ho_verts->Size()];
2581 GmshHOTetrahedronMapping(el_order, ho_tet[el_order]);
2582 }
2583 vm = ho_tet[el_order];
2584 break;
2586 ho_verts = ho_verts_3D[el];
2587 el_order = ho_el_order_3D[el];
2588 if (!ho_hex[el_order])
2589 {
2590 ho_hex[el_order] = new int[ho_verts->Size()];
2591 GmshHOHexahedronMapping(el_order, ho_hex[el_order]);
2592 }
2593 vm = ho_hex[el_order];
2594 break;
2595 case Element::WEDGE:
2596 ho_verts = ho_verts_3D[el];
2597 el_order = ho_el_order_3D[el];
2598 if (!ho_wdg[el_order])
2599 {
2600 ho_wdg[el_order] = new int[ho_verts->Size()];
2601 GmshHOWedgeMapping(el_order, ho_wdg[el_order]);
2602 }
2603 vm = ho_wdg[el_order];
2604 break;
2605 case Element::PYRAMID:
2606 ho_verts = ho_verts_3D[el];
2607 el_order = ho_el_order_3D[el];
2608 if (!ho_pyr[el_order])
2609 {
2610 ho_pyr[el_order] = new int[ho_verts->Size()];
2611 GmshHOPyramidMapping(el_order, ho_pyr[el_order]);
2612 }
2613 vm = ho_pyr[el_order];
2614 break;
2615 default: // Any other element type
2616 MFEM_WARNING("Unsupported Gmsh element type.");
2617 break;
2618 }
2619 int nv = (ho_verts) ? ho_verts->Size() : 0;
2620
2621 for (int v = 0; v<nv; v++)
2622 {
2623 real_t * c = GetVertex((*ho_verts)[vm[v]]);
2624 for (int d=0; d<spaceDim; d++)
2625 {
2626 Nodes_gf(spaceDim * (o + v) + d) = c[d];
2627 }
2628 }
2629 o += nv;
2630 }
2631 }
2632
2633 // Delete any high order element to vertex connectivity
2634 for (size_t el=0; el<ho_verts_1D.size(); el++)
2635 {
2636 delete ho_verts_1D[el];
2637 }
2638 for (size_t el=0; el<ho_verts_2D.size(); el++)
2639 {
2640 delete ho_verts_2D[el];
2641 }
2642 for (size_t el=0; el<ho_verts_3D.size(); el++)
2643 {
2644 delete ho_verts_3D[el];
2645 }
2646
2647 // Delete dynamically allocated high vertex order mappings
2648 for (int ord=4; ord<ho_lin.Size(); ord++)
2649 {
2650 if (ho_lin[ord] != NULL) { delete [] ho_lin[ord]; }
2651 }
2652 for (int ord=4; ord<ho_tri.Size(); ord++)
2653 {
2654 if (ho_tri[ord] != NULL) { delete [] ho_tri[ord]; }
2655 }
2656 for (int ord=4; ord<ho_sqr.Size(); ord++)
2657 {
2658 if (ho_sqr[ord] != NULL) { delete [] ho_sqr[ord]; }
2659 }
2660 for (int ord=4; ord<ho_tet.Size(); ord++)
2661 {
2662 if (ho_tet[ord] != NULL) { delete [] ho_tet[ord]; }
2663 }
2664 for (int ord=4; ord<ho_hex.Size(); ord++)
2665 {
2666 if (ho_hex[ord] != NULL) { delete [] ho_hex[ord]; }
2667 }
2668 for (int ord=4; ord<ho_wdg.Size(); ord++)
2669 {
2670 if (ho_wdg[ord] != NULL) { delete [] ho_wdg[ord]; }
2671 }
2672 for (int ord=4; ord<ho_pyr.Size(); ord++)
2673 {
2674 if (ho_pyr[ord] != NULL) { delete [] ho_pyr[ord]; }
2675 }
2676
2677 // Suppress warnings (MFEM_CONTRACT_VAR does not work here with nvcc):
2678 ++n_partitions;
2679 ++elem_domain;
2680 MFEM_CONTRACT_VAR(n_partitions);
2681 MFEM_CONTRACT_VAR(elem_domain);
2682
2683 } // section '$Elements'
2684 else if (buff == "$PhysicalNames") // Named element sets
2685 {
2686 int num_names = 0;
2687 int mdim,num;
2688 string name;
2689 input >> num_names;
2690 for (int i=0; i < num_names; i++)
2691 {
2692 input >> mdim >> num;
2693 getline(input, name);
2694
2695 // Trim leading white space
2696 while (!name.empty() &&
2697 (*name.begin() == ' ' || *name.begin() == '\t'))
2698 { name.erase(0,1);}
2699
2700 // Trim trailing white space
2701 while (!name.empty() &&
2702 (*name.rbegin() == ' ' || *name.rbegin() == '\t' ||
2703 *name.rbegin() == '\n' || *name.rbegin() == '\r'))
2704 { name.resize(name.length()-1);}
2705
2706 // Remove enclosing quotes
2707 if ( (*name.begin() == '"' || *name.begin() == '\'') &&
2708 (*name.rbegin() == '"' || *name.rbegin() == '\''))
2709 {
2710 name = name.substr(1,name.length()-2);
2711 }
2712
2713 phys_names_by_dim[mdim][num] = name;
2714 }
2715 }
2716 else if (buff == "$Periodic") // Reading master/slave node pairs
2717 {
2718 curved = 1;
2719 read_gf = 0;
2720 periodic = true;
2721
2723 for (int i = 0; i < v2v.Size(); i++)
2724 {
2725 v2v[i] = i;
2726 }
2727 int num_per_ent;
2728 int num_nodes;
2729 input >> num_per_ent;
2730 getline(input, buff); // Read end-of-line
2731 for (int i = 0; i < num_per_ent; i++)
2732 {
2733 getline(input, buff); // Read and ignore entity dimension and tags
2734 getline(input, buff); // If affine mapping exist, read and ignore
2735 if (!strncmp(buff.c_str(), "Affine", 6))
2736 {
2737 input >> num_nodes;
2738 }
2739 else
2740 {
2741 num_nodes = atoi(buff.c_str());
2742 }
2743 for (int j=0; j<num_nodes; j++)
2744 {
2745 int slave, master;
2746 input >> slave >> master;
2747 v2v[slave - 1] = master - 1;
2748 }
2749 getline(input, buff); // Read end-of-line
2750 }
2751
2752 // Follow existing long chains of slave->master in v2v array.
2753 // Upon completion of this loop, each v2v[slave] will point to a true
2754 // master vertex. This algorithm is useful for periodicity defined in
2755 // multiple directions.
2756 for (int slave = 0; slave < v2v.Size(); slave++)
2757 {
2758 int master = v2v[slave];
2759 if (master != slave)
2760 {
2761 // This loop will end if it finds a circular dependency.
2762 while (v2v[master] != master && master != slave)
2763 {
2764 master = v2v[master];
2765 }
2766 if (master == slave)
2767 {
2768 // if master and slave are the same vertex, circular dependency
2769 // exists. We need to fix the problem, we choose slave.
2770 v2v[slave] = slave;
2771 }
2772 else
2773 {
2774 // the long chain has ended on the true master vertex.
2775 v2v[slave] = master;
2776 }
2777 }
2778 }
2779
2780 // Convert nodes to discontinuous GridFunction (if they aren't already)
2781 if (mesh_order == 1)
2782 {
2783 this->FinalizeTopology();
2784 this->SetMeshGen();
2785 this->SetCurvature(1, true, spaceDim, Ordering::byVDIM);
2786 }
2787
2788 // Replace "slave" vertex indices in the element connectivity
2789 // with their corresponding "master" vertex indices.
2790 for (int i = 0; i < this->GetNE(); i++)
2791 {
2792 Element *el = this->GetElement(i);
2793 int *v = el->GetVertices();
2794 int nv = el->GetNVertices();
2795 for (int j = 0; j < nv; j++)
2796 {
2797 v[j] = v2v[v[j]];
2798 }
2799 }
2800 // Replace "slave" vertex indices in the boundary element connectivity
2801 // with their corresponding "master" vertex indices.
2802 for (int i = 0; i < this->GetNBE(); i++)
2803 {
2804 Element *el = this->GetBdrElement(i);
2805 int *v = el->GetVertices();
2806 int nv = el->GetNVertices();
2807 for (int j = 0; j < nv; j++)
2808 {
2809 v[j] = v2v[v[j]];
2810 }
2811 }
2812 }
2813 } // we reach the end of the file
2814
2815 // Process set names
2816 if (phys_names_by_dim.size() > 0)
2817 {
2818 // Process boundary attribute set names
2819 for (auto const &bdr_attr : phys_names_by_dim[Dim-1])
2820 {
2821 if (!bdr_attribute_sets.AttributeSetExists(bdr_attr.second))
2822 {
2823 bdr_attribute_sets.CreateAttributeSet(bdr_attr.second);
2824 }
2825 bdr_attribute_sets.AddToAttributeSet(bdr_attr.second, bdr_attr.first);
2826 }
2827
2828 // Process element attribute set names
2829 for (auto const &attr : phys_names_by_dim[Dim])
2830 {
2831 if (!attribute_sets.AttributeSetExists(attr.second))
2832 {
2834 }
2835 attribute_sets.AddToAttributeSet(attr.second, attr.first);
2836 }
2837 }
2838
2839 this->RemoveUnusedVertices();
2840 this->FinalizeTopology();
2841
2842 // If a high order coordinate field was created project it onto the mesh
2843 if (mesh_order > 1)
2844 {
2845 SetCurvature(mesh_order, periodic, spaceDim, Ordering::byVDIM);
2846
2847 VectorGridFunctionCoefficient NodesCoef(&Nodes_gf);
2848 Nodes->ProjectCoefficient(NodesCoef);
2849 }
2850}
2851
2852
2853#ifdef MFEM_USE_NETCDF
2854
2855
2856namespace cubit
2857{
2858
2860{
2861 // 1,2,3,4,5,6,7,8,9,10
2862 1,2,3,4,5,7,8,6,9,10
2863};
2864
2866{
2867 // 1,2,3,4,5,6,7,8,9,10,11,
2868 1,2,3,4,5,6,7,8,9,10,11,
2869
2870 // 12,13,14,15,16,17,18,19
2871 12,17,18,19,20,13,14,15,
2872
2873 // 20,21,22,23,24,25,26,27
2874 16,22,26,25,27,24,23,21
2875};
2876
2878{
2879 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
2880};
2881
2883{
2884 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 10, 11, 12, 16, 17, 18
2885};
2886
2888{
2889 1,2,3,4,5,6
2890};
2891
2893{
2894 1,2,3,4,5,6,7,8,9
2895};
2896
2897const int cubit_side_map_tri3[3][2] =
2898{
2899 {1,2}, // 1
2900 {2,3}, // 2
2901 {3,1}, // 3
2902};
2903
2904const int cubit_side_map_quad4[4][2] =
2905{
2906 {1,2}, // 1
2907 {2,3}, // 2
2908 {3,4}, // 3
2909 {4,1}, // 4
2910};
2911
2912const int cubit_side_map_tet4[4][3] =
2913{
2914 {1,2,4}, // 1
2915 {2,3,4}, // 2
2916 {1,4,3}, // 3
2917 {1,3,2} // 4
2918};
2919
2920const int cubit_side_map_hex8[6][4] =
2921{
2922 {1,2,6,5}, // 1 <-- Exodus II side_ids
2923 {2,3,7,6}, // 2
2924 {3,4,8,7}, // 3
2925 {1,5,8,4}, // 4
2926 {1,4,3,2}, // 5
2927 {5,6,7,8} // 6
2928};
2929
2930const int cubit_side_map_wedge6[5][4] =
2931{
2932 {1,2,5,4}, // 1 (Quad4)
2933 {2,3,6,5}, // 2
2934 {3,1,4,6}, // 3
2935 {1,3,2,0}, // 4 (Tri3; NB: 0 is placeholder!)
2936 {4,5,6,0} // 5
2937};
2938
2940{
2941 {1, 2, 5, 0}, // 1 (Tri3)
2942 {2, 3, 5, 0}, // 2
2943 {3, 4, 5, 0}, // 3
2944 {1, 5, 4, 0}, // 4
2945 {1, 4, 3, 2} // 5 (Quad4)
2946};
2947
2957
2973
2974
2975/**
2976 * CubitElement
2977 *
2978 * Stores information about a particular element.
2979 */
2980class CubitElement
2981{
2982public:
2983 /// Default constructor.
2984 CubitElement(CubitElementType element_type);
2985 CubitElement() = delete;
2986
2987 /// Destructor.
2988 ~CubitElement() = default;
2989
2990 /// Returns the Cubit element type.
2991 inline CubitElementType GetElementType() const { return _element_type; }
2992
2993 /// Returns the face type for a specified face. NB: sides have 1-based indexing.
2994 CubitFaceType GetFaceType(size_t side_id = 1) const;
2995
2996 /// Returns the number of faces.
2997 inline size_t GetNumFaces() const { return _num_faces; }
2998
2999 /// Returns the number of vertices.
3000 inline size_t GetNumVertices() const { return _num_vertices; }
3001
3002 /// Returns the number of nodes (vertices + higher-order control points).
3003 inline size_t GetNumNodes() const { return _num_nodes; }
3004
3005 /// Returns the number of vertices for a particular face.
3006 size_t GetNumFaceVertices(size_t iface = 1) const;
3007
3008 /// Returns the order of the element.
3009 inline uint8_t GetOrder() const { return _order; }
3010
3011 /// Creates an MFEM equivalent element using the supplied vertex IDs and block ID.
3012 Element * BuildElement(Mesh & mesh, const int * vertex_ids,
3013 const int block_id) const;
3014
3015 /// Creates an MFEM boundary element using the supplied vertex IDs and block ID.
3016 Element * BuildBoundaryElement(Mesh & mesh, const int iface,
3017 const int * vertex_ids, const int sideset_id) const;
3018
3019 /// Static method returning the element type for a given number of nodes per element and dimension.
3020 static CubitElementType GetElementType(size_t num_nodes,
3021 uint8_t dimension = 3);
3022protected:
3023 /// Static method which returns the 2D Cubit element type for the number of nodes per element.
3024 static CubitElementType Get2DElementType(size_t num_nodes);
3025
3026 /// Static method which returns the 3D Cubit element type for the number of nodes per element.
3027 static CubitElementType Get3DElementType(size_t num_nodes);
3028
3029 /// Creates a new MFEM element. Used internally in BuildElement and BuildBoundaryElement.
3030 Element * NewElement(Mesh & mesh, Geometry::Type geom, const int *vertices,
3031 const int attribute) const;
3032
3033private:
3034 CubitElementType _element_type;
3035
3036 uint8_t _order;
3037
3038 size_t _num_vertices;
3039 size_t _num_faces;
3040 size_t _num_nodes;
3041};
3042
3043CubitElement::CubitElement(CubitElementType element_type)
3044{
3045 _element_type = element_type;
3046
3047 switch (element_type)
3048 {
3049 case ELEMENT_TRI3: // 2D.
3050 _order = 1;
3051 _num_vertices = 3;
3052 _num_nodes = 3;
3053 _num_faces = 3;
3054 break;
3055 case ELEMENT_TRI6:
3056 _order = 2;
3057 _num_vertices = 3;
3058 _num_nodes = 6;
3059 _num_faces = 3;
3060 break;
3061 case ELEMENT_QUAD4:
3062 _order = 1;
3063 _num_vertices = 4;
3064 _num_nodes = 4;
3065 _num_faces = 4;
3066 break;
3067 case ELEMENT_QUAD9:
3068 _order = 2;
3069 _num_vertices = 4;
3070 _num_nodes = 9;
3071 _num_faces = 4;
3072 break;
3073 case ELEMENT_TET4: // 3D.
3074 _order = 1;
3075 _num_vertices = 4;
3076 _num_nodes = 4;
3077 _num_faces = 4;
3078 break;
3079 case ELEMENT_TET10:
3080 _order = 2;
3081 _num_vertices = 4;
3082 _num_nodes = 10;
3083 _num_faces = 4;
3084 break;
3085 case ELEMENT_HEX8:
3086 _order = 1;
3087 _num_vertices = 8;
3088 _num_nodes = 8;
3089 _num_faces = 6;
3090 break;
3091 case ELEMENT_HEX27:
3092 _order = 2;
3093 _num_vertices = 8;
3094 _num_nodes = 27;
3095 _num_faces = 6;
3096 break;
3097 case ELEMENT_WEDGE6:
3098 _order = 1;
3099 _num_vertices = 6;
3100 _num_nodes = 6;
3101 _num_faces = 5;
3102 break;
3103 case ELEMENT_WEDGE18:
3104 _order = 2;
3105 _num_vertices = 6;
3106 _num_nodes = 18;
3107 _num_faces = 5;
3108 break;
3109 case ELEMENT_PYRAMID5:
3110 _order = 1;
3111 _num_vertices = 5;
3112 _num_nodes = 5;
3113 _num_faces = 5;
3114 break;
3115 case ELEMENT_PYRAMID14:
3116 _order = 2;
3117 _num_vertices = 5;
3118 _num_nodes = 14;
3119 _num_faces = 5;
3120 break;
3121 default:
3122 MFEM_ABORT("Unsupported Cubit element type " << element_type << ".");
3123 break;
3124 }
3125}
3126
3127CubitElementType CubitElement::Get3DElementType(size_t num_nodes)
3128{
3129 switch (num_nodes)
3130 {
3131 case 4:
3132 return ELEMENT_TET4;
3133 case 10:
3134 return ELEMENT_TET10;
3135 case 8:
3136 return ELEMENT_HEX8;
3137 case 27:
3138 return ELEMENT_HEX27;
3139 case 6:
3140 return ELEMENT_WEDGE6;
3141 case 18:
3142 return ELEMENT_WEDGE18;
3143 case 5:
3144 return ELEMENT_PYRAMID5;
3145 case 14:
3146 return ELEMENT_PYRAMID14;
3147 default:
3148 MFEM_ABORT("Unsupported 3D element with " << num_nodes << " nodes.");
3149 }
3150}
3151
3152CubitElementType CubitElement::Get2DElementType(size_t num_nodes)
3153{
3154 switch (num_nodes)
3155 {
3156 case 3:
3157 return ELEMENT_TRI3;
3158 case 6:
3159 return ELEMENT_TRI6;
3160 case 4:
3161 return ELEMENT_QUAD4;
3162 case 9:
3163 return ELEMENT_QUAD9;
3164 default:
3165 MFEM_ABORT("Unsupported 2D element with " << num_nodes << " nodes.");
3166 }
3167}
3168
3169CubitElementType CubitElement::GetElementType(size_t num_nodes,
3170 uint8_t dimension)
3171{
3172 if (dimension == 2)
3173 {
3174 return Get2DElementType(num_nodes);
3175 }
3176 else if (dimension == 3)
3177 {
3178 return Get3DElementType(num_nodes);
3179 }
3180 else
3181 {
3182 MFEM_ABORT("Unsupported Cubit dimension " << dimension << ".");
3183 }
3184}
3185
3186CubitFaceType CubitElement::GetFaceType(size_t side_id) const
3187{
3188 // NB: 1-based indexing. See Exodus II file format specifications.
3189 bool valid_id = (side_id >= 1 &&
3190 side_id <= GetNumFaces());
3191 if (!valid_id)
3192 {
3193 MFEM_ABORT("Encountered invalid side ID: " << side_id << ".");
3194 }
3195
3196 switch (_element_type)
3197 {
3198 case ELEMENT_TRI3: // 2D.
3199 return FACE_EDGE2;
3200 case ELEMENT_TRI6:
3201 return FACE_EDGE3;
3202 case ELEMENT_QUAD4:
3203 return FACE_EDGE2;
3204 case ELEMENT_QUAD9:
3205 return FACE_EDGE3;
3206 case ELEMENT_TET4: // 3D.
3207 return FACE_TRI3;
3208 case ELEMENT_TET10:
3209 return FACE_TRI6;
3210 case ELEMENT_HEX8:
3211 return FACE_QUAD4;
3212 case ELEMENT_HEX27:
3213 return FACE_QUAD9;
3214 case ELEMENT_WEDGE6: // [Quad4, Quad4, Quad4, Tri3, Tri3]
3215 return (side_id < 4 ? FACE_QUAD4 : FACE_TRI3);
3216 case ELEMENT_WEDGE18: // [Quad9, Quad9, Quad9, Tri6, Tri6]
3217 return (side_id < 4 ? FACE_QUAD9 : FACE_TRI6);
3218 case ELEMENT_PYRAMID5: // [Tri3, Tri3, Tri3, Tri3, Quad4]
3219 return (side_id < 5 ? FACE_TRI3 : FACE_QUAD4);
3220 case ELEMENT_PYRAMID14: // [Tri6, Tri6, Tri6, Tri6, Quad9]
3221 return (side_id < 5 ? FACE_TRI6 : FACE_QUAD9);
3222 default:
3223 MFEM_ABORT("Unknown element type: " << _element_type << ".");
3224 }
3225}
3226
3227
3228size_t CubitElement::GetNumFaceVertices(size_t side_id) const
3229{
3230 switch (GetFaceType(side_id))
3231 {
3232 case FACE_EDGE2:
3233 case FACE_EDGE3:
3234 return 2;
3235 case FACE_TRI3:
3236 case FACE_TRI6:
3237 return 3;
3238 case FACE_QUAD4:
3239 case FACE_QUAD9:
3240 return 4;
3241 default:
3242 MFEM_ABORT("Unrecognized Cubit face type " << GetFaceType(side_id) << ".");
3243 }
3244}
3245
3246
3247mfem::Element * CubitElement::NewElement(Mesh &mesh, Geometry::Type geom,
3248 const int *vertices,
3249 const int attribute) const
3250{
3251 Element *new_element = mesh.NewElement(geom);
3252 new_element->SetVertices(vertices);
3253 new_element->SetAttribute(attribute);
3254 return new_element;
3255}
3256
3257
3258mfem::Element * CubitElement::BuildElement(Mesh &mesh,
3259 const int *vertex_ids,
3260 const int block_id) const
3261{
3262 switch (GetElementType())
3263 {
3264 case ELEMENT_TRI3:
3265 case ELEMENT_TRI6:
3266 return NewElement(mesh, Geometry::TRIANGLE, vertex_ids, block_id);
3267 case ELEMENT_QUAD4:
3268 case ELEMENT_QUAD9:
3269 return NewElement(mesh, Geometry::SQUARE, vertex_ids, block_id);
3270 case ELEMENT_TET4:
3271 case ELEMENT_TET10:
3272 return NewElement(mesh, Geometry::TETRAHEDRON, vertex_ids, block_id);
3273 case ELEMENT_HEX8:
3274 case ELEMENT_HEX27:
3275 return NewElement(mesh, Geometry::CUBE, vertex_ids, block_id);
3276 case ELEMENT_WEDGE6:
3277 case ELEMENT_WEDGE18:
3278 return NewElement(mesh, Geometry::PRISM, vertex_ids, block_id);
3279 case ELEMENT_PYRAMID5:
3280 case ELEMENT_PYRAMID14:
3281 return NewElement(mesh, Geometry::PYRAMID, vertex_ids, block_id);
3282 default:
3283 MFEM_ABORT("Unsupported Cubit element type encountered.");
3284 }
3285}
3286
3287
3288mfem::Element * CubitElement::BuildBoundaryElement(Mesh &mesh,
3289 const int face_id,
3290 const int *vertex_ids,
3291 const int sideset_id) const
3292{
3293 switch (GetFaceType(face_id))
3294 {
3295 case FACE_EDGE2:
3296 case FACE_EDGE3:
3297 return NewElement(mesh, Geometry::SEGMENT, vertex_ids, sideset_id);
3298 case FACE_TRI3:
3299 case FACE_TRI6:
3300 return NewElement(mesh, Geometry::TRIANGLE, vertex_ids, sideset_id);
3301 case FACE_QUAD4:
3302 case FACE_QUAD9:
3303 return NewElement(mesh, Geometry::SQUARE, vertex_ids, sideset_id);
3304 default:
3305 MFEM_ABORT("Unsupported Cubit face type encountered.");
3306 }
3307}
3308
3309/**
3310 * CubitBlock
3311 *
3312 * Stores the information about each block in a mesh. Each block can contain a different
3313 * element type (although all element types must be of the same order and dimension).
3314 */
3315class CubitBlock
3316{
3317public:
3318 CubitBlock() = delete;
3319 ~CubitBlock() = default;
3320
3321 /**
3322 * Default initializer.
3323 */
3324 CubitBlock(int dimension);
3325
3326 /**
3327 * Returns a constant reference to the element info for a particular block.
3328 */
3329 const CubitElement & GetBlockElement(int block_id) const;
3330
3331 /**
3332 * Call to add each block individually.
3333 */
3334 void AddBlockElement(int block_id, CubitElementType element_type);
3335
3336 /**
3337 * Accessors.
3338 */
3339 uint8_t GetOrder() const;
3340 inline uint8_t GetDimension() const { return _dimension; }
3341
3342 inline size_t GetNumBlocks() const { return BlockIDs().size(); }
3343 inline bool HasBlocks() const { return !BlockIDs().empty(); }
3344
3345protected:
3346 /**
3347 * Checks that the order of a new block element matches the order of existing blocks. Called
3348 * internally in method "addBlockElement".
3349 */
3350 void CheckElementBlockIsCompatible(const CubitElement & new_block_element)
3351 const;
3352
3353 /**
3354 * Reset all block elements. Called internally in initializer.
3355 */
3356 void ClearBlockElements();
3357
3358 /**
3359 * Helper methods.
3360 */
3361 inline const std::set<int> & BlockIDs() const { return _block_ids; }
3362
3363 bool HasBlockID(int block_id) const;
3364 bool ValidBlockID(int block_id) const;
3365 bool ValidDimension(int dimension) const;
3366
3367private:
3368 /**
3369 * Stores all block IDs.
3370 */
3371 std::set<int> _block_ids;
3372
3373 /**
3374 * Maps from block ID to element.
3375 */
3376 std::map<int, CubitElement> _block_element_for_block_id;
3377
3378 /**
3379 * Dimension and order of block elements.
3380 */
3381 uint8_t _dimension;
3382 uint8_t _order;
3383};
3384
3385CubitBlock::CubitBlock(int dimension)
3386{
3387 if (!ValidDimension(dimension))
3388 {
3389 MFEM_ABORT("Invalid dimension '" << dimension << "' specified.");
3390 }
3391
3392 _dimension = dimension;
3393
3394 ClearBlockElements();
3395}
3396
3397void
3398CubitBlock::AddBlockElement(int block_id, CubitElementType element_type)
3399{
3400 if (HasBlockID(block_id))
3401 {
3402 MFEM_ABORT("Block with ID '" << block_id << "' has already been added.");
3403 }
3404 else if (!ValidBlockID(block_id))
3405 {
3406 MFEM_ABORT("Illegal block ID '" << block_id << "'.");
3407 }
3408
3409 CubitElement block_element = CubitElement(element_type);
3410
3411 /**
3412 * Check element is compatible with existing element blocks.
3413 */
3414 CheckElementBlockIsCompatible(block_element);
3415
3416 if (!HasBlocks()) // Set order of elements.
3417 {
3418 _order = block_element.GetOrder();
3419 }
3420
3421 _block_ids.insert(block_id);
3422 _block_element_for_block_id.emplace(block_id,
3423 block_element);
3424}
3425
3426uint8_t
3427CubitBlock::GetOrder() const
3428{
3429 if (!HasBlocks())
3430 {
3431 MFEM_ABORT("No elements have been added.");
3432 }
3433
3434 return _order;
3435}
3436
3437void
3438CubitBlock::ClearBlockElements()
3439{
3440 _order = 0;
3441 _block_ids.clear();
3442 _block_element_for_block_id.clear();
3443}
3444
3445bool
3446CubitBlock::HasBlockID(int block_id) const
3447{
3448 return (_block_ids.count(block_id) > 0);
3449}
3450
3451bool
3452CubitBlock::ValidBlockID(int block_id) const
3453{
3454 return (block_id > 0); // 1-based indexing.
3455}
3456
3457bool
3458CubitBlock::ValidDimension(int dimension) const
3459{
3460 return (dimension == 2 || dimension == 3);
3461}
3462
3463const CubitElement &
3464CubitBlock::GetBlockElement(int block_id) const
3465{
3466 if (!HasBlockID(block_id))
3467 {
3468 MFEM_ABORT("No element info for block ID '" << block_id << "'.");
3469 }
3470
3471 return _block_element_for_block_id.at(block_id);
3472}
3473
3474void
3475CubitBlock::CheckElementBlockIsCompatible(const CubitElement &
3476 new_block_element) const
3477{
3478 if (!HasBlocks())
3479 {
3480 return;
3481 }
3482
3483 // Enforce block orders to be the same for now.
3484 if (GetOrder() != new_block_element.GetOrder())
3485 {
3486 MFEM_ABORT("All block elements must be of the same order.");
3487 }
3488}
3489
3490/**
3491 * Lightweight wrapper around NetCDF C functions.
3492 */
3493class NetCDFReader
3494{
3495public:
3496 NetCDFReader() = delete;
3497 NetCDFReader(const std::string fname);
3498
3499 ~NetCDFReader();
3500
3501 /// Returns true if variable id for that name exists.
3502 bool HasVariable(const char * name);
3503
3504 /// Read variable info from file and write to int buffer.
3505 void ReadVariable(const char * name, int * data);
3506
3507 /// Read variable info from file and write to double buffer.
3508 void ReadVariable(const char * name, double * data);
3509
3510 /// Returns true if dimension id for that name exists.
3511 bool HasDimension(const char * name);
3512
3513 /// Read dimension info from file.
3514 void ReadDimension(const char * name, size_t *dimension);
3515
3516 /// Build the map from quantity ID to name, e.g. block ID to block name or boundary ID to boundary name
3517 void BuildIDToNameMap(const vector<int> & ids,
3518 unordered_map<int, string> & ids_to_names,
3519 const string & quantity_name);
3520
3521protected:
3522 /// Called internally. Calls HandleNetCDFError if _netcdf_status is not "NC_NOERR".
3523 void CheckForNetCDFError();
3524
3525 /// Called in "ReadVariable" methods to extract variable id.
3526 int ReadVariableID(const char * name);
3527
3528 /// Called in "ReadDimension" to extract dimension id.
3529 int ReadDimensionID(const char * name);
3530
3531private:
3532 /// Calls MFEM_Abort with string description of NetCDF error.
3533 void HandleNetCDFError(const int error_code);
3534
3535 int _netcdf_status{NC_NOERR};
3536 int _netcdf_descriptor;
3537
3538 /// Internal buffer. Used in ReadDimension to write unwanted name to.
3539 char *_name_buffer{NULL};
3540};
3541
3542
3543NetCDFReader::NetCDFReader(const std::string fname)
3544{
3545 _netcdf_status = nc_open(fname.c_str(), NC_NOWRITE, &_netcdf_descriptor);
3546 CheckForNetCDFError();
3547
3548 // NB: add byte for '\0' terminating char.
3549 _name_buffer = new char[NC_MAX_NAME + 1];
3550}
3551
3552NetCDFReader::~NetCDFReader()
3553{
3554 _netcdf_status = nc_close(_netcdf_descriptor);
3555 CheckForNetCDFError();
3556
3557 if (_name_buffer)
3558 {
3559 delete[] _name_buffer;
3560 }
3561}
3562
3563void NetCDFReader::CheckForNetCDFError()
3564{
3565 if (_netcdf_status != NC_NOERR)
3566 {
3567 HandleNetCDFError(_netcdf_status);
3568 }
3569}
3570
3571void NetCDFReader::HandleNetCDFError(const int error_code)
3572{
3573 MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(error_code));
3574}
3575
3576int NetCDFReader::ReadVariableID(const char * var_name)
3577{
3578 int variable_id;
3579
3580 _netcdf_status = nc_inq_varid(_netcdf_descriptor, var_name,
3581 &variable_id);
3582 CheckForNetCDFError();
3583
3584 return variable_id;
3585}
3586
3587int NetCDFReader::ReadDimensionID(const char * name)
3588{
3589 int dim_id;
3590
3591 _netcdf_status = nc_inq_dimid(_netcdf_descriptor, name, &dim_id);
3592 CheckForNetCDFError();
3593
3594 return dim_id;
3595}
3596
3597void NetCDFReader::ReadDimension(const char * name, size_t *dimension)
3598{
3599 const int dimension_id = ReadDimensionID(name);
3600
3601 // NB: ignore name output (write to private buffer).
3602 _netcdf_status = nc_inq_dim(_netcdf_descriptor, dimension_id, _name_buffer,
3603 dimension);
3604 CheckForNetCDFError();
3605}
3606
3607bool NetCDFReader::HasVariable(const char * name)
3608{
3609 int var_id;
3610 const int status = nc_inq_varid(_netcdf_descriptor, name, &var_id);
3611
3612 switch (status)
3613 {
3614 case NC_NOERR: // Found!
3615 return true;
3616 case NC_ENOTVAR: // Not found.
3617 return false;
3618 default:
3619 HandleNetCDFError(status);
3620 return false;
3621 }
3622}
3623
3624bool NetCDFReader::HasDimension(const char * name)
3625{
3626 int dim_id;
3627 const int status = nc_inq_dimid(_netcdf_descriptor, name, &dim_id);
3628
3629 switch (status)
3630 {
3631 case NC_NOERR: // Found!
3632 return true;
3633 case NC_EBADDIM: // Not found.
3634 return false;
3635 default:
3636 HandleNetCDFError(status);
3637 return false;
3638 }
3639}
3640
3641void NetCDFReader::ReadVariable(const char * name, int * data)
3642{
3643 const int variable_id = ReadVariableID(name);
3644
3645 _netcdf_status = nc_get_var_int(_netcdf_descriptor, variable_id, data);
3646 CheckForNetCDFError();
3647}
3648
3649
3650void NetCDFReader::ReadVariable(const char * name, double * data)
3651{
3652 const int variable_id = ReadVariableID(name);
3653
3654 _netcdf_status = nc_get_var_double(_netcdf_descriptor, variable_id, data);
3655 CheckForNetCDFError();
3656}
3657
3658
3659void NetCDFReader::BuildIDToNameMap(const vector<int> & ids,
3660 unordered_map<int, string> & ids_to_names,
3661 const string & quantity_name)
3662{
3663 int varid_names;
3664
3665 // Find the variable ID for the given quantity_name (e.g. eb_names, ss_names)
3666 _netcdf_status = nc_inq_varid(_netcdf_descriptor, quantity_name.c_str(),
3667 &varid_names);
3668 // It's possible the netcdf file doesn't contain the variable, in which case
3669 // there's nothing to do
3670 if (_netcdf_status == NC_ENOTVAR)
3671 {
3672 return;
3673 }
3674 else
3675 {
3676 CheckForNetCDFError();
3677 }
3678
3679 // Get type of quantity_name
3680 nc_type var_type;
3681 _netcdf_status = nc_inq_vartype(_netcdf_descriptor, varid_names,
3682 &var_type);
3683 CheckForNetCDFError();
3684
3685 if (var_type == NC_CHAR)
3686 {
3687 int dimids_names[2], names_ndim;
3688 size_t num_names, name_len;
3689
3690 _netcdf_status = nc_inq_varndims(_netcdf_descriptor, varid_names,
3691 &names_ndim);
3692 CheckForNetCDFError();
3693 MFEM_ASSERT(names_ndim == 2, "This variable should have two dimensions");
3694
3695 _netcdf_status = nc_inq_vardimid(_netcdf_descriptor, varid_names,
3696 dimids_names);
3697 CheckForNetCDFError();
3698
3699 _netcdf_status = nc_inq_dimlen(_netcdf_descriptor, dimids_names[0], &num_names);
3700 CheckForNetCDFError();
3701 MFEM_ASSERT(num_names == ids.size(),
3702 "The block id and block name lengths don't match");
3703 // Check the maximum string length
3704 _netcdf_status = nc_inq_dimlen(_netcdf_descriptor, dimids_names[1], &name_len);
3705 CheckForNetCDFError();
3706
3707 // Read the block names
3708 vector<char> names(ids.size() * name_len);
3709 _netcdf_status = nc_get_var_text(_netcdf_descriptor, varid_names,
3710 names.data());
3711 CheckForNetCDFError();
3712
3713 for (size_t i = 0; i < ids.size(); ++i)
3714 {
3715 string name(&names[i * name_len], name_len);
3716 // shorten string
3717 name.resize(name.find('\0'));
3718 ids_to_names[ids[i]] = name;
3719 }
3720 }
3721 else
3722 {
3723 mfem_error("Unexpected netcdf variable type");
3724 }
3725}
3726
3727
3728/// @brief Reads the coordinate data from the Genesis file.
3729static void ReadCubitNodeCoordinates(NetCDFReader & cubit_reader,
3730 double *coordx,
3731 double *coordy,
3732 double *coordz)
3733{
3734 cubit_reader.ReadVariable("coordx", coordx);
3735 cubit_reader.ReadVariable("coordy", coordy);
3736
3737 if (coordz)
3738 {
3739 cubit_reader.ReadVariable("coordz", coordz);
3740 }
3741}
3742
3743
3744/// @brief Reads the number of elements in each block.
3745static void ReadCubitNumElementsInBlock(NetCDFReader & cubit_reader,
3746 const vector<int> & block_ids,
3747 map<int, size_t> &num_elements_for_block_id)
3748{
3749 num_elements_for_block_id.clear();
3750
3751 // NB: need to add 1 for '\0' terminating character.
3752 const int buffer_size = NC_MAX_NAME + 1;
3753 char string_buffer[buffer_size];
3754
3755 int iblock = 1;
3756 for (const auto block_id : block_ids)
3757 {
3758 // Write variable name to buffer.
3759 snprintf(string_buffer, buffer_size, "num_el_in_blk%d", iblock++);
3760
3761 size_t num_elements_for_block = 0;
3762 cubit_reader.ReadDimension(string_buffer, &num_elements_for_block);
3763
3764 num_elements_for_block_id[block_id] = num_elements_for_block;
3765 }
3766}
3767
3768/// @brief Builds the mappings:
3769/// (blockID --> (elements in block)); (elementID --> blockID)
3770static void BuildElementIDsForBlockID(
3771 const vector<int> & block_ids,
3772 const map<int, size_t> & num_elements_for_block_id,
3773 map<int, vector<int>> & element_ids_for_block_id,
3774 map<int, int> & block_id_for_element_id)
3775{
3776 element_ids_for_block_id.clear();
3777 block_id_for_element_id.clear();
3778
3779 // From the Exodus II specifications, the element ID is numbered contiguously starting
3780 // from 1 across the element blocks.
3781 int element_id = 1;
3782 for (int block_id : block_ids)
3783 {
3784 const int num_elements_for_block = num_elements_for_block_id.at(block_id);
3785
3786 vector<int> element_ids(num_elements_for_block);
3787
3788 for (int i = 0; i < num_elements_for_block; i++, element_id++)
3789 {
3790 element_ids[i] = element_id;
3791 block_id_for_element_id[element_id] = block_id;
3792 }
3793
3794 element_ids_for_block_id[block_id] = std::move(element_ids);
3795 }
3796}
3797
3798/// @brief Reads the element types for each block.
3799static void ReadCubitBlocks(NetCDFReader & cubit_reader,
3800 const vector<int> block_ids,
3801 CubitBlock & cubit_blocks)
3802{
3803 const int buffer_size = NC_MAX_NAME + 1;
3804 char string_buffer[buffer_size];
3805
3806 size_t num_nodes_per_element;
3807
3808 int iblock = 1;
3809 for (int block_id : block_ids)
3810 {
3811 // Write variable name to buffer.
3812 snprintf(string_buffer, buffer_size, "num_nod_per_el%d", iblock++);
3813
3814 cubit_reader.ReadDimension(string_buffer, &num_nodes_per_element);
3815
3816 // Determine the element type:
3817 CubitElementType element_type = CubitElement::GetElementType(
3818 num_nodes_per_element, cubit_blocks.GetDimension());
3819 cubit_blocks.AddBlockElement(block_id, element_type);
3820 }
3821}
3822
3823
3824/// @brief Extracts core dimension information from Genesis file.
3825static void ReadCubitDimensions(NetCDFReader & cubit_reader,
3826 size_t &num_dim,
3827 size_t &num_nodes,
3828 size_t &num_elem,
3829 size_t &num_el_blk,
3830 size_t &num_side_sets)
3831{
3832 cubit_reader.ReadDimension("num_dim", &num_dim);
3833 cubit_reader.ReadDimension("num_nodes", &num_nodes);
3834 cubit_reader.ReadDimension("num_elem", &num_elem);
3835 cubit_reader.ReadDimension("num_el_blk", &num_el_blk);
3836
3837 // Optional: if not present, num_side_sets = 0.
3838 if (cubit_reader.HasDimension("num_side_sets"))
3839 {
3840 cubit_reader.ReadDimension("num_side_sets", &num_side_sets);
3841 }
3842 else
3843 {
3844 num_side_sets = 0;
3845 }
3846}
3847
3848/// @brief Extracts the element ids corresponding to elements that lie on each boundary;
3849/// also extracts the side ids of those elements which lie on the boundary.
3850static void ReadCubitBoundaries(NetCDFReader & cubit_reader,
3851 const vector<int> & boundary_ids,
3852 map<int, vector<int>> & element_ids_for_boundary_id,
3853 map<int, vector<int>> & side_ids_for_boundary_id)
3854{
3855 const int buffer_size = NC_MAX_NAME + 1;
3856 char string_buffer[buffer_size];
3857
3858 int ibdr = 1;
3859 for (int boundary_id : boundary_ids)
3860 {
3861 // 1. Extract number of elements/sides for boundary.
3862 size_t num_sides = 0;
3863
3864 snprintf(string_buffer, buffer_size, "num_side_ss%d", ibdr);
3865 cubit_reader.ReadDimension(string_buffer, &num_sides);
3866
3867 // 2. Extract elements and sides on each boundary (1-indexed!)
3868 vector<int> boundary_element_ids(num_sides); // (element, face) pairs.
3869 vector<int> boundary_side_ids(num_sides);
3870
3871 //
3872 snprintf(string_buffer, buffer_size, "elem_ss%d", ibdr);
3873 cubit_reader.ReadVariable(string_buffer, boundary_element_ids.data());
3874
3875 //
3876 snprintf(string_buffer, buffer_size,"side_ss%d", ibdr++);
3877 cubit_reader.ReadVariable(string_buffer, boundary_side_ids.data());
3878
3879 // 3. Add to maps.
3880 element_ids_for_boundary_id[boundary_id] = std::move(boundary_element_ids);
3881 side_ids_for_boundary_id[boundary_id] = std::move(boundary_side_ids);
3882 }
3883}
3884
3885/// @brief Reads the block ids from the Genesis file.
3886static void BuildCubitBlockIDs(NetCDFReader & cubit_reader,
3887 const int num_element_blocks,
3888 vector<int> & block_ids)
3889{
3890 block_ids.resize(num_element_blocks);
3891 cubit_reader.ReadVariable("eb_prop1", block_ids.data());
3892}
3893
3894/// @brief Reads the boundary ids from the Genesis file.
3895static void ReadCubitBoundaryIDs(NetCDFReader & cubit_reader,
3896 const int num_boundaries, vector<int> & boundary_ids)
3897{
3898 boundary_ids.clear();
3899
3900 if (num_boundaries < 1) { return; }
3901
3902 boundary_ids.resize(num_boundaries);
3903 cubit_reader.ReadVariable("ss_prop1", boundary_ids.data());
3904}
3905
3906/// @brief Reads the node ids for each element from the Genesis file.
3907static void ReadCubitElementBlocks(NetCDFReader & cubit_reader,
3908 const CubitBlock & cubit_blocks,
3909 const vector<int> & block_ids,
3910 const map<int, vector<int>> & element_ids_for_block_id,
3911 map<int, vector<int>> &node_ids_for_element_id)
3912{
3913 const int buffer_size = NC_MAX_NAME + 1;
3914 char string_buffer[buffer_size];
3915
3916 int iblock = 1;
3917 for (const int block_id : block_ids)
3918 {
3919 const CubitElement & block_element = cubit_blocks.GetBlockElement(block_id);
3920
3921 const vector<int> & block_element_ids = element_ids_for_block_id.at(block_id);
3922
3923 const size_t num_nodes_for_block = block_element_ids.size() *
3924 block_element.GetNumNodes();
3925
3926 vector<int> node_ids_for_block(num_nodes_for_block);
3927
3928 // Write variable name to buffer.
3929 snprintf(string_buffer, buffer_size, "connect%d", iblock++);
3930
3931 cubit_reader.ReadVariable(string_buffer, node_ids_for_block.data());
3932
3933 // Now map from the element id to the nodes:
3934 int ielement = 0;
3935 for (int element_id : block_element_ids)
3936 {
3937 vector<int> element_node_ids(block_element.GetNumNodes());
3938
3939 for (int i = 0; i < (int)block_element.GetNumNodes(); i++)
3940 {
3941 element_node_ids[i] = node_ids_for_block[ielement * block_element.GetNumNodes()
3942 + i];
3943 }
3944
3945 ielement++;
3946
3947 node_ids_for_element_id[element_id] = std::move(element_node_ids);
3948 }
3949 }
3950}
3951
3952/// @brief Builds a mapping from the boundary ID to the face vertices of each element that lie on the boundary.
3953static void BuildBoundaryNodeIDs(const vector<int> & boundary_ids,
3954 const CubitBlock & blocks,
3955 const map<int, vector<int>> & node_ids_for_element_id,
3956 const map<int, vector<int>> & element_ids_for_boundary_id,
3957 const map<int, vector<int>> & side_ids_for_boundary_id,
3958 const map<int, int> & block_id_for_element_id,
3959 map<int, vector<vector<int>>> & node_ids_for_boundary_id)
3960{
3961 for (int boundary_id : boundary_ids)
3962 {
3963 // Get element IDs of element on boundary (and their sides that are on boundary).
3964 auto & boundary_element_ids = element_ids_for_boundary_id.at(
3965 boundary_id);
3966 auto & boundary_element_sides = side_ids_for_boundary_id.at(
3967 boundary_id);
3968
3969 // Create vector to store the node ids of all boundary nodes.
3970 vector<vector<int>> boundary_node_ids(
3971 boundary_element_ids.size());
3972
3973 // Iterate over elements on boundary.
3974 for (int jelement = 0; jelement < (int)boundary_element_ids.size(); jelement++)
3975 {
3976 // Get element ID and the boundary side.
3977 const int boundary_element_global_id = boundary_element_ids[jelement];
3978 const int boundary_side = boundary_element_sides[jelement];
3979
3980 // Get the element information:
3981 const int block_id = block_id_for_element_id.at(boundary_element_global_id);
3982 const CubitElement & block_element = blocks.GetBlockElement(block_id);
3983
3984 const int num_face_vertices = block_element.GetNumFaceVertices(boundary_side);
3985 vector<int> nodes_of_element_on_side(num_face_vertices);
3986
3987 // Get all of the element's nodes on boundary side of element.
3988 const vector<int> & element_node_ids =
3989 node_ids_for_element_id.at(boundary_element_global_id);
3990
3991 // Iterate over the element's face nodes on the matching side.
3992 // NB: only adding vertices on face (ignore higher-order).
3993 for (int knode = 0; knode < num_face_vertices; knode++)
3994 {
3995 int inode;
3996
3997 switch (block_element.GetElementType())
3998 {
3999 case ELEMENT_TRI3:
4000 case ELEMENT_TRI6:
4001 inode = cubit_side_map_tri3[boundary_side - 1][knode];
4002 break;
4003 case ELEMENT_QUAD4:
4004 case ELEMENT_QUAD9:
4005 inode = cubit_side_map_quad4[boundary_side - 1][knode];
4006 break;
4007 case ELEMENT_TET4:
4008 case ELEMENT_TET10:
4009 inode = cubit_side_map_tet4[boundary_side - 1][knode];
4010 break;
4011 case ELEMENT_HEX8:
4012 case ELEMENT_HEX27:
4013 inode = cubit_side_map_hex8[boundary_side - 1][knode];
4014 break;
4015 case ELEMENT_WEDGE6:
4016 case ELEMENT_WEDGE18:
4017 inode = cubit_side_map_wedge6[boundary_side - 1][knode];
4018 break;
4019 case ELEMENT_PYRAMID5:
4020 case ELEMENT_PYRAMID14:
4021 inode = cubit_side_map_pyramid5[boundary_side - 1][knode];
4022 break;
4023 default:
4024 MFEM_ABORT("Unsupported element type encountered.\n");
4025 break;
4026 }
4027
4028 nodes_of_element_on_side[knode] = element_node_ids[inode - 1];
4029 }
4030
4031 boundary_node_ids[jelement] = std::move(nodes_of_element_on_side);
4032 }
4033
4034 // Add to the map.
4035 node_ids_for_boundary_id[boundary_id] = std::move(boundary_node_ids);
4036 }
4037}
4038
4039/// @brief Generates a vector of unique vertex ID.
4040static void BuildUniqueVertexIDs(const vector<int> & unique_block_ids,
4041 const CubitBlock & blocks,
4042 const map<int, vector<int>> & element_ids_for_block_id,
4043 const map<int, vector<int>> & node_ids_for_element_id,
4044 vector<int> & unique_vertex_ids)
4045{
4046 // Iterate through all vertices and add their global IDs to the unique_vertex_ids vector.
4047 for (int block_id : unique_block_ids)
4048 {
4049 auto & element_ids = element_ids_for_block_id.at(block_id);
4050
4051 auto & block_element = blocks.GetBlockElement(block_id);
4052
4053 for (int element_id : element_ids)
4054 {
4055 auto & node_ids = node_ids_for_element_id.at(element_id);
4056
4057 for (size_t knode = 0; knode < block_element.GetNumVertices(); knode++)
4058 {
4059 unique_vertex_ids.push_back(node_ids[knode]);
4060 }
4061 }
4062 }
4063
4064 // Sort unique_vertex_ids in ascending order and remove duplicate node IDs.
4065 std::sort(unique_vertex_ids.begin(), unique_vertex_ids.end());
4066
4067 auto new_end = std::unique(unique_vertex_ids.begin(), unique_vertex_ids.end());
4068
4069 unique_vertex_ids.resize(std::distance(unique_vertex_ids.begin(), new_end));
4070}
4071
4072/// @brief unique_vertex_ids contains a 1-based sorted list of vertex IDs used by the mesh. We
4073/// now create a map by running over the vertex IDs and remapping to a contiguous
4074/// 1-based array of integers.
4075static void BuildCubitToMFEMVertexMap(const vector<int> & unique_vertex_ids,
4076 map<int, int> & cubit_to_mfem_vertex_map)
4077{
4078 cubit_to_mfem_vertex_map.clear();
4079
4080 int ivertex = 1;
4081 for (int vertex_id : unique_vertex_ids)
4082 {
4083 cubit_to_mfem_vertex_map[vertex_id] = ivertex++;
4084 }
4085}
4086
4087
4088/// @brief The final step in constructing the mesh from a Genesis file. This is
4089/// only called if the mesh order == 2 (determined internally from the cubit
4090/// element type).
4091static void FinalizeCubitSecondOrderMesh(Mesh &mesh,
4092 const vector<int> & unique_block_ids,
4093 const CubitBlock & blocks,
4094 const map<int, vector<int>> & element_ids_for_block_id,
4095 const map<int, vector<int>> & node_ids_for_element_id,
4096 const double *coordx,
4097 const double *coordy,
4098 const double *coordz)
4099{
4100 mesh.FinalizeTopology();
4101
4102 // Define quadratic FE space.
4103 const int Dim = mesh.Dimension();
4104 FiniteElementCollection *fec = new H1_FECollection(2, Dim);
4105 FiniteElementSpace *fes = new FiniteElementSpace(&mesh, fec, Dim,
4107 GridFunction *Nodes = new GridFunction(fes);
4108 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
4109 mesh.SetNodalGridFunction(Nodes, true);
4110
4111 for (int block_id : unique_block_ids)
4112 {
4113 const CubitElement & block_element = blocks.GetBlockElement(block_id);
4114
4115 int *mfem_to_genesis_map = NULL;
4116
4117 switch (block_element.GetElementType())
4118 {
4119 case ELEMENT_TRI6:
4120 mfem_to_genesis_map = (int *) mfem_to_genesis_tri6;
4121 break;
4122 case ELEMENT_QUAD9:
4123 mfem_to_genesis_map = (int *) mfem_to_genesis_quad9;
4124 break;
4125 case ELEMENT_TET10:
4126 mfem_to_genesis_map = (int *) mfem_to_genesis_tet10;
4127 break;
4128 case ELEMENT_HEX27:
4129 mfem_to_genesis_map = (int *) mfem_to_genesis_hex27;
4130 break;
4131 case ELEMENT_WEDGE18:
4132 mfem_to_genesis_map = (int *) mfem_to_genesis_wedge18;
4133 break;
4134 case ELEMENT_PYRAMID14:
4135 mfem_to_genesis_map = (int *) mfem_to_genesis_pyramid14;
4136 break;
4137 default:
4138 MFEM_ABORT("Something went wrong. Linear elements detected when order is 2.");
4139 }
4140
4141 auto & element_ids = element_ids_for_block_id.at(block_id);
4142
4143 for (int element_id : element_ids)
4144 {
4145 // NB: 1-index (Exodus) --> 0-index (MFEM).
4146 Array<int> dofs;
4147 fes->GetElementDofs(element_id - 1, dofs);
4148
4149 Array<int> vdofs = dofs; // Deep copy.
4150 fes->DofsToVDofs(vdofs);
4151
4152 const vector<int> & element_node_ids = node_ids_for_element_id.at(element_id);
4153
4154 for (int jnode = 0; jnode < dofs.Size(); jnode++)
4155 {
4156 const int node_index = element_node_ids[mfem_to_genesis_map[jnode] - 1] - 1;
4157
4158 (*Nodes)(vdofs[jnode]) = coordx[node_index];
4159 (*Nodes)(vdofs[jnode] + 1) = coordy[node_index];
4160
4161 if (Dim == 3)
4162 {
4163 (*Nodes)(vdofs[jnode] + 2) = coordz[node_index];
4164 }
4165 }
4166 }
4167 }
4168}
4169
4170} // end of namespace cubit.
4171
4172/// @brief Set the coordinates of the Cubit vertices.
4173void Mesh::BuildCubitVertices(const vector<int> & unique_vertex_ids,
4174 const vector<double> & coordx,
4175 const vector<double> & coordy,
4176 const vector<double> & coordz)
4177{
4178 NumOfVertices = unique_vertex_ids.size();
4179 vertices.SetSize(NumOfVertices);
4180
4181 for (int ivertex = 0; ivertex < NumOfVertices; ivertex++)
4182 {
4183 const int original_1based_id = unique_vertex_ids[ivertex];
4184
4185 vertices[ivertex](0) = coordx[original_1based_id - 1];
4186 vertices[ivertex](1) = coordy[original_1based_id - 1];
4187
4188 if (Dim == 3)
4189 {
4190 vertices[ivertex](2) = coordz[original_1based_id - 1];
4191 }
4192 }
4193}
4194
4195/// @brief Create Cubit elements.
4196void Mesh::BuildCubitElements(const int num_elements,
4197 const cubit::CubitBlock * blocks,
4198 const vector<int> & block_ids,
4199 const map<int, vector<int>> & element_ids_for_block_id,
4200 const map<int, vector<int>> & node_ids_for_element_id,
4201 const map<int, int> & cubit_to_mfem_vertex_map)
4202{
4203 using namespace cubit;
4204
4205 NumOfElements = num_elements;
4206 elements.SetSize(num_elements);
4207
4208 int element_counter = 0;
4209
4210 // Iterate over blocks.
4211 for (int block_id : block_ids)
4212 {
4213 const CubitElement & block_element = blocks->GetBlockElement(block_id);
4214
4215 vector<int> renumbered_vertex_ids(block_element.GetNumVertices());
4216
4217 const vector<int> &block_element_ids = element_ids_for_block_id.at(block_id);
4218
4219 // Iterate over elements in block.
4220 for (int element_id : block_element_ids)
4221 {
4222 const vector<int> & element_node_ids = node_ids_for_element_id.at(element_id);
4223
4224 // Iterate over linear (vertex) nodes in block.
4225 for (size_t knode = 0; knode < block_element.GetNumVertices(); knode++)
4226 {
4227 const int node_id = element_node_ids[knode];
4228
4229 // Renumber using the mapping.
4230 renumbered_vertex_ids[knode] = cubit_to_mfem_vertex_map.at(node_id) - 1;
4231 }
4232
4233 // Create element.
4234 elements[element_counter++] = block_element.BuildElement(*this,
4235 renumbered_vertex_ids.data(),
4236 block_id);
4237 }
4238 }
4239}
4240
4241/// @brief Build the Cubit boundaries.
4243 const cubit::CubitBlock * blocks,
4244 const vector<int> & boundary_ids,
4245 const map<int, vector<int>> & element_ids_for_boundary_id,
4246 const map<int, vector<vector<int>>> & node_ids_for_boundary_id,
4247 const map<int, vector<int>> & side_ids_for_boundary_id,
4248 const map<int, int> & block_id_for_element_id,
4249 const map<int, int> & cubit_to_mfem_vertex_map)
4250{
4251 using namespace cubit;
4252
4253 NumOfBdrElements = 0;
4254 for (int boundary_id : boundary_ids)
4255 {
4256 NumOfBdrElements += element_ids_for_boundary_id.at(boundary_id).size();
4257 }
4258
4259 boundary.SetSize(NumOfBdrElements);
4260
4261 array<int, 8> renumbered_vertex_ids; // Set to max number of vertices (Hex27).
4262
4263 // Iterate over boundaries.
4264 int boundary_counter = 0;
4265 for (int boundary_id : boundary_ids)
4266 {
4267 const vector<int> &elements_on_boundary = element_ids_for_boundary_id.at(
4268 boundary_id);
4269
4270 const vector<vector<int>> &nodes_on_boundary = node_ids_for_boundary_id.at(
4271 boundary_id);
4272
4273 int jelement = 0;
4274 for (int side_id : side_ids_for_boundary_id.at(boundary_id))
4275 {
4276 // Determine the block the element originates from and the element type.
4277 const int element_id = elements_on_boundary.at(jelement);
4278 const int element_block = block_id_for_element_id.at(element_id);
4279 const CubitElement & block_element = blocks->GetBlockElement(element_block);
4280
4281 const vector<int> & element_nodes_on_side = nodes_on_boundary.at(jelement);
4282
4283 // Iterate over element's face vertices.
4284 for (size_t knode = 0; knode < element_nodes_on_side.size(); knode++)
4285 {
4286 const int node_id = element_nodes_on_side[knode];
4287
4288 // Renumber using the mapping.
4289 renumbered_vertex_ids[knode] = cubit_to_mfem_vertex_map.at(node_id) - 1;
4290 }
4291
4292 // Create boundary element.
4293 boundary[boundary_counter++] = block_element.BuildBoundaryElement(*this,
4294 side_id,
4295 renumbered_vertex_ids.data(),
4296 boundary_id);
4297
4298 jelement++;
4299 }
4300 }
4301}
4302
4303void Mesh::ReadCubit(const std::string &filename, int &curved, int &read_gf)
4304{
4305 using namespace cubit;
4306
4307 read_gf = 0;
4308 curved = 0; // Set to 1 if mesh is curved.
4309
4310 //
4311 // Open the file.
4312 //
4313 NetCDFReader cubit_reader(filename);
4314
4315 //
4316 // Read important dimensions from file.
4317 //
4318 size_t num_dimensions, num_nodes, num_elements, num_element_blocks,
4319 num_boundaries;
4320
4321 ReadCubitDimensions(cubit_reader, num_dimensions, num_nodes, num_elements,
4322 num_element_blocks, num_boundaries);
4323
4324 Dim = num_dimensions;
4325
4326 //
4327 // Read the blocks.
4328 //
4329 vector<int> block_ids;
4330 BuildCubitBlockIDs(cubit_reader, num_element_blocks, block_ids);
4331 unordered_map<int, string> blk_ids_to_names;
4332 cubit_reader.BuildIDToNameMap(block_ids, blk_ids_to_names, "eb_names");
4333 for (const auto & pr : blk_ids_to_names)
4334 {
4335 const auto blk_id = pr.first;
4336 const auto & blk_name = pr.second;
4337 if (!blk_name.empty())
4338 {
4339 if (!attribute_sets.AttributeSetExists(blk_name))
4340 {
4342 }
4343 attribute_sets.AddToAttributeSet(blk_name, blk_id);
4344 }
4345 }
4346
4347 map<int, size_t> num_elements_for_block_id;
4348 ReadCubitNumElementsInBlock(cubit_reader, block_ids,
4349 num_elements_for_block_id);
4350
4351 map<int, vector<int>> element_ids_for_block_id;
4352 map<int, int> block_id_for_element_id;
4353 BuildElementIDsForBlockID(
4354 block_ids, num_elements_for_block_id, element_ids_for_block_id,
4355 block_id_for_element_id);
4356
4357 //
4358 // Read number of nodes for each element.
4359 CubitBlock blocks(num_dimensions);
4360 ReadCubitBlocks(cubit_reader, block_ids, blocks);
4361
4362 // Read the elements that make-up each block.
4363 map<int, vector<int>> node_ids_for_element_id;
4364 ReadCubitElementBlocks(cubit_reader,
4365 blocks,
4366 block_ids,
4367 element_ids_for_block_id,
4368 node_ids_for_element_id);
4369
4370 //
4371 // Read the boundary ids.
4372 //
4373 vector<int> boundary_ids;
4374 ReadCubitBoundaryIDs(cubit_reader, num_boundaries, boundary_ids);
4375 unordered_map<int, string> bnd_ids_to_names;
4376 cubit_reader.BuildIDToNameMap(boundary_ids, bnd_ids_to_names, "ss_names");
4377 for (const auto & pr : bnd_ids_to_names)
4378 {
4379 const auto bnd_id = pr.first;
4380 const auto & bnd_name = pr.second;
4381 if (!bnd_name.empty())
4382 {
4384 {
4386 }
4387 bdr_attribute_sets.AddToAttributeSet(bnd_name, bnd_id);
4388 }
4389 }
4390
4391
4392 //
4393 // Read the (element, corresponding side) on each of the boundaries.
4394 //
4395 map<int, vector<int>> element_ids_for_boundary_id;
4396 map<int, vector<int>> side_ids_for_boundary_id;
4397
4398 ReadCubitBoundaries(cubit_reader, boundary_ids,
4399 element_ids_for_boundary_id, side_ids_for_boundary_id);
4400
4401 map<int, vector<vector<int>>> node_ids_for_boundary_id;
4402
4403 BuildBoundaryNodeIDs(boundary_ids, blocks, node_ids_for_element_id,
4404 element_ids_for_boundary_id, side_ids_for_boundary_id,
4405 block_id_for_element_id,
4406 node_ids_for_boundary_id);
4407
4408 //
4409 // Read the xyz coordinates for each node.
4410 //
4411 vector<double> coordx(num_nodes);
4412 vector<double> coordy(num_nodes);
4413 vector<double> coordz(num_dimensions == 3 ? num_nodes : 0);
4414
4415 ReadCubitNodeCoordinates(cubit_reader, coordx.data(), coordy.data(),
4416 coordz.data());
4417
4418 //
4419 // We need another node ID mapping since MFEM needs contiguous vertex ids.
4420 //
4421 vector<int> unique_vertex_ids;
4422 BuildUniqueVertexIDs(block_ids, blocks, element_ids_for_block_id,
4423 node_ids_for_element_id, unique_vertex_ids);
4424
4425 //
4426 // unique_vertex_ids now contains a 1-based sorted list of node IDs for each
4427 // node used by the mesh. We now create a map by running over the node IDs
4428 // and remapping to contiguous 1-based integers.
4429 // ie. [1, 4, 5, 8, 9] --> [1, 2, 3, 4, 5].
4430 //
4431 map<int, int> cubit_to_mfem_vertex_map;
4432 BuildCubitToMFEMVertexMap(unique_vertex_ids, cubit_to_mfem_vertex_map);
4433
4434 //
4435 // Load up the vertices.
4436 //
4437 BuildCubitVertices(unique_vertex_ids, coordx, coordy, coordz);
4438
4439 //
4440 // Now load the elements.
4441 //
4442 BuildCubitElements(num_elements, &blocks, block_ids,
4443 element_ids_for_block_id,
4444 node_ids_for_element_id, cubit_to_mfem_vertex_map);
4445
4446 //
4447 // Load up the boundary elements.
4448 //
4449 BuildCubitBoundaries(&blocks, boundary_ids,
4450 element_ids_for_boundary_id, node_ids_for_boundary_id, side_ids_for_boundary_id,
4451 block_id_for_element_id,
4452 cubit_to_mfem_vertex_map);
4453
4454 //
4455 // Additional setup for second order.
4456 //
4457 if (blocks.GetOrder() == 2)
4458 {
4459 curved = 1;
4460
4461 FinalizeCubitSecondOrderMesh(*this,
4462 block_ids,
4463 blocks,
4464 element_ids_for_block_id,
4465 node_ids_for_element_id,
4466 coordx.data(),
4467 coordy.data(),
4468 coordz.data());
4469 }
4470}
4471
4472#endif // #ifdef MFEM_USE_NETCDF
4473
4474} // namespace mfem
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
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
int Size() const
Return the logical size of the array.
Definition array.hpp:147
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:830
T * GetData()
Returns the data.
Definition array.hpp:121
void SortAll()
Sort each named array in the container.
void UniqueAll()
Remove duplicates from each, previously sorted, named array.
void Load(std::istream &in)
Load the contents of the container from an input stream.
bool AttributeSetExists(const std::string &name) const
Return true is the named attribute set is present.
void AddToAttributeSet(const std::string &set_name, int attr)
Add a single entry to an existing attribute set.
ArraysByName< int > attr_sets
Named sets of attributes.
Array< int > & CreateAttributeSet(const std::string &set_name)
Create an empty named attribute set.
@ ClosedUniform
Nodes: x_i = i/(n-1), i=0,...,n-1.
Definition fe_base.hpp:39
Abstract data type element.
Definition element.hpp:29
virtual void GetVertices(Array< int > &v) const =0
Get the indices defining the vertices.
void SetAttribute(const int attr)
Set element's attribute.
Definition element.hpp:61
Type
Constants for the classes derived from Element.
Definition element.hpp:41
virtual int GetNVertices() const =0
virtual void SetVertices(const Array< int > &v)=0
Set the indices defining the vertices.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:258
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
Definition fespace.cpp:3513
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3835
Abstract class for all finite elements.
Definition fe_base.hpp:244
static const int Dimension[NumGeom]
Definition geom.hpp:51
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:31
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec_owned and fes.
Definition gridfunc.hpp:122
virtual void ProjectCoefficient(Coefficient &coeff)
Project coeff Coefficient to this GridFunction. The projection computation depends on the choice of t...
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
Definition gridfunc.cpp:353
void GetNodalValues(int i, Array< real_t > &nval, int vdim=1) const
Returns the values in the vertices of i'th element for dimension vdim.
Definition gridfunc.cpp:392
virtual void SetSpace(FiniteElementSpace *f)
Associate a new FiniteElementSpace with the GridFunction.
Definition gridfunc.cpp:225
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:275
Data type hexahedron element.
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition mesh.cpp:6575
Array< Vertex > vertices
Definition mesh.hpp:105
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
Element * NewElement(int geom)
Definition mesh.cpp:4672
void BuildCubitElements(const int num_elements, const cubit::CubitBlock *blocks, const std::vector< int > &block_ids, const std::map< int, std::vector< int > > &element_ids_for_block_id, const std::map< int, std::vector< int > > &node_ids_for_element_id, const std::map< int, int > &cubit_to_mfem_vertex_map)
Called internally in ReadCubit. This method builds the mesh elements.
MemAlloc< Tetrahedron, 1024 > TetMemory
Definition mesh.hpp:270
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:298
void ReadTrueGridMesh(std::istream &input)
static const int vtk_quadratic_tet[10]
Definition mesh.hpp:263
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:7635
int NumOfBdrElements
Definition mesh.hpp:79
void ReadNetgen3DMesh(std::istream &input)
void ReadInlineMesh(std::istream &input, bool generate_edges=false)
void ReadLineMesh(std::istream &input)
int Dim
Definition mesh.hpp:76
static const int vtk_quadratic_wedge[18]
Definition mesh.hpp:265
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:296
void Make1D(int n, real_t sx=1.0)
Definition mesh.cpp:4266
friend class Tetrahedron
Definition mesh.hpp:269
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1339
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3362
void ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo, const std::string &xml_prefix="")
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
void Make3D(int nx, int ny, int nz, Element::Type type, real_t sx, real_t sy, real_t sz, bool sfc_ordering)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
Definition mesh.cpp:3543
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1354
friend class NURBSExtension
Definition mesh.hpp:66
static bool remove_unused_vertices
Definition mesh.hpp:307
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf, bool spacing=false)
void ReadCubit(const std::string &filename, int &curved, int &read_gf)
Load a mesh from a Genesis file.
static const int vtk_quadratic_pyramid[13]
Definition mesh.hpp:264
int NumOfVertices
Definition mesh.hpp:79
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:293
void ReadVTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo)
GridFunction * Nodes
Definition mesh.hpp:260
int NumOfElements
Definition mesh.hpp:79
static const int vtk_quadratic_hex[27]
Definition mesh.hpp:266
int spaceDim
Definition mesh.hpp:77
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1285
void ReadMFEMMesh(std::istream &input, int version, int &curved)
void CreateVTKMesh(const Vector &points, const Array< int > &cell_data, const Array< int > &cell_offsets, const Array< int > &cell_types, const Array< int > &cell_attributes, int &curved, int &read_gf, bool &finalize_topo)
int own_nodes
Definition mesh.hpp:261
void BuildCubitBoundaries(const cubit::CubitBlock *blocks, const std::vector< int > &boundary_ids, const std::map< int, std::vector< int > > &element_ids_for_boundary_id, const std::map< int, std::vector< std::vector< int > > > &node_ids_for_boundary_id, const std::map< int, std::vector< int > > &side_ids_for_boundary_id, const std::map< int, int > &block_id_for_element_id, const std::map< int, int > &cubit_to_mfem_vertex_map)
Called internally in ReadCubit. This method adds the mesh boundary elements.
Array< Element * > boundary
Definition mesh.hpp:106
void SetMeshGen()
Determine the mesh generator bitmask meshgen, see MeshGenerator().
Definition mesh.cpp:4744
void Make2D(int nx, int ny, Element::Type type, real_t sx, real_t sy, bool generate_edges, bool sfc_ordering)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4088
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:6484
Element * ReadElement(std::istream &input)
Definition mesh.cpp:4726
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1455
void BuildCubitVertices(const std::vector< int > &unique_vertex_ids, const std::vector< double > &coordx, const std::vector< double > &coordy, const std::vector< double > &coordz)
Called internally in ReadCubit. This method creates the vertices.
void ReadNetgen2DMesh(std::istream &input, int &curved)
Array< Element * > elements
Definition mesh.hpp:100
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1321
void RemoveUnusedVertices()
Remove unused vertices and rebuild mesh connectivity.
Definition mesh.cpp:13197
int GetNBE() const
Return the number of active boundary elements.
Definition nurbs.hpp:771
void SetCoordsFromPatches(Vector &Nodes)
Set FE coordinates in Nodes, using data from patches, and erase patches.
Definition nurbs.cpp:4267
void GetElementTopo(Array< Element * > &elements) const
Generate the active mesh elements and return them in elements.
Definition nurbs.cpp:3497
bool HavePatches() const
Return true if at least 1 patch is defined, false otherwise.
Definition nurbs.hpp:804
void GetBdrElementTopo(Array< Element * > &boundary) const
Generate the active mesh boundary elements and return them in boundary.
Definition nurbs.cpp:3626
int GetOrder() const
If all KnotVector orders are identical, return that number. Otherwise, return NURBSFECollection::Vari...
Definition nurbs.hpp:755
int GetNV() const
Return the local number of active vertices.
Definition nurbs.hpp:763
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:742
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:767
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:699
Class for standard nodal finite elements.
Definition fe_base.hpp:721
const Array< int > & GetLexicographicOrdering() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:801
Data type point element.
Definition point.hpp:23
Data type Pyramid element.
Definition pyramid.hpp:23
Piecewise-(bi)quadratic continuous finite elements.
Definition fe_coll.hpp:889
Data type quadrilateral element.
Data type line segment element.
Definition segment.hpp:23
Data type tetrahedron element.
void SetVertices(const Array< int > &v) override
Set the indices defining the vertices.
Data type triangle element.
Definition triangle.hpp:24
Vector coefficient defined by a vector GridFunction.
Vector data type.
Definition vector.hpp:82
void Load(std::istream **in, int np, int *dim)
Reads a vector from multiple files.
Definition vector.cpp:126
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:235
Data type for vertex.
Definition vertex.hpp:23
Data type Wedge element.
Definition wedge.hpp:23
Vector bb_min
Definition ex9.cpp:67
Vector bb_max
Definition ex9.cpp:67
constexpr int dimension
This example only works in 3D. Kernels for 2D are not implemented.
Definition hooke.cpp:45
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t a
Definition lissajous.cpp:41
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
size_t NumBase64Chars(size_t nbytes)
Return the number of characters needed to encode nbytes in base-64.
Definition binaryio.cpp:103
T read(std::istream &is)
Read a value from the stream and return it.
Definition binaryio.hpp:37
const int mfem_to_genesis_tri6[6]
const int mfem_to_genesis_pyramid14[14]
const int cubit_side_map_tri3[3][2]
const int mfem_to_genesis_wedge18[18]
const int cubit_side_map_hex8[6][4]
const int cubit_side_map_quad4[4][2]
const int mfem_to_genesis_quad9[9]
const int cubit_side_map_tet4[4][3]
const int cubit_side_map_wedge6[5][4]
const int mfem_to_genesis_hex27[27]
const int cubit_side_map_pyramid5[5][4]
const int mfem_to_genesis_tet10[10]
bool StringCompare(const char *s1, const char *s2)
void mfem_error(const char *msg)
Definition error.cpp:154
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
void GmshHOTriangleMapping(int order, int *map)
Generate Gmsh vertex mapping for a Triangle.
Definition gmsh.cpp:388
void GmshHOPyramidMapping(int order, int *map)
Generate Gmsh vertex mapping for a Pyramid.
Definition gmsh.cpp:470
void filter_dos(std::string &line)
Check for, and remove, a trailing '\r' from and std::string.
Definition text.hpp:45
float real_t
Definition config.hpp:43
const char * VTKByteOrder()
Determine the byte order and return either "BigEndian" or "LittleEndian".
Definition vtk.cpp:602
void GmshHOTetrahedronMapping(int order, int *map)
Generate Gmsh vertex mapping for a Tetrahedron.
Definition gmsh.cpp:417
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:497
void GmshHOSegmentMapping(int order, int *map)
Generate Gmsh vertex mapping for a Segment.
Definition gmsh.cpp:378
void GmshHOQuadrilateralMapping(int order, int *map)
Generate Gmsh vertex mapping for a Quadrilateral.
Definition gmsh.cpp:403
void GmshHOHexahedronMapping(int order, int *map)
Generate Gmsh vertex mapping for a Hexahedron.
Definition gmsh.cpp:436
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
static const int LAGRANGE_PRISM
Definition vtk.hpp:65
static const int PrismMap[6]
Permutation from MFEM's prism ordering to VTK's prism ordering.
Definition vtk.hpp:71
static bool IsLagrange(int vtk_geom)
Does the given VTK geometry type describe an arbitrary-order Lagrange element?
Definition vtk.cpp:85
static Geometry::Type GetMFEMGeometry(int vtk_geom)
Given a VTK geometry type, return the corresponding MFEM Geometry::Type.
Definition vtk.cpp:46
static int GetOrder(int vtk_geom, int npoints)
For the given VTK geometry type and number of points, return the order of the element.
Definition vtk.cpp:96