Kratos / MDPA (.mdpa)
The Kratos Multiphysics model-part data format: block-structured ASCII (Begin ... / End ...). This is the largest and most feature-rich format meshio++ supports — it has no C++ implementation.
| Format name | mdpa |
| Extensions | .mdpa |
| Read / Write | ✓ / ✓ |
| Extra dependencies | — |
Reading & writing
import meshioplusplus
mesh = meshioplusplus.read("model.mdpa")
meshioplusplus.mdpa.write("out.mdpa", mesh, float_fmt=".16e", binary=False)float_fmt— coordinate format.binary— MDPA is ASCII-only; passingbinary=TrueraisesWriteErrorunconditionally.
File structure
A single pass over Begin <X> ... End <X> blocks:
ModelPartData—key valuepairs (//comments stripped) →field_data[key](parsed as float when possible, else kept as a string).Nodes— rows of eitherid x y zor barex y z(auto-detected by column count).Elements <KratosType>/Conditions <KratosType>— the header's Kratos type name is matched by exact match first, then by longest-substring match (to avoid e.g."Line"ambiguously matching inside"Line3D2"); each row isid property_id node_ids.... If the type can't be resolved from the header at all, it's inferred purely from node count. Property ids becomegmsh:physical/gmsh:geometrical-style tags (MDPA reuses gmsh's tag-key convention here).Geometries <Type>— like Elements/Conditions but with no property id column; stored separately asmesh.geometries_block(not part ofmesh.cells), a non-standard mesh-level attribute.Table <id> <var1> <var2> ...— rows untilEnd Table; a malformed header (too few parts, non-integer id, no variables) is warned-and- skipped; stored asfield_data[f"table_{id}"] = {"variables": [...], "data": ndarray}.Properties <id>— key/value pairs (auto-typed float → int-if-integer → else string) plus any nestedTableblocks, stored underfield_data[f"properties_{id}"].NodalData <VAR[n]>/ElementalData/ConditionalData <VAR>— values per entity; missing entities are densified withNaN. An optional leading "fixed" flag column (0/1) is heuristically detected (only treated as a flag if the value is exactly 0/1 and more numeric values follow on the same row) — if any fixed-status is seen, a parallel{var}_fixed_statusarray is produced (sentinel-1= "not specified"). Scalar (0-component) variables are treated as boolean-by-presence: listed ids get1, unlisted get0.SubModelPart <Name>(nestable, joined with/for a hierarchical key e.g."Outer/Inner") — sub-blocksSubModelPartData,SubModelPartTables,SubModelPartNodes(0-based, validated against the point count),SubModelPartElements/Conditions(raw 1-based ids kept unconverted, explicitly to preserve exact round-trip values). Not implemented:SubModelPartGeometries,Constraintssub-blocks.Mesh <id> [name]— an alternate/coarser mesh representation;id=0is invalid per Kratos convention and skipped with a warning. Sub-blocks:MeshData,MeshNodes(0-based, validated),MeshElements/Conditions(raw 1-based ids kept as-is).
All of the round-trip-only bookkeeping above (element/condition/geometry id maps, SubModelPart hierarchy, alternate-mesh data) accumulates in mesh.misc_data — a non-standard mesh attribute specific to this format.
Cell types & node ordering
The Kratos type tables (Geometries/Elements/Conditions) are large; a representative slice:
| Kratos | meshio++ | Kratos | meshio++ |
|---|---|---|---|
Line2D2, Element3D2N | line | Tetrahedra3D4, Element3D4N | tetra |
Line2D3, LineElement3D3N | line3 | Tetrahedra3D10 | tetra10 |
Triangle2D3, Element3D3N | triangle | Hexahedra3D8, Element3D8N | hexahedron |
Triangle2D6, Element2D6N | triangle6 | Hexahedra3D20 | hexahedron20 |
Quadrilateral2D4, Element2D4N | quad | Hexahedra3D27 | hexahedron27 |
Quadrilateral2D8 | quad8 | Prism3D6, Element3D6N | wedge |
Quadrilateral2D9 | quad9 | Element3D5N | pyramid |
Point2D, Element2D1N | vertex | Element3D13N/15N | wedge15 |
Quadratic hexahedron node-order permutation (the format's key gotcha, applied only for hexahedron20/hexahedron27) — read applies the argsort of the Kratos-order array below; write applies the array itself directly (a true inverse pair):
hex20 kratos order: [0,1,2,3,4,5,6,7,8,11,10,9,16,19,18,17,12,13,14,15]
hex27 kratos order: [0,1,2,3,4,5,6,7,8,11,10,9,16,19,18,17,12,15,14,13,
20,23,21,24,22,25,26]All other cell types are assumed to already share meshio++'s VTK-style ordering (no permutation applied).
Data mapping
MDPA has an unusually rich set of data keys, several structured differently from every other format meshio++ supports:
field_data[key]—ModelPartDatascalars.field_data[f"table_{id}"]—{"variables": [...], "data": ndarray}.field_data[f"properties_{id}"]— a dict, possibly containing nestedtable_<id>entries.point_data[VAR]— Kratos variable names verbatim (e.g.TEMPERATURE,DISPLACEMENTas an(n,3)array from aDISPLACEMENT[3]header).point_data[f"{VAR}_fixed_status"]— sentinel-1/0/1.cell_data[<meshio_type>]["gmsh:physical"]/["gmsh:geometrical"]/[VAR]— unlike every other meshio++ format, MDPA's cell_data is nested by cell type name as an inner dict ({cell_type: {var: array}}), not the usual flat{var: [array_per_block]}convention.mesh.misc_data— non-standard attribute:reader_element_ids_info,reader_condition_ids_info,mdpa_geometry_ids_info,submodelpart_info,meshes.mesh.geometries_block— non-standard attribute, a list ofCellBlocks fromBegin Geometries.
Quirks & limitations
- The
cell_datanested-by-type structure ({cell_type: {var: array}}) is a genuine structural departure from meshio++'s usual flat convention — code consuming MDPA-read meshes needs to account for this specifically. - The h20/h27 permutation tables are applied directly on write (not their inverse) and via argsort on read — this is intentional and correct (the two operations really are exact inverses of each other), but worth internalizing since it looks asymmetric at first glance.
SubModelPartElements/ConditionsandMeshElements/Conditionsstore raw, unconverted 1-based ids rather than remapped local indices — this assumes element/condition ids are stable across a read→write cycle (true unless entities are reordered in between).- Malformed rows in almost every block type (bad
Tableheaders, data-row/variable-count mismatches, out-of-rangeSubModelPartNodesentries) are warned-and-skipped rather than raising — MDPA parsing is deliberately lenient/best-effort given how varied real Kratos input decks are. - Writing
ElementalData/ConditionalDataomits any entity that was entirelyNaN(never had data) rather than writingNaNliterally. - No C++ implementation exists for this format at all.
Notes
tests/meshes/mdpa/test_small_cube.mdpa— a small unit-cube tet mesh.tests/meshes/mdpa/test_submodelpart.mdpa— a 2D quad mesh with a nestedSubModelPartcontaining Nodes/Elements/Conditions/empty-Geometries/ empty-Constraints sub-blocks.- Additional
tests/input/mdpa/test_*.mdpafixtures target node-order permutation edge cases, minimal/degenerate geometries, hierarchical SubModelParts, and varied Table layouts. tests/test_mdpa.pyalso builds many MDPA snippets inline (not as files) covering nearly every block type, including atest_roundtrip_all_blocksexercising almost all of them at once with a NaN-aware comparison helper.