libMesh (.xda, .xdr)
libMesh's native mesh file (XdrIO), also the mesh MOOSE writes with --mesh-only and in its checkpoints. .xda is ASCII, .xdr the same stream of values in big-endian XDR. MOOSE users are also served by Exodus; this format is for meshes that exist only in libMesh's own file.
| Format name | libmesh |
| Extensions | .xda, .xdr, and each with .gz or .bz2 (also recognised by content: the libMesh- version string, as text or as an XDR string) |
| Read / Write | ✓ / ✓ (writer v16.11.0) |
| Extra dependencies | — (the native reader inflates gzip with zlib when built with it) |
Reading
import meshioplusplus
mesh = meshioplusplus.read("mesh.xdr") # or meshioplusplus.libmesh.read(...)read takes no options. The encoding comes from the content, not the extension, and gzip or bzip2 compression (as libMesh writes .xda.gz/.xdr.bz2) is inflated. Both engines (the C++ core and the pure-Python reference) read the same meshes; the core inflates gzip through zlib and leaves bzip2 to the Python reader, so the native CLI and the C, Fortran, Julia, R and WASM bindings read gzip only.
The stream
A version string (libMesh-0.7.0+ to libMesh-1.8.0), the element and node counts, four "inline or separate file" flags (boundary conditions, subdomain, processor and p-level ids), then:
| Section | Since | Read as |
|---|---|---|
| per-field integer sizes | 0.9.2 | the connectivity integer width (4 or 8 bytes in XDR; header integers are 8 bytes from 1.3.0 on) |
| extra integer names, elemsets | 1.8.0 | skipped |
| subdomain id → name map | 0.9.2 | region names |
| one connectivity block per refinement level | cells (see below) | |
| coordinates | points | |
| node unique ids | 0.9.6 | skipped |
| side sets (with a name map from 0.9.2) | side regions | |
| node sets | 0.9.2 | point regions |
| edge and shell-face sets | 1.1.0 | line cells and cell regions (see Boundaries) |
Legacy pre-libMesh files (DEAL 003, LIBM 0) are refused, as libMesh itself refuses them.
Cells
- Only active elements (the leaves of the refinement tree) become cells. When the file holds refinement levels,
libmesh:levelrecords each cell's level. - The subdomain id is the
libmesh:subdomaincell data and acellregion per subdomain, named by the file's map, elsesubdomain_<id>(tag = id). An inline p-level becomeslibmesh:p_level. - Node ids that no element uses (libMesh writes their coordinates as NaN) are dropped; the original ids are then kept as
libmesh:idpoint data.
| libMesh type | meshio++ type |
|---|---|
| EDGE2 / EDGE3 / EDGE4 | line / line3 / line4 |
| TRI3, TRISHELL3, TRI3SUBDIVISION / TRI6 / TRI7 | triangle / triangle6 / triangle7 |
| QUAD4, QUADSHELL4 / QUAD8, QUADSHELL8 / QUAD9, QUADSHELL9 | quad / quad8 / quad9 |
| TET4 / TET10 / TET14 | tetra / tetra10 / tetra10 (face nodes dropped) |
| HEX8 / HEX20 / HEX27 | hexahedron / hexahedron20 / hexahedron27 |
| PRISM6 / PRISM15 / PRISM18 / PRISM20, PRISM21 | wedge / wedge15 / wedge18 / wedge18 (extra nodes dropped) |
| PYRAMID5 / PYRAMID13 / PYRAMID14 / PYRAMID18 | pyramid / pyramid13 / pyramid14 / pyramid14 (extra nodes dropped) |
| NODEELEM | vertex |
| infinite elements (INF*) | skipped with a warning |
| C0POLYGON, C0POLYHEDRON, REMOTEELEM | ReadError (see below) |
Dropping extra nodes is logged and recorded as a provenance note; the dropped nodes stay as points.
No .xda/.xdr file can hold a C0POLYGON or C0POLYHEDRON: XdrIO writes no node count per element and takes it from the element type, which for these two types has none, so libMesh cannot write them either (XdrIO::pack_element asserts the fixed count). They remain a ReadError.
Node order
HEX20, HEX27, PRISM15 and PRISM18 list their vertical mid-edge nodes before the top ring (and HEX27 its face centres bottom, y−, x+, y+, x−, top), so they use the "libmesh" tables of the node-ordering registry, taken from libMesh's own VTK connectivity (cell_hex27.C …). Every other type is already in meshio++'s order.
Boundaries
A side set entry names an element and one of its sides in libMesh's numbering (Hex8::side_nodes_map …). It becomes a side region entry through the facet with the same corner nodes. libMesh keeps boundary conditions on the coarse elements, so a side of a refined element is carried down to every active descendant whose side lies on it. The sides of line elements (their end points) have no side-region form and are skipped with a warning.
Edge sets and shell-face sets (libMesh 1.1.0 on) share the side sets' id space and name map:
- An edge set entry names an element and one of its edges (
Hex8::edge_nodes_map…). Each named edge becomes alinecell (aline3with the element's mid-edge node when the element is quadratic), shared by every entry naming it, in acellregion<name>:edge(tag = id). These cells are not libMesh elements: theirlibmesh:subdomain(andlibmesh:level,libmesh:p_level) is −1. An edge of a refined element spans its coarse edge. - A shell-face set entry names a 2-D element and its face 0 or 1. It becomes a
cellregion<name>:shellface0or<name>:shellface1on the element (on every active descendant of a refined one). libMesh gives the two faces no geometric meaning, so the names keep its numbers.
<name> is the side-set name map's entry for the id, else boundary_<id>.
Writing
meshioplusplus.write("mesh.xdr", mesh) # XDR; "mesh.xda" for ASCII
meshioplusplus.write("mesh.xda.gz", mesh) # compressed (Python only)write takes no options and emits the libMesh-1.8.0 layout that XdrIO::write produces: 8-byte ids, inline subdomain ids, no unique ids, no processor ids. The encoding comes from the extension (.xdr is XDR, anything else ASCII). A trailing .gz or .bz2 compresses the file; the native writer does not compress, so the Python layer has the core write the plain stream and compresses it (gzip without a timestamp, so the bytes are reproducible). Both engines write the same bytes.
- Cells. Every cell whose type is in the table above (the plain type, not the shell or subdivision variant) becomes a level-0 element; refinement trees are not written, so a refined mesh read from a file is written flat, as its active cells.
polygon,polyhedron, Lagrange and other cells libMesh has no type for are dropped with a warning and a provenance note. The"libmesh"node-ordering tables are applied in reverse. - Subdomains.
libmesh:subdomainwhen present, else the firstcellregion that holds the cell (its tag, or a fresh id past the largest tag), else 0. Ids must fit libMesh'ssubdomain_id_type(0 to 65534). Region names other thansubdomain_<id>go into the subdomain name map. - Boundaries.
sideregions become side sets, each entry matched to the libMesh side with the same corner nodes. The<name>:edgeand<name>:shellface<k>cell regions the reader makes become edge and shell-face sets again; their line cells are not written as elements.pointregions become node sets. A region's tag is its id; untagged regions get fresh ids (one id space for side, edge and shell-face sets, another for node sets). Names other thanboundary_<id>/nodeset_<id>go into the name maps. A side or edge that no written element has is dropped with a warning. - Node ids. Points are numbered in order, unless
libmesh:idholds distinct non-negative ids (as the reader leaves it when the file had unused ids): then those ids are kept and the unused ones written as NaN in XDR, as libMesh writes them, and as 0 in ASCII, because libMesh's ASCII reader cannot parse thenanits own writer prints (it only loads the nodes elements use, so the value is never looked at). - p-levels.
libmesh:p_levelis written inline.libmesh:leveland other data arrays are not written.
Validation
The fixtures under tests/python/meshes/libmesh/ are written by tools/gen_libmesh_fixtures.py in libMesh's own layout, since libMesh (LGPL) is not a dependency. For v16.11.0 libMesh 1.8.0 was built from source outside the repository: it reads every fixture and every file the writer produces (from these fixtures, from libMesh-written meshes with edge and shell-face sets, gzip and bzip2 files, and from Gmsh, Exodus and Abaqus meshes) with the same elements, subdomains, side, edge, shell-face and node sets and names, and the same measure; meshio++ reads libMesh's own files the same way. Outside the repository the reader was run on libMesh's reference_elements/ and tests/meshes/ samples (both encodings for every element type, and an AMR mesh with side sets): every cell has a positive volume and every higher-order node sits where meshio++'s tables put it. Three of libMesh's hand-written reference files (one_pyramid13/14/18.xda) announce inline subdomain ids they do not contain; libMesh itself cannot read them either, and meshio++ refuses them.