// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mesh_headers.hpp" #include "../fem/fem.hpp" #include "../general/binaryio.hpp" #include "../general/text.hpp" #include "../general/tinyxml2.h" #include "gmsh.hpp" #include #include #include #include #ifdef MFEM_USE_NETCDF #include "netcdf.h" #endif #ifdef MFEM_USE_ZLIB #include #endif using namespace std; namespace mfem { bool Mesh::remove_unused_vertices = true; void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved) { // Read MFEM mesh v1.0 or v1.2 format MFEM_VERIFY(version == 10 || version == 12, "unknown MFEM mesh version"); string ident; // read lines beginning with '#' (comments) skip_comment_lines(input, '#'); input >> ident; // 'dimension' MFEM_VERIFY(ident == "dimension", "invalid mesh file"); input >> Dim; skip_comment_lines(input, '#'); input >> ident; // 'elements' MFEM_VERIFY(ident == "elements", "invalid mesh file"); input >> NumOfElements; elements.SetSize(NumOfElements); for (int j = 0; j < NumOfElements; j++) { elements[j] = ReadElement(input); } skip_comment_lines(input, '#'); input >> ident; // 'boundary' MFEM_VERIFY(ident == "boundary", "invalid mesh file"); input >> NumOfBdrElements; boundary.SetSize(NumOfBdrElements); for (int j = 0; j < NumOfBdrElements; j++) { boundary[j] = ReadElement(input); } skip_comment_lines(input, '#'); input >> ident; // 'vertices' MFEM_VERIFY(ident == "vertices", "invalid mesh file"); input >> NumOfVertices; vertices.SetSize(NumOfVertices); input >> ws >> ident; if (ident != "nodes") { // read the vertices spaceDim = atoi(ident.c_str()); for (int j = 0; j < NumOfVertices; j++) { for (int i = 0; i < spaceDim; i++) { input >> vertices[j](i); } } } else { // prepare to read the nodes input >> ws; curved = 1; } // When visualizing solutions on non-conforming grids, PETSc // may dump additional vertices if (remove_unused_vertices) { RemoveUnusedVertices(); } } void Mesh::ReadLineMesh(std::istream &input) { int j,p1,p2,a; Dim = 1; input >> NumOfVertices; vertices.SetSize(NumOfVertices); // Sets vertices and the corresponding coordinates for (j = 0; j < NumOfVertices; j++) { input >> vertices[j](0); } input >> NumOfElements; elements.SetSize(NumOfElements); // Sets elements and the corresponding indices of vertices for (j = 0; j < NumOfElements; j++) { input >> a >> p1 >> p2; elements[j] = new Segment(p1-1, p2-1, a); } int ind[1]; input >> NumOfBdrElements; boundary.SetSize(NumOfBdrElements); for (j = 0; j < NumOfBdrElements; j++) { input >> a >> ind[0]; ind[0]--; boundary[j] = new Point(ind,a); } } void Mesh::ReadNetgen2DMesh(std::istream &input, int &curved) { int ints[32], attr, n; // Read planar mesh in Netgen format. Dim = 2; // Read the boundary elements. input >> NumOfBdrElements; boundary.SetSize(NumOfBdrElements); for (int i = 0; i < NumOfBdrElements; i++) { input >> attr >> ints[0] >> ints[1]; ints[0]--; ints[1]--; boundary[i] = new Segment(ints, attr); } // Read the elements. input >> NumOfElements; elements.SetSize(NumOfElements); for (int i = 0; i < NumOfElements; i++) { input >> attr >> n; for (int j = 0; j < n; j++) { input >> ints[j]; ints[j]--; } switch (n) { case 2: elements[i] = new Segment(ints, attr); break; case 3: elements[i] = new Triangle(ints, attr); break; case 4: elements[i] = new Quadrilateral(ints, attr); break; } } if (!curved) { // Read the vertices. input >> NumOfVertices; vertices.SetSize(NumOfVertices); for (int i = 0; i < NumOfVertices; i++) for (int j = 0; j < Dim; j++) { input >> vertices[i](j); } } else { input >> NumOfVertices; vertices.SetSize(NumOfVertices); input >> ws; } } void Mesh::ReadNetgen3DMesh(std::istream &input) { int ints[32], attr; // Read a Netgen format mesh of tetrahedra. Dim = 3; // Read the vertices input >> NumOfVertices; vertices.SetSize(NumOfVertices); for (int i = 0; i < NumOfVertices; i++) for (int j = 0; j < Dim; j++) { input >> vertices[i](j); } // Read the elements input >> NumOfElements; elements.SetSize(NumOfElements); for (int i = 0; i < NumOfElements; i++) { input >> attr; for (int j = 0; j < 4; j++) { input >> ints[j]; ints[j]--; } #ifdef MFEM_USE_MEMALLOC Tetrahedron *tet; tet = TetMemory.Alloc(); tet->SetVertices(ints); tet->SetAttribute(attr); elements[i] = tet; #else elements[i] = new Tetrahedron(ints, attr); #endif } // Read the boundary information. input >> NumOfBdrElements; boundary.SetSize(NumOfBdrElements); for (int i = 0; i < NumOfBdrElements; i++) { input >> attr; for (int j = 0; j < 3; j++) { input >> ints[j]; ints[j]--; } boundary[i] = new Triangle(ints, attr); } } void Mesh::ReadTrueGridMesh(std::istream &input) { int i, j, ints[32], attr; const int buflen = 1024; char buf[buflen]; // TODO: find the actual dimension Dim = 3; if (Dim == 2) { int vari; double varf; input >> vari >> NumOfVertices >> vari >> vari >> NumOfElements; input.getline(buf, buflen); input.getline(buf, buflen); input >> vari; input.getline(buf, buflen); input.getline(buf, buflen); input.getline(buf, buflen); // Read the vertices. vertices.SetSize(NumOfVertices); for (i = 0; i < NumOfVertices; i++) { input >> vari >> varf >> vertices[i](0) >> vertices[i](1); input.getline(buf, buflen); } // Read the elements. elements.SetSize(NumOfElements); for (i = 0; i < NumOfElements; i++) { input >> vari >> attr; for (j = 0; j < 4; j++) { input >> ints[j]; ints[j]--; } input.getline(buf, buflen); input.getline(buf, buflen); elements[i] = new Quadrilateral(ints, attr); } } else if (Dim == 3) { int vari; double varf; input >> vari >> NumOfVertices >> NumOfElements; input.getline(buf, buflen); input.getline(buf, buflen); input >> vari >> vari >> NumOfBdrElements; input.getline(buf, buflen); input.getline(buf, buflen); input.getline(buf, buflen); // Read the vertices. vertices.SetSize(NumOfVertices); for (i = 0; i < NumOfVertices; i++) { input >> vari >> varf >> vertices[i](0) >> vertices[i](1) >> vertices[i](2); input.getline(buf, buflen); } // Read the elements. elements.SetSize(NumOfElements); for (i = 0; i < NumOfElements; i++) { input >> vari >> attr; for (j = 0; j < 8; j++) { input >> ints[j]; ints[j]--; } input.getline(buf, buflen); elements[i] = new Hexahedron(ints, attr); } // Read the boundary elements. boundary.SetSize(NumOfBdrElements); for (i = 0; i < NumOfBdrElements; i++) { input >> attr; for (j = 0; j < 4; j++) { input >> ints[j]; ints[j]--; } input.getline(buf, buflen); boundary[i] = new Quadrilateral(ints, attr); } } } // see Tetrahedron::edges const int Mesh::vtk_quadratic_tet[10] = { 0, 1, 2, 3, 4, 7, 5, 6, 8, 9 }; // see Pyramid::edges & Mesh::GenerateFaces // https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html const int Mesh::vtk_quadratic_pyramid[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; // see Wedge::edges & Mesh::GenerateFaces // https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html const int Mesh::vtk_quadratic_wedge[18] = { 0, 2, 1, 3, 5, 4, 8, 7, 6, 11, 10, 9, 12, 14, 13, 17, 16, 15}; // see Hexahedron::edges & Mesh::GenerateFaces const int Mesh::vtk_quadratic_hex[27] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 24, 22, 21, 23, 20, 25, 26 }; void Mesh::CreateVTKMesh(const Vector &points, const Array &cell_data, const Array &cell_offsets, const Array &cell_types, const Array &cell_attributes, int &curved, int &read_gf, bool &finalize_topo) { int np = points.Size()/3; Dim = -1; NumOfElements = cell_types.Size(); elements.SetSize(NumOfElements); int order = -1; bool legacy_elem = false, lagrange_elem = false; for (int i = 0; i < NumOfElements; i++) { int j = (i > 0) ? cell_offsets[i-1] : 0; int ct = cell_types[i]; Geometry::Type geom = VTKGeometry::GetMFEMGeometry(ct); elements[i] = NewElement(geom); if (cell_attributes.Size() > 0) { elements[i]->SetAttribute(cell_attributes[i]); } // VTK ordering of vertices is the same as MFEM ordering of vertices // for all element types *except* prisms, which require a permutation if (geom == Geometry::PRISM && ct != VTKGeometry::LAGRANGE_PRISM) { int prism_vertices[6]; for (int k=0; k<6; ++k) { prism_vertices[k] = cell_data[j+VTKGeometry::PrismMap[k]]; } elements[i]->SetVertices(prism_vertices); } else { elements[i]->SetVertices(&cell_data[j]); } int elem_dim = Geometry::Dimension[geom]; int elem_order = VTKGeometry::GetOrder(ct, cell_offsets[i] - j); if (VTKGeometry::IsLagrange(ct)) { lagrange_elem = true; } else { legacy_elem = true; } MFEM_VERIFY(Dim == -1 || Dim == elem_dim, "Elements with different dimensions are not supported"); MFEM_VERIFY(order == -1 || order == elem_order, "Elements with different orders are not supported"); MFEM_VERIFY(legacy_elem != lagrange_elem, "Mixing of legacy and Lagrange cell types is not supported"); Dim = elem_dim; order = elem_order; } // determine spaceDim based on min/max differences detected each dimension spaceDim = 0; if (np > 0) { double min_value, max_value; for (int d = 3; d > 0; --d) { min_value = max_value = points(3*0 + d-1); for (int i = 1; i < np; i++) { min_value = std::min(min_value, points(3*i + d-1)); max_value = std::max(max_value, points(3*i + d-1)); if (min_value != max_value) { spaceDim = d; break; } } if (spaceDim > 0) { break; } } } if (order == 1 && !lagrange_elem) { NumOfVertices = np; vertices.SetSize(np); for (int i = 0; i < np; i++) { vertices[i](0) = points(3*i+0); vertices[i](1) = points(3*i+1); vertices[i](2) = points(3*i+2); } // No boundary is defined in a VTK mesh NumOfBdrElements = 0; FinalizeTopology(); CheckElementOrientation(true); } else { // The following section of code is shared for legacy quadratic and the // Lagrange high order elements curved = 1; // generate new enumeration for the vertices Array pts_dof(np); pts_dof = -1; // mark vertex points for (int i = 0; i < NumOfElements; i++) { int *v = elements[i]->GetVertices(); int nv = elements[i]->GetNVertices(); for (int j = 0; j < nv; j++) { if (pts_dof[v[j]] == -1) { pts_dof[v[j]] = 0; } } } // The following loop reorders pts_dofs so vertices are visited in // canonical order // Keep the original ordering of the vertices NumOfVertices = 0; for (int i = 0; i < np; i++) { if (pts_dof[i] != -1) { pts_dof[i] = NumOfVertices++; } } // update the element vertices for (int i = 0; i < NumOfElements; i++) { int *v = elements[i]->GetVertices(); int nv = elements[i]->GetNVertices(); for (int j = 0; j < nv; j++) { v[j] = pts_dof[v[j]]; } } // Define the 'vertices' from the 'points' through the 'pts_dof' map vertices.SetSize(NumOfVertices); for (int i = 0; i < np; i++) { int j = pts_dof[i]; if (j != -1) { vertices[j](0) = points(3*i+0); vertices[j](1) = points(3*i+1); vertices[j](2) = points(3*i+2); } } // No boundary is defined in a VTK mesh NumOfBdrElements = 0; // Generate faces and edges so that we can define FE space on the mesh FinalizeTopology(); FiniteElementCollection *fec; FiniteElementSpace *fes; if (legacy_elem) { // Define quadratic FE space fec = new QuadraticFECollection; fes = new FiniteElementSpace(this, fec, spaceDim); Nodes = new GridFunction(fes); Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes' own_nodes = 1; // Map vtk points to edge/face/element dofs Array dofs; for (int i = 0; i < NumOfElements; i++) { fes->GetElementDofs(i, dofs); const int *vtk_mfem; switch (elements[i]->GetGeometryType()) { case Geometry::TRIANGLE: case Geometry::SQUARE: vtk_mfem = vtk_quadratic_hex; break; // identity map case Geometry::TETRAHEDRON: vtk_mfem = vtk_quadratic_tet; break; case Geometry::CUBE: vtk_mfem = vtk_quadratic_hex; break; case Geometry::PRISM: vtk_mfem = vtk_quadratic_wedge; break; case Geometry::PYRAMID: vtk_mfem = vtk_quadratic_pyramid; break; default: vtk_mfem = NULL; // suppress a warning break; } int offset = (i == 0) ? 0 : cell_offsets[i-1]; for (int j = 0; j < dofs.Size(); j++) { if (pts_dof[cell_data[offset+j]] == -1) { pts_dof[cell_data[offset+j]] = dofs[vtk_mfem[j]]; } else { if (pts_dof[cell_data[offset+j]] != dofs[vtk_mfem[j]]) { MFEM_ABORT("VTK mesh: inconsistent quadratic mesh!"); } } } } } else { // Define H1 FE space fec = new H1_FECollection(order,Dim,BasisType::ClosedUniform); fes = new FiniteElementSpace(this, fec, spaceDim); Nodes = new GridFunction(fes); Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes' own_nodes = 1; Array dofs; std::map> vtk_inv_maps; std::map*> lex_orderings; int i, n; for (n = i = 0; i < NumOfElements; i++) { Geometry::Type geom = GetElementBaseGeometry(i); fes->GetElementDofs(i, dofs); Array &vtk_inv_map = vtk_inv_maps[geom]; if (vtk_inv_map.Size() == 0) { Array vtk_map; CreateVTKElementConnectivity(vtk_map, geom, order); vtk_inv_map.SetSize(vtk_map.Size()); for (int j=0; j *&lex_ordering = lex_orderings[geom]; if (!lex_ordering) { const FiniteElement *fe = fes->GetFE(i); const NodalFiniteElement *nodal_fe = dynamic_cast(fe); MFEM_ASSERT(nodal_fe != NULL, "Unsupported element type"); lex_ordering = &nodal_fe->GetLexicographicOrdering(); } for (int lex_idx = 0; lex_idx < dofs.Size(); lex_idx++) { int mfem_idx = (*lex_ordering)[lex_idx]; int vtk_idx = vtk_inv_map[lex_idx]; int pt_idx = cell_data[n + vtk_idx]; if (pts_dof[pt_idx] == -1) { pts_dof[pt_idx] = dofs[mfem_idx]; } else { if (pts_dof[pt_idx] != dofs[mfem_idx]) { MFEM_ABORT("VTK mesh: inconsistent Lagrange mesh!"); } } } n += dofs.Size(); } } // Define the 'Nodes' from the 'points' through the 'pts_dof' map Array dofs; for (int i = 0; i < np; i++) { dofs.SetSize(1); if (pts_dof[i] != -1) { dofs[0] = pts_dof[i]; fes->DofsToVDofs(dofs); for (int d = 0; d < dofs.Size(); d++) { (*Nodes)(dofs[d]) = points(3*i+d); } } } read_gf = 0; } } namespace vtk_xml { using namespace tinyxml2; /// Return false if either string is NULL or if the strings differ, return true /// if the strings are the same. bool StringCompare(const char *s1, const char *s2) { if (s1 == NULL || s2 == NULL) { return false; } return strcmp(s1, s2) == 0; } /// Abstract base class for reading contiguous arrays of (potentially /// compressed, potentially base-64 encoded) binary data from a buffer into a /// destination array. The types of the source and destination arrays may be /// different (e.g. read data of type uint8_t into destination array of /// uint32_t), which is handled by the templated derived class @a BufferReader. struct BufferReaderBase { enum HeaderType { UINT32_HEADER, UINT64_HEADER }; virtual void ReadBinary(const char *buf, void *dest, int n) const = 0; virtual void ReadBase64(const char *txt, void *dest, int n) const = 0; virtual ~BufferReaderBase() { } }; /// Read an array of source data stored as (potentially compressed, potentially /// base-64 encoded) into a destination array. The types of the elements in the /// source array are given by template parameter @a F ("from") and the types of /// the elements of the destination array are given by @a T ("to"). The binary /// data has a header, which is one integer if the data is uncompressed, and is /// four integers if the data is compressed. The integers may either by uint32_t /// or uint64_t, according to the @a header_type. If the data is compressed and /// base-64 encoded, then the header is encoded separately from the data. If the /// data is uncompressed and base-64 encoded, then the header and data are /// encoded together. template struct BufferReader : BufferReaderBase { bool compressed; HeaderType header_type; BufferReader(bool compressed_, HeaderType header_type_) : compressed(compressed_), header_type(header_type_) { } /// Return the number of bytes of each header entry. size_t HeaderEntrySize() const { return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t); } /// Return the value of the header entry pointer to by @a header_buf. The /// value is stored as either uint32_t or uint64_t, according to the @a /// header_type, and is returned as uint64_t. uint64_t ReadHeaderEntry(const char *header_buf) const { return (header_type == UINT64_HEADER) ? bin_io::read(header_buf) : bin_io::read(header_buf); } /// Return the number of bytes in the header. The header consists of one /// integer if the data is uncompressed, and @a N + 3 integers if the data is /// compressed, where @a N is the number of blocks. The integers are either /// 32 or 64 bytes depending on the value of @a header_type. The number of /// blocks is determined by reading the first integer (of type @a /// header_type) pointed to by @a header_buf. int NumHeaderBytes(const char *header_buf) const { if (!compressed) { return HeaderEntrySize(); } return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize(); } /// Read @a n elements of type @a F from the source buffer @a buf into the /// (pre-allocated) destination array of elements of type @a T stored in /// the buffer @a dest_void. The header is stored @b separately from the /// rest of the data, in the buffer @a header_buf. The data buffer @a buf /// does @b not contain a header. void ReadBinaryWithHeader(const char *header_buf, const char *buf, void *dest_void, int n) const { std::vector uncompressed_data; T *dest = static_cast(dest_void); if (compressed) { #ifdef MFEM_USE_ZLIB // The header has format (where header_t is uint32_t or uint64_t): // header_t number_of_blocks; // header_t uncompressed_block_size; // header_t uncompressed_last_block_size; // header_t compressed_size[number_of_blocks]; int header_entry_size = HeaderEntrySize(); int nblocks = ReadHeaderEntry(header_buf); header_buf += header_entry_size; std::vector header(nblocks + 2); for (int i=0; i::value) { // Special case: no type conversions necessary, so can just memcpy memcpy(dest, buf, sizeof(T)*n); } else { for (int i=0; i(buf + i*sizeof(F)); } } } /// Read @a n elements of type @a F from source buffer @a buf into /// (pre-allocated) array of elements of type @a T stored in destination /// buffer @a dest. The input buffer contains both the header and the data. void ReadBinary(const char *buf, void *dest, int n) const override { ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n); } /// Read @a n elements of type @a F from base-64 encoded source buffer into /// (pre-allocated) array of elements of type @a T stored in destination /// buffer @a dest. The base-64-encoded data is given by the null-terminated /// string @a txt, which contains both the header and the data. void ReadBase64(const char *txt, void *dest, int n) const override { // Skip whitespace while (*txt) { if (*txt != ' ' && *txt != '\n') { break; } ++txt; } if (compressed) { // Decode the first entry of the header, which we need to determine // how long the rest of the header is. std::vector nblocks_buf; int nblocks_b64 = bin_io::NumBase64Chars(HeaderEntrySize()); bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf); std::vector data, header; // Compute number of characters needed to encode header in base 64, // then round to nearest multiple of 4 to take padding into account. int header_b64 = bin_io::NumBase64Chars(NumHeaderBytes(nblocks_buf.data())); // If data is compressed, header is encoded separately bin_io::DecodeBase64(txt, header_b64, header); bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data); ReadBinaryWithHeader(header.data(), data.data(), dest, n); } else { std::vector data; bin_io::DecodeBase64(txt, strlen(txt), data); ReadBinary(data.data(), dest, n); } } }; /// Class to read data from VTK's @a DataArary elements. Each @a DataArray can /// contain inline ASCII data, inline base-64-encoded data (potentially /// compressed), or reference "appended data", which may be raw or base-64, and /// may be compressed or uncompressed. struct XMLDataReader { const char *appended_data, *byte_order, *compressor; enum AppendedDataEncoding { RAW, BASE64 }; map type_map; AppendedDataEncoding encoding; /// Create the data reader, where @a vtk is the @a VTKFile XML element, and /// @a vtu is the child @a UnstructuredGrid XML element. This will determine /// the header type (32 or 64 bit integers) and whether compression is /// enabled or not. The appended data will be loaded. XMLDataReader(const XMLElement *vtk, const XMLElement *vtu) { // Determine whether binary data header is 32 or 64 bit integer BufferReaderBase::HeaderType htype; if (StringCompare(vtk->Attribute("header_type"), "UInt64")) { htype = BufferReaderBase::UINT64_HEADER; } else { htype = BufferReaderBase::UINT32_HEADER; } // Get the byte order of the file (will check if we encounter binary data) byte_order = vtk->Attribute("byte_order"); // Get the compressor. We will check that MFEM can handle the compression // if we encounter binary data. compressor = vtk->Attribute("compressor"); bool compressed = (compressor != NULL); // Find the appended data. appended_data = NULL; for (const XMLElement *xml_elem = vtu->NextSiblingElement(); xml_elem != NULL; xml_elem = xml_elem->NextSiblingElement()) { if (StringCompare(xml_elem->Name(), "AppendedData")) { const char *encoding_str = xml_elem->Attribute("encoding"); if (StringCompare(encoding_str, "raw")) { appended_data = xml_elem->GetAppendedData(); encoding = RAW; } else if (StringCompare(encoding_str, "base64")) { appended_data = xml_elem->GetText(); encoding = BASE64; } MFEM_VERIFY(appended_data != NULL, "Invalid AppendedData"); // Appended data follows first underscore bool found_leading_underscore = false; while (*appended_data) { ++appended_data; if (*appended_data == '_') { found_leading_underscore = true; ++appended_data; break; } } MFEM_VERIFY(found_leading_underscore, "Invalid AppendedData"); break; } } type_map["Int8"] = new BufferReader(compressed, htype); type_map["Int16"] = new BufferReader(compressed, htype); type_map["Int32"] = new BufferReader(compressed, htype); type_map["Int64"] = new BufferReader(compressed, htype); type_map["UInt8"] = new BufferReader(compressed, htype); type_map["UInt16"] = new BufferReader(compressed, htype); type_map["UInt32"] = new BufferReader(compressed, htype); type_map["UInt64"] = new BufferReader(compressed, htype); type_map["Float32"] = new BufferReader(compressed, htype); type_map["Float64"] = new BufferReader(compressed, htype); } /// Read the @a DataArray XML element given by @a xml_elem into /// (pre-allocated) destination array @a dest, where @a dest stores @a n /// elements of type @a T. template void Read(const XMLElement *xml_elem, T *dest, int n) { static const char *erstr = "Error reading XML DataArray"; MFEM_VERIFY(StringCompare(xml_elem->Name(), "DataArray"), erstr); const char *format = xml_elem->Attribute("format"); if (StringCompare(format, "ascii")) { const char *txt = xml_elem->GetText(); MFEM_VERIFY(txt != NULL, erstr); std::istringstream data_stream(txt); for (int i=0; i> dest[i]; } } else if (StringCompare(format, "appended")) { VerifyBinaryOptions(); int offset = xml_elem->IntAttribute("offset"); const char *type = xml_elem->Attribute("type"); MFEM_VERIFY(type != NULL, erstr); BufferReaderBase *reader = type_map[type]; MFEM_VERIFY(reader != NULL, erstr); MFEM_VERIFY(appended_data != NULL, "No AppendedData found"); if (encoding == RAW) { reader->ReadBinary(appended_data + offset, dest, n); } else { reader->ReadBase64(appended_data + offset, dest, n); } } else if (StringCompare(format, "binary")) { VerifyBinaryOptions(); const char *txt = xml_elem->GetText(); MFEM_VERIFY(txt != NULL, erstr); const char *type = xml_elem->Attribute("type"); if (type == NULL) { MFEM_ABORT(erstr); } BufferReaderBase *reader = type_map[type]; if (reader == NULL) { MFEM_ABORT(erstr); } reader->ReadBase64(txt, dest, n); } else { MFEM_ABORT("Invalid XML VTK DataArray format"); } } /// Check that the byte order of the file is the same as the native byte /// order that we're running with. We don't currently support converting /// between byte orders. The byte order is only verified if we encounter /// binary data. void VerifyByteOrder() const { // Can't handle reading big endian from little endian or vice versa if (byte_order && !StringCompare(byte_order, VTKByteOrder())) { MFEM_ABORT("Converting between different byte orders is unsupported."); } } /// Check that the compressor is compatible (MFEM currently only supports /// zlib compression). If MFEM is not compiled with zlib, then we cannot /// read binary data with compression. void VerifyCompressor() const { if (compressor && !StringCompare(compressor, "vtkZLibDataCompressor")) { MFEM_ABORT("Unsupported compressor. Only zlib is supported.") } #ifndef MFEM_USE_ZLIB MFEM_VERIFY(compressor == NULL, "MFEM must be compiled with zlib enabled " "to support reading compressed data."); #endif } /// Verify that the binary data is stored with compatible options (i.e. /// native byte order and compatible compression). void VerifyBinaryOptions() const { VerifyByteOrder(); VerifyCompressor(); } ~XMLDataReader() { for (auto &x : type_map) { delete x.second; } } }; } // namespace vtk_xml void Mesh::ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo, const std::string &xml_prefix) { using namespace vtk_xml; static const char *erstr = "XML parsing error"; // Create buffer beginning with xml_prefix, then read the rest of the stream std::vector buf(xml_prefix.begin(), xml_prefix.end()); std::istreambuf_iterator eos; buf.insert(buf.end(), std::istreambuf_iterator(input), eos); buf.push_back('\0'); // null-terminate buffer XMLDocument xml; xml.Parse(buf.data(), buf.size()); if (xml.ErrorID() != XML_SUCCESS) { MFEM_ABORT("Error parsing XML VTK file.\n" << xml.ErrorStr()); } const XMLElement *vtkfile = xml.FirstChildElement(); MFEM_VERIFY(vtkfile, erstr); MFEM_VERIFY(StringCompare(vtkfile->Name(), "VTKFile"), erstr); const XMLElement *vtu = vtkfile->FirstChildElement(); MFEM_VERIFY(vtu, erstr); MFEM_VERIFY(StringCompare(vtu->Name(), "UnstructuredGrid"), erstr); XMLDataReader data_reader(vtkfile, vtu); // Count the number of points and cells const XMLElement *piece = vtu->FirstChildElement(); MFEM_VERIFY(StringCompare(piece->Name(), "Piece"), erstr); MFEM_VERIFY(piece->NextSiblingElement() == NULL, "XML VTK meshes with more than one Piece are not supported"); int npts = piece->IntAttribute("NumberOfPoints"); int ncells = piece->IntAttribute("NumberOfCells"); // Read the points Vector points(3*npts); const XMLElement *pts_xml; for (pts_xml = piece->FirstChildElement(); pts_xml != NULL; pts_xml = pts_xml->NextSiblingElement()) { if (StringCompare(pts_xml->Name(), "Points")) { const XMLElement *pts_data = pts_xml->FirstChildElement(); MFEM_VERIFY(pts_data->IntAttribute("NumberOfComponents") == 3, "XML VTK Points DataArray must have 3 components"); data_reader.Read(pts_data, points.GetData(), points.Size()); break; } } if (pts_xml == NULL) { MFEM_ABORT(erstr); } // Read the cells Array cell_data, cell_offsets(ncells), cell_types(ncells); const XMLElement *cells_xml; for (cells_xml = piece->FirstChildElement(); cells_xml != NULL; cells_xml = cells_xml->NextSiblingElement()) { if (StringCompare(cells_xml->Name(), "Cells")) { const XMLElement *cell_data_xml = NULL; for (const XMLElement *data_xml = cells_xml->FirstChildElement(); data_xml != NULL; data_xml = data_xml->NextSiblingElement()) { const char *data_name = data_xml->Attribute("Name"); if (StringCompare(data_name, "offsets")) { data_reader.Read(data_xml, cell_offsets.GetData(), ncells); } else if (StringCompare(data_name, "types")) { data_reader.Read(data_xml, cell_types.GetData(), ncells); } else if (StringCompare(data_name, "connectivity")) { // Have to read the connectivity after the offsets, because we // don't know how many points to read until we have the offsets // (size of connectivity array is equal to the last offset), so // store the XML element pointer and read this data later. cell_data_xml = data_xml; } } MFEM_VERIFY(cell_data_xml != NULL, erstr); int cell_data_size = cell_offsets.Last(); cell_data.SetSize(cell_data_size); data_reader.Read(cell_data_xml, cell_data.GetData(), cell_data_size); break; } } if (cells_xml == NULL) { MFEM_ABORT(erstr); } // Read the element attributes, which are stored as CellData named "material" Array cell_attributes; for (const XMLElement *cell_data_xml = piece->FirstChildElement(); cell_data_xml != NULL; cell_data_xml = cell_data_xml->NextSiblingElement()) { if (StringCompare(cell_data_xml->Name(), "CellData") && StringCompare(cell_data_xml->Attribute("Scalars"), "material")) { const XMLElement *data_xml = cell_data_xml->FirstChildElement(); if (data_xml != NULL && StringCompare(data_xml->Name(), "DataArray")) { cell_attributes.SetSize(ncells); data_reader.Read(data_xml, cell_attributes.GetData(), ncells); } } } CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes, curved, read_gf, finalize_topo); } void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf, bool &finalize_topo) { // VTK resources: // * https://www.vtk.org/doc/nightly/html/vtkCellType_8h_source.html // * https://www.vtk.org/doc/nightly/html/classvtkCell.html // * https://lorensen.github.io/VTKExamples/site/VTKFileFormats // * https://www.kitware.com/products/books/VTKUsersGuide.pdf string buff; getline(input, buff); // comment line getline(input, buff); filter_dos(buff); if (buff != "ASCII") { MFEM_ABORT("VTK mesh is not in ASCII format!"); return; } do { getline(input, buff); filter_dos(buff); if (!input.good()) { MFEM_ABORT("VTK mesh is not UNSTRUCTURED_GRID!"); } } while (buff != "DATASET UNSTRUCTURED_GRID"); // Read the points, skipping optional sections such as the FIELD data from // VisIt's VTK export (or from Mesh::PrintVTK with field_data==1). do { input >> buff; if (!input.good()) { MFEM_ABORT("VTK mesh does not have POINTS data!"); } } while (buff != "POINTS"); Vector points; int np; input >> np >> ws; getline(input, buff); // "double" points.Load(input, 3*np); //skip metadata // Looks like: // METADATA //INFORMATION 2 //NAME L2_NORM_RANGE LOCATION vtkDataArray //DATA 2 0 5.19615 //NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray //DATA 2 0 5.19615 do { input >> buff; if (!input.good()) { MFEM_ABORT("VTK mesh does not have CELLS data!"); } } while (buff != "CELLS"); // Read the cells Array cell_data, cell_offsets; if (buff == "CELLS") { int ncells, n; input >> ncells >> n >> ws; cell_offsets.SetSize(ncells); cell_data.SetSize(n - ncells); int offset = 0; for (int i=0; i> nv; cell_offsets[i] = offset + nv; for (int j=0; j> cell_data[offset + j]; } offset += nv; } } // Read the cell types input >> ws >> buff; Array cell_types; int ncells; MFEM_VERIFY(buff == "CELL_TYPES", "CELL_TYPES not provided in VTK mesh.") input >> ncells; cell_types.Load(ncells, input); while ((input.good()) && (buff != "CELL_DATA")) { input >> buff; } getline(input, buff); // finish the line // Read the cell materials // bool found_material = false; Array cell_attributes; while ((input.good())) { getline(input, buff); if (buff.rfind("POINT_DATA") == 0) { break; // We have entered the POINT_DATA block. Quit. } else if (buff.rfind("SCALARS material") == 0) { getline(input, buff); // LOOKUP_TABLE default if (buff.rfind("LOOKUP_TABLE default") != 0) { MFEM_ABORT("Invalid LOOKUP_TABLE for material array in VTK file."); } cell_attributes.Load(ncells, input); // found_material = true; break; } } // if (!found_material) // { // MFEM_WARNING("Material array not found in VTK file. " // "Assuming uniform material composition."); // } CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes, curved, read_gf, finalize_topo); } // end ReadVTKMesh void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf) { NURBSext = new NURBSExtension(input); Dim = NURBSext->Dimension(); NumOfVertices = NURBSext->GetNV(); NumOfElements = NURBSext->GetNE(); NumOfBdrElements = NURBSext->GetNBE(); NURBSext->GetElementTopo(elements); NURBSext->GetBdrElementTopo(boundary); vertices.SetSize(NumOfVertices); curved = 1; if (NURBSext->HavePatches()) { NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder()); FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim, Ordering::byVDIM); Nodes = new GridFunction(fes); Nodes->MakeOwner(fec); NURBSext->SetCoordsFromPatches(*Nodes); own_nodes = 1; read_gf = 0; spaceDim = Nodes->VectorDim(); for (int i = 0; i < spaceDim; i++) { Vector vert_val; Nodes->GetNodalValues(vert_val, i+1); for (int j = 0; j < NumOfVertices; j++) { vertices[j](i) = vert_val(j); } } } else { read_gf = 1; } } void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges) { // Initialize to negative numbers so that we know if they've been set. We're // using Element::POINT as our flag, since we're not going to make a 0D mesh, // ever. int nx = -1; int ny = -1; int nz = -1; double sx = -1.0; double sy = -1.0; double sz = -1.0; Element::Type type = Element::POINT; while (true) { skip_comment_lines(input, '#'); // Break out if we reached the end of the file after gobbling up the // whitespace and comments after the last keyword. if (!input.good()) { break; } // Read the next keyword std::string name; input >> name; input >> std::ws; // Make sure there's an equal sign MFEM_VERIFY(input.get() == '=', "Inline mesh expected '=' after keyword " << name); input >> std::ws; if (name == "nx") { input >> nx; } else if (name == "ny") { input >> ny; } else if (name == "nz") { input >> nz; } else if (name == "sx") { input >> sx; } else if (name == "sy") { input >> sy; } else if (name == "sz") { input >> sz; } else if (name == "type") { std::string eltype; input >> eltype; if (eltype == "segment") { type = Element::SEGMENT; } else if (eltype == "quad") { type = Element::QUADRILATERAL; } else if (eltype == "tri") { type = Element::TRIANGLE; } else if (eltype == "hex") { type = Element::HEXAHEDRON; } else if (eltype == "wedge") { type = Element::WEDGE; } else if (eltype == "pyramid") { type = Element::PYRAMID; } else if (eltype == "tet") { type = Element::TETRAHEDRON; } else { MFEM_ABORT("unrecognized element type (read '" << eltype << "') in inline mesh format. " "Allowed: segment, tri, quad, tet, hex, wedge"); } } else { MFEM_ABORT("unrecognized keyword (" << name << ") in inline mesh format. " "Allowed: nx, ny, nz, type, sx, sy, sz"); } input >> std::ws; // Allow an optional semi-colon at the end of each line. if (input.peek() == ';') { input.get(); } // Done reading file if (!input) { break; } } // Now make the mesh. if (type == Element::SEGMENT) { MFEM_VERIFY(nx > 0 && sx > 0.0, "invalid 1D inline mesh format, all values must be " "positive\n" << " nx = " << nx << "\n" << " sx = " << sx << "\n"); Make1D(nx, sx); } else if (type == Element::TRIANGLE || type == Element::QUADRILATERAL) { MFEM_VERIFY(nx > 0 && ny > 0 && sx > 0.0 && sy > 0.0, "invalid 2D inline mesh format, all values must be " "positive\n" << " nx = " << nx << "\n" << " ny = " << ny << "\n" << " sx = " << sx << "\n" << " sy = " << sy << "\n"); Make2D(nx, ny, type, sx, sy, generate_edges, true); } else if (type == Element::TETRAHEDRON || type == Element::WEDGE || type == Element::HEXAHEDRON || type == Element::PYRAMID) { MFEM_VERIFY(nx > 0 && ny > 0 && nz > 0 && sx > 0.0 && sy > 0.0 && sz > 0.0, "invalid 3D inline mesh format, all values must be " "positive\n" << " nx = " << nx << "\n" << " ny = " << ny << "\n" << " nz = " << nz << "\n" << " sx = " << sx << "\n" << " sy = " << sy << "\n" << " sz = " << sz << "\n"); Make3D(nx, ny, nz, type, sx, sy, sz, true); // TODO: maybe have an option in the file to control ordering? } else { MFEM_ABORT("For inline mesh, must specify an element type =" " [segment, tri, quad, tet, hex, wedge]"); } } void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf) { string buff; double version; int binary, dsize; input >> version >> binary >> dsize; if (version < 2.2) { MFEM_ABORT("Gmsh file version < 2.2"); } if (dsize != sizeof(double)) { MFEM_ABORT("Gmsh file : dsize != sizeof(double)"); } getline(input, buff); // There is a number 1 in binary format if (binary) { int one; input.read(reinterpret_cast(&one), sizeof(one)); if (one != 1) { MFEM_ABORT("Gmsh file : wrong binary format"); } } // A map between a serial number of the vertex and its number in the file // (there may be gaps in the numbering, and also Gmsh enumerates vertices // starting from 1, not 0) map vertices_map; // Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the // mesh element dimension (Dim) and the dimension of the space in which the // mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and // spaceDim = 2, while a 2D surface mesh in 3D has Dim = 2 but spaceDim = 3. // Below we set spaceDim by measuring the mesh bounding box and checking for // a lower dimensional subspace. The assumption is that the mesh is at least // 2D if the y-dimension of the box is non-trivial and 3D if the z-dimension // is non-trivial. Note that with these assumptions a 2D mesh parallel to the // yz plane will be considered a surface mesh embedded in 3D whereas the same // 2D mesh parallel to the xy plane will be considered a 2D mesh. double bb_tol = 1e-14; double bb_min[3]; double bb_max[3]; // Mesh order int mesh_order = 1; // Mesh type bool periodic = false; // Vector field to store uniformly spaced Gmsh high order mesh coords GridFunction Nodes_gf; // Read the lines of the mesh file. If we face specific keyword, we'll treat // the section. while (input >> buff) { if (buff == "$Nodes") // reading mesh vertices { input >> NumOfVertices; getline(input, buff); vertices.SetSize(NumOfVertices); int serial_number; const int gmsh_dim = 3; // Gmsh always outputs 3 coordinates double coord[gmsh_dim]; for (int ver = 0; ver < NumOfVertices; ++ver) { if (binary) { input.read(reinterpret_cast(&serial_number), sizeof(int)); input.read(reinterpret_cast(coord), gmsh_dim*sizeof(double)); } else // ASCII { input >> serial_number; for (int ci = 0; ci < gmsh_dim; ++ci) { input >> coord[ci]; } } vertices[ver] = Vertex(coord, gmsh_dim); vertices_map[serial_number] = ver; for (int ci = 0; ci < gmsh_dim; ++ci) { bb_min[ci] = (ver == 0) ? coord[ci] : std::min(bb_min[ci], coord[ci]); bb_max[ci] = (ver == 0) ? coord[ci] : std::max(bb_max[ci], coord[ci]); } } double bb_size = std::max(bb_max[0] - bb_min[0], std::max(bb_max[1] - bb_min[1], bb_max[2] - bb_min[2])); spaceDim = 1; if (bb_max[1] - bb_min[1] > bb_size * bb_tol) { spaceDim++; } if (bb_max[2] - bb_min[2] > bb_size * bb_tol) { spaceDim++; } if (static_cast(vertices_map.size()) != NumOfVertices) { MFEM_ABORT("Gmsh file : vertices indices are not unique"); } } // section '$Nodes' else if (buff == "$Elements") // reading mesh elements { int num_of_all_elements; input >> num_of_all_elements; // = NumOfElements + NumOfBdrElements + (maybe, PhysicalPoints) getline(input, buff); int serial_number; // serial number of an element int type_of_element; // ID describing a type of a mesh element int n_tags; // number of different tags describing an element int phys_domain; // element's attribute int elem_domain; // another element's attribute (rarely used) int n_partitions; // number of partitions where an element takes place // number of nodes for each type of Gmsh elements, type is the index of // the array + 1 int nodes_of_gmsh_element[] = { 2, // 2-node line. 3, // 3-node triangle. 4, // 4-node quadrangle. 4, // 4-node tetrahedron. 8, // 8-node hexahedron. 6, // 6-node prism. 5, // 5-node pyramid. 3, /* 3-node second order line (2 nodes associated with the vertices and 1 with the edge). */ 6, /* 6-node second order triangle (3 nodes associated with the vertices and 3 with the edges). */ 9, /* 9-node second order quadrangle (4 nodes associated with the vertices, 4 with the edges and 1 with the face). */ 10, /* 10-node second order tetrahedron (4 nodes associated with the vertices and 6 with the edges). */ 27, /* 27-node second order hexahedron (8 nodes associated with the vertices, 12 with the edges, 6 with the faces and 1 with the volume). */ 18, /* 18-node second order prism (6 nodes associated with the vertices, 9 with the edges and 3 with the quadrangular faces). */ 14, /* 14-node second order pyramid (5 nodes associated with the vertices, 8 with the edges and 1 with the quadrangular face). */ 1, // 1-node point. 8, /* 8-node second order quadrangle (4 nodes associated with the vertices and 4 with the edges). */ 20, /* 20-node second order hexahedron (8 nodes associated with the vertices and 12 with the edges). */ 15, /* 15-node second order prism (6 nodes associated with the vertices and 9 with the edges). */ 13, /* 13-node second order pyramid (5 nodes associated with the vertices and 8 with the edges). */ 9, /* 9-node third order incomplete triangle (3 nodes associated with the vertices, 6 with the edges) */ 10, /* 10-node third order triangle (3 nodes associated with the vertices, 6 with the edges, 1 with the face) */ 12, /* 12-node fourth order incomplete triangle (3 nodes associated with the vertices, 9 with the edges) */ 15, /* 15-node fourth order triangle (3 nodes associated with the vertices, 9 with the edges, 3 with the face) */ 15, /* 15-node fifth order incomplete triangle (3 nodes associated with the vertices, 12 with the edges) */ 21, /* 21-node fifth order complete triangle (3 nodes associated with the vertices, 12 with the edges, 6 with the face) */ 4, /* 4-node third order edge (2 nodes associated with the vertices, 2 internal to the edge) */ 5, /* 5-node fourth order edge (2 nodes associated with the vertices, 3 internal to the edge) */ 6, /* 6-node fifth order edge (2 nodes associated with the vertices, 4 internal to the edge) */ 20, /* 20-node third order tetrahedron (4 nodes associated with the vertices, 12 with the edges, 4 with the faces) */ 35, /* 35-node fourth order tetrahedron (4 nodes associated with the vertices, 18 with the edges, 12 with the faces, and 1 with the volume) */ 56, /* 56-node fifth order tetrahedron (4 nodes associated with the vertices, 24 with the edges, 24 with the faces, and 4 with the volume) */ -1,-1, /* unsupported tetrahedral types */ -1,-1, /* unsupported polygonal and polyhedral types */ 16, /* 16-node third order quadrilateral (4 nodes associated with the vertices, 8 with the edges, 4 with the face) */ 25, /* 25-node fourth order quadrilateral (4 nodes associated with the vertices, 12 with the edges, 9 with the face) */ 36, /* 36-node fifth order quadrilateral (4 nodes associated with the vertices, 16 with the edges, 16 with the face) */ -1,-1,-1, /* unsupported quadrilateral types */ 28, /* 28-node sixth order complete triangle (3 nodes associated with the vertices, 15 with the edges, 10 with the face) */ 36, /* 36-node seventh order complete triangle (3 nodes associated with the vertices, 18 with the edges, 15 with the face) */ 45, /* 45-node eighth order complete triangle (3 nodes associated with the vertices, 21 with the edges, 21 with the face) */ 55, /* 55-node ninth order complete triangle (3 nodes associated with the vertices, 24 with the edges, 28 with the face) */ 66, /* 66-node tenth order complete triangle (3 nodes associated with the vertices, 27 with the edges, 36 with the face) */ 49, /* 49-node sixth order quadrilateral (4 nodes associated with the vertices, 20 with the edges, 25 with the face) */ 64, /* 64-node seventh order quadrilateral (4 nodes associated with the vertices, 24 with the edges, 36 with the face) */ 81, /* 81-node eighth order quadrilateral (4 nodes associated with the vertices, 28 with the edges, 49 with the face) */ 100, /* 100-node ninth order quadrilateral (4 nodes associated with the vertices, 32 with the edges, 64 with the face) */ 121, /* 121-node tenth order quadrilateral (4 nodes associated with the vertices, 36 with the edges, 81 with the face) */ -1,-1,-1,-1,-1, /* unsupported triangular types */ -1,-1,-1,-1,-1, /* unsupported quadrilateral types */ 7, /* 7-node sixth order edge (2 nodes associated with the vertices, 5 internal to the edge) */ 8, /* 8-node seventh order edge (2 nodes associated with the vertices, 6 internal to the edge) */ 9, /* 9-node eighth order edge (2 nodes associated with the vertices, 7 internal to the edge) */ 10, /* 10-node ninth order edge (2 nodes associated with the vertices, 8 internal to the edge) */ 11, /* 11-node tenth order edge (2 nodes associated with the vertices, 9 internal to the edge) */ -1, /* unsupported linear types */ -1,-1,-1, /* unsupported types */ 84, /* 84-node sixth order tetrahedron (4 nodes associated with the vertices, 30 with the edges, 40 with the faces, and 10 with the volume) */ 120, /* 120-node seventh order tetrahedron (4 nodes associated with the vertices, 36 with the edges, 60 with the faces, and 20 with the volume) */ 165, /* 165-node eighth order tetrahedron (4 nodes associated with the vertices, 42 with the edges, 84 with the faces, and 35 with the volume) */ 220, /* 220-node ninth order tetrahedron (4 nodes associated with the vertices, 48 with the edges, 112 with the faces, and 56 with the volume) */ 286, /* 286-node tenth order tetrahedron (4 nodes associated with the vertices, 54 with the edges, 144 with the faces, and 84 with the volume) */ -1,-1,-1, /* undefined types */ -1,-1,-1,-1,-1, /* unsupported tetrahedral types */ -1,-1,-1,-1,-1,-1, /* unsupported types */ 40, /* 40-node third order prism (6 nodes associated with the vertices, 18 with the edges, 14 with the faces, and 2 with the volume) */ 75, /* 75-node fourth order prism (6 nodes associated with the vertices, 27 with the edges, 33 with the faces, and 9 with the volume) */ 64, /* 64-node third order hexahedron (8 nodes associated with the vertices, 24 with the edges, 24 with the faces and 8 with the volume).*/ 125, /* 125-node fourth order hexahedron (8 nodes associated with the vertices, 36 with the edges, 54 with the faces and 27 with the volume).*/ 216, /* 216-node fifth order hexahedron (8 nodes associated with the vertices, 48 with the edges, 96 with the faces and 64 with the volume).*/ 343, /* 343-node sixth order hexahedron (8 nodes associated with the vertices, 60 with the edges, 150 with the faces and 125 with the volume).*/ 512, /* 512-node seventh order hexahedron (8 nodes associated with the vertices, 72 with the edges, 216 with the faces and 216 with the volume).*/ 729, /* 729-node eighth order hexahedron (8 nodes associated with the vertices, 84 with the edges, 294 with the faces and 343 with the volume).*/ 1000,/* 1000-node ninth order hexahedron (8 nodes associated with the vertices, 96 with the edges, 384 with the faces and 512 with the volume).*/ -1,-1,-1,-1,-1,-1,-1, /* unsupported hexahedron types */ 126, /* 126-node fifth order prism (6 nodes associated with the vertices, 36 with the edges, 60 with the faces, and 24 with the volume) */ 196, /* 196-node sixth order prism (6 nodes associated with the vertices, 45 with the edges, 95 with the faces, and 50 with the volume) */ 288, /* 288-node seventh order prism (6 nodes associated with the vertices, 54 with the edges, 138 with the faces, and 90 with the volume) */ 405, /* 405-node eighth order prism (6 nodes associated with the vertices, 63 with the edges, 189 with the faces, and 147 with the volume) */ 550, /* 550-node ninth order prism (6 nodes associated with the vertices, 72 with the edges, 248 with the faces, and 224 with the volume) */ -1,-1,-1,-1,-1,-1,-1, /* unsupported prism types */ 30, /* 30-node third order pyramid (5 nodes associated with the vertices, 16 with the edges and 8 with the faces, and 1 with the volume). */ 55, /* 55-node fourth order pyramid (5 nodes associated with the vertices, 24 with the edges and 21 with the faces, and 5 with the volume). */ 91, /* 91-node fifth order pyramid (5 nodes associated with the vertices, 32 with the edges and 40 with the faces, and 14 with the volume). */ 140, /* 140-node sixth order pyramid (5 nodes associated with the vertices, 40 with the edges and 65 with the faces, and 30 with the volume). */ 204, /* 204-node seventh order pyramid (5 nodes associated with the vertices, 48 with the edges and 96 with the faces, and 55 with the volume). */ 285, /* 285-node eighth order pyramid (5 nodes associated with the vertices, 56 with the edges and 133 with the faces, and 91 with the volume). */ 385 /* 385-node ninth order pyramid (5 nodes associated with the vertices, 64 with the edges and 176 with the faces, and 140 with the volume). */ }; /** The following mappings convert the Gmsh node orderings for high order elements to MFEM's L2 degree of freedom ordering. To support more options examine Gmsh's ordering and read off the indices in MFEM's order. For example 2nd order Gmsh quadrilaterals use the following ordering: 3--6--2 | | | 7 8 5 | | | 0--4--1 (from https://gmsh.info/doc/texinfo/gmsh.html#Node-ordering) Whereas MFEM uses a tensor product ordering with the horizontal axis cycling fastest so we would read off: 0 4 1 7 8 5 3 6 2 This corresponds to the quad9 mapping below. */ int lin3[] = {0,2,1}; // 2nd order segment int lin4[] = {0,2,3,1}; // 3rd order segment int tri6[] = {0,3,1,5,4,2}; // 2nd order triangle int tri10[] = {0,3,4,1,8,9,5,7,6,2}; // 3rd order triangle int quad9[] = {0,4,1,7,8,5,3,6,2}; // 2nd order quadrilateral int quad16[] = {0,4,5,1,11,12,13,6, // 3rd order quadrilateral 10,15,14,7,3,9,8,2 }; int tet10[] {0,4,1,6,5,2,7,9,8,3}; // 2nd order tetrahedron int tet20[] = {0,4,5,1,9,16,6,8,7,2, // 3rd order tetrahedron 11,17,15,18,19,13,10,14,12,3 }; int hex27[] {0,8,1,9,20,11,3,13,2, // 2nd order hexahedron 10,21,12,22,26,23,15,24,14, 4,16,5,17,25,18,7,19,6 }; int hex64[] {0,8,9,1,10,32,35,14, // 3rd order hexahedron 11,33,34,15,3,19,18,2, 12,36,37,16,40,56,57,44, 43,59,58,45,22,49,48,20, 13,39,38,17,41,60,61,47, 42,63,62,46,23,50,51,21, 4,24,25,5,26,52,53,28, 27,55,54,29,7,31,30,6 }; int wdg18[] = {0,6,1,7,9,2,8,15,10, // 2nd order wedge/prism 16,17,11,3,12,4,13,14,5 }; int wdg40[] = {0,6,7,1,8,24,12,9,13,2, // 3rd order wedge/prism 10,26,27,14,30,38,34,33,35,16, 11,29,28,15,31,39,37,32,36,17, 3,18,19,4,20,25,22,21,23,5 }; int pyr14[] = {0,5,1,6,13,8,3, // 2nd order pyramid 10,2,7,9,12,11,4 }; int pyr30[] = {0,5,6,1,7,25,28,11,8,26, // 3rd order pyramid 27,12,3,16,15,2,9,21,13,22, 29,23,19,24,17,10,14,20,18,4 }; vector elements_0D, elements_1D, elements_2D, elements_3D; elements_0D.reserve(num_of_all_elements); elements_1D.reserve(num_of_all_elements); elements_2D.reserve(num_of_all_elements); elements_3D.reserve(num_of_all_elements); // Temporary storage for high order vertices, if present vector*> ho_verts_1D, ho_verts_2D, ho_verts_3D; ho_verts_1D.reserve(num_of_all_elements); ho_verts_2D.reserve(num_of_all_elements); ho_verts_3D.reserve(num_of_all_elements); // Temporary storage for order of elements vector ho_el_order_1D, ho_el_order_2D, ho_el_order_3D; ho_el_order_1D.reserve(num_of_all_elements); ho_el_order_2D.reserve(num_of_all_elements); ho_el_order_3D.reserve(num_of_all_elements); // Vertex order mappings Array ho_lin(11); ho_lin = NULL; Array ho_tri(11); ho_tri = NULL; Array ho_sqr(11); ho_sqr = NULL; Array ho_tet(11); ho_tet = NULL; Array ho_hex(10); ho_hex = NULL; Array ho_wdg(10); ho_wdg = NULL; Array ho_pyr(10); ho_pyr = NULL; // Use predefined arrays at lowest orders (for efficiency) ho_lin[2] = lin3; ho_lin[3] = lin4; ho_tri[2] = tri6; ho_tri[3] = tri10; ho_sqr[2] = quad9; ho_sqr[3] = quad16; ho_tet[2] = tet10; ho_tet[3] = tet20; ho_hex[2] = hex27; ho_hex[3] = hex64; ho_wdg[2] = wdg18; ho_wdg[3] = wdg40; ho_pyr[2] = pyr14; ho_pyr[3] = pyr30; bool has_nonpositive_phys_domain = false; bool has_positive_phys_domain = false; if (binary) { int n_elem_part = 0; // partial sum of elements that are read const int header_size = 3; // header consists of 3 numbers: type of the element, number of // elements of this type, and number of tags int header[header_size]; int n_elem_one_type; // number of elements of a specific type while (n_elem_part < num_of_all_elements) { input.read(reinterpret_cast(header), header_size*sizeof(int)); type_of_element = header[0]; n_elem_one_type = header[1]; n_tags = header[2]; n_elem_part += n_elem_one_type; const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1]; vector data(1+n_tags+n_elem_nodes); for (int el = 0; el < n_elem_one_type; ++el) { input.read(reinterpret_cast(&data[0]), data.size()*sizeof(int)); int dd = 0; // index for data array serial_number = data[dd++]; // physical domain - the most important value (to distinguish // materials with different properties) phys_domain = (n_tags > 0) ? data[dd++] : 1; // elementary domain - to distinguish different geometrical // domains (typically, it's used rarely) elem_domain = (n_tags > 1) ? data[dd++] : 0; // the number of tags is bigger than 2 if there are some // partitions (domain decompositions) n_partitions = (n_tags > 2) ? data[dd++] : 0; // we currently just skip the partitions if they exist, and go // directly to vertices describing the mesh element vector vert_indices(n_elem_nodes); for (int vi = 0; vi < n_elem_nodes; ++vi) { map::const_iterator it = vertices_map.find(data[1+n_tags+vi]); if (it == vertices_map.end()) { MFEM_ABORT("Gmsh file : vertex index doesn't exist"); } vert_indices[vi] = it->second; } // Non-positive attributes are not allowed in MFEM. However, // by default, Gmsh sets the physical domain of all elements // to zero. In the case that all elements have physical domain // zero, we will given them attribute 1. If only some elements // have physical domain zero, we will throw an error. if (phys_domain <= 0) { has_nonpositive_phys_domain = true; phys_domain = 1; } else { has_positive_phys_domain = true; } // initialize the mesh element int el_order = 11; switch (type_of_element) { case 1: // 2-node line case 8: // 3-node line (2nd order) case 26: // 4-node line (3rd order) case 27: // 5-node line (4th order) case 28: // 6-node line (5th order) case 62: // 7-node line (6th order) case 63: // 8-node line (7th order) case 64: // 9-node line (8th order) case 65: // 10-node line (9th order) case 66: // 11-node line (10th order) { elements_1D.push_back( new Segment(&vert_indices[0], phys_domain)); if (type_of_element != 1) { el_order = n_elem_nodes - 1; Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_1D.push_back(hov); ho_el_order_1D.push_back(el_order); } break; } case 2: el_order--; // 3-node triangle case 9: el_order--; // 6-node triangle (2nd order) case 21: el_order--; // 10-node triangle (3rd order) case 23: el_order--; // 15-node triangle (4th order) case 25: el_order--; // 21-node triangle (5th order) case 42: el_order--; // 28-node triangle (6th order) case 43: el_order--; // 36-node triangle (7th order) case 44: el_order--; // 45-node triangle (8th order) case 45: el_order--; // 55-node triangle (9th order) case 46: el_order--; // 66-node triangle (10th order) { elements_2D.push_back( new Triangle(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_2D.push_back(hov); ho_el_order_2D.push_back(el_order); } break; } case 3: el_order--; // 4-node quadrangle case 10: el_order--; // 9-node quadrangle (2nd order) case 36: el_order--; // 16-node quadrangle (3rd order) case 37: el_order--; // 25-node quadrangle (4th order) case 38: el_order--; // 36-node quadrangle (5th order) case 47: el_order--; // 49-node quadrangle (6th order) case 48: el_order--; // 64-node quadrangle (7th order) case 49: el_order--; // 81-node quadrangle (8th order) case 50: el_order--; // 100-node quadrangle (9th order) case 51: el_order--; // 121-node quadrangle (10th order) { elements_2D.push_back( new Quadrilateral(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_2D.push_back(hov); ho_el_order_2D.push_back(el_order); } break; } case 4: el_order--; // 4-node tetrahedron case 11: el_order--; // 10-node tetrahedron (2nd order) case 29: el_order--; // 20-node tetrahedron (3rd order) case 30: el_order--; // 35-node tetrahedron (4th order) case 31: el_order--; // 56-node tetrahedron (5th order) case 71: el_order--; // 84-node tetrahedron (6th order) case 72: el_order--; // 120-node tetrahedron (7th order) case 73: el_order--; // 165-node tetrahedron (8th order) case 74: el_order--; // 220-node tetrahedron (9th order) case 75: el_order--; // 286-node tetrahedron (10th order) { #ifdef MFEM_USE_MEMALLOC elements_3D.push_back(TetMemory.Alloc()); elements_3D.back()->SetVertices(&vert_indices[0]); elements_3D.back()->SetAttribute(phys_domain); #else elements_3D.push_back( new Tetrahedron(&vert_indices[0], phys_domain)); #endif if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 5: el_order--; // 8-node hexahedron case 12: el_order--; // 27-node hexahedron (2nd order) case 92: el_order--; // 64-node hexahedron (3rd order) case 93: el_order--; // 125-node hexahedron (4th order) case 94: el_order--; // 216-node hexahedron (5th order) case 95: el_order--; // 343-node hexahedron (6th order) case 96: el_order--; // 512-node hexahedron (7th order) case 97: el_order--; // 729-node hexahedron (8th order) case 98: el_order--; // 1000-node hexahedron (9th order) { el_order--; // Gmsh does not define an order 10 hex elements_3D.push_back( new Hexahedron(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 6: el_order--; // 6-node wedge case 13: el_order--; // 18-node wedge (2nd order) case 90: el_order--; // 40-node wedge (3rd order) case 91: el_order--; // 75-node wedge (4th order) case 106: el_order--; // 126-node wedge (5th order) case 107: el_order--; // 196-node wedge (6th order) case 108: el_order--; // 288-node wedge (7th order) case 109: el_order--; // 405-node wedge (8th order) case 110: el_order--; // 550-node wedge (9th order) { el_order--; // Gmsh does not define an order 10 wedge elements_3D.push_back( new Wedge(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 7: el_order--; // 5-node pyramid case 14: el_order--; // 14-node pyramid (2nd order) case 118: el_order--; // 30-node pyramid (3rd order) case 119: el_order--; // 55-node pyramid (4th order) case 120: el_order--; // 91-node pyramid (5th order) case 121: el_order--; // 140-node pyramid (6th order) case 122: el_order--; // 204-node pyramid (7th order) case 123: el_order--; // 285-node pyramid (8th order) case 124: el_order--; // 385-node pyramid (9th order) { el_order--; // Gmsh does not define an order 10 pyr elements_3D.push_back( new Pyramid(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 15: // 1-node point { elements_0D.push_back( new Point(&vert_indices[0], phys_domain)); break; } default: // any other element MFEM_WARNING("Unsupported Gmsh element type."); break; } // switch (type_of_element) } // el (elements of one type) } // all elements } // if binary else // ASCII { for (int el = 0; el < num_of_all_elements; ++el) { input >> serial_number >> type_of_element >> n_tags; vector data(n_tags); for (int i = 0; i < n_tags; ++i) { input >> data[i]; } // physical domain - the most important value (to distinguish // materials with different properties) phys_domain = (n_tags > 0) ? data[0] : 1; // elementary domain - to distinguish different geometrical // domains (typically, it's used rarely) elem_domain = (n_tags > 1) ? data[1] : 0; // the number of tags is bigger than 2 if there are some // partitions (domain decompositions) n_partitions = (n_tags > 2) ? data[2] : 0; // we currently just skip the partitions if they exist, and go // directly to vertices describing the mesh element const int n_elem_nodes = nodes_of_gmsh_element[type_of_element-1]; vector vert_indices(n_elem_nodes); int index; for (int vi = 0; vi < n_elem_nodes; ++vi) { input >> index; map::const_iterator it = vertices_map.find(index); if (it == vertices_map.end()) { MFEM_ABORT("Gmsh file : vertex index doesn't exist"); } vert_indices[vi] = it->second; } // Non-positive attributes are not allowed in MFEM. However, // by default, Gmsh sets the physical domain of all elements // to zero. In the case that all elements have physical domain // zero, we will given them attribute 1. If only some elements // have physical domain zero, we will throw an error. if (phys_domain <= 0) { has_nonpositive_phys_domain = true; phys_domain = 1; } else { has_positive_phys_domain = true; } // initialize the mesh element int el_order = 11; switch (type_of_element) { case 1: // 2-node line case 8: // 3-node line (2nd order) case 26: // 4-node line (3rd order) case 27: // 5-node line (4th order) case 28: // 6-node line (5th order) case 62: // 7-node line (6th order) case 63: // 8-node line (7th order) case 64: // 9-node line (8th order) case 65: // 10-node line (9th order) case 66: // 11-node line (10th order) { elements_1D.push_back( new Segment(&vert_indices[0], phys_domain)); if (type_of_element != 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_1D.push_back(hov); el_order = n_elem_nodes - 1; ho_el_order_1D.push_back(el_order); } break; } case 2: el_order--; // 3-node triangle case 9: el_order--; // 6-node triangle (2nd order) case 21: el_order--; // 10-node triangle (3rd order) case 23: el_order--; // 15-node triangle (4th order) case 25: el_order--; // 21-node triangle (5th order) case 42: el_order--; // 28-node triangle (6th order) case 43: el_order--; // 36-node triangle (7th order) case 44: el_order--; // 45-node triangle (8th order) case 45: el_order--; // 55-node triangle (9th order) case 46: el_order--; // 66-node triangle (10th order) { elements_2D.push_back( new Triangle(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_2D.push_back(hov); ho_el_order_2D.push_back(el_order); } break; } case 3: el_order--; // 4-node quadrangle case 10: el_order--; // 9-node quadrangle (2nd order) case 36: el_order--; // 16-node quadrangle (3rd order) case 37: el_order--; // 25-node quadrangle (4th order) case 38: el_order--; // 36-node quadrangle (5th order) case 47: el_order--; // 49-node quadrangle (6th order) case 48: el_order--; // 64-node quadrangle (7th order) case 49: el_order--; // 81-node quadrangle (8th order) case 50: el_order--; // 100-node quadrangle (9th order) case 51: el_order--; // 121-node quadrangle (10th order) { elements_2D.push_back( new Quadrilateral(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_2D.push_back(hov); ho_el_order_2D.push_back(el_order); } break; } case 4: el_order--; // 4-node tetrahedron case 11: el_order--; // 10-node tetrahedron (2nd order) case 29: el_order--; // 20-node tetrahedron (3rd order) case 30: el_order--; // 35-node tetrahedron (4th order) case 31: el_order--; // 56-node tetrahedron (5th order) case 71: el_order--; // 84-node tetrahedron (6th order) case 72: el_order--; // 120-node tetrahedron (7th order) case 73: el_order--; // 165-node tetrahedron (8th order) case 74: el_order--; // 220-node tetrahedron (9th order) case 75: el_order--; // 286-node tetrahedron (10th order) { #ifdef MFEM_USE_MEMALLOC elements_3D.push_back(TetMemory.Alloc()); elements_3D.back()->SetVertices(&vert_indices[0]); elements_3D.back()->SetAttribute(phys_domain); #else elements_3D.push_back( new Tetrahedron(&vert_indices[0], phys_domain)); #endif if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 5: el_order--; // 8-node hexahedron case 12: el_order--; // 27-node hexahedron (2nd order) case 92: el_order--; // 64-node hexahedron (3rd order) case 93: el_order--; // 125-node hexahedron (4th order) case 94: el_order--; // 216-node hexahedron (5th order) case 95: el_order--; // 343-node hexahedron (6th order) case 96: el_order--; // 512-node hexahedron (7th order) case 97: el_order--; // 729-node hexahedron (8th order) case 98: el_order--; // 1000-node hexahedron (9th order) { el_order--; elements_3D.push_back( new Hexahedron(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 6: el_order--; // 6-node wedge case 13: el_order--; // 18-node wedge (2nd order) case 90: el_order--; // 40-node wedge (3rd order) case 91: el_order--; // 75-node wedge (4th order) case 106: el_order--; // 126-node wedge (5th order) case 107: el_order--; // 196-node wedge (6th order) case 108: el_order--; // 288-node wedge (7th order) case 109: el_order--; // 405-node wedge (8th order) case 110: el_order--; // 550-node wedge (9th order) { el_order--; elements_3D.push_back( new Wedge(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 7: el_order--; // 5-node pyramid case 14: el_order--; // 14-node pyramid (2nd order) case 118: el_order--; // 30-node pyramid (3rd order) case 119: el_order--; // 55-node pyramid (4th order) case 120: el_order--; // 91-node pyramid (5th order) case 121: el_order--; // 140-node pyramid (6th order) case 122: el_order--; // 204-node pyramid (7th order) case 123: el_order--; // 285-node pyramid (8th order) case 124: el_order--; // 385-node pyramid (9th order) { el_order--; elements_3D.push_back( new Pyramid(&vert_indices[0], phys_domain)); if (el_order > 1) { Array * hov = new Array; hov->Append(&vert_indices[0], n_elem_nodes); ho_verts_3D.push_back(hov); ho_el_order_3D.push_back(el_order); } break; } case 15: // 1-node point { elements_0D.push_back( new Point(&vert_indices[0], phys_domain)); break; } default: // any other element MFEM_WARNING("Unsupported Gmsh element type."); break; } // switch (type_of_element) } // el (all elements) } // if ASCII if (has_positive_phys_domain && has_nonpositive_phys_domain) { MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n" "By default Gmsh sets element tags (attributes)" " to '0' but MFEM requires that they be" " positive integers.\n" "Use \"Physical Curve\", \"Physical Surface\"," " or \"Physical Volume\" to set tags/attributes" " for all curves, surfaces, or volumes in your" " Gmsh geometry to values which are >= 1."); } else if (has_nonpositive_phys_domain) { mfem::out << "\nGmsh reader: all element attributes were zero.\n" << "MFEM only supports positive element attributes.\n" << "Setting element attributes to 1.\n\n"; } if (!elements_3D.empty()) { Dim = 3; NumOfElements = elements_3D.size(); elements.SetSize(NumOfElements); for (int el = 0; el < NumOfElements; ++el) { elements[el] = elements_3D[el]; } NumOfBdrElements = elements_2D.size(); boundary.SetSize(NumOfBdrElements); for (int el = 0; el < NumOfBdrElements; ++el) { boundary[el] = elements_2D[el]; } for (size_t el = 0; el < ho_el_order_3D.size(); el++) { mesh_order = max(mesh_order, ho_el_order_3D[el]); } // discard other elements for (size_t el = 0; el < elements_1D.size(); ++el) { delete elements_1D[el]; } for (size_t el = 0; el < elements_0D.size(); ++el) { delete elements_0D[el]; } } else if (!elements_2D.empty()) { Dim = 2; NumOfElements = elements_2D.size(); elements.SetSize(NumOfElements); for (int el = 0; el < NumOfElements; ++el) { elements[el] = elements_2D[el]; } NumOfBdrElements = elements_1D.size(); boundary.SetSize(NumOfBdrElements); for (int el = 0; el < NumOfBdrElements; ++el) { boundary[el] = elements_1D[el]; } for (size_t el = 0; el < ho_el_order_2D.size(); el++) { mesh_order = max(mesh_order, ho_el_order_2D[el]); } // discard other elements for (size_t el = 0; el < elements_0D.size(); ++el) { delete elements_0D[el]; } } else if (!elements_1D.empty()) { Dim = 1; NumOfElements = elements_1D.size(); elements.SetSize(NumOfElements); for (int el = 0; el < NumOfElements; ++el) { elements[el] = elements_1D[el]; } NumOfBdrElements = elements_0D.size(); boundary.SetSize(NumOfBdrElements); for (int el = 0; el < NumOfBdrElements; ++el) { boundary[el] = elements_0D[el]; } for (size_t el = 0; el < ho_el_order_1D.size(); el++) { mesh_order = max(mesh_order, ho_el_order_1D[el]); } } else { MFEM_ABORT("Gmsh file : no elements found"); return; } if (mesh_order > 1) { curved = 1; read_gf = 0; // initialize mesh_geoms so we can create Nodes FE space below this->SetMeshGen(); // Generate faces and edges so that we can define // FE space on the mesh this->FinalizeTopology(); // Construct GridFunction for uniformly spaced high order coords FiniteElementCollection* nfec; FiniteElementSpace* nfes; nfec = new L2_FECollection(mesh_order, Dim, BasisType::ClosedUniform); nfes = new FiniteElementSpace(this, nfec, spaceDim, Ordering::byVDIM); Nodes_gf.SetSpace(nfes); Nodes_gf.MakeOwner(nfec); int o = 0; int el_order = 1; for (int el = 0; el < NumOfElements; el++) { const int * vm = NULL; Array * ho_verts = NULL; switch (GetElementType(el)) { case Element::SEGMENT: ho_verts = ho_verts_1D[el]; el_order = ho_el_order_1D[el]; if (!ho_lin[el_order]) { ho_lin[el_order] = new int[ho_verts->Size()]; GmshHOSegmentMapping(el_order, ho_lin[el_order]); } vm = ho_lin[el_order]; break; case Element::TRIANGLE: ho_verts = ho_verts_2D[el]; el_order = ho_el_order_2D[el]; if (!ho_tri[el_order]) { ho_tri[el_order] = new int[ho_verts->Size()]; GmshHOTriangleMapping(el_order, ho_tri[el_order]); } vm = ho_tri[el_order]; break; case Element::QUADRILATERAL: ho_verts = ho_verts_2D[el]; el_order = ho_el_order_2D[el]; if (!ho_sqr[el_order]) { ho_sqr[el_order] = new int[ho_verts->Size()]; GmshHOQuadrilateralMapping(el_order, ho_sqr[el_order]); } vm = ho_sqr[el_order]; break; case Element::TETRAHEDRON: ho_verts = ho_verts_3D[el]; el_order = ho_el_order_3D[el]; if (!ho_tet[el_order]) { ho_tet[el_order] = new int[ho_verts->Size()]; GmshHOTetrahedronMapping(el_order, ho_tet[el_order]); } vm = ho_tet[el_order]; break; case Element::HEXAHEDRON: ho_verts = ho_verts_3D[el]; el_order = ho_el_order_3D[el]; if (!ho_hex[el_order]) { ho_hex[el_order] = new int[ho_verts->Size()]; GmshHOHexahedronMapping(el_order, ho_hex[el_order]); } vm = ho_hex[el_order]; break; case Element::WEDGE: ho_verts = ho_verts_3D[el]; el_order = ho_el_order_3D[el]; if (!ho_wdg[el_order]) { ho_wdg[el_order] = new int[ho_verts->Size()]; GmshHOWedgeMapping(el_order, ho_wdg[el_order]); } vm = ho_wdg[el_order]; break; case Element::PYRAMID: ho_verts = ho_verts_3D[el]; el_order = ho_el_order_3D[el]; if (!ho_pyr[el_order]) { ho_pyr[el_order] = new int[ho_verts->Size()]; GmshHOPyramidMapping(el_order, ho_pyr[el_order]); } vm = ho_pyr[el_order]; break; default: // Any other element type MFEM_WARNING("Unsupported Gmsh element type."); break; } int nv = (ho_verts) ? ho_verts->Size() : 0; for (int v = 0; v v2v(NumOfVertices); for (int i = 0; i < v2v.Size(); i++) { v2v[i] = i; } int num_per_ent; int num_nodes; input >> num_per_ent; getline(input, buff); // Read end-of-line for (int i = 0; i < num_per_ent; i++) { getline(input, buff); // Read and ignore entity dimension and tags getline(input, buff); // If affine mapping exist, read and ignore if (!strncmp(buff.c_str(), "Affine", 6)) { input >> num_nodes; } else { num_nodes = atoi(buff.c_str()); } for (int j=0; j> slave >> master; v2v[slave - 1] = master - 1; } getline(input, buff); // Read end-of-line } // Follow existing long chains of slave->master in v2v array. // Upon completion of this loop, each v2v[slave] will point to a true // master vertex. This algorithm is useful for periodicity defined in // multiple directions. for (int slave = 0; slave < v2v.Size(); slave++) { int master = v2v[slave]; if (master != slave) { // This loop will end if it finds a circular dependency. while (v2v[master] != master && master != slave) { master = v2v[master]; } if (master == slave) { // if master and slave are the same vertex, circular dependency // exists. We need to fix the problem, we choose slave. v2v[slave] = slave; } else { // the long chain has ended on the true master vertex. v2v[slave] = master; } } } // Convert nodes to discontinuous GridFunction (if they aren't already) if (mesh_order == 1) { this->FinalizeTopology(); this->SetMeshGen(); this->SetCurvature(1, true, spaceDim, Ordering::byVDIM); } // Replace "slave" vertex indices in the element connectivity // with their corresponding "master" vertex indices. for (int i = 0; i < this->GetNE(); i++) { Element *el = this->GetElement(i); int *v = el->GetVertices(); int nv = el->GetNVertices(); for (int j = 0; j < nv; j++) { v[j] = v2v[v[j]]; } } // Replace "slave" vertex indices in the boundary element connectivity // with their corresponding "master" vertex indices. for (int i = 0; i < this->GetNBE(); i++) { Element *el = this->GetBdrElement(i); int *v = el->GetVertices(); int nv = el->GetNVertices(); for (int j = 0; j < nv; j++) { v[j] = v2v[v[j]]; } } } } // we reach the end of the file this->RemoveUnusedVertices(); if (periodic) { this->RemoveInternalBoundaries(); } this->FinalizeTopology(); // If a high order coordinate field was created project it onto the mesh if (mesh_order > 1) { SetCurvature(mesh_order, periodic, spaceDim, Ordering::byVDIM); VectorGridFunctionCoefficient NodesCoef(&Nodes_gf); Nodes->ProjectCoefficient(NodesCoef); } } #ifdef MFEM_USE_NETCDF void Mesh::ReadCubit(const char *filename, int &curved, int &read_gf) { read_gf = 0; // curved set to zero will change if mesh is indeed curved curved = 0; const int sideMapTri3[3][2] = { {1,2}, {2,3}, {3,1}, }; const int sideMapQuad4[4][2] = { {1,2}, {2,3}, {3,4}, {4,1}, }; const int sideMapTri6[3][3] = { {1,2,4}, {2,3,5}, {3,1,6}, }; const int sideMapQuad9[4][3] = { {1,2,5}, {2,3,6}, {3,4,7}, {4,1,8}, }; const int sideMapTet4[4][3] = { {1,2,4}, {2,3,4}, {1,4,3}, {1,3,2} }; const int sideMapTet10[4][6] = { {1,2,4,5,9,8}, {2,3,4,6,10,9}, {1,4,3,8,10,7}, {1,3,2,7,6,5} }; const int sideMapHex8[6][4] = { {1,2,6,5}, {2,3,7,6}, {4,3,7,8}, {1,4,8,5}, {1,4,3,2}, {5,8,7,6} }; const int sideMapHex27[6][9] = { {1,2,6,5,9,14,17,13,26}, {2,3,7,6,10,15,18,14,25}, {4,3,7,8,11,15,19,16,27}, {1,4,8,5,12,16,20,13,24}, {1,4,3,2,12,11,10,9,22}, {5,8,7,6,20,19,18,17,23} }; // 1,2,3,4,5,6,7,8,9,10 const int mfemToGenesisTet10[10] = {1,2,3,4,5,7,8,6,9,10}; // 1,2,3,4,5,6,7,8,9,10,11, const int mfemToGenesisHex27[27] = {1,2,3,4,5,6,7,8,9,10,11, // 12,13,14,15,16,17,18,19 12,17,18,19,20,13,14,15, // 20,21,22,23,24,25,26,27 16,22,26,25,27,24,23,21 }; const int mfemToGenesisTri6[6] = {1,2,3,4,5,6}; const int mfemToGenesisQuad9[9] = {1,2,3,4,5,6,7,8,9}; // error handling. int retval; // dummy string constexpr size_t buf_size = 256; char str_dummy[buf_size]; char temp_str[buf_size]; int temp_id; // open the file. int ncid; if ((retval = nc_open(filename, NC_NOWRITE, &ncid))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } // read important dimensions int id; size_t num_dim=0, num_nodes=0, num_elem=0, num_el_blk=0, num_side_sets=0; if ((retval = nc_inq_dimid(ncid, "num_dim", &id)) || (retval = nc_inq_dim(ncid, id, str_dummy, &num_dim)) || (retval = nc_inq_dimid(ncid, "num_nodes", &id)) || (retval = nc_inq_dim(ncid, id, str_dummy, &num_nodes)) || (retval = nc_inq_dimid(ncid, "num_elem", &id)) || (retval = nc_inq_dim(ncid, id, str_dummy, &num_elem)) || (retval = nc_inq_dimid(ncid, "num_el_blk", &id)) || (retval = nc_inq_dim(ncid, id, str_dummy, &num_el_blk))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } if ((retval = nc_inq_dimid(ncid, "num_side_sets", &id)) || (retval = nc_inq_dim(ncid, id, str_dummy, &num_side_sets))) { num_side_sets = 0; } Dim = num_dim; // create arrays for element blocks size_t *num_el_in_blk = new size_t[num_el_blk]; size_t num_node_per_el; int previous_num_node_per_el = 0; for (int i = 0; i < (int) num_el_blk; i++) { snprintf(temp_str, buf_size, "num_el_in_blk%d", i+1); if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) || (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_el_in_blk[i]))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } snprintf(temp_str, buf_size, "num_nod_per_el%d", i+1); if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) || (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_node_per_el))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } // check for different element types in each block // which is not currently supported if (i != 0) { if ((int) num_node_per_el != previous_num_node_per_el) { MFEM_ABORT("Element blocks of different element types not supported"); } } previous_num_node_per_el = num_node_per_el; } // Determine CUBIT element and face type enum CubitElementType { ELEMENT_TRI3, ELEMENT_TRI6, ELEMENT_QUAD4, ELEMENT_QUAD9, ELEMENT_TET4, ELEMENT_TET10, ELEMENT_HEX8, ELEMENT_HEX27 }; enum CubitFaceType { FACE_EDGE2, FACE_EDGE3, FACE_TRI3, FACE_TRI6, FACE_QUAD4, FACE_QUAD9 }; CubitElementType cubit_element_type = ELEMENT_TRI3; // suppress a warning CubitFaceType cubit_face_type = FACE_EDGE2; // suppress a warning int num_element_linear_nodes = 0; // initialize to suppress a warning if (num_dim == 2) { switch (num_node_per_el) { case (3) : { cubit_element_type = ELEMENT_TRI3; cubit_face_type = FACE_EDGE2; num_element_linear_nodes = 3; break; } case (6) : { cubit_element_type = ELEMENT_TRI6; cubit_face_type = FACE_EDGE3; num_element_linear_nodes = 3; break; } case (4) : { cubit_element_type = ELEMENT_QUAD4; cubit_face_type = FACE_EDGE2; num_element_linear_nodes = 4; break; } case (9) : { cubit_element_type = ELEMENT_QUAD9; cubit_face_type = FACE_EDGE3; num_element_linear_nodes = 4; break; } default : { MFEM_ABORT("Don't know what to do with a " << num_node_per_el << " node 2D element\n"); } } } else if (num_dim == 3) { switch (num_node_per_el) { case (4) : { cubit_element_type = ELEMENT_TET4; cubit_face_type = FACE_TRI3; num_element_linear_nodes = 4; break; } case (10) : { cubit_element_type = ELEMENT_TET10; cubit_face_type = FACE_TRI6; num_element_linear_nodes = 4; break; } case (8) : { cubit_element_type = ELEMENT_HEX8; cubit_face_type = FACE_QUAD4; num_element_linear_nodes = 8; break; } case (27) : { cubit_element_type = ELEMENT_HEX27; cubit_face_type = FACE_QUAD9; num_element_linear_nodes = 8; break; } default : { MFEM_ABORT("Don't know what to do with a " << num_node_per_el << " node 3D element\n"); } } } else { MFEM_ABORT("Invalid dimension: num_dim = " << num_dim); } // Determine order of elements int order = 0; if (cubit_element_type == ELEMENT_TRI3 || cubit_element_type == ELEMENT_QUAD4 || cubit_element_type == ELEMENT_TET4 || cubit_element_type == ELEMENT_HEX8) { order = 1; } else if (cubit_element_type == ELEMENT_TRI6 || cubit_element_type == ELEMENT_QUAD9 || cubit_element_type == ELEMENT_TET10 || cubit_element_type == ELEMENT_HEX27) { order = 2; } // create array for number of sides in side sets size_t *num_side_in_ss = new size_t[num_side_sets]; for (int i = 0; i < (int) num_side_sets; i++) { snprintf(temp_str, buf_size, "num_side_ss%d", i+1); if ((retval = nc_inq_dimid(ncid, temp_str, &temp_id)) || (retval = nc_inq_dim(ncid, temp_id, str_dummy, &num_side_in_ss[i]))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } } // read the coordinates double *coordx = new double[num_nodes]; double *coordy = new double[num_nodes]; double *coordz = new double[num_nodes]; if ((retval = nc_inq_varid(ncid, "coordx", &id)) || (retval = nc_get_var_double(ncid, id, coordx)) || (retval = nc_inq_varid(ncid, "coordy", &id)) || (retval = nc_get_var_double(ncid, id, coordy))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } if (num_dim == 3) { if ((retval = nc_inq_varid(ncid, "coordz", &id)) || (retval = nc_get_var_double(ncid, id, coordz))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } } // read the element blocks int **elem_blk = new int*[num_el_blk]; for (int i = 0; i < (int) num_el_blk; i++) { elem_blk[i] = new int[num_el_in_blk[i] * num_node_per_el]; snprintf(temp_str, buf_size, "connect%d", i+1); if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) || (retval = nc_get_var_int(ncid, temp_id, elem_blk[i]))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } } int *ebprop = new int[num_el_blk]; if ((retval = nc_inq_varid(ncid, "eb_prop1", &id)) || (retval = nc_get_var_int(ncid, id, ebprop))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } // read the side sets, a side is is given by (element, face) pairs int **elem_ss = new int*[num_side_sets]; int **side_ss = new int*[num_side_sets]; for (int i = 0; i < (int) num_side_sets; i++) { elem_ss[i] = new int[num_side_in_ss[i]]; side_ss[i] = new int[num_side_in_ss[i]]; snprintf(temp_str, buf_size, "elem_ss%d", i+1); if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) || (retval = nc_get_var_int(ncid, temp_id, elem_ss[i]))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } snprintf(temp_str, buf_size,"side_ss%d",i+1); if ((retval = nc_inq_varid(ncid, temp_str, &temp_id)) || (retval = nc_get_var_int(ncid, temp_id, side_ss[i]))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } } int *ssprop = new int[num_side_sets]; if ((num_side_sets > 0) && ((retval = nc_inq_varid(ncid, "ss_prop1", &id)) || (retval = nc_get_var_int(ncid, id, ssprop)))) { MFEM_ABORT("Fatal NetCDF error: " << nc_strerror(retval)); } // convert (elem,side) pairs to 2D elements int num_face_nodes = 0; int num_face_linear_nodes = 0; switch (cubit_face_type) { case (FACE_EDGE2): { num_face_nodes = 2; num_face_linear_nodes = 2; break; } case (FACE_EDGE3): { num_face_nodes = 3; num_face_linear_nodes = 2; break; } case (FACE_TRI3): { num_face_nodes = 3; num_face_linear_nodes = 3; break; } case (FACE_TRI6): { num_face_nodes = 6; num_face_linear_nodes = 3; break; } case (FACE_QUAD4): { num_face_nodes = 4; num_face_linear_nodes = 4; break; } case (FACE_QUAD9): { num_face_nodes = 9; num_face_linear_nodes = 4; break; } } // given a global element number, determine the element block and local // element number int *start_of_block = new int[num_el_blk+1]; start_of_block[0] = 0; for (int i = 1; i < (int) num_el_blk+1; i++) { start_of_block[i] = start_of_block[i-1] + num_el_in_blk[i-1]; } int **ss_node_id = new int*[num_side_sets]; for (int i = 0; i < (int) num_side_sets; i++) { ss_node_id[i] = new int[num_side_in_ss[i]*num_face_nodes]; for (int j = 0; j < (int) num_side_in_ss[i]; j++) { int glob_ind = elem_ss[i][j]-1; int iblk = 0; int loc_ind; while (iblk < (int) num_el_blk && glob_ind >= start_of_block[iblk+1]) { iblk++; } if (iblk >= (int) num_el_blk) { MFEM_ABORT("Sideset element does not exist"); } loc_ind = glob_ind - start_of_block[iblk]; int this_side = side_ss[i][j]; int ielem = loc_ind*num_node_per_el; for (int k = 0; k < num_face_nodes; k++) { int inode; switch (cubit_element_type) { case (ELEMENT_TRI3): { inode = sideMapTri3[this_side-1][k]; break; } case (ELEMENT_TRI6): { inode = sideMapTri6[this_side-1][k]; break; } case (ELEMENT_QUAD4): { inode = sideMapQuad4[this_side-1][k]; break; } case (ELEMENT_QUAD9): { inode = sideMapQuad9[this_side-1][k]; break; } case (ELEMENT_TET4): { inode = sideMapTet4[this_side-1][k]; break; } case (ELEMENT_TET10): { inode = sideMapTet10[this_side-1][k]; break; } case (ELEMENT_HEX8): { inode = sideMapHex8[this_side-1][k]; break; } case (ELEMENT_HEX27): { inode = sideMapHex27[this_side-1][k]; break; } } ss_node_id[i][j*num_face_nodes+k] = elem_blk[iblk][ielem + inode - 1]; } } } // we need another node ID mapping since MFEM needs contiguous vertex IDs std::vector uniqueVertexID; for (int iblk = 0; iblk < (int) num_el_blk; iblk++) { for (int i = 0; i < (int) num_el_in_blk[iblk]; i++) { for (int j = 0; j < num_element_linear_nodes; j++) { uniqueVertexID.push_back(elem_blk[iblk][i*num_node_per_el + j]); } } } std::sort(uniqueVertexID.begin(), uniqueVertexID.end()); std::vector::iterator newEnd; newEnd = std::unique(uniqueVertexID.begin(), uniqueVertexID.end()); uniqueVertexID.resize(std::distance(uniqueVertexID.begin(), newEnd)); // OK at this point uniqueVertexID contains a list of all the nodes that are // actually used by the mesh, 1-based, and sorted. We need to invert this // list, the inverse is a map std::map cubitToMFEMVertMap; for (int i = 0; i < (int) uniqueVertexID.size(); i++) { cubitToMFEMVertMap[uniqueVertexID[i]] = i+1; } MFEM_ASSERT(cubitToMFEMVertMap.size() == uniqueVertexID.size(), "This should never happen\n"); // OK now load up the MFEM mesh structures // load up the vertices NumOfVertices = uniqueVertexID.size(); vertices.SetSize(NumOfVertices); for (int i = 0; i < (int) uniqueVertexID.size(); i++) { vertices[i](0) = coordx[uniqueVertexID[i] - 1]; vertices[i](1) = coordy[uniqueVertexID[i] - 1]; if (Dim == 3) { vertices[i](2) = coordz[uniqueVertexID[i] - 1]; } } NumOfElements = num_elem; elements.SetSize(num_elem); int elcount = 0; int renumberedVertID[8]; for (int iblk = 0; iblk < (int) num_el_blk; iblk++) { int NumNodePerEl = num_node_per_el; for (int i = 0; i < (int) num_el_in_blk[iblk]; i++) { for (int j = 0; j < num_element_linear_nodes; j++) { renumberedVertID[j] = cubitToMFEMVertMap[elem_blk[iblk][i*NumNodePerEl+j]]-1; } switch (cubit_element_type) { case (ELEMENT_TRI3): case (ELEMENT_TRI6): { elements[elcount] = new Triangle(renumberedVertID,ebprop[iblk]); break; } case (ELEMENT_QUAD4): case (ELEMENT_QUAD9): { elements[elcount] = new Quadrilateral(renumberedVertID,ebprop[iblk]); break; } case (ELEMENT_TET4): case (ELEMENT_TET10): { #ifdef MFEM_USE_MEMALLOC elements[elcount] = TetMemory.Alloc(); elements[elcount]->SetVertices(renumberedVertID); elements[elcount]->SetAttribute(ebprop[iblk]); #else elements[elcount] = new Tetrahedron(renumberedVertID, ebprop[iblk]); #endif break; } case (ELEMENT_HEX8): case (ELEMENT_HEX27): { elements[elcount] = new Hexahedron(renumberedVertID,ebprop[iblk]); break; } } elcount++; } } // load up the boundary elements NumOfBdrElements = 0; for (int iss = 0; iss < (int) num_side_sets; iss++) { NumOfBdrElements += num_side_in_ss[iss]; } boundary.SetSize(NumOfBdrElements); int sidecount = 0; for (int iss = 0; iss < (int) num_side_sets; iss++) { for (int i = 0; i < (int) num_side_in_ss[iss]; i++) { for (int j = 0; j < num_face_linear_nodes; j++) { renumberedVertID[j] = cubitToMFEMVertMap[ss_node_id[iss][i*num_face_nodes+j]] - 1; } switch (cubit_face_type) { case (FACE_EDGE2): case (FACE_EDGE3): { boundary[sidecount] = new Segment(renumberedVertID,ssprop[iss]); break; } case (FACE_TRI3): case (FACE_TRI6): { boundary[sidecount] = new Triangle(renumberedVertID,ssprop[iss]); break; } case (FACE_QUAD4): case (FACE_QUAD9): { boundary[sidecount] = new Quadrilateral(renumberedVertID,ssprop[iss]); break; } } sidecount++; } } if (order == 2) { curved = 1; int *mymap = NULL; switch (cubit_element_type) { case (ELEMENT_TRI6): { mymap = (int *) mfemToGenesisTri6; break; } case (ELEMENT_QUAD9): { mymap = (int *) mfemToGenesisQuad9; break; } case (ELEMENT_TET10): { mymap = (int *) mfemToGenesisTet10; break; } case (ELEMENT_HEX27): { mymap = (int *) mfemToGenesisHex27; break; } case (ELEMENT_TRI3): case (ELEMENT_QUAD4): case (ELEMENT_TET4): case (ELEMENT_HEX8): { MFEM_ABORT("Something went wrong. Linear elements detected when order is 2."); break; } } FinalizeTopology(); // Define quadratic FE space FiniteElementCollection *fec = new H1_FECollection(2,3); FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim, Ordering::byVDIM); Nodes = new GridFunction(fes); Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes' own_nodes = 1; // int nTotDofs = fes->GetNDofs(); // int nTotVDofs = fes->GetVSize(); // mfem::out << endl << "nTotDofs = " << nTotDofs << " nTotVDofs " // << nTotVDofs << endl << endl; for (int i = 0; i < NumOfElements; i++) { Array dofs; fes->GetElementDofs(i, dofs); Array vdofs; vdofs.SetSize(dofs.Size()); for (int l = 0; l < dofs.Size(); l++) { vdofs[l] = dofs[l]; } fes->DofsToVDofs(vdofs); int iblk = 0; int loc_ind; while (iblk < (int) num_el_blk && i >= start_of_block[iblk+1]) { iblk++; } loc_ind = i - start_of_block[iblk]; for (int j = 0; j < dofs.Size(); j++) { int point_id = elem_blk[iblk][loc_ind*num_node_per_el + mymap[j] - 1] - 1; (*Nodes)(vdofs[j]) = coordx[point_id]; (*Nodes)(vdofs[j]+1) = coordy[point_id]; if (Dim == 3) { (*Nodes)(vdofs[j]+2) = coordz[point_id]; } } } } // clean up all netcdf stuff nc_close(ncid); for (int i = 0; i < (int) num_side_sets; i++) { delete [] elem_ss[i]; delete [] side_ss[i]; } delete [] elem_ss; delete [] side_ss; delete [] num_el_in_blk; delete [] num_side_in_ss; delete [] coordx; delete [] coordy; delete [] coordz; for (int i = 0; i < (int) num_el_blk; i++) { delete [] elem_blk[i]; } delete [] elem_blk; delete [] start_of_block; for (int i = 0; i < (int) num_side_sets; i++) { delete [] ss_node_id[i]; } delete [] ss_node_id; delete [] ebprop; delete [] ssprop; } #endif // #ifdef MFEM_USE_NETCDF } // namespace mfem