10def save_vti(filename, fields, spacing=(1.0, 1.0, 1.0), origin=(0.0, 0.0, 0.0)):
12 General VTI saver that infers grid dimensions from the input fields.
16 fields : dict { "Name": ndarray }
17 Arrays should be shape (Nz, Ny, Nx) for scalars
18 or (Nz, Ny, Nx, 3) for vectors.
19 spacing : tuple (dz, dy, dx)
20 origin : tuple (oz, oy, ox)
23 first_field = next(iter(fields.values()))
24 shape_3d = first_field.shape[:3]
29 ex, ey, ez = nx, ny, nz
33 with open(filename,
'wb')
as f:
35 f.write(b
'<?xml version="1.0"?>\n')
36 f.write(b
'<VTKFile type="ImageData" version="1.0" byte_order="LittleEndian" header_type="UInt64">\n')
37 f.write(f
' <ImageData WholeExtent="0 {ex} 0 {ey} 0 {ez}" Origin="{ox} {oy} {oz}" Spacing="{dx} {dy} {dz}">\n'.encode(
'ascii'))
38 f.write(f
' <Piece Extent="0 {ex} 0 {ey} 0 {ez}">\n'.encode(
'ascii'))
39 f.write(b
' <CellData>\n')
45 for name, arr
in fields.items():
46 if arr.shape[:3] != shape_3d:
47 raise ValueError(f
"Field '{name}' shape {arr.shape} inconsistent with {shape_3d}")
50 if np.issubdtype(arr.dtype, np.integer):
52 arr_fixed = arr.astype(
'<i4')
53 elif arr.dtype == np.float64:
55 arr_fixed = arr.astype(
'<f8')
58 arr_fixed = arr.astype(
'<f4')
60 n_comp = 3
if arr.ndim == 4
else 1
61 n_bytes = arr_fixed.nbytes
63 f.write(f
' <DataArray type="{dtype_str}" Name="{name}" NumberOfComponents="{n_comp}" format="appended" offset="{offset}"/>\n'.encode(
'ascii'))
65 data_to_write.append(arr_fixed)
68 f.write(b
' </CellData>\n </Piece>\n </ImageData>\n')
71 f.write(b
' <AppendedData encoding="raw">\n_')
73 for arr
in data_to_write:
76 flat_bytes = arr.tobytes()
77 f.write(np.array([len(flat_bytes)], dtype=
'<u8').tobytes())
80 f.write(b
'\n </AppendedData>\n</VTKFile>\n')