VTK Legacy#
.vtk read + write lazy: binary only
Summary of the specification#
The legacy VTK format is a single-dataset serial file with a five-line ASCII preamble - version banner, title, ASCII or BINARY data mode, and a DATASET keyword naming the geometry type. What follows depends on that type: an unstructured grid lists POINTS, then CELLS as connectivity lists prefixed by their point count, then a parallel CELL_TYPES array of integer type codes. Point and cell attributes arrive afterwards in POINT_DATA / CELL_DATA sections as named SCALARS, VECTORS or FIELD arrays. In BINARY mode the arrays are raw big-endian values packed straight after their declaration line.
Specification at a glance#
banner |
# vtk DataFile Version x.y |
data mode |
ASCII or BINARY (binary payload is big-endian) |
dataset types |
STRUCTURED_POINTS, STRUCTURED_GRID, RECTILINEAR_GRID, POLYDATA, UNSTRUCTURED_GRID, FIELD |
connectivity |
CELLS <n> <size> followed by CELL_TYPES <n> integer codes |
attributes |
POINT_DATA / CELL_DATA with SCALARS, COLOR_SCALARS, VECTORS, NORMALS, TENSORS, TEXTURE_COORDINATES, FIELD |
Reading#
import polyxios as px
mesh = px.read("model.vtk")
mesh.vertices # (n, 3)
mesh.element_types # element groups found in the file
Binary bodies can be memory-mapped instead of loaded:
mesh = px.read("big.vtk", lazy=True)
Writing#
px.write(mesh, "out.vtk")
Format-specific options:
Option |
Default |
Effect |
|---|---|---|
|
|
Write a BINARY body instead of ASCII. |
Quirks worth knowing#
A tag group travels as one
POINT_DATAorCELL_DATAcolumn of ones and zeros namedpolyxios_tag_<group>, so an element in two groups is named by both columns. A legacy header names its array in a whitespace-separated field and nothing in the format escapes one, so a group whose name holds whitespace is dropped with a warning rather than written as a name and a stray token.A
FIELD FieldDatablock between theDATASETline and the geometry belongs to the mesh rather than to its points or cells, and is read intoglobal_attrs;writeputs one back there. Unlike a point or cell array, which is written as a double, a field array keeps the type it is held in, so an integer comes home an integer. AFIELDinside aPOINT_DATAorCELL_DATAsection still names arrays over the points or the cells, and is read as attributes. ASTRUCTURED_POINTS,RECTILINEAR_GRIDorSTRUCTURED_GRIDfile reads a dataset-level block too, which is where VTK’s own writer puts a time value.A value no numeric array holds - a string, a mapping - is dropped with a warning naming the key; the XML family’s
<FieldData>holds text and this block does not. Thevtk_*grid entries a structured read recorded do travel in the block: this writer spells anUNSTRUCTURED_GRIDand rebuilds no grid, so holding them back would drop them without a word. A structured read takes the grid it rebuilt over anything a field block names, so carrying them costs the next read nothing.POINTSand a v4.2CELLSblock are a run of numbers the header counts, not a line apiece. One row to a vertex or a cell is what VTK’s own writer emits and what this reads first; a block wrapped some other way is read again as that run, so a file no reader of rows could take is read as the mesh it holds.Binary files can be memory-mapped with
lazy=True; ASCII files must be parsed end to end before any value is available.Cell type codes are mapped to polyxios element types, so a file mixing triangles, quads and tetrahedra keeps every group separate.
Point and cell data arrays are carried through as named vertex and element attributes rather than being dropped on read.
SCALARS,VECTORS,NORMALS,TENSORS,COLOR_SCALARS,TEXTURE_COORDINATESandFIELDsections are all read, in every dataset type - unstructured, polydata, structured points, structured grid and rectilinear grid alike. ALOOKUP_TABLEdefinition is a palette rather than a value per point, so it becomes no attribute, but it is counted past so the arrays after it are still found. A keyword outside that set stops the scan, and says so.COLOR_SCALARSis the one attribute whose type its own line does not name: one unsigned char per component in a binary file, a float in 0..1 in an ASCII one. The byte is scaled onto 0..1, so the same colour reads back the same from either flavour.An attribute section that declares more values than the file holds raises
CodecErrornaming the array, rather than anIndexErrornaming nothing in ASCII or a reshape failure naming nothing in binary.STRUCTURED_POINTSkeepsDIMENSIONS,ORIGINandSPACINGinglobal_attrs(vtk_dimensions,vtk_origin,vtk_spacing);STRUCTURED_GRIDandRECTILINEAR_GRIDkeepDIMENSIONS. The points are expanded into an explicit array, so without those the grid behind them would be lost.Those
vtk_*entries are read-only:writealways emits anUNSTRUCTURED_GRIDand does not consume them.CELL_DATAis read from the structured datasets as well as the unstructured ones. An array whose declared length matches neither the points nor the cells of the grid the header describes is dropped with a warning naming it, rather than reachingPolyDataas a validation error about lengths.A structured grid extends along whichever axes its
DIMENSIONSdeclare:3 1 3is a sheet of quads in the x-z plane, not a run of lines, and a column alongyorzis indexed with its own stride.A
DIMENSIONSof one is an axis the grid does not extend along; one of zero - or the negative a malformed header spells - is an axis with no point on it at all, and one such axis empties the grid, since the point count is a product. It holds no cells either: counting the other two axes’ cells handed back quadrilaterals whose corners named points the file never laid out.VTK 5.1 cells - the default since VTK 9.0 - are read wherever they appear:
CELLSin an unstructured grid andPOLYGONS,LINES,VERTICESorTRIANGLE_STRIPSin polydata. The two numbers on such a line are the length of theOFFSETSarray and the length ofCONNECTIVITY, so the mesh holds one cell fewer than the first of them; the offsets are counted up to theCONNECTIVITYkeyword, so a file spelling that line either way is read.write(..., vtk_version="5.1")declares the offsets length, which is what VTK’s own reader expects.A
METADATAblock - component names and information keys, written after every array by VTK 4.2 and later - is stepped over rather than read as an array. It is text even in a binary file, and it appears between the entries of aFIELDblock as well as after a section.A
METADATAblock also sits inside a v5.1CELLSsection, between its offsets and its connectivity, and is stepped over there too.A
DATASET FIELDfile carries field arrays and no geometry. It reads as an emptyPolyDatawhoseglobal_attrshold the arrays, with a warning saying so.Which cell spelling a v5.1
CELLSsection uses is decided by what follows the header, not by the version in the first line, so a file declaring a version this reader has never heard of is still read by what it holds.A
RECTILINEAR_GRIDtakes its grid from its coordinate arrays: the points are their outer product, so aDIMENSIONSheader that disagrees with them is warned about and ignored.A
STRUCTURED_GRIDcarries an explicitPOINTSarray, which itsDIMENSIONScannot be reconciled against the way a rectilinear grid’s coordinates can. When the two disagree the points are handed back without cells, with a warning naming both counts: the cells the header describes would index points the file does not hold.An attribute section is read by the count its own header declares, which is the only thing that says where one array ends and the next begins. An array that then covers no point or cell of the mesh is dropped with a warning naming it.
The
LOOKUP_TABLEline after aSCALARSsection is optional, and a binary file without one is read as such rather than losing the head of its payload.A header missing a field, or spelling a count as something that is not a number, raises
CodecErrornaming the line it is on - the byte offset, in a binary file. This covers the geometry headers -POINTS,CELLS,CELL_TYPES,DIMENSIONS,ORIGIN,SPACING, the coordinate arrays - as well as the attribute ones.A binary block is read as the type its header names,
POINTSincluded: an integer point array is not read at the width of a float. A type name with no numpy equivalent raisesCodecErrornaming it rather than being guessed at, since a guessed width reads numbers the file never held. An ASCII payload is text whatever its header calls it, so it is read either way.
See also
Supported formats - the full format table.