MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
mesh_readers.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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
19#include <iostream>
20#include <cstdio>
21#include <vector>
22#include <algorithm>
23#include <map>
24
25#ifdef MFEM_USE_NETCDF
26#include "netcdf.h"
27#endif
28
29#ifdef MFEM_USE_ZLIB
30#include <zlib.h>
31#endif
32
33using namespace std;
34
35namespace mfem
36{
37
39
40void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
41{
42 // Read MFEM mesh v1.0, v1.2, or v1.3 format
43 MFEM_VERIFY(version == 10 || version == 12 || version == 13,
44 "unknown MFEM mesh version");
45
46 string ident;
47
48 // read lines beginning with '#' (comments)
49 skip_comment_lines(input, '#');
50 input >> ident; // 'dimension'
51
52 MFEM_VERIFY(ident == "dimension", "invalid mesh file");
53 input >> Dim;
54
55 skip_comment_lines(input, '#');
56 input >> ident; // 'elements'
57
58 MFEM_VERIFY(ident == "elements", "invalid mesh file");
59 input >> NumOfElements;
60 elements.SetSize(NumOfElements);
61 for (int j = 0; j < NumOfElements; j++)
62 {
63 elements[j] = ReadElement(input);
64 }
65
66 if (version == 13)
67 {
68 skip_comment_lines(input, '#');
69 input >> ident; // 'attribute_sets'
70
71 MFEM_VERIFY(ident == "attribute_sets", "invalid mesh file");
72
76 }
77
78 skip_comment_lines(input, '#');
79 input >> ident; // 'boundary'
80
81 MFEM_VERIFY(ident == "boundary", "invalid mesh file");
82 input >> NumOfBdrElements;
84 for (int j = 0; j < NumOfBdrElements; j++)
85 {
86 boundary[j] = ReadElement(input);
87 }
88
89 if (version == 13)
90 {
91 skip_comment_lines(input, '#');
92 input >> ident; // 'bdr_attribute_sets'
93
94 MFEM_VERIFY(ident == "bdr_attribute_sets", "invalid mesh file");
95
99 }
100
101 skip_comment_lines(input, '#');
102 input >> ident; // 'vertices'
103
104 MFEM_VERIFY(ident == "vertices", "invalid mesh file");
105 input >> NumOfVertices;
106 vertices.SetSize(NumOfVertices);
107
108 input >> ws >> ident;
109 if (ident != "nodes")
110 {
111 // read the vertices
112 spaceDim = atoi(ident.c_str());
113 for (int j = 0; j < NumOfVertices; j++)
114 {
115 for (int i = 0; i < spaceDim; i++)
116 {
117 input >> vertices[j](i);
118 }
119 }
120 }
121 else
122 {
123 // prepare to read the nodes
124 input >> ws;
125 curved = 1;
126 }
127
128 // When visualizing solutions on non-conforming grids, PETSc
129 // may dump additional vertices
131}
132
133void Mesh::ReadLineMesh(std::istream &input)
134{
135 int j,p1,p2,a;
136
137 Dim = 1;
138
139 input >> NumOfVertices;
140 vertices.SetSize(NumOfVertices);
141 // Sets vertices and the corresponding coordinates
142 for (j = 0; j < NumOfVertices; j++)
143 {
144 input >> vertices[j](0);
145 }
146
147 input >> NumOfElements;
148 elements.SetSize(NumOfElements);
149 // Sets elements and the corresponding indices of vertices
150 for (j = 0; j < NumOfElements; j++)
151 {
152 input >> a >> p1 >> p2;
153 elements[j] = new Segment(p1-1, p2-1, a);
154 }
155
156 int ind[1];
157 input >> NumOfBdrElements;
158 boundary.SetSize(NumOfBdrElements);
159 for (j = 0; j < NumOfBdrElements; j++)
160 {
161 input >> a >> ind[0];
162 ind[0]--;
163 boundary[j] = new Point(ind,a);
164 }
165}
166
167void Mesh::ReadNetgen2DMesh(std::istream &input, int &curved)
168{
169 int ints[32], attr, n;
170
171 // Read planar mesh in Netgen format.
172 Dim = 2;
173
174 // Read the boundary elements.
175 input >> NumOfBdrElements;
176 boundary.SetSize(NumOfBdrElements);
177 for (int i = 0; i < NumOfBdrElements; i++)
178 {
179 input >> attr
180 >> ints[0] >> ints[1];
181 ints[0]--; ints[1]--;
182 boundary[i] = new Segment(ints, attr);
183 }
184
185 // Read the elements.
186 input >> NumOfElements;
187 elements.SetSize(NumOfElements);
188 for (int i = 0; i < NumOfElements; i++)
189 {
190 input >> attr >> n;
191 for (int j = 0; j < n; j++)
192 {
193 input >> ints[j];
194 ints[j]--;
195 }
196 switch (n)
197 {
198 case 2:
199 elements[i] = new Segment(ints, attr);
200 break;
201 case 3:
202 elements[i] = new Triangle(ints, attr);
203 break;
204 case 4:
205 elements[i] = new Quadrilateral(ints, attr);
206 break;
207 }
208 }
209
210 if (!curved)
211 {
212 // Read the vertices.
213 input >> NumOfVertices;
214 vertices.SetSize(NumOfVertices);
215 for (int i = 0; i < NumOfVertices; i++)
216 for (int j = 0; j < Dim; j++)
217 {
218 input >> vertices[i](j);
219 }
220 }
221 else
222 {
223 input >> NumOfVertices;
224 vertices.SetSize(NumOfVertices);
225 input >> ws;
226 }
227}
228
229void Mesh::ReadNetgen3DMesh(std::istream &input)
230{
231 int ints[32], attr;
232
233 // Read a Netgen format mesh of tetrahedra.
234 Dim = 3;
235
236 // Read the vertices
237 input >> NumOfVertices;
238
239 vertices.SetSize(NumOfVertices);
240 for (int i = 0; i < NumOfVertices; i++)
241 for (int j = 0; j < Dim; j++)
242 {
243 input >> vertices[i](j);
244 }
245
246 // Read the elements
247 input >> NumOfElements;
248 elements.SetSize(NumOfElements);
249 for (int i = 0; i < NumOfElements; i++)
250 {
251 input >> attr;
252 for (int j = 0; j < 4; j++)
253 {
254 input >> ints[j];
255 ints[j]--;
256 }
257#ifdef MFEM_USE_MEMALLOC
258 Tetrahedron *tet;
259 tet = TetMemory.Alloc();
260 tet->SetVertices(ints);
261 tet->SetAttribute(attr);
262 elements[i] = tet;
263#else
264 elements[i] = new Tetrahedron(ints, attr);
265#endif
266 }
267
268 // Read the boundary information.
269 input >> NumOfBdrElements;
270 boundary.SetSize(NumOfBdrElements);
271 for (int i = 0; i < NumOfBdrElements; i++)
272 {
273 input >> attr;
274 for (int j = 0; j < 3; j++)
275 {
276 input >> ints[j];
277 ints[j]--;
278 }
279 boundary[i] = new Triangle(ints, attr);
280 }
281}
282
283void Mesh::ReadTrueGridMesh(std::istream &input)
284{
285 int i, j, ints[32], attr;
286 const int buflen = 1024;
287 char buf[buflen];
288
289 // TODO: find the actual dimension
290 Dim = 3;
291
292 if (Dim == 2)
293 {
294 int vari;
295 real_t varf;
296
297 input >> vari >> NumOfVertices >> vari >> vari >> NumOfElements;
298 input.getline(buf, buflen);
299 input.getline(buf, buflen);
300 input >> vari;
301 input.getline(buf, buflen);
302 input.getline(buf, buflen);
303 input.getline(buf, buflen);
304
305 // Read the vertices.
306 vertices.SetSize(NumOfVertices);
307 for (i = 0; i < NumOfVertices; i++)
308 {
309 input >> vari >> varf >> vertices[i](0) >> vertices[i](1);
310 input.getline(buf, buflen);
311 }
312
313 // Read the elements.
314 elements.SetSize(NumOfElements);
315 for (i = 0; i < NumOfElements; i++)
316 {
317 input >> vari >> attr;
318 for (j = 0; j < 4; j++)
319 {
320 input >> ints[j];
321 ints[j]--;
322 }
323 input.getline(buf, buflen);
324 input.getline(buf, buflen);
325 elements[i] = new Quadrilateral(ints, attr);
326 }
327 }
328 else if (Dim == 3)
329 {
330 int vari;
331 real_t varf;
332 input >> vari >> NumOfVertices >> NumOfElements;
333 input.getline(buf, buflen);
334 input.getline(buf, buflen);
335 input >> vari >> vari >> NumOfBdrElements;
336 input.getline(buf, buflen);
337 input.getline(buf, buflen);
338 input.getline(buf, buflen);
339 // Read the vertices.
340 vertices.SetSize(NumOfVertices);
341 for (i = 0; i < NumOfVertices; i++)
342 {
343 input >> vari >> varf >> vertices[i](0) >> vertices[i](1)
344 >> vertices[i](2);
345 input.getline(buf, buflen);
346 }
347 // Read the elements.
348 elements.SetSize(NumOfElements);
349 for (i = 0; i < NumOfElements; i++)
350 {
351 input >> vari >> attr;
352 for (j = 0; j < 8; j++)
353 {
354 input >> ints[j];
355 ints[j]--;
356 }
357 input.getline(buf, buflen);
358 elements[i] = new Hexahedron(ints, attr);
359 }
360 // Read the boundary elements.
361 boundary.SetSize(NumOfBdrElements);
362 for (i = 0; i < NumOfBdrElements; i++)
363 {
364 input >> attr;
365 for (j = 0; j < 4; j++)
366 {
367 input >> ints[j];
368 ints[j]--;
369 }
370 input.getline(buf, buflen);
371 boundary[i] = new Quadrilateral(ints, attr);
372 }
373 }
374}
375
376// see Tetrahedron::edges
377const int Mesh::vtk_quadratic_tet[10] =
378{ 0, 1, 2, 3, 4, 7, 5, 6, 8, 9 };
379
380// see Pyramid::edges & Mesh::GenerateFaces
381// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
382const int Mesh::vtk_quadratic_pyramid[13] =
383{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
384
385// see Wedge::edges & Mesh::GenerateFaces
386// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
387const int Mesh::vtk_quadratic_wedge[18] =
388{ 0, 2, 1, 3, 5, 4, 8, 7, 6, 11, 10, 9, 12, 14, 13, 17, 16, 15};
389
390// see Hexahedron::edges & Mesh::GenerateFaces
391const int Mesh::vtk_quadratic_hex[27] =
392{
393 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
394 24, 22, 21, 23, 20, 25, 26
395};
396
397void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
398 const Array<int> &cell_offsets,
399 const Array<int> &cell_types,
400 const Array<int> &cell_attributes,
401 int &curved, int &read_gf, bool &finalize_topo)
402{
403 int np = points.Size()/3;
404 Dim = -1;
405 NumOfElements = cell_types.Size();
406 elements.SetSize(NumOfElements);
407
408 int order = -1;
409 bool legacy_elem = false, lagrange_elem = false;
410
411 for (int i = 0; i < NumOfElements; i++)
412 {
413 int j = (i > 0) ? cell_offsets[i-1] : 0;
414 int ct = cell_types[i];
416 elements[i] = NewElement(geom);
417 if (cell_attributes.Size() > 0)
418 {
419 elements[i]->SetAttribute(cell_attributes[i]);
420 }
421 // VTK ordering of vertices is the same as MFEM ordering of vertices
422 // for all element types *except* prisms, which require a permutation
423 if (geom == Geometry::PRISM && ct != VTKGeometry::LAGRANGE_PRISM)
424 {
425 int prism_vertices[6];
426 for (int k=0; k<6; ++k)
427 {
428 prism_vertices[k] = cell_data[j+VTKGeometry::PrismMap[k]];
429 }
430 elements[i]->SetVertices(prism_vertices);
431 }
432 else
433 {
434 elements[i]->SetVertices(&cell_data[j]);
435 }
436
437 int elem_dim = Geometry::Dimension[geom];
438 int elem_order = VTKGeometry::GetOrder(ct, cell_offsets[i] - j);
439
440 if (VTKGeometry::IsLagrange(ct)) { lagrange_elem = true; }
441 else { legacy_elem = true; }
442
443 MFEM_VERIFY(Dim == -1 || Dim == elem_dim,
444 "Elements with different dimensions are not supported");
445 MFEM_VERIFY(order == -1 || order == elem_order,
446 "Elements with different orders are not supported");
447 MFEM_VERIFY(legacy_elem != lagrange_elem,
448 "Mixing of legacy and Lagrange cell types is not supported");
449 Dim = elem_dim;
450 order = elem_order;
451 }
452
453 // determine spaceDim based on min/max differences detected each dimension
454 spaceDim = 0;
455 if (np > 0)
456 {
457 real_t min_value, max_value;
458 for (int d = 3; d > 0; --d)
459 {
460 min_value = max_value = points(3*0 + d-1);
461 for (int i = 1; i < np; i++)
462 {
463 min_value = std::min(min_value, points(3*i + d-1));
464 max_value = std::max(max_value, points(3*i + d-1));
465 if (min_value != max_value)
466 {
467 spaceDim = d;
468 break;
469 }
470 }
471 if (spaceDim > 0) { break; }
472 }
473 }
474
475 if (order == 1 && !lagrange_elem)
476 {
477 NumOfVertices = np;
478 vertices.SetSize(np);
479 for (int i = 0; i < np; i++)
480 {
481 vertices[i](0) = points(3*i+0);
482 vertices[i](1) = points(3*i+1);
483 vertices[i](2) = points(3*i+2);
484 }
485 // No boundary is defined in a VTK mesh
489 }
490 else
491 {
492 // The following section of code is shared for legacy quadratic and the
493 // Lagrange high order elements
494 curved = 1;
495
496 // generate new enumeration for the vertices
497 Array<int> pts_dof(np);
498 pts_dof = -1;
499 // mark vertex points
500 for (int i = 0; i < NumOfElements; i++)
501 {
502 int *v = elements[i]->GetVertices();
503 int nv = elements[i]->GetNVertices();
504 for (int j = 0; j < nv; j++)
505 {
506 if (pts_dof[v[j]] == -1) { pts_dof[v[j]] = 0; }
507 }
508 }
509
510 // The following loop reorders pts_dofs so vertices are visited in
511 // canonical order
512
513 // Keep the original ordering of the vertices
514 NumOfVertices = 0;
515 for (int i = 0; i < np; i++)
516 {
517 if (pts_dof[i] != -1)
518 {
519 pts_dof[i] = NumOfVertices++;
520 }
521 }
522 // update the element vertices
523 for (int i = 0; i < NumOfElements; i++)
524 {
525 int *v = elements[i]->GetVertices();
526 int nv = elements[i]->GetNVertices();
527 for (int j = 0; j < nv; j++)
528 {
529 v[j] = pts_dof[v[j]];
530 }
531 }
532 // Define the 'vertices' from the 'points' through the 'pts_dof' map
534 for (int i = 0; i < np; i++)
535 {
536 int j = pts_dof[i];
537 if (j != -1)
538 {
539 vertices[j](0) = points(3*i+0);
540 vertices[j](1) = points(3*i+1);
541 vertices[j](2) = points(3*i+2);
542 }
543 }
544
545 // No boundary is defined in a VTK mesh
547
548 // Generate faces and edges so that we can define FE space on the mesh
550
553 if (legacy_elem)
554 {
555 // Define quadratic FE space
556 fec = new QuadraticFECollection;
557 fes = new FiniteElementSpace(this, fec, spaceDim);
558 Nodes = new GridFunction(fes);
559 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
560 own_nodes = 1;
561
562 // Map vtk points to edge/face/element dofs
563 Array<int> dofs;
564 for (int i = 0; i < NumOfElements; i++)
565 {
566 fes->GetElementDofs(i, dofs);
567 const int *vtk_mfem;
568 switch (elements[i]->GetGeometryType())
569 {
571 case Geometry::SQUARE:
572 vtk_mfem = vtk_quadratic_hex; break; // identity map
574 vtk_mfem = vtk_quadratic_tet; break;
575 case Geometry::CUBE:
576 vtk_mfem = vtk_quadratic_hex; break;
577 case Geometry::PRISM:
578 vtk_mfem = vtk_quadratic_wedge; break;
580 vtk_mfem = vtk_quadratic_pyramid; break;
581 default:
582 vtk_mfem = NULL; // suppress a warning
583 break;
584 }
585
586 int offset = (i == 0) ? 0 : cell_offsets[i-1];
587 for (int j = 0; j < dofs.Size(); j++)
588 {
589 if (pts_dof[cell_data[offset+j]] == -1)
590 {
591 pts_dof[cell_data[offset+j]] = dofs[vtk_mfem[j]];
592 }
593 else
594 {
595 if (pts_dof[cell_data[offset+j]] != dofs[vtk_mfem[j]])
596 {
597 MFEM_ABORT("VTK mesh: inconsistent quadratic mesh!");
598 }
599 }
600 }
601 }
602 }
603 else
604 {
605 // Define H1 FE space
607 fes = new FiniteElementSpace(this, fec, spaceDim);
608 Nodes = new GridFunction(fes);
609 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
610 own_nodes = 1;
611 Array<int> dofs;
612
613 std::map<Geometry::Type,Array<int>> vtk_inv_maps;
614 std::map<Geometry::Type,const Array<int>*> lex_orderings;
615
616 int i, n;
617 for (n = i = 0; i < NumOfElements; i++)
618 {
620 fes->GetElementDofs(i, dofs);
621
622 Array<int> &vtk_inv_map = vtk_inv_maps[geom];
623 if (vtk_inv_map.Size() == 0)
624 {
625 Array<int> vtk_map;
626 CreateVTKElementConnectivity(vtk_map, geom, order);
627 vtk_inv_map.SetSize(vtk_map.Size());
628 for (int j=0; j<vtk_map.Size(); ++j)
629 {
630 vtk_inv_map[vtk_map[j]] = j;
631 }
632 }
633 const Array<int> *&lex_ordering = lex_orderings[geom];
634 if (!lex_ordering)
635 {
636 const FiniteElement *fe = fes->GetFE(i);
637 const NodalFiniteElement *nodal_fe =
638 dynamic_cast<const NodalFiniteElement*>(fe);
639 MFEM_ASSERT(nodal_fe != NULL, "Unsupported element type");
640 lex_ordering = &nodal_fe->GetLexicographicOrdering();
641 }
642
643 for (int lex_idx = 0; lex_idx < dofs.Size(); lex_idx++)
644 {
645 int mfem_idx = (*lex_ordering)[lex_idx];
646 int vtk_idx = vtk_inv_map[lex_idx];
647 int pt_idx = cell_data[n + vtk_idx];
648 if (pts_dof[pt_idx] == -1)
649 {
650 pts_dof[pt_idx] = dofs[mfem_idx];
651 }
652 else
653 {
654 if (pts_dof[pt_idx] != dofs[mfem_idx])
655 {
656 MFEM_ABORT("VTK mesh: inconsistent Lagrange mesh!");
657 }
658 }
659 }
660 n += dofs.Size();
661 }
662 }
663 // Define the 'Nodes' from the 'points' through the 'pts_dof' map
664 Array<int> dofs;
665 for (int i = 0; i < np; i++)
666 {
667 dofs.SetSize(1);
668 if (pts_dof[i] != -1)
669 {
670 dofs[0] = pts_dof[i];
671 fes->DofsToVDofs(dofs);
672 for (int d = 0; d < dofs.Size(); d++)
673 {
674 (*Nodes)(dofs[d]) = points(3*i+d);
675 }
676 }
677 }
678 read_gf = 0;
679 }
680}
681
682namespace vtk_xml
683{
684
685using namespace tinyxml2;
686
687/// Return false if either string is NULL or if the strings differ, return true
688/// if the strings are the same.
689bool StringCompare(const char *s1, const char *s2)
690{
691 if (s1 == NULL || s2 == NULL) { return false; }
692 return strcmp(s1, s2) == 0;
693}
694
695/// Abstract base class for reading contiguous arrays of (potentially
696/// compressed, potentially base-64 encoded) binary data from a buffer into a
697/// destination array. The types of the source and destination arrays may be
698/// different (e.g. read data of type uint8_t into destination array of
699/// uint32_t), which is handled by the templated derived class @a BufferReader.
700struct BufferReaderBase
701{
702 enum HeaderType { UINT32_HEADER, UINT64_HEADER };
703 virtual void ReadBinary(const char *buf, void *dest, int n) const = 0;
704 virtual void ReadBase64(const char *txt, void *dest, int n) const = 0;
705 virtual ~BufferReaderBase() { }
706};
707
708/// Read an array of source data stored as (potentially compressed, potentially
709/// base-64 encoded) into a destination array. The types of the elements in the
710/// source array are given by template parameter @a F ("from") and the types of
711/// the elements of the destination array are given by @a T ("to"). The binary
712/// data has a header, which is one integer if the data is uncompressed, and is
713/// four integers if the data is compressed. The integers may either by uint32_t
714/// or uint64_t, according to the @a header_type. If the data is compressed and
715/// base-64 encoded, then the header is encoded separately from the data. If the
716/// data is uncompressed and base-64 encoded, then the header and data are
717/// encoded together.
718template <typename T, typename F>
719struct BufferReader : BufferReaderBase
720{
721 bool compressed;
722 HeaderType header_type;
723 BufferReader(bool compressed_, HeaderType header_type_)
724 : compressed(compressed_), header_type(header_type_) { }
725
726 /// Return the number of bytes of each header entry.
727 size_t HeaderEntrySize() const
728 {
729 return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t);
730 }
731
732 /// Return the value of the header entry pointer to by @a header_buf. The
733 /// value is stored as either uint32_t or uint64_t, according to the @a
734 /// header_type, and is returned as uint64_t.
735 uint64_t ReadHeaderEntry(const char *header_buf) const
736 {
737 return (header_type == UINT64_HEADER) ? bin_io::read<uint64_t>(header_buf)
738 : bin_io::read<uint32_t>(header_buf);
739 }
740
741 /// Return the number of bytes in the header. The header consists of one
742 /// integer if the data is uncompressed, and @a N + 3 integers if the data is
743 /// compressed, where @a N is the number of blocks. The integers are either
744 /// 32 or 64 bytes depending on the value of @a header_type. The number of
745 /// blocks is determined by reading the first integer (of type @a
746 /// header_type) pointed to by @a header_buf.
747 int NumHeaderBytes(const char *header_buf) const
748 {
749 if (!compressed) { return static_cast<int>(HeaderEntrySize()); }
750 return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize();
751 }
752
753 /// Read @a n elements of type @a F from the source buffer @a buf into the
754 /// (pre-allocated) destination array of elements of type @a T stored in
755 /// the buffer @a dest_void. The header is stored @b separately from the
756 /// rest of the data, in the buffer @a header_buf. The data buffer @a buf
757 /// does @b not contain a header.
758 void ReadBinaryWithHeader(const char *header_buf, const char *buf,
759 void *dest_void, int n) const
760 {
761 std::vector<char> uncompressed_data;
762 T *dest = static_cast<T*>(dest_void);
763
764 if (compressed)
765 {
766#ifdef MFEM_USE_ZLIB
767 // The header has format (where header_t is uint32_t or uint64_t):
768 // header_t number_of_blocks;
769 // header_t uncompressed_block_size;
770 // header_t uncompressed_last_block_size;
771 // header_t compressed_size[number_of_blocks];
772 int header_entry_size = HeaderEntrySize();
773 int nblocks = ReadHeaderEntry(header_buf);
774 header_buf += header_entry_size;
775 std::vector<size_t> header(nblocks + 2);
776 for (int i=0; i<nblocks+2; ++i)
777 {
778 header[i] = ReadHeaderEntry(header_buf);
779 header_buf += header_entry_size;
780 }
781 uncompressed_data.resize((nblocks-1)*header[0] + header[1]);
782 Bytef *dest_ptr = (Bytef *)uncompressed_data.data();
783 Bytef *dest_start = dest_ptr;
784 const Bytef *source_ptr = (const Bytef *)buf;
785 for (int i=0; i<nblocks; ++i)
786 {
787 uLongf source_len = header[i+2];
788 uLong dest_len = (i == nblocks-1) ? header[1] : header[0];
789 int res = uncompress(dest_ptr, &dest_len, source_ptr, source_len);
790 MFEM_VERIFY(res == Z_OK, "Error uncompressing");
791 dest_ptr += dest_len;
792 source_ptr += source_len;
793 }
794 MFEM_VERIFY(size_t(sizeof(F)*n) == size_t(dest_ptr - dest_start),
795 "AppendedData: wrong data size");
796 buf = uncompressed_data.data();
797#else
798 MFEM_ABORT("MFEM must be compiled with zlib enabled to uncompress.")
799#endif
800 }
801 else
802 {
803 // Each "data block" is preceded by a header that is either UInt32 or
804 // UInt64. The rest of the data follows.
805 MFEM_VERIFY(sizeof(F)*n == ReadHeaderEntry(header_buf),
806 "AppendedData: wrong data size");
807 }
808
809 if (std::is_same_v<T, F>)
810 {
811 // Special case: no type conversions necessary, so can just memcpy
812 memcpy(dest, buf, sizeof(T)*n);
813 }
814 else
815 {
816 for (int i=0; i<n; ++i)
817 {
818 // Read binary data as type F, place in array as type T
819 dest[i] = bin_io::read<F>(buf + i*sizeof(F));
820 }
821 }
822 }
823
824 /// Read @a n elements of type @a F from source buffer @a buf into
825 /// (pre-allocated) array of elements of type @a T stored in destination
826 /// buffer @a dest. The input buffer contains both the header and the data.
827 void ReadBinary(const char *buf, void *dest, int n) const override
828 {
829 ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n);
830 }
831
832 /// Read @a n elements of type @a F from base-64 encoded source buffer into
833 /// (pre-allocated) array of elements of type @a T stored in destination
834 /// buffer @a dest. The base-64-encoded data is given by the null-terminated
835 /// string @a txt, which contains both the header and the data.
836 void ReadBase64(const char *txt, void *dest, int n) const override
837 {
838 // Skip whitespace
839 while (*txt)
840 {
841 if (*txt != ' ' && *txt != '\n') { break; }
842 ++txt;
843 }
844 if (compressed)
845 {
846 // Decode the first entry of the header, which we need to determine
847 // how long the rest of the header is.
848 std::vector<char> nblocks_buf;
849 int nblocks_b64 = static_cast<int>(bin_io::NumBase64Chars(HeaderEntrySize()));
850 bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf);
851 std::vector<char> data, header;
852 // Compute number of characters needed to encode header in base 64,
853 // then round to nearest multiple of 4 to take padding into account.
854 int header_b64 = static_cast<int>(bin_io::NumBase64Chars(NumHeaderBytes(
855 nblocks_buf.data())));
856 // If data is compressed, header is encoded separately
857 bin_io::DecodeBase64(txt, header_b64, header);
858 bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data);
859 ReadBinaryWithHeader(header.data(), data.data(), dest, n);
860 }
861 else
862 {
863 std::vector<char> data;
864 bin_io::DecodeBase64(txt, strlen(txt), data);
865 ReadBinary(data.data(), dest, n);
866 }
867 }
868};
869
870/// Class to read data from VTK's @a DataArary elements. Each @a DataArray can
871/// contain inline ASCII data, inline base-64-encoded data (potentially
872/// compressed), or reference "appended data", which may be raw or base-64, and
873/// may be compressed or uncompressed.
874struct XMLDataReader
875{
876 const char *appended_data, *byte_order, *compressor;
877 enum AppendedDataEncoding { RAW, BASE64 };
878 map<string,BufferReaderBase*> type_map;
879 AppendedDataEncoding encoding;
880
881 /// Create the data reader, where @a vtk is the @a VTKFile XML element, and
882 /// @a vtu is the child @a UnstructuredGrid XML element. This will determine
883 /// the header type (32 or 64 bit integers) and whether compression is
884 /// enabled or not. The appended data will be loaded.
885 XMLDataReader(const XMLElement *vtk, const XMLElement *vtu)
886 {
887 // Determine whether binary data header is 32 or 64 bit integer
888 BufferReaderBase::HeaderType htype;
889 if (StringCompare(vtk->Attribute("header_type"), "UInt64"))
890 {
891 htype = BufferReaderBase::UINT64_HEADER;
892 }
893 else
894 {
895 htype = BufferReaderBase::UINT32_HEADER;
896 }
897
898 // Get the byte order of the file (will check if we encounter binary data)
899 byte_order = vtk->Attribute("byte_order");
900
901 // Get the compressor. We will check that MFEM can handle the compression
902 // if we encounter binary data.
903 compressor = vtk->Attribute("compressor");
904 bool compressed = (compressor != NULL);
905
906 // Find the appended data.
907 appended_data = NULL;
908 for (const XMLElement *xml_elem = vtu->NextSiblingElement();
909 xml_elem != NULL;
910 xml_elem = xml_elem->NextSiblingElement())
911 {
912 if (StringCompare(xml_elem->Name(), "AppendedData"))
913 {
914 const char *encoding_str = xml_elem->Attribute("encoding");
915 if (StringCompare(encoding_str, "raw"))
916 {
917 appended_data = xml_elem->GetAppendedData();
918 encoding = RAW;
919 }
920 else if (StringCompare(encoding_str, "base64"))
921 {
922 appended_data = xml_elem->GetText();
923 encoding = BASE64;
924 }
925 MFEM_VERIFY(appended_data != NULL, "Invalid AppendedData");
926 // Appended data follows first underscore
927 bool found_leading_underscore = false;
928 while (*appended_data)
929 {
930 ++appended_data;
931 if (*appended_data == '_')
932 {
933 found_leading_underscore = true;
934 ++appended_data;
935 break;
936 }
937 }
938 MFEM_VERIFY(found_leading_underscore, "Invalid AppendedData");
939 break;
940 }
941 }
942
943 type_map["Int8"] = new BufferReader<int, int8_t>(compressed, htype);
944 type_map["Int16"] = new BufferReader<int, int16_t>(compressed, htype);
945 type_map["Int32"] = new BufferReader<int, int32_t>(compressed, htype);
946 type_map["Int64"] = new BufferReader<int, int64_t>(compressed, htype);
947 type_map["UInt8"] = new BufferReader<int, uint8_t>(compressed, htype);
948 type_map["UInt16"] = new BufferReader<int, uint16_t>(compressed, htype);
949 type_map["UInt32"] = new BufferReader<int, uint32_t>(compressed, htype);
950 type_map["UInt64"] = new BufferReader<int, uint64_t>(compressed, htype);
951 type_map["Float32"] = new BufferReader<double, float>(compressed, htype);
952 type_map["Float64"] = new BufferReader<double, double>(compressed, htype);
953 }
954
955 /// Read the @a DataArray XML element given by @a xml_elem into
956 /// (pre-allocated) destination array @a dest, where @a dest stores @a n
957 /// elements of type @a T.
958 template <typename T>
959 void Read(const XMLElement *xml_elem, T *dest, int n)
960 {
961 static const char *erstr = "Error reading XML DataArray";
962 MFEM_VERIFY(StringCompare(xml_elem->Name(), "DataArray"), erstr);
963 const char *format = xml_elem->Attribute("format");
964 if (StringCompare(format, "ascii"))
965 {
966 const char *txt = xml_elem->GetText();
967 MFEM_VERIFY(txt != NULL, erstr);
968 std::istringstream data_stream(txt);
969 for (int i=0; i<n; ++i) { data_stream >> dest[i]; }
970 }
971 else if (StringCompare(format, "appended"))
972 {
973 VerifyBinaryOptions();
974 int offset = xml_elem->IntAttribute("offset");
975 const char *type = xml_elem->Attribute("type");
976 MFEM_VERIFY(type != NULL, erstr);
977 BufferReaderBase *reader = type_map[type];
978 MFEM_VERIFY(reader != NULL, erstr);
979 MFEM_VERIFY(appended_data != NULL, "No AppendedData found");
980 if (encoding == RAW)
981 {
982 reader->ReadBinary(appended_data + offset, dest, n);
983 }
984 else
985 {
986 reader->ReadBase64(appended_data + offset, dest, n);
987 }
988 }
989 else if (StringCompare(format, "binary"))
990 {
991 VerifyBinaryOptions();
992 const char *txt = xml_elem->GetText();
993 MFEM_VERIFY(txt != NULL, erstr);
994 const char *type = xml_elem->Attribute("type");
995 if (type == NULL) { MFEM_ABORT(erstr); }
996 BufferReaderBase *reader = type_map[type];
997 if (reader == NULL) { MFEM_ABORT(erstr); }
998 reader->ReadBase64(txt, dest, n);
999 }
1000 else
1001 {
1002 MFEM_ABORT("Invalid XML VTK DataArray format");
1003 }
1004 }
1005
1006 /// Check that the byte order of the file is the same as the native byte
1007 /// order that we're running with. We don't currently support converting
1008 /// between byte orders. The byte order is only verified if we encounter
1009 /// binary data.
1010 void VerifyByteOrder() const
1011 {
1012 // Can't handle reading big endian from little endian or vice versa
1013 if (byte_order && !StringCompare(byte_order, VTKByteOrder()))
1014 {
1015 MFEM_ABORT("Converting between different byte orders is unsupported.");
1016 }
1017 }
1018
1019 /// Check that the compressor is compatible (MFEM currently only supports
1020 /// zlib compression). If MFEM is not compiled with zlib, then we cannot
1021 /// read binary data with compression.
1022 void VerifyCompressor() const
1023 {
1024 if (compressor && !StringCompare(compressor, "vtkZLibDataCompressor"))
1025 {
1026 MFEM_ABORT("Unsupported compressor. Only zlib is supported.")
1027 }
1028#ifndef MFEM_USE_ZLIB
1029 MFEM_VERIFY(compressor == NULL, "MFEM must be compiled with zlib enabled "
1030 "to support reading compressed data.");
1031#endif
1032 }
1033
1034 /// Verify that the binary data is stored with compatible options (i.e.
1035 /// native byte order and compatible compression).
1036 void VerifyBinaryOptions() const
1037 {
1038 VerifyByteOrder();
1039 VerifyCompressor();
1040 }
1041
1042 ~XMLDataReader()
1043 {
1044 for (auto &x : type_map) { delete x.second; }
1045 }
1046};
1047
1048} // namespace vtk_xml
1049
1050void Mesh::ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf,
1051 bool &finalize_topo, const std::string &xml_prefix)
1052{
1053 using namespace vtk_xml;
1054
1055 static const char *erstr = "XML parsing error";
1056
1057 // Create buffer beginning with xml_prefix, then read the rest of the stream
1058 std::vector<char> buf(xml_prefix.begin(), xml_prefix.end());
1059 std::istreambuf_iterator<char> eos;
1060 buf.insert(buf.end(), std::istreambuf_iterator<char>(input), eos);
1061 buf.push_back('\0'); // null-terminate buffer
1062
1063 XMLDocument xml;
1064 xml.Parse(buf.data(), buf.size());
1065 if (xml.ErrorID() != XML_SUCCESS)
1066 {
1067 MFEM_ABORT("Error parsing XML VTK file.\n" << xml.ErrorStr());
1068 }
1069
1070 const XMLElement *vtkfile = xml.FirstChildElement();
1071 MFEM_VERIFY(vtkfile, erstr);
1072 MFEM_VERIFY(StringCompare(vtkfile->Name(), "VTKFile"), erstr);
1073 const XMLElement *vtu = vtkfile->FirstChildElement();
1074 MFEM_VERIFY(vtu, erstr);
1075 MFEM_VERIFY(StringCompare(vtu->Name(), "UnstructuredGrid"), erstr);
1076
1077 XMLDataReader data_reader(vtkfile, vtu);
1078
1079 // Count the number of points and cells
1080 const XMLElement *piece = vtu->FirstChildElement();
1081 MFEM_VERIFY(StringCompare(piece->Name(), "Piece"), erstr);
1082 MFEM_VERIFY(piece->NextSiblingElement() == NULL,
1083 "XML VTK meshes with more than one Piece are not supported");
1084 int npts = piece->IntAttribute("NumberOfPoints");
1085 int ncells = piece->IntAttribute("NumberOfCells");
1086
1087 // Read the points
1088 Vector points(3*npts);
1089 const XMLElement *pts_xml;
1090 for (pts_xml = piece->FirstChildElement();
1091 pts_xml != NULL;
1092 pts_xml = pts_xml->NextSiblingElement())
1093 {
1094 if (StringCompare(pts_xml->Name(), "Points"))
1095 {
1096 const XMLElement *pts_data = pts_xml->FirstChildElement();
1097 MFEM_VERIFY(pts_data->IntAttribute("NumberOfComponents") == 3,
1098 "XML VTK Points DataArray must have 3 components");
1099 data_reader.Read(pts_data, points.GetData(), points.Size());
1100 break;
1101 }
1102 }
1103 if (pts_xml == NULL) { MFEM_ABORT(erstr); }
1104
1105 // Read the cells
1106 Array<int> cell_data, cell_offsets(ncells), cell_types(ncells);
1107 const XMLElement *cells_xml;
1108 for (cells_xml = piece->FirstChildElement();
1109 cells_xml != NULL;
1110 cells_xml = cells_xml->NextSiblingElement())
1111 {
1112 if (StringCompare(cells_xml->Name(), "Cells"))
1113 {
1114 const XMLElement *cell_data_xml = NULL;
1115 for (const XMLElement *data_xml = cells_xml->FirstChildElement();
1116 data_xml != NULL;
1117 data_xml = data_xml->NextSiblingElement())
1118 {
1119 const char *data_name = data_xml->Attribute("Name");
1120 if (StringCompare(data_name, "offsets"))
1121 {
1122 data_reader.Read(data_xml, cell_offsets.GetData(), ncells);
1123 }
1124 else if (StringCompare(data_name, "types"))
1125 {
1126 data_reader.Read(data_xml, cell_types.GetData(), ncells);
1127 }
1128 else if (StringCompare(data_name, "connectivity"))
1129 {
1130 // Have to read the connectivity after the offsets, because we
1131 // don't know how many points to read until we have the offsets
1132 // (size of connectivity array is equal to the last offset), so
1133 // store the XML element pointer and read this data later.
1134 cell_data_xml = data_xml;
1135 }
1136 }
1137 MFEM_VERIFY(cell_data_xml != NULL, erstr);
1138 int cell_data_size = cell_offsets.Last();
1139 cell_data.SetSize(cell_data_size);
1140 data_reader.Read(cell_data_xml, cell_data.GetData(), cell_data_size);
1141 break;
1142 }
1143 }
1144 if (cells_xml == NULL) { MFEM_ABORT(erstr); }
1145
1146 // Read the element attributes, which are stored as CellData named either
1147 // "material" or "attribute". We prioritize "material" over "attribute" for
1148 // backwards compatibility.
1149 Array<int> cell_attributes;
1150 bool found_attributes = false;
1151 for (const XMLElement *cell_data_xml = piece->FirstChildElement();
1152 cell_data_xml != NULL;
1153 cell_data_xml = cell_data_xml->NextSiblingElement())
1154 {
1155 const bool is_cell_data =
1156 StringCompare(cell_data_xml->Name(), "CellData");
1157 const bool is_material =
1158 StringCompare(cell_data_xml->Attribute("Scalars"), "material");
1159 const bool is_attribute =
1160 StringCompare(cell_data_xml->Attribute("Scalars"), "attribute");
1161 if (is_cell_data && (is_material || (is_attribute && !found_attributes)))
1162 {
1163 found_attributes = true;
1164 const XMLElement *data_xml = cell_data_xml->FirstChildElement();
1165 if (data_xml != NULL && StringCompare(data_xml->Name(), "DataArray"))
1166 {
1167 cell_attributes.SetSize(ncells);
1168 data_reader.Read(data_xml, cell_attributes.GetData(), ncells);
1169 }
1170 }
1171 }
1172
1173 CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1174 curved, read_gf, finalize_topo);
1175}
1176
1177void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
1178 bool &finalize_topo)
1179{
1180 // VTK resources:
1181 // * https://www.vtk.org/doc/nightly/html/vtkCellType_8h_source.html
1182 // * https://www.vtk.org/doc/nightly/html/classvtkCell.html
1183 // * https://lorensen.github.io/VTKExamples/site/VTKFileFormats
1184 // * https://www.kitware.com/products/books/VTKUsersGuide.pdf
1185
1186 string buff;
1187 getline(input, buff); // comment line
1188 getline(input, buff);
1189 filter_dos(buff);
1190 if (buff != "ASCII")
1191 {
1192 MFEM_ABORT("VTK mesh is not in ASCII format!");
1193 return;
1194 }
1195 do
1196 {
1197 getline(input, buff);
1198 filter_dos(buff);
1199 if (!input.good()) { MFEM_ABORT("VTK mesh is not UNSTRUCTURED_GRID!"); }
1200 }
1201 while (buff != "DATASET UNSTRUCTURED_GRID");
1202
1203 // Read the points, skipping optional sections such as the FIELD data from
1204 // VisIt's VTK export (or from Mesh::PrintVTK with field_data==1).
1205 do
1206 {
1207 input >> buff;
1208 if (!input.good())
1209 {
1210 MFEM_ABORT("VTK mesh does not have POINTS data!");
1211 }
1212 }
1213 while (buff != "POINTS");
1214
1215 Vector points;
1216 int np;
1217 input >> np >> ws;
1218 getline(input, buff); // "double"
1219 points.Load(input, 3*np);
1220
1221 //skip metadata
1222 // Looks like:
1223 // METADATA
1224 //INFORMATION 2
1225 //NAME L2_NORM_RANGE LOCATION vtkDataArray
1226 //DATA 2 0 5.19615
1227 //NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray
1228 //DATA 2 0 5.19615
1229 do
1230 {
1231 input >> buff;
1232 if (!input.good())
1233 {
1234 MFEM_ABORT("VTK mesh does not have CELLS data!");
1235 }
1236 }
1237 while (buff != "CELLS");
1238
1239 // Read the cells
1240 Array<int> cell_data, cell_offsets;
1241 if (buff == "CELLS")
1242 {
1243 int ncells, n;
1244 input >> ncells >> n >> ws;
1245 cell_offsets.SetSize(ncells);
1246 cell_data.SetSize(n - ncells);
1247 int offset = 0;
1248 for (int i=0; i<ncells; ++i)
1249 {
1250 int nv;
1251 input >> nv;
1252 cell_offsets[i] = offset + nv;
1253 for (int j=0; j<nv; ++j)
1254 {
1255 input >> cell_data[offset + j];
1256 }
1257 offset += nv;
1258 }
1259 }
1260
1261 // Read the cell types
1262 input >> ws >> buff;
1263 Array<int> cell_types;
1264 int ncells;
1265 MFEM_VERIFY(buff == "CELL_TYPES", "CELL_TYPES not provided in VTK mesh.")
1266 input >> ncells;
1267 cell_types.Load(ncells, input);
1268
1269 while ((input.good()) && (buff != "CELL_DATA"))
1270 {
1271 input >> buff;
1272 }
1273 getline(input, buff); // finish the line
1274
1275 // Read the cell materials
1276 // bool found_material = false;
1277 Array<int> cell_attributes;
1278 bool found_attributes = false;
1279 while ((input.good()))
1280 {
1281 getline(input, buff);
1282 if (buff.rfind("POINT_DATA") == 0)
1283 {
1284 break; // We have entered the POINT_DATA block. Quit.
1285 }
1286 else if (buff.rfind("SCALARS material") == 0 ||
1287 (buff.rfind("SCALARS attribute") == 0 && !found_attributes))
1288 {
1289 found_attributes = true;
1290 getline(input, buff); // LOOKUP_TABLE default
1291 if (buff.rfind("LOOKUP_TABLE default") != 0)
1292 {
1293 MFEM_ABORT("Invalid LOOKUP_TABLE for material array in VTK file.");
1294 }
1295 cell_attributes.Load(ncells, input);
1296 // found_material = true;
1297 break;
1298 }
1299 }
1300
1301 // if (!found_material)
1302 // {
1303 // MFEM_WARNING("Material array not found in VTK file. "
1304 // "Assuming uniform material composition.");
1305 // }
1306
1307 CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
1308 curved, read_gf, finalize_topo);
1309} // end ReadVTKMesh
1310
1311void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
1312 bool spacing, bool nc)
1313{
1314 NURBSext = nc ? new NCNURBSExtension(input, spacing):
1315 new NURBSExtension(input, spacing);
1316
1317 Dim = NURBSext->Dimension();
1321
1324
1325 vertices.SetSize(NumOfVertices);
1326 curved = 1;
1327 if (NURBSext->HavePatches())
1328 {
1330 const int vdim = NURBSext->GetPatchSpaceDimension();
1331 FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
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
1515#ifdef MFEM_USE_NETCDF
1516
1517namespace cubit
1518{
1519
1521{
1522 // 1,2,3,4,5,6,7,8,9,10
1523 1,2,3,4,5,7,8,6,9,10
1524};
1525
1527{
1528 // 1,2,3,4,5,6,7,8,9,10,11,
1529 1,2,3,4,5,6,7,8,9,10,11,
1530
1531 // 12,13,14,15,16,17,18,19
1532 12,17,18,19,20,13,14,15,
1533
1534 // 20,21,22,23,24,25,26,27
1535 16,22,26,25,27,24,23,21
1536};
1537
1539{
1540 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
1541};
1542
1544{
1545 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 10, 11, 12, 16, 17, 18
1546};
1547
1549{
1550 1,2,3,4,5,6
1551};
1552
1554{
1555 1,2,3,4,5,6,7,8,9
1556};
1557
1558const int cubit_side_map_tri3[3][2] =
1559{
1560 {1,2}, // 1
1561 {2,3}, // 2
1562 {3,1}, // 3
1563};
1564
1565const int cubit_side_map_quad4[4][2] =
1566{
1567 {1,2}, // 1
1568 {2,3}, // 2
1569 {3,4}, // 3
1570 {4,1}, // 4
1571};
1572
1573const int cubit_side_map_tet4[4][3] =
1574{
1575 {1,2,4}, // 1
1576 {2,3,4}, // 2
1577 {1,4,3}, // 3
1578 {1,3,2} // 4
1579};
1580
1581const int cubit_side_map_hex8[6][4] =
1582{
1583 {1,2,6,5}, // 1 <-- Exodus II side_ids
1584 {2,3,7,6}, // 2
1585 {3,4,8,7}, // 3
1586 {1,5,8,4}, // 4
1587 {1,4,3,2}, // 5
1588 {5,6,7,8} // 6
1589};
1590
1591const int cubit_side_map_wedge6[5][4] =
1592{
1593 {1,2,5,4}, // 1 (Quad4)
1594 {2,3,6,5}, // 2
1595 {3,1,4,6}, // 3
1596 {1,3,2,0}, // 4 (Tri3; NB: 0 is placeholder!)
1597 {4,5,6,0} // 5
1598};
1599
1601{
1602 {1, 2, 5, 0}, // 1 (Tri3)
1603 {2, 3, 5, 0}, // 2
1604 {3, 4, 5, 0}, // 3
1605 {1, 5, 4, 0}, // 4
1606 {1, 4, 3, 2} // 5 (Quad4)
1607};
1608
1618
1634
1635
1636/**
1637 * CubitElement
1638 *
1639 * Stores information about a particular element.
1640 */
1641class CubitElement
1642{
1643public:
1644 /// Default constructor.
1645 CubitElement(CubitElementType element_type);
1646 CubitElement() = delete;
1647
1648 /// Destructor.
1649 ~CubitElement() = default;
1650
1651 /// Returns the Cubit element type.
1652 inline CubitElementType GetElementType() const { return _element_type; }
1653
1654 /// Returns the face type for a specified face. NB: sides have 1-based indexing.
1655 CubitFaceType GetFaceType(size_t side_id = 1) const;
1656
1657 /// Returns the number of faces.
1658 inline size_t GetNumFaces() const { return _num_faces; }
1659
1660 /// Returns the number of vertices.
1661 inline size_t GetNumVertices() const { return _num_vertices; }
1662
1663 /// Returns the number of nodes (vertices + higher-order control points).
1664 inline size_t GetNumNodes() const { return _num_nodes; }
1665
1666 /// Returns the number of vertices for a particular face.
1667 size_t GetNumFaceVertices(size_t iface = 1) const;
1668
1669 /// Returns the order of the element.
1670 inline uint8_t GetOrder() const { return _order; }
1671
1672 /// Creates an MFEM equivalent element using the supplied vertex IDs and block ID.
1673 Element * BuildElement(Mesh & mesh, const int * vertex_ids,
1674 const int block_id) const;
1675
1676 /// Creates an MFEM boundary element using the supplied vertex IDs and block ID.
1677 Element * BuildBoundaryElement(Mesh & mesh, const int iface,
1678 const int * vertex_ids, const int sideset_id) const;
1679
1680 /// Static method returning the element type for a given number of nodes per element and dimension.
1681 static CubitElementType GetElementType(size_t num_nodes,
1682 uint8_t dimension = 3);
1683protected:
1684 /// Static method which returns the 2D Cubit element type for the number of nodes per element.
1685 static CubitElementType Get2DElementType(size_t num_nodes);
1686
1687 /// Static method which returns the 3D Cubit element type for the number of nodes per element.
1688 static CubitElementType Get3DElementType(size_t num_nodes);
1689
1690 /// Creates a new MFEM element. Used internally in BuildElement and BuildBoundaryElement.
1691 Element * NewElement(Mesh & mesh, Geometry::Type geom, const int *vertices,
1692 const int attribute) const;
1693
1694private:
1695 CubitElementType _element_type;
1696
1697 uint8_t _order;
1698
1699 size_t _num_vertices;
1700 size_t _num_faces;
1701 size_t _num_nodes;
1702};
1703
1704CubitElement::CubitElement(CubitElementType element_type)
1705{
1706 _element_type = element_type;
1707
1708 switch (element_type)
1709 {
1710 case ELEMENT_TRI3: // 2D.
1711 _order = 1;
1712 _num_vertices = 3;
1713 _num_nodes = 3;
1714 _num_faces = 3;
1715 break;
1716 case ELEMENT_TRI6:
1717 _order = 2;
1718 _num_vertices = 3;
1719 _num_nodes = 6;
1720 _num_faces = 3;
1721 break;
1722 case ELEMENT_QUAD4:
1723 _order = 1;
1724 _num_vertices = 4;
1725 _num_nodes = 4;
1726 _num_faces = 4;
1727 break;
1728 case ELEMENT_QUAD9:
1729 _order = 2;
1730 _num_vertices = 4;
1731 _num_nodes = 9;
1732 _num_faces = 4;
1733 break;
1734 case ELEMENT_TET4: // 3D.
1735 _order = 1;
1736 _num_vertices = 4;
1737 _num_nodes = 4;
1738 _num_faces = 4;
1739 break;
1740 case ELEMENT_TET10:
1741 _order = 2;
1742 _num_vertices = 4;
1743 _num_nodes = 10;
1744 _num_faces = 4;
1745 break;
1746 case ELEMENT_HEX8:
1747 _order = 1;
1748 _num_vertices = 8;
1749 _num_nodes = 8;
1750 _num_faces = 6;
1751 break;
1752 case ELEMENT_HEX27:
1753 _order = 2;
1754 _num_vertices = 8;
1755 _num_nodes = 27;
1756 _num_faces = 6;
1757 break;
1758 case ELEMENT_WEDGE6:
1759 _order = 1;
1760 _num_vertices = 6;
1761 _num_nodes = 6;
1762 _num_faces = 5;
1763 break;
1764 case ELEMENT_WEDGE18:
1765 _order = 2;
1766 _num_vertices = 6;
1767 _num_nodes = 18;
1768 _num_faces = 5;
1769 break;
1770 case ELEMENT_PYRAMID5:
1771 _order = 1;
1772 _num_vertices = 5;
1773 _num_nodes = 5;
1774 _num_faces = 5;
1775 break;
1776 case ELEMENT_PYRAMID14:
1777 _order = 2;
1778 _num_vertices = 5;
1779 _num_nodes = 14;
1780 _num_faces = 5;
1781 break;
1782 default:
1783 MFEM_ABORT("Unsupported Cubit element type " << element_type << ".");
1784 break;
1785 }
1786}
1787
1788CubitElementType CubitElement::Get3DElementType(size_t num_nodes)
1789{
1790 switch (num_nodes)
1791 {
1792 case 4:
1793 return ELEMENT_TET4;
1794 case 10:
1795 return ELEMENT_TET10;
1796 case 8:
1797 return ELEMENT_HEX8;
1798 case 27:
1799 return ELEMENT_HEX27;
1800 case 6:
1801 return ELEMENT_WEDGE6;
1802 case 18:
1803 return ELEMENT_WEDGE18;
1804 case 5:
1805 return ELEMENT_PYRAMID5;
1806 case 14:
1807 return ELEMENT_PYRAMID14;
1808 default:
1809 MFEM_ABORT("Unsupported 3D element with " << num_nodes << " nodes.");
1810 }
1811}
1812
1813CubitElementType CubitElement::Get2DElementType(size_t num_nodes)
1814{
1815 switch (num_nodes)
1816 {
1817 case 3:
1818 return ELEMENT_TRI3;
1819 case 6:
1820 return ELEMENT_TRI6;
1821 case 4:
1822 return ELEMENT_QUAD4;
1823 case 9:
1824 return ELEMENT_QUAD9;
1825 default:
1826 MFEM_ABORT("Unsupported 2D element with " << num_nodes << " nodes.");
1827 }
1828}
1829
1830CubitElementType CubitElement::GetElementType(size_t num_nodes,
1831 uint8_t dimension)
1832{
1833 if (dimension == 2)
1834 {
1835 return Get2DElementType(num_nodes);
1836 }
1837 else if (dimension == 3)
1838 {
1839 return Get3DElementType(num_nodes);
1840 }
1841 else
1842 {
1843 MFEM_ABORT("Unsupported Cubit dimension " << dimension << ".");
1844 }
1845}
1846
1847CubitFaceType CubitElement::GetFaceType(size_t side_id) const
1848{
1849 // NB: 1-based indexing. See Exodus II file format specifications.
1850 bool valid_id = (side_id >= 1 &&
1851 side_id <= GetNumFaces());
1852 if (!valid_id)
1853 {
1854 MFEM_ABORT("Encountered invalid side ID: " << side_id << ".");
1855 }
1856
1857 switch (_element_type)
1858 {
1859 case ELEMENT_TRI3: // 2D.
1860 return FACE_EDGE2;
1861 case ELEMENT_TRI6:
1862 return FACE_EDGE3;
1863 case ELEMENT_QUAD4:
1864 return FACE_EDGE2;
1865 case ELEMENT_QUAD9:
1866 return FACE_EDGE3;
1867 case ELEMENT_TET4: // 3D.
1868 return FACE_TRI3;
1869 case ELEMENT_TET10:
1870 return FACE_TRI6;
1871 case ELEMENT_HEX8:
1872 return FACE_QUAD4;
1873 case ELEMENT_HEX27:
1874 return FACE_QUAD9;
1875 case ELEMENT_WEDGE6: // [Quad4, Quad4, Quad4, Tri3, Tri3]
1876 return (side_id < 4 ? FACE_QUAD4 : FACE_TRI3);
1877 case ELEMENT_WEDGE18: // [Quad9, Quad9, Quad9, Tri6, Tri6]
1878 return (side_id < 4 ? FACE_QUAD9 : FACE_TRI6);
1879 case ELEMENT_PYRAMID5: // [Tri3, Tri3, Tri3, Tri3, Quad4]
1880 return (side_id < 5 ? FACE_TRI3 : FACE_QUAD4);
1881 case ELEMENT_PYRAMID14: // [Tri6, Tri6, Tri6, Tri6, Quad9]
1882 return (side_id < 5 ? FACE_TRI6 : FACE_QUAD9);
1883 default:
1884 MFEM_ABORT("Unknown element type: " << _element_type << ".");
1885 }
1886}
1887
1888
1889size_t CubitElement::GetNumFaceVertices(size_t side_id) const
1890{
1891 switch (GetFaceType(side_id))
1892 {
1893 case FACE_EDGE2:
1894 case FACE_EDGE3:
1895 return 2;
1896 case FACE_TRI3:
1897 case FACE_TRI6:
1898 return 3;
1899 case FACE_QUAD4:
1900 case FACE_QUAD9:
1901 return 4;
1902 default:
1903 MFEM_ABORT("Unrecognized Cubit face type " << GetFaceType(side_id) << ".");
1904 }
1905}
1906
1907
1908mfem::Element * CubitElement::NewElement(Mesh &mesh, Geometry::Type geom,
1909 const int *vertices,
1910 const int attribute) const
1911{
1912 Element *new_element = mesh.NewElement(geom);
1913 new_element->SetVertices(vertices);
1914 new_element->SetAttribute(attribute);
1915 return new_element;
1916}
1917
1918
1919mfem::Element * CubitElement::BuildElement(Mesh &mesh,
1920 const int *vertex_ids,
1921 const int block_id) const
1922{
1923 switch (GetElementType())
1924 {
1925 case ELEMENT_TRI3:
1926 case ELEMENT_TRI6:
1927 return NewElement(mesh, Geometry::TRIANGLE, vertex_ids, block_id);
1928 case ELEMENT_QUAD4:
1929 case ELEMENT_QUAD9:
1930 return NewElement(mesh, Geometry::SQUARE, vertex_ids, block_id);
1931 case ELEMENT_TET4:
1932 case ELEMENT_TET10:
1933 return NewElement(mesh, Geometry::TETRAHEDRON, vertex_ids, block_id);
1934 case ELEMENT_HEX8:
1935 case ELEMENT_HEX27:
1936 return NewElement(mesh, Geometry::CUBE, vertex_ids, block_id);
1937 case ELEMENT_WEDGE6:
1938 case ELEMENT_WEDGE18:
1939 return NewElement(mesh, Geometry::PRISM, vertex_ids, block_id);
1940 case ELEMENT_PYRAMID5:
1941 case ELEMENT_PYRAMID14:
1942 return NewElement(mesh, Geometry::PYRAMID, vertex_ids, block_id);
1943 default:
1944 MFEM_ABORT("Unsupported Cubit element type encountered.");
1945 }
1946}
1947
1948
1949mfem::Element * CubitElement::BuildBoundaryElement(Mesh &mesh,
1950 const int face_id,
1951 const int *vertex_ids,
1952 const int sideset_id) const
1953{
1954 switch (GetFaceType(face_id))
1955 {
1956 case FACE_EDGE2:
1957 case FACE_EDGE3:
1958 return NewElement(mesh, Geometry::SEGMENT, vertex_ids, sideset_id);
1959 case FACE_TRI3:
1960 case FACE_TRI6:
1961 return NewElement(mesh, Geometry::TRIANGLE, vertex_ids, sideset_id);
1962 case FACE_QUAD4:
1963 case FACE_QUAD9:
1964 return NewElement(mesh, Geometry::SQUARE, vertex_ids, sideset_id);
1965 default:
1966 MFEM_ABORT("Unsupported Cubit face type encountered.");
1967 }
1968}
1969
1970/**
1971 * CubitBlock
1972 *
1973 * Stores the information about each block in a mesh. Each block can contain a different
1974 * element type (although all element types must be of the same order and dimension).
1975 */
1976class CubitBlock
1977{
1978public:
1979 CubitBlock() = delete;
1980 ~CubitBlock() = default;
1981
1982 /**
1983 * Default initializer.
1984 */
1985 CubitBlock(int dimension);
1986
1987 /**
1988 * Returns a constant reference to the element info for a particular block.
1989 */
1990 const CubitElement & GetBlockElement(int block_id) const;
1991
1992 /**
1993 * Call to add each block individually.
1994 */
1995 void AddBlockElement(int block_id, CubitElementType element_type);
1996
1997 /**
1998 * Accessors.
1999 */
2000 uint8_t GetOrder() const;
2001 inline uint8_t GetDimension() const { return _dimension; }
2002
2003 inline size_t GetNumBlocks() const { return BlockIDs().size(); }
2004 inline bool HasBlocks() const { return !BlockIDs().empty(); }
2005
2006protected:
2007 /**
2008 * Checks that the order of a new block element matches the order of existing blocks. Called
2009 * internally in method "addBlockElement".
2010 */
2011 void CheckElementBlockIsCompatible(const CubitElement & new_block_element)
2012 const;
2013
2014 /**
2015 * Reset all block elements. Called internally in initializer.
2016 */
2017 void ClearBlockElements();
2018
2019 /**
2020 * Helper methods.
2021 */
2022 inline const std::set<int> & BlockIDs() const { return _block_ids; }
2023
2024 bool HasBlockID(int block_id) const;
2025 bool ValidBlockID(int block_id) const;
2026 bool ValidDimension(int dimension) const;
2027
2028private:
2029 /**
2030 * Stores all block IDs.
2031 */
2032 std::set<int> _block_ids;
2033
2034 /**
2035 * Maps from block ID to element.
2036 */
2037 std::map<int, CubitElement> _block_element_for_block_id;
2038
2039 /**
2040 * Dimension and order of block elements.
2041 */
2042 uint8_t _dimension;
2043 uint8_t _order;
2044};
2045
2046CubitBlock::CubitBlock(int dimension)
2047{
2048 if (!ValidDimension(dimension))
2049 {
2050 MFEM_ABORT("Invalid dimension '" << dimension << "' specified.");
2051 }
2052
2053 _dimension = dimension;
2054
2055 ClearBlockElements();
2056}
2057
2058void
2059CubitBlock::AddBlockElement(int block_id, CubitElementType element_type)
2060{
2061 if (HasBlockID(block_id))
2062 {
2063 MFEM_ABORT("Block with ID '" << block_id << "' has already been added.");
2064 }
2065 else if (!ValidBlockID(block_id))
2066 {
2067 MFEM_ABORT("Illegal block ID '" << block_id << "'.");
2068 }
2069
2070 CubitElement block_element = CubitElement(element_type);
2071
2072 /**
2073 * Check element is compatible with existing element blocks.
2074 */
2075 CheckElementBlockIsCompatible(block_element);
2076
2077 if (!HasBlocks()) // Set order of elements.
2078 {
2079 _order = block_element.GetOrder();
2080 }
2081
2082 _block_ids.insert(block_id);
2083 _block_element_for_block_id.emplace(block_id,
2084 block_element);
2085}
2086
2087uint8_t
2088CubitBlock::GetOrder() const
2089{
2090 if (!HasBlocks())
2091 {
2092 MFEM_ABORT("No elements have been added.");
2093 }
2094
2095 return _order;
2096}
2097
2098void
2099CubitBlock::ClearBlockElements()
2100{
2101 _order = 0;
2102 _block_ids.clear();
2103 _block_element_for_block_id.clear();
2104}
2105
2106bool
2107CubitBlock::HasBlockID(int block_id) const
2108{
2109 return (_block_ids.count(block_id) > 0);
2110}
2111
2112bool
2113CubitBlock::ValidBlockID(int block_id) const
2114{
2115 return (block_id > 0); // 1-based indexing.
2116}
2117
2118bool
2119CubitBlock::ValidDimension(int dimension) const
2120{
2121 return (dimension == 2 || dimension == 3);
2122}
2123
2124const CubitElement &
2125CubitBlock::GetBlockElement(int block_id) const
2126{
2127 if (!HasBlockID(block_id))
2128 {
2129 MFEM_ABORT("No element info for block ID '" << block_id << "'.");
2130 }
2131
2132 return _block_element_for_block_id.at(block_id);
2133}
2134
2135void
2136CubitBlock::CheckElementBlockIsCompatible(const CubitElement &
2137 new_block_element) const
2138{
2139 if (!HasBlocks())
2140 {
2141 return;
2142 }
2143
2144 // Enforce block orders to be the same for now.
2145 if (GetOrder() != new_block_element.GetOrder())
2146 {
2147 MFEM_ABORT("All block elements must be of the same order.");
2148 }
2149}
2150
2151/**
2152 * Lightweight wrapper around NetCDF C functions.
2153 */
2154class NetCDFReader
2155{
2156public:
2157 NetCDFReader() = delete;
2158 NetCDFReader(const std::string fname);
2159
2160 ~NetCDFReader();
2161
2162 /// Returns true if variable id for that name exists.
2163 bool HasVariable(const char * name);
2164
2165 /// Read variable info from file and write to int buffer.
2166 void ReadVariable(const char * name, int * data);
2167
2168 /// Read variable info from file and write to double buffer.
2169 void ReadVariable(const char * name, double * data);
2170
2171 /// Returns true if dimension id for that name exists.
2172 bool HasDimension(const char * name);
2173
2174 /// Read dimension info from file.
2175 void ReadDimension(const char * name, size_t *dimension);
2176
2177 /// Build the map from quantity ID to name, e.g. block ID to block name or boundary ID to boundary name
2178 void BuildIDToNameMap(const vector<int> & ids,
2179 unordered_map<int, string> & ids_to_names,
2180 const string & quantity_name);
2181
2182protected:
2183 /// Called internally. Calls HandleNetCDFError if _netcdf_status is not "NC_NOERR".
2184 void CheckForNetCDFError();
2185
2186 /// Called in "ReadVariable" methods to extract variable id.
2187 int ReadVariableID(const char * name);
2188
2189 /// Called in "ReadDimension" to extract dimension id.
2190 int ReadDimensionID(const char * name);
2191
2192private:
2193 /// Calls MFEM_Abort with string description of NetCDF error.
2194 void HandleNetCDFError(const int error_code);
2195
2196 int _netcdf_status{NC_NOERR};
2197 int _netcdf_descriptor;
2198
2199 /// Internal buffer. Used in ReadDimension to write unwanted name to.
2200 char *_name_buffer{NULL};
2201};
2202
2203
2204NetCDFReader::NetCDFReader(const std::string fname)
2205{
2206 _netcdf_status = nc_open(fname.c_str(), NC_NOWRITE, &_netcdf_descriptor);
2207 CheckForNetCDFError();
2208
2209 // NB: add byte for '\0' terminating char.
2210 _name_buffer = new char[NC_MAX_NAME + 1];
2211}
2212
2213NetCDFReader::~NetCDFReader()
2214{
2215 _netcdf_status = nc_close(_netcdf_descriptor);
2216 CheckForNetCDFError();
2217
2218 if (_name_buffer)
2219 {
2220 delete[] _name_buffer;
2221 }
2222}
2223
2224void NetCDFReader::CheckForNetCDFError()
2225{
2226 if (_netcdf_status != NC_NOERR)
2227 {
2228 HandleNetCDFError(_netcdf_status);
2229 }
2230}
2231
2232void NetCDFReader::HandleNetCDFError(const int error_code)
2233{
2234 MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(error_code));
2235}
2236
2237int NetCDFReader::ReadVariableID(const char * var_name)
2238{
2239 int variable_id;
2240
2241 _netcdf_status = nc_inq_varid(_netcdf_descriptor, var_name,
2242 &variable_id);
2243 CheckForNetCDFError();
2244
2245 return variable_id;
2246}
2247
2248int NetCDFReader::ReadDimensionID(const char * name)
2249{
2250 int dim_id;
2251
2252 _netcdf_status = nc_inq_dimid(_netcdf_descriptor, name, &dim_id);
2253 CheckForNetCDFError();
2254
2255 return dim_id;
2256}
2257
2258void NetCDFReader::ReadDimension(const char * name, size_t *dimension)
2259{
2260 const int dimension_id = ReadDimensionID(name);
2261
2262 // NB: ignore name output (write to private buffer).
2263 _netcdf_status = nc_inq_dim(_netcdf_descriptor, dimension_id, _name_buffer,
2264 dimension);
2265 CheckForNetCDFError();
2266}
2267
2268bool NetCDFReader::HasVariable(const char * name)
2269{
2270 int var_id;
2271 const int status = nc_inq_varid(_netcdf_descriptor, name, &var_id);
2272
2273 switch (status)
2274 {
2275 case NC_NOERR: // Found!
2276 return true;
2277 case NC_ENOTVAR: // Not found.
2278 return false;
2279 default:
2280 HandleNetCDFError(status);
2281 return false;
2282 }
2283}
2284
2285bool NetCDFReader::HasDimension(const char * name)
2286{
2287 int dim_id;
2288 const int status = nc_inq_dimid(_netcdf_descriptor, name, &dim_id);
2289
2290 switch (status)
2291 {
2292 case NC_NOERR: // Found!
2293 return true;
2294 case NC_EBADDIM: // Not found.
2295 return false;
2296 default:
2297 HandleNetCDFError(status);
2298 return false;
2299 }
2300}
2301
2302void NetCDFReader::ReadVariable(const char * name, int * data)
2303{
2304 const int variable_id = ReadVariableID(name);
2305
2306 _netcdf_status = nc_get_var_int(_netcdf_descriptor, variable_id, data);
2307 CheckForNetCDFError();
2308}
2309
2310
2311void NetCDFReader::ReadVariable(const char * name, double * data)
2312{
2313 const int variable_id = ReadVariableID(name);
2314
2315 _netcdf_status = nc_get_var_double(_netcdf_descriptor, variable_id, data);
2316 CheckForNetCDFError();
2317}
2318
2319
2320void NetCDFReader::BuildIDToNameMap(const vector<int> & ids,
2321 unordered_map<int, string> & ids_to_names,
2322 const string & quantity_name)
2323{
2324 int varid_names;
2325
2326 // Find the variable ID for the given quantity_name (e.g. eb_names, ss_names)
2327 _netcdf_status = nc_inq_varid(_netcdf_descriptor, quantity_name.c_str(),
2328 &varid_names);
2329 // It's possible the netcdf file doesn't contain the variable, in which case
2330 // there's nothing to do
2331 if (_netcdf_status == NC_ENOTVAR)
2332 {
2333 return;
2334 }
2335 else
2336 {
2337 CheckForNetCDFError();
2338 }
2339
2340 // Get type of quantity_name
2341 nc_type var_type;
2342 _netcdf_status = nc_inq_vartype(_netcdf_descriptor, varid_names,
2343 &var_type);
2344 CheckForNetCDFError();
2345
2346 if (var_type == NC_CHAR)
2347 {
2348 int dimids_names[2], names_ndim;
2349 size_t num_names, name_len;
2350
2351 _netcdf_status = nc_inq_varndims(_netcdf_descriptor, varid_names,
2352 &names_ndim);
2353 CheckForNetCDFError();
2354 MFEM_ASSERT(names_ndim == 2, "This variable should have two dimensions");
2355
2356 _netcdf_status = nc_inq_vardimid(_netcdf_descriptor, varid_names,
2357 dimids_names);
2358 CheckForNetCDFError();
2359
2360 _netcdf_status = nc_inq_dimlen(_netcdf_descriptor, dimids_names[0], &num_names);
2361 CheckForNetCDFError();
2362 MFEM_ASSERT(num_names == ids.size(),
2363 "The block id and block name lengths don't match");
2364 // Check the maximum string length
2365 _netcdf_status = nc_inq_dimlen(_netcdf_descriptor, dimids_names[1], &name_len);
2366 CheckForNetCDFError();
2367
2368 // Read the block names
2369 vector<char> names(ids.size() * name_len);
2370 _netcdf_status = nc_get_var_text(_netcdf_descriptor, varid_names,
2371 names.data());
2372 CheckForNetCDFError();
2373
2374 for (size_t i = 0; i < ids.size(); ++i)
2375 {
2376 string name(&names[i * name_len], name_len);
2377 // shorten string
2378 name.resize(name.find('\0'));
2379 ids_to_names[ids[i]] = name;
2380 }
2381 }
2382 else
2383 {
2384 mfem_error("Unexpected netcdf variable type");
2385 }
2386}
2387
2388
2389/// @brief Reads the coordinate data from the Genesis file.
2390static void ReadCubitNodeCoordinates(NetCDFReader & cubit_reader,
2391 double *coordx,
2392 double *coordy,
2393 double *coordz)
2394{
2395 cubit_reader.ReadVariable("coordx", coordx);
2396 cubit_reader.ReadVariable("coordy", coordy);
2397
2398 if (coordz)
2399 {
2400 cubit_reader.ReadVariable("coordz", coordz);
2401 }
2402}
2403
2404
2405/// @brief Reads the number of elements in each block.
2406static void ReadCubitNumElementsInBlock(NetCDFReader & cubit_reader,
2407 const vector<int> & block_ids,
2408 map<int, size_t> &num_elements_for_block_id)
2409{
2410 num_elements_for_block_id.clear();
2411
2412 // NB: need to add 1 for '\0' terminating character.
2413 const int buffer_size = NC_MAX_NAME + 1;
2414 char string_buffer[buffer_size];
2415
2416 int iblock = 1;
2417 for (const auto block_id : block_ids)
2418 {
2419 // Write variable name to buffer.
2420 snprintf(string_buffer, buffer_size, "num_el_in_blk%d", iblock++);
2421
2422 size_t num_elements_for_block = 0;
2423 cubit_reader.ReadDimension(string_buffer, &num_elements_for_block);
2424
2425 num_elements_for_block_id[block_id] = num_elements_for_block;
2426 }
2427}
2428
2429/// @brief Builds the mappings:
2430/// (blockID --> (elements in block)); (elementID --> blockID)
2431static void BuildElementIDsForBlockID(
2432 const vector<int> & block_ids,
2433 const map<int, size_t> & num_elements_for_block_id,
2434 map<int, vector<int>> & element_ids_for_block_id,
2435 map<int, int> & block_id_for_element_id)
2436{
2437 element_ids_for_block_id.clear();
2438 block_id_for_element_id.clear();
2439
2440 // From the Exodus II specifications, the element ID is numbered contiguously starting
2441 // from 1 across the element blocks.
2442 int element_id = 1;
2443 for (int block_id : block_ids)
2444 {
2445 const int num_elements_for_block = num_elements_for_block_id.at(block_id);
2446
2447 vector<int> element_ids(num_elements_for_block);
2448
2449 for (int i = 0; i < num_elements_for_block; i++, element_id++)
2450 {
2451 element_ids[i] = element_id;
2452 block_id_for_element_id[element_id] = block_id;
2453 }
2454
2455 element_ids_for_block_id[block_id] = std::move(element_ids);
2456 }
2457}
2458
2459/// @brief Reads the element types for each block.
2460static void ReadCubitBlocks(NetCDFReader & cubit_reader,
2461 const vector<int> block_ids,
2462 CubitBlock & cubit_blocks)
2463{
2464 const int buffer_size = NC_MAX_NAME + 1;
2465 char string_buffer[buffer_size];
2466
2467 size_t num_nodes_per_element;
2468
2469 int iblock = 1;
2470 for (int block_id : block_ids)
2471 {
2472 // Write variable name to buffer.
2473 snprintf(string_buffer, buffer_size, "num_nod_per_el%d", iblock++);
2474
2475 cubit_reader.ReadDimension(string_buffer, &num_nodes_per_element);
2476
2477 // Determine the element type:
2478 CubitElementType element_type = CubitElement::GetElementType(
2479 num_nodes_per_element, cubit_blocks.GetDimension());
2480 cubit_blocks.AddBlockElement(block_id, element_type);
2481 }
2482}
2483
2484
2485/// @brief Extracts core dimension information from Genesis file.
2486static void ReadCubitDimensions(NetCDFReader & cubit_reader,
2487 size_t &num_dim,
2488 size_t &num_nodes,
2489 size_t &num_elem,
2490 size_t &num_el_blk,
2491 size_t &num_side_sets)
2492{
2493 cubit_reader.ReadDimension("num_dim", &num_dim);
2494 cubit_reader.ReadDimension("num_nodes", &num_nodes);
2495 cubit_reader.ReadDimension("num_elem", &num_elem);
2496 cubit_reader.ReadDimension("num_el_blk", &num_el_blk);
2497
2498 // Optional: if not present, num_side_sets = 0.
2499 if (cubit_reader.HasDimension("num_side_sets"))
2500 {
2501 cubit_reader.ReadDimension("num_side_sets", &num_side_sets);
2502 }
2503 else
2504 {
2505 num_side_sets = 0;
2506 }
2507}
2508
2509/// @brief Extracts the element ids corresponding to elements that lie on each boundary;
2510/// also extracts the side ids of those elements which lie on the boundary.
2511static void ReadCubitBoundaries(NetCDFReader & cubit_reader,
2512 const vector<int> & boundary_ids,
2513 map<int, vector<int>> & element_ids_for_boundary_id,
2514 map<int, vector<int>> & side_ids_for_boundary_id)
2515{
2516 const int buffer_size = NC_MAX_NAME + 1;
2517 char string_buffer[buffer_size];
2518
2519 int ibdr = 1;
2520 for (int boundary_id : boundary_ids)
2521 {
2522 // 1. Extract number of elements/sides for boundary.
2523 size_t num_sides = 0;
2524
2525 snprintf(string_buffer, buffer_size, "num_side_ss%d", ibdr);
2526 cubit_reader.ReadDimension(string_buffer, &num_sides);
2527
2528 // 2. Extract elements and sides on each boundary (1-indexed!)
2529 vector<int> boundary_element_ids(num_sides); // (element, face) pairs.
2530 vector<int> boundary_side_ids(num_sides);
2531
2532 //
2533 snprintf(string_buffer, buffer_size, "elem_ss%d", ibdr);
2534 cubit_reader.ReadVariable(string_buffer, boundary_element_ids.data());
2535
2536 //
2537 snprintf(string_buffer, buffer_size,"side_ss%d", ibdr++);
2538 cubit_reader.ReadVariable(string_buffer, boundary_side_ids.data());
2539
2540 // 3. Add to maps.
2541 element_ids_for_boundary_id[boundary_id] = std::move(boundary_element_ids);
2542 side_ids_for_boundary_id[boundary_id] = std::move(boundary_side_ids);
2543 }
2544}
2545
2546/// @brief Reads the block ids from the Genesis file.
2547static void BuildCubitBlockIDs(NetCDFReader & cubit_reader,
2548 const int num_element_blocks,
2549 vector<int> & block_ids)
2550{
2551 block_ids.resize(num_element_blocks);
2552 cubit_reader.ReadVariable("eb_prop1", block_ids.data());
2553}
2554
2555/// @brief Reads the boundary ids from the Genesis file.
2556static void ReadCubitBoundaryIDs(NetCDFReader & cubit_reader,
2557 const int num_boundaries, vector<int> & boundary_ids)
2558{
2559 boundary_ids.clear();
2560
2561 if (num_boundaries < 1) { return; }
2562
2563 boundary_ids.resize(num_boundaries);
2564 cubit_reader.ReadVariable("ss_prop1", boundary_ids.data());
2565}
2566
2567/// @brief Reads the node ids for each element from the Genesis file.
2568static void ReadCubitElementBlocks(NetCDFReader & cubit_reader,
2569 const CubitBlock & cubit_blocks,
2570 const vector<int> & block_ids,
2571 const map<int, vector<int>> & element_ids_for_block_id,
2572 map<int, vector<int>> &node_ids_for_element_id)
2573{
2574 const int buffer_size = NC_MAX_NAME + 1;
2575 char string_buffer[buffer_size];
2576
2577 int iblock = 1;
2578 for (const int block_id : block_ids)
2579 {
2580 const CubitElement & block_element = cubit_blocks.GetBlockElement(block_id);
2581
2582 const vector<int> & block_element_ids = element_ids_for_block_id.at(block_id);
2583
2584 const size_t num_nodes_for_block = block_element_ids.size() *
2585 block_element.GetNumNodes();
2586
2587 vector<int> node_ids_for_block(num_nodes_for_block);
2588
2589 // Write variable name to buffer.
2590 snprintf(string_buffer, buffer_size, "connect%d", iblock++);
2591
2592 cubit_reader.ReadVariable(string_buffer, node_ids_for_block.data());
2593
2594 // Now map from the element id to the nodes:
2595 int ielement = 0;
2596 for (int element_id : block_element_ids)
2597 {
2598 vector<int> element_node_ids(block_element.GetNumNodes());
2599
2600 for (int i = 0; i < (int)block_element.GetNumNodes(); i++)
2601 {
2602 element_node_ids[i] = node_ids_for_block[ielement * block_element.GetNumNodes()
2603 + i];
2604 }
2605
2606 ielement++;
2607
2608 node_ids_for_element_id[element_id] = std::move(element_node_ids);
2609 }
2610 }
2611}
2612
2613/// @brief Builds a mapping from the boundary ID to the face vertices of each element that lie on the boundary.
2614static void BuildBoundaryNodeIDs(const vector<int> & boundary_ids,
2615 const CubitBlock & blocks,
2616 const map<int, vector<int>> & node_ids_for_element_id,
2617 const map<int, vector<int>> & element_ids_for_boundary_id,
2618 const map<int, vector<int>> & side_ids_for_boundary_id,
2619 const map<int, int> & block_id_for_element_id,
2620 map<int, vector<vector<int>>> & node_ids_for_boundary_id)
2621{
2622 for (int boundary_id : boundary_ids)
2623 {
2624 // Get element IDs of element on boundary (and their sides that are on boundary).
2625 auto & boundary_element_ids = element_ids_for_boundary_id.at(
2626 boundary_id);
2627 auto & boundary_element_sides = side_ids_for_boundary_id.at(
2628 boundary_id);
2629
2630 // Create vector to store the node ids of all boundary nodes.
2631 vector<vector<int>> boundary_node_ids(
2632 boundary_element_ids.size());
2633
2634 // Iterate over elements on boundary.
2635 for (int jelement = 0; jelement < (int)boundary_element_ids.size(); jelement++)
2636 {
2637 // Get element ID and the boundary side.
2638 const int boundary_element_global_id = boundary_element_ids[jelement];
2639 const int boundary_side = boundary_element_sides[jelement];
2640
2641 // Get the element information:
2642 const int block_id = block_id_for_element_id.at(boundary_element_global_id);
2643 const CubitElement & block_element = blocks.GetBlockElement(block_id);
2644
2645 const int num_face_vertices = block_element.GetNumFaceVertices(boundary_side);
2646 vector<int> nodes_of_element_on_side(num_face_vertices);
2647
2648 // Get all of the element's nodes on boundary side of element.
2649 const vector<int> & element_node_ids =
2650 node_ids_for_element_id.at(boundary_element_global_id);
2651
2652 // Iterate over the element's face nodes on the matching side.
2653 // NB: only adding vertices on face (ignore higher-order).
2654 for (int knode = 0; knode < num_face_vertices; knode++)
2655 {
2656 int inode;
2657
2658 switch (block_element.GetElementType())
2659 {
2660 case ELEMENT_TRI3:
2661 case ELEMENT_TRI6:
2662 inode = cubit_side_map_tri3[boundary_side - 1][knode];
2663 break;
2664 case ELEMENT_QUAD4:
2665 case ELEMENT_QUAD9:
2666 inode = cubit_side_map_quad4[boundary_side - 1][knode];
2667 break;
2668 case ELEMENT_TET4:
2669 case ELEMENT_TET10:
2670 inode = cubit_side_map_tet4[boundary_side - 1][knode];
2671 break;
2672 case ELEMENT_HEX8:
2673 case ELEMENT_HEX27:
2674 inode = cubit_side_map_hex8[boundary_side - 1][knode];
2675 break;
2676 case ELEMENT_WEDGE6:
2677 case ELEMENT_WEDGE18:
2678 inode = cubit_side_map_wedge6[boundary_side - 1][knode];
2679 break;
2680 case ELEMENT_PYRAMID5:
2681 case ELEMENT_PYRAMID14:
2682 inode = cubit_side_map_pyramid5[boundary_side - 1][knode];
2683 break;
2684 default:
2685 MFEM_ABORT("Unsupported element type encountered.\n");
2686 break;
2687 }
2688
2689 nodes_of_element_on_side[knode] = element_node_ids[inode - 1];
2690 }
2691
2692 boundary_node_ids[jelement] = std::move(nodes_of_element_on_side);
2693 }
2694
2695 // Add to the map.
2696 node_ids_for_boundary_id[boundary_id] = std::move(boundary_node_ids);
2697 }
2698}
2699
2700/// @brief Generates a vector of unique vertex ID.
2701static void BuildUniqueVertexIDs(const vector<int> & unique_block_ids,
2702 const CubitBlock & blocks,
2703 const map<int, vector<int>> & element_ids_for_block_id,
2704 const map<int, vector<int>> & node_ids_for_element_id,
2705 vector<int> & unique_vertex_ids)
2706{
2707 // Iterate through all vertices and add their global IDs to the unique_vertex_ids vector.
2708 for (int block_id : unique_block_ids)
2709 {
2710 auto & element_ids = element_ids_for_block_id.at(block_id);
2711
2712 auto & block_element = blocks.GetBlockElement(block_id);
2713
2714 for (int element_id : element_ids)
2715 {
2716 auto & node_ids = node_ids_for_element_id.at(element_id);
2717
2718 for (size_t knode = 0; knode < block_element.GetNumVertices(); knode++)
2719 {
2720 unique_vertex_ids.push_back(node_ids[knode]);
2721 }
2722 }
2723 }
2724
2725 // Sort unique_vertex_ids in ascending order and remove duplicate node IDs.
2726 std::sort(unique_vertex_ids.begin(), unique_vertex_ids.end());
2727
2728 auto new_end = std::unique(unique_vertex_ids.begin(), unique_vertex_ids.end());
2729
2730 unique_vertex_ids.resize(std::distance(unique_vertex_ids.begin(), new_end));
2731}
2732
2733/// @brief unique_vertex_ids contains a 1-based sorted list of vertex IDs used by the mesh. We
2734/// now create a map by running over the vertex IDs and remapping to a contiguous
2735/// 1-based array of integers.
2736static void BuildCubitToMFEMVertexMap(const vector<int> & unique_vertex_ids,
2737 map<int, int> & cubit_to_mfem_vertex_map)
2738{
2739 cubit_to_mfem_vertex_map.clear();
2740
2741 int ivertex = 1;
2742 for (int vertex_id : unique_vertex_ids)
2743 {
2744 cubit_to_mfem_vertex_map[vertex_id] = ivertex++;
2745 }
2746}
2747
2748
2749/// @brief The final step in constructing the mesh from a Genesis file. This is
2750/// only called if the mesh order == 2 (determined internally from the cubit
2751/// element type).
2752static void FinalizeCubitSecondOrderMesh(Mesh &mesh,
2753 const vector<int> & unique_block_ids,
2754 const CubitBlock & blocks,
2755 const map<int, vector<int>> & element_ids_for_block_id,
2756 const map<int, vector<int>> & node_ids_for_element_id,
2757 const double *coordx,
2758 const double *coordy,
2759 const double *coordz)
2760{
2761 mesh.FinalizeTopology();
2762
2763 // Define quadratic FE space.
2764 const int Dim = mesh.Dimension();
2765 FiniteElementCollection *fec = new H1_FECollection(2, Dim);
2766 FiniteElementSpace *fes = new FiniteElementSpace(&mesh, fec, Dim,
2768 GridFunction *Nodes = new GridFunction(fes);
2769 Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
2770 mesh.SetNodalGridFunction(Nodes, true);
2771
2772 for (int block_id : unique_block_ids)
2773 {
2774 const CubitElement & block_element = blocks.GetBlockElement(block_id);
2775
2776 int *mfem_to_genesis_map = NULL;
2777
2778 switch (block_element.GetElementType())
2779 {
2780 case ELEMENT_TRI6:
2781 mfem_to_genesis_map = (int *) mfem_to_genesis_tri6;
2782 break;
2783 case ELEMENT_QUAD9:
2784 mfem_to_genesis_map = (int *) mfem_to_genesis_quad9;
2785 break;
2786 case ELEMENT_TET10:
2787 mfem_to_genesis_map = (int *) mfem_to_genesis_tet10;
2788 break;
2789 case ELEMENT_HEX27:
2790 mfem_to_genesis_map = (int *) mfem_to_genesis_hex27;
2791 break;
2792 case ELEMENT_WEDGE18:
2793 mfem_to_genesis_map = (int *) mfem_to_genesis_wedge18;
2794 break;
2795 case ELEMENT_PYRAMID14:
2796 mfem_to_genesis_map = (int *) mfem_to_genesis_pyramid14;
2797 break;
2798 default:
2799 MFEM_ABORT("Something went wrong. Linear elements detected when order is 2.");
2800 }
2801
2802 auto & element_ids = element_ids_for_block_id.at(block_id);
2803
2804 for (int element_id : element_ids)
2805 {
2806 // NB: 1-index (Exodus) --> 0-index (MFEM).
2807 Array<int> dofs;
2808 fes->GetElementDofs(element_id - 1, dofs);
2809
2810 Array<int> vdofs = dofs; // Deep copy.
2811 fes->DofsToVDofs(vdofs);
2812
2813 const vector<int> & element_node_ids = node_ids_for_element_id.at(element_id);
2814
2815 for (int jnode = 0; jnode < dofs.Size(); jnode++)
2816 {
2817 const int node_index = element_node_ids[mfem_to_genesis_map[jnode] - 1] - 1;
2818
2819 (*Nodes)(vdofs[jnode]) = coordx[node_index];
2820 (*Nodes)(vdofs[jnode] + 1) = coordy[node_index];
2821
2822 if (Dim == 3)
2823 {
2824 (*Nodes)(vdofs[jnode] + 2) = coordz[node_index];
2825 }
2826 }
2827 }
2828 }
2829}
2830
2831} // end of namespace cubit.
2832
2833/// @brief Set the coordinates of the Cubit vertices.
2834void Mesh::BuildCubitVertices(const vector<int> & unique_vertex_ids,
2835 const vector<double> & coordx,
2836 const vector<double> & coordy,
2837 const vector<double> & coordz)
2838{
2839 NumOfVertices = unique_vertex_ids.size();
2840 vertices.SetSize(NumOfVertices);
2841
2842 for (int ivertex = 0; ivertex < NumOfVertices; ivertex++)
2843 {
2844 const int original_1based_id = unique_vertex_ids[ivertex];
2845
2846 vertices[ivertex](0) = coordx[original_1based_id - 1];
2847 vertices[ivertex](1) = coordy[original_1based_id - 1];
2848
2849 if (Dim == 3)
2850 {
2851 vertices[ivertex](2) = coordz[original_1based_id - 1];
2852 }
2853 }
2854}
2855
2856/// @brief Create Cubit elements.
2857void Mesh::BuildCubitElements(const int num_elements,
2858 const cubit::CubitBlock * blocks,
2859 const vector<int> & block_ids,
2860 const map<int, vector<int>> & element_ids_for_block_id,
2861 const map<int, vector<int>> & node_ids_for_element_id,
2862 const map<int, int> & cubit_to_mfem_vertex_map)
2863{
2864 using namespace cubit;
2865
2866 NumOfElements = num_elements;
2867 elements.SetSize(num_elements);
2868
2869 int element_counter = 0;
2870
2871 // Iterate over blocks.
2872 for (int block_id : block_ids)
2873 {
2874 const CubitElement & block_element = blocks->GetBlockElement(block_id);
2875
2876 vector<int> renumbered_vertex_ids(block_element.GetNumVertices());
2877
2878 const vector<int> &block_element_ids = element_ids_for_block_id.at(block_id);
2879
2880 // Iterate over elements in block.
2881 for (int element_id : block_element_ids)
2882 {
2883 const vector<int> & element_node_ids = node_ids_for_element_id.at(element_id);
2884
2885 // Iterate over linear (vertex) nodes in block.
2886 for (size_t knode = 0; knode < block_element.GetNumVertices(); knode++)
2887 {
2888 const int node_id = element_node_ids[knode];
2889
2890 // Renumber using the mapping.
2891 renumbered_vertex_ids[knode] = cubit_to_mfem_vertex_map.at(node_id) - 1;
2892 }
2893
2894 // Create element.
2895 elements[element_counter++] = block_element.BuildElement(*this,
2896 renumbered_vertex_ids.data(),
2897 block_id);
2898 }
2899 }
2900}
2901
2902/// @brief Build the Cubit boundaries.
2904 const cubit::CubitBlock * blocks,
2905 const vector<int> & boundary_ids,
2906 const map<int, vector<int>> & element_ids_for_boundary_id,
2907 const map<int, vector<vector<int>>> & node_ids_for_boundary_id,
2908 const map<int, vector<int>> & side_ids_for_boundary_id,
2909 const map<int, int> & block_id_for_element_id,
2910 const map<int, int> & cubit_to_mfem_vertex_map)
2911{
2912 using namespace cubit;
2913
2914 NumOfBdrElements = 0;
2915 for (int boundary_id : boundary_ids)
2916 {
2917 NumOfBdrElements += element_ids_for_boundary_id.at(boundary_id).size();
2918 }
2919
2920 boundary.SetSize(NumOfBdrElements);
2921
2922 array<int, 8> renumbered_vertex_ids; // Set to max number of vertices (Hex27).
2923
2924 // Iterate over boundaries.
2925 int boundary_counter = 0;
2926 for (int boundary_id : boundary_ids)
2927 {
2928 const vector<int> &elements_on_boundary = element_ids_for_boundary_id.at(
2929 boundary_id);
2930
2931 const vector<vector<int>> &nodes_on_boundary = node_ids_for_boundary_id.at(
2932 boundary_id);
2933
2934 int jelement = 0;
2935 for (int side_id : side_ids_for_boundary_id.at(boundary_id))
2936 {
2937 // Determine the block the element originates from and the element type.
2938 const int element_id = elements_on_boundary.at(jelement);
2939 const int element_block = block_id_for_element_id.at(element_id);
2940 const CubitElement & block_element = blocks->GetBlockElement(element_block);
2941
2942 const vector<int> & element_nodes_on_side = nodes_on_boundary.at(jelement);
2943
2944 // Iterate over element's face vertices.
2945 for (size_t knode = 0; knode < element_nodes_on_side.size(); knode++)
2946 {
2947 const int node_id = element_nodes_on_side[knode];
2948
2949 // Renumber using the mapping.
2950 renumbered_vertex_ids[knode] = cubit_to_mfem_vertex_map.at(node_id) - 1;
2951 }
2952
2953 // Create boundary element.
2954 boundary[boundary_counter++] = block_element.BuildBoundaryElement(*this,
2955 side_id,
2956 renumbered_vertex_ids.data(),
2957 boundary_id);
2958
2959 jelement++;
2960 }
2961 }
2962}
2963
2964void Mesh::ReadCubit(const std::string &filename, int &curved, int &read_gf)
2965{
2966 using namespace cubit;
2967
2968 read_gf = 0;
2969 curved = 0; // Set to 1 if mesh is curved.
2970
2971 //
2972 // Open the file.
2973 //
2974 NetCDFReader cubit_reader(filename);
2975
2976 //
2977 // Read important dimensions from file.
2978 //
2979 size_t num_dimensions, num_nodes, num_elements, num_element_blocks,
2980 num_boundaries;
2981
2982 ReadCubitDimensions(cubit_reader, num_dimensions, num_nodes, num_elements,
2983 num_element_blocks, num_boundaries);
2984
2985 Dim = num_dimensions;
2986
2987 //
2988 // Read the blocks.
2989 //
2990 vector<int> block_ids;
2991 BuildCubitBlockIDs(cubit_reader, num_element_blocks, block_ids);
2992 unordered_map<int, string> blk_ids_to_names;
2993 cubit_reader.BuildIDToNameMap(block_ids, blk_ids_to_names, "eb_names");
2994 for (const auto & pr : blk_ids_to_names)
2995 {
2996 const auto blk_id = pr.first;
2997 const auto & blk_name = pr.second;
2998 if (!blk_name.empty())
2999 {
3000 if (!attribute_sets.AttributeSetExists(blk_name))
3001 {
3003 }
3004 attribute_sets.AddToAttributeSet(blk_name, blk_id);
3005 }
3006 }
3007
3008 map<int, size_t> num_elements_for_block_id;
3009 ReadCubitNumElementsInBlock(cubit_reader, block_ids,
3010 num_elements_for_block_id);
3011
3012 map<int, vector<int>> element_ids_for_block_id;
3013 map<int, int> block_id_for_element_id;
3014 BuildElementIDsForBlockID(
3015 block_ids, num_elements_for_block_id, element_ids_for_block_id,
3016 block_id_for_element_id);
3017
3018 //
3019 // Read number of nodes for each element.
3020 CubitBlock blocks(num_dimensions);
3021 ReadCubitBlocks(cubit_reader, block_ids, blocks);
3022
3023 // Read the elements that make-up each block.
3024 map<int, vector<int>> node_ids_for_element_id;
3025 ReadCubitElementBlocks(cubit_reader,
3026 blocks,
3027 block_ids,
3028 element_ids_for_block_id,
3029 node_ids_for_element_id);
3030
3031 //
3032 // Read the boundary ids.
3033 //
3034 vector<int> boundary_ids;
3035 ReadCubitBoundaryIDs(cubit_reader, num_boundaries, boundary_ids);
3036 unordered_map<int, string> bnd_ids_to_names;
3037 cubit_reader.BuildIDToNameMap(boundary_ids, bnd_ids_to_names, "ss_names");
3038 for (const auto & pr : bnd_ids_to_names)
3039 {
3040 const auto bnd_id = pr.first;
3041 const auto & bnd_name = pr.second;
3042 if (!bnd_name.empty())
3043 {
3045 {
3047 }
3048 bdr_attribute_sets.AddToAttributeSet(bnd_name, bnd_id);
3049 }
3050 }
3051
3052
3053 //
3054 // Read the (element, corresponding side) on each of the boundaries.
3055 //
3056 map<int, vector<int>> element_ids_for_boundary_id;
3057 map<int, vector<int>> side_ids_for_boundary_id;
3058
3059 ReadCubitBoundaries(cubit_reader, boundary_ids,
3060 element_ids_for_boundary_id, side_ids_for_boundary_id);
3061
3062 map<int, vector<vector<int>>> node_ids_for_boundary_id;
3063
3064 BuildBoundaryNodeIDs(boundary_ids, blocks, node_ids_for_element_id,
3065 element_ids_for_boundary_id, side_ids_for_boundary_id,
3066 block_id_for_element_id,
3067 node_ids_for_boundary_id);
3068
3069 //
3070 // Read the xyz coordinates for each node.
3071 //
3072 vector<double> coordx(num_nodes);
3073 vector<double> coordy(num_nodes);
3074 vector<double> coordz(num_dimensions == 3 ? num_nodes : 0);
3075
3076 ReadCubitNodeCoordinates(cubit_reader, coordx.data(), coordy.data(),
3077 coordz.data());
3078
3079 //
3080 // We need another node ID mapping since MFEM needs contiguous vertex ids.
3081 //
3082 vector<int> unique_vertex_ids;
3083 BuildUniqueVertexIDs(block_ids, blocks, element_ids_for_block_id,
3084 node_ids_for_element_id, unique_vertex_ids);
3085
3086 //
3087 // unique_vertex_ids now contains a 1-based sorted list of node IDs for each
3088 // node used by the mesh. We now create a map by running over the node IDs
3089 // and remapping to contiguous 1-based integers.
3090 // ie. [1, 4, 5, 8, 9] --> [1, 2, 3, 4, 5].
3091 //
3092 map<int, int> cubit_to_mfem_vertex_map;
3093 BuildCubitToMFEMVertexMap(unique_vertex_ids, cubit_to_mfem_vertex_map);
3094
3095 //
3096 // Load up the vertices.
3097 //
3098 BuildCubitVertices(unique_vertex_ids, coordx, coordy, coordz);
3099
3100 //
3101 // Now load the elements.
3102 //
3103 BuildCubitElements(num_elements, &blocks, block_ids,
3104 element_ids_for_block_id,
3105 node_ids_for_element_id, cubit_to_mfem_vertex_map);
3106
3107 //
3108 // Load up the boundary elements.
3109 //
3110 BuildCubitBoundaries(&blocks, boundary_ids,
3111 element_ids_for_boundary_id, node_ids_for_boundary_id, side_ids_for_boundary_id,
3112 block_id_for_element_id,
3113 cubit_to_mfem_vertex_map);
3114
3115 //
3116 // Additional setup for second order.
3117 //
3118 if (blocks.GetOrder() == 2)
3119 {
3120 curved = 1;
3121
3122 FinalizeCubitSecondOrderMesh(*this,
3123 block_ids,
3124 blocks,
3125 element_ids_for_block_id,
3126 node_ids_for_element_id,
3127 coordx.data(),
3128 coordy.data(),
3129 coordz.data());
3130 }
3131}
3132
3133#endif // #ifdef MFEM_USE_NETCDF
3134
3135} // 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:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
T * GetData()
Returns the data.
Definition array.hpp:159
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 if 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
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 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:210
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:232
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:3538
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:3860
Abstract class for all finite elements.
Definition fe_base.hpp:294
static const int Dimension[NumGeom]
Definition geom.hpp:51
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec_owned and fes.
Definition gridfunc.hpp:160
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
Definition gridfunc.hpp:166
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:377
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
Data type hexahedron element.
int CheckElementOrientation(bool fix_it=true)
Check (and optionally attempt to fix) the orientation of the elements.
Definition mesh.cpp:7346
Array< Vertex > vertices
Definition mesh.hpp:110
Element * NewElement(int geom)
Definition mesh.cpp:4978
friend class NCNURBSExtension
Definition mesh.hpp:70
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:282
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:317
void ReadTrueGridMesh(std::istream &input)
static const int vtk_quadratic_tet[10]
Definition mesh.hpp:275
int NumOfBdrElements
Definition mesh.hpp:84
void ReadNetgen3DMesh(std::istream &input)
void ReadInlineMesh(std::istream &input, bool generate_edges=false)
void ReadLineMesh(std::istream &input)
int Dim
Definition mesh.hpp:81
static const int vtk_quadratic_wedge[18]
Definition mesh.hpp:277
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:315
void Make1D(int n, real_t sx=1.0)
Definition mesh.cpp:4566
friend class Tetrahedron
Definition mesh.hpp:281
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3660
void ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo, const std::string &xml_prefix="")
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:3841
friend class NURBSExtension
Definition mesh.hpp:69
static bool remove_unused_vertices
Definition mesh.hpp:326
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:276
int NumOfVertices
Definition mesh.hpp:84
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:312
void ReadVTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo)
GridFunction * Nodes
Definition mesh.hpp:272
int NumOfElements
Definition mesh.hpp:84
static const int vtk_quadratic_hex[27]
Definition mesh.hpp:278
int spaceDim
Definition mesh.hpp:82
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:273
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:111
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf, bool spacing=false, bool nc=false)
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:4388
Element * ReadElement(std::istream &input)
Definition mesh.cpp:5032
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1569
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:105
void RemoveUnusedVertices()
Remove unused vertices and rebuild mesh connectivity.
Definition mesh.cpp:14107
int GetNBE() const
Return the number of active boundary elements.
Definition nurbs.hpp:962
int GetPatchSpaceDimension() const
Return the physical dimension of the NURBS geometry.
Definition nurbs.cpp:5680
void SetCoordsFromPatches(Vector &Nodes, int vdim)
Set FE coordinates in Nodes, using data from patches, with physical vector dimension vdim,...
Definition nurbs.cpp:5044
void GetElementTopo(Array< Element * > &elements) const
Generate the active mesh elements and return them in elements.
Definition nurbs.cpp:4267
bool HavePatches() const
Return true if at least 1 patch is defined, false otherwise.
Definition nurbs.hpp:995
void GetBdrElementTopo(Array< Element * > &boundary) const
Generate the active mesh boundary elements and return them in boundary.
Definition nurbs.cpp:4396
int GetOrder() const
If all KnotVector orders are identical, return that number. Otherwise, return NURBSFECollection::Vari...
Definition nurbs.hpp:946
int GetNV() const
Return the local number of active vertices.
Definition nurbs.hpp:954
int Dimension() const
Return the dimension of the reference space (not physical space).
Definition nurbs.hpp:926
int GetNE() const
Return the number of active elements.
Definition nurbs.hpp:958
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:749
Class for standard nodal finite elements.
Definition fe_base.hpp:798
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:878
Data type point element.
Definition point.hpp:23
Piecewise-(bi)quadratic continuous finite elements.
Definition fe_coll.hpp:939
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 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
constexpr int dimension
This example only works in 3D. Kernels for 2D are not implemented.
Definition hooke.cpp:45
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:44
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... > &t)
a function intended to be used for extracting the ith type from a tuple.
Definition tuple.hpp:376
bool StringCompare(const char *s1, const char *s2)
void mfem_error(const char *msg)
Definition error.cpp:154
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 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 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
STL namespace.
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