Skip to content

PERMAS (.post, .dato)

The PERMAS data-file format: $-delimited keyword sections in plain text, optionally gzip-compressed.

Format namepermas
Extensions.post, .post.gz, .dato, .dato.gz
Read / Write✓ / ✓
Extra dependencies

Reading & writing

python
import meshioplusplus

mesh = meshioplusplus.read("model.post")
meshioplusplus.permas.write("out.post", mesh)

write takes no keyword arguments.

File structure

Lines starting ! are comments and are skipped. A keyword line is $KEYWORD[=...] (leading $ stripped, upper-cased for matching).

  • $COOR... → node block: lines <gid> <x> <y> <z...> (split on single spaces — multiple consecutive spaces create empty tokens, which are not filtered here), read until a $/! line, building a gid → running index map.
  • $ELEMENT TYPE=<permas type> → element block: lines are <running_index> <node gid> <node gid> ..., resolved through the node-gid map. PERMAS uses a trailing ! as a line-continuation marker: node ids accumulate across lines until one does not end in !, at which point the accumulated ids are emitted as one completed cell.
  • $NSET/$ESET blocks are parsed (including GENERATE, expanding exactly 3 ids start,end,step via np.arange with an exclusive stop, i.e. literal Python range semantics rather than an inclusive PERMAS/Abaqus- style generate) but the parsed sets are never attached to the returned Mesh — this is a currently-dead read path.
  • Everything else is silently ignored ("too many PERMAS keywords to explicitly skip").

Write emits: !PERMAS DataFile Version 18.0, a header line crediting meshio++ (!written by meshio++ v<version> from the Python writer, !written by meshio++ (C++ core) from the C++ writer), $ENTER COMPONENT NAME=DFLT_COMP, $STRUCTURE, $COOR, then node rows (sequential 1-based index, not the original PERMAS gid — none is tracked on write), then per element type a ! separator + $ELEMENT TYPE=... + rows (<running_id> <n1+1> ..., id counting continuously across all cell blocks), then $END STRUCTURE / $EXIT COMPONENT / $FIN.

Cell types

PERMASmeshio++PERMASmeshio++
PLOT1vertexHEXFO8hexahedron
FSCPIPE2 (and 10 other beam/rod names)lineHEXE20hexahedron20
PLOTL3line3HEXE27hexahedron27
TRIMS3 (and 6 other names)triangleTET4tetra
TRIMS6triangle6TET10tetra10
SHELL4 (and 5 other names)quadPYRA5pyramid
QUAMS8quad8PENTA6wedge
QUAMS9quad9PENTA15wedge15

(Several PERMAS names collapse onto one meshio++ type on read; the write-side reverse map picks one canonical name per meshio++ type as shown, determined by Python dict-insertion-order "last wins" — the C++ port hardcodes the resulting map directly to guarantee it matches.)

Write-only node-order permutation for second-order elements (no analogous reorder exists on read — see quirks):

typepermutation
triangle6[0, 3, 1, 4, 2, 5]
tetra10[0, 4, 1, 5, 2, 6, 7, 8, 9, 3]
quad9[0, 4, 1, 7, 8, 5, 3, 6, 2]
wedge15[0, 6, 1, 7, 2, 8, 9, 10, 11, 3, 12, 4, 13, 5, 14]

Data mapping

None — PERMAS produces no point_data/cell_data/field_data at all; $NSET/$ESET are parsed but discarded (see above).

Quirks & limitations

  • Asymmetric quadratic-element round-trip: the four second-order node reorder tables above are applied only on write — there is no inverse permutation on read. A file written by meshio++ and read back by meshio++'s own reader would therefore not restore the original meshio++ node order for triangle6/tetra10/quad9/wedge15 without external correction.
  • $NSET/$ESET sets are read into local dicts but never surface on the returned Mesh — effectively dead code in the current reader.
  • The !-continuation parsing is essential to get right: a standalone ! separator line between element blocks must yield no cell (not an empty one) — getting this wrong previously caused an out-of-bounds crash on a multi-block mesh during development of the C++ port, now fixed and covered by tests.
  • GENERATE set expansion uses exclusive-stop np.arange(start, end, step) semantics, not an inclusive range — a possible off-by-one relative to true PERMAS/Abaqus-style GENERATE (moot in practice since sets are discarded regardless).

Notes

  • The C++ core handles the plain-text .post/.dato files. The gzip .gz containers fall back to the Python implementation.
  • No reference fixture exists under tests/meshes/permas/; tests round-trip synthetic meshes and check .post/.post.gz/.0.post/.0.post.gz extension/suffix dispatch.

Released under the MIT License.