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