VTKio 0.1.0.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vtkio/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ VTKio - A package for reading and writing VTK data files.
4
+
5
+ Supports reading and writing VTK files from XML and VTKHDF file formats.
6
+
7
+ Created at 14:39, 15 Apr, 2022
8
+ """
9
+
10
+ __author__ = 'J.P. Morrissey'
11
+ __copyright__ = 'Copyright 2022-2025'
12
+ __maintainer__ = 'J.P. Morrissey'
13
+ __email__ = 'morrissey.jp@gmail.com'
14
+ __status__ = 'Development'
15
+
16
+ # Standard Library
17
+
18
+
19
+ # Imports
20
+
21
+
22
+ # Local Sources
23
+ from .simplified import *
24
+ from .version import VERSION, version_info, version_short
25
+
26
+ __version__ = VERSION
27
+
vtkio/_git.py ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Git utilities, adopted from mypy's git utilities (https://github.com/python/mypy/blob/master/mypy/git.py).
4
+
5
+ Created at 13:11, 23 Feb, 2025
6
+ """
7
+
8
+ __author__ = 'J.P. Morrissey'
9
+ __copyright__ = 'Copyright 2022-2025'
10
+ __maintainer__ = 'J.P. Morrissey'
11
+ __email__ = 'morrissey.jp@gmail.com'
12
+ __status__ = 'Development'
13
+
14
+
15
+ # Standard Library
16
+ from __future__ import annotations
17
+
18
+ import os
19
+ import subprocess
20
+
21
+ # Imports
22
+
23
+
24
+ # Local Sources
25
+
26
+
27
+ def is_git_repo(dir: str) -> bool:
28
+ """Is the given directory version-controlled with git?"""
29
+ return os.path.exists(os.path.join(dir, '.git'))
30
+
31
+
32
+ def have_git() -> bool:
33
+ """Can we run the git executable?"""
34
+ try:
35
+ subprocess.check_output(['git', '--help'])
36
+ return True
37
+ except subprocess.CalledProcessError:
38
+ return False
39
+ except OSError:
40
+ return False
41
+
42
+
43
+ def git_revision(dir: str) -> str:
44
+ """Get the SHA-1 of the HEAD of a git repository."""
45
+ return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=dir).decode('utf-8').strip()
vtkio/helpers.py ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Module documentation goes here.
4
+
5
+ Created at 16:44, 18 May, 2025
6
+ """
7
+
8
+
9
+ __author__ = 'J.P. Morrissey'
10
+ __copyright__ = 'Copyright 2025, {project_name}'
11
+ __credits__ = ['{credit_list}']
12
+ __license__ = '{license}'
13
+ __version__ = '{mayor}.{minor}.{rel}'
14
+ __maintainer__ = 'J.P. Morrissey'
15
+ __email__ = 'j.morrissey@ed.ac.uk'
16
+ __status__ = '{dev_status}'
17
+
18
+
19
+
20
+ # Standard Library
21
+
22
+
23
+ # Imports
24
+
25
+
26
+ # Local Sources
27
+
28
+
29
+
30
+ LITTLE_ENDIAN = '<' # Constant for little-endian byte order
31
+ BIG_ENDIAN = '>' # Constant for big-endian byte order
32
+
33
+ def _parse_dtype_size(data_type):
34
+ """Resolves the NumPy dtype and start index based on data type."""
35
+ type_size_mapping = {
36
+ 'float16': 2,
37
+ 'float32': 4,
38
+ 'float64': 8,
39
+ 'int8': 1,
40
+ 'int16': 2,
41
+ 'int32': 4,
42
+ 'int64': 8,
43
+ 'uint8': 1,
44
+ 'uint16': 2,
45
+ 'uint32': 4,
46
+ 'uint64': 8,
47
+ }
48
+ return type_size_mapping.get(data_type.lower(), (None, None))
49
+
50
+
51
+ def _parse_bytecount_type(data_type, byteorder):
52
+ """Resolves the NumPy dtype and start index based on data type."""
53
+ fmt_byteorder = LITTLE_ENDIAN if byteorder.lower() in ['<', 'littleendian'] else BIG_ENDIAN
54
+
55
+ type_size_mapping = {
56
+ 'int8': ('b', 1),
57
+ 'short': ('h', 2),
58
+ 'int16': ('h', 2),
59
+ 'int': ('i', 4),
60
+ 'int32': ('i', 4),
61
+ 'int64': ('q', 8),
62
+ 'longlong': ('q', 8),
63
+ 'uint8': ('B', 1),
64
+ 'ushort': ('H', 2),
65
+ 'uint16': ('H', 2),
66
+ 'uint': ('I', 4),
67
+ 'uint32': ('I', 4),
68
+ 'uint64': ('Q', 8),
69
+ 'ulonglong': ('Q', 8),
70
+
71
+ }
72
+ mapped = type_size_mapping.get(data_type.lower(), (None, None))
73
+
74
+ return fmt_byteorder + mapped[0], mapped[1]
75
+
76
+
77
+ def _get_numpy_struct_mapping(data_type):
78
+
79
+ # Mapping of numpy dtype to struct format
80
+ np_to_struct_mapping = {
81
+ "int8": "b",
82
+ "uint8": "B",
83
+ "int16": "h",
84
+ "uint16": "H",
85
+ "int32": "i",
86
+ "uint32": "I",
87
+ "int64": "q",
88
+ "uint64": "Q",
89
+ "float32": "f",
90
+ "float64": "d",
91
+ 'short': 'h',
92
+ 'int': 'i',
93
+ 'uint': 'I',
94
+ 'ushort': 'H',
95
+ 'longlong': 'q',
96
+ 'ulonglong': 'Q',
97
+ }
98
+
99
+ return np_to_struct_mapping.get(data_type.lower(), "d")
100
+
101
+
102
+ def _determine_points_key(points_data):
103
+ """Determine the correct key for points in the recovered data."""
104
+ try:
105
+ return points_data['Points']
106
+ except KeyError:
107
+ try:
108
+ return points_data['points']
109
+ except KeyError:
110
+ return points_data[next(iter(points_data))]
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ VTKio Reader Module.
4
+
5
+ Read data files in XML and VTKHDF file format
6
+ """
7
+
8
+ __author__ = 'J.P. Morrissey'
9
+ __copyright__ = 'Copyright 2022-2025'
10
+ __maintainer__ = 'J.P. Morrissey'
11
+ __email__ = 'morrissey.jp@gmail.com'
12
+ __status__ = 'Development'
13
+
14
+ from .hdf5 import *
15
+ from .xml import *
vtkio/reader/hdf5.py ADDED
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env python
2
+ """VTKHDF Reader Module."""
3
+
4
+ __author__ = 'J.P. Morrissey'
5
+ __copyright__ = 'Copyright 2022-2025'
6
+ __maintainer__ = 'J.P. Morrissey'
7
+ __email__ = 'morrissey.jp@gmail.com'
8
+ __status__ = 'Development'
9
+
10
+
11
+ # Standard Library
12
+
13
+
14
+ # Imports
15
+ import h5py
16
+
17
+ # Local Sources
18
+ from ..vtk_structures import (
19
+ Cell,
20
+ Grid,
21
+ GridCoordinates,
22
+ ImageData,
23
+ PolyData,
24
+ PolyDataTopology,
25
+ RectilinearData,
26
+ StructuredData,
27
+ UnstructuredGrid,
28
+ )
29
+
30
+ __all__ = ['read_vtkhdf_data']
31
+
32
+
33
+ def read_data_arrays_from_group(vtk_group):
34
+ """
35
+ Reads data arrays from a vtkGroup object and processes them into a dictionary.
36
+
37
+ This function iterates through the items of a given vtkGroup, processes each data
38
+ array based on its number of components, and stores the result in a dictionary.
39
+ If the number of components is less than or equal to 1, the function reshapes and
40
+ flattens the array. If more components are present, the function reshapes the
41
+ array without flattening. The returned dictionary contains the processed data
42
+ arrays with the original keys from the vtkGroup. If no data is extracted, the
43
+ function returns None.
44
+
45
+ Parameters
46
+ ----------
47
+ vtk_group : HDF5 File Group
48
+ A dictionary-like object representing a vtkGroup where keys are the names
49
+ of data arrays, and values are multidimensional NumPy arrays.
50
+
51
+ Returns
52
+ -------
53
+ dict or None
54
+ A dictionary containing the processed data arrays, where keys are the
55
+ same as in the input vtkGroup, or None if no data arrays are available.
56
+
57
+ """
58
+ data = {}
59
+ for dname, darray in vtk_group.items():
60
+ num_comp = darray.shape[-1]
61
+
62
+ if num_comp <= 1:
63
+ data[dname] = darray[:].reshape(-1, num_comp).flatten()
64
+ else:
65
+ data[dname] = darray[:].reshape(-1, num_comp)
66
+
67
+ if data:
68
+ return data
69
+ else:
70
+ return None
71
+
72
+
73
+ def read_unstructured_grid(vtkhdf):
74
+ """
75
+ Reads an unstructured grid from a provided VTK HDF5 file structure. This function extracts
76
+ data arrays for points, point data, cell data, and field data, as well as topology information
77
+ including connectivity, offsets, and cell types. It creates an UnstructuredGrid object
78
+ using the extracted information.
79
+
80
+ Parameters
81
+ ----------
82
+ vtkhdf : h5py.File or h5py.Group
83
+ A VTK HDF5 group or file object containing the unstructured grid information.
84
+
85
+ Returns
86
+ -------
87
+ UnstructuredGrid
88
+ An object representing the unstructured grid with its point coordinates, cell topology,
89
+ and associated data arrays for points, cells, and fields.
90
+ """
91
+ # get point coordinates
92
+ points = vtkhdf['Points'][:]
93
+
94
+ # Get Data Arrays
95
+ # point data
96
+ point_data = read_data_arrays_from_group(vtkhdf['PointData'])
97
+
98
+ # cell data
99
+ cell_data = read_data_arrays_from_group(vtkhdf['CellData'])
100
+
101
+ # field data
102
+ field_data = read_data_arrays_from_group(vtkhdf['FieldData'])
103
+
104
+ # get cell data
105
+ connectivity = vtkhdf['Connectivity'][:]
106
+ offsets = vtkhdf['Offsets'][1::]
107
+ types = vtkhdf['Types'][:]
108
+
109
+ topology = Cell(connectivity=connectivity, offsets=offsets, types=types)
110
+
111
+ num_points = vtkhdf['NumberOfPoints'][:][0]
112
+ num_cells = vtkhdf['NumberOfCells'][:][0]
113
+ num_connectivity = vtkhdf['NumberOfConnectivityIds'][:][0]
114
+
115
+ # check
116
+ # data_check = num_points == len(points)
117
+
118
+ return UnstructuredGrid(points=points, cells=topology,
119
+ point_data=point_data, cell_data=cell_data, field_data=field_data)
120
+
121
+
122
+ def read_polydata(vtkhdf):
123
+ """
124
+ Reads and processes polydata from a VTK HDF dataset. This function extracts
125
+ the point coordinates, data arrays (associated with points, cells, and fields), and
126
+ geometrical topology information (vertices, lines, strips, polygons) from the provided
127
+ VTK HDF file and returns a structured PolyData object.
128
+
129
+ Parameters
130
+ ----------
131
+ vtkhdf : h5py.File
132
+ An HDF5 group/file object representing the structure and data of a
133
+ VTK HDF PolyData file.
134
+
135
+ Returns
136
+ -------
137
+ PolyData
138
+ A PolyData object containing the points, topology (vertices, lines, strips, polygons),
139
+ as well as the point data, cell data, and field data arrays extracted from the
140
+ VTK HDF dataset.
141
+
142
+ Raises
143
+ ------
144
+ Exception
145
+ This function may raise exceptions when accessing or processing data arrays or topology
146
+ groups (e.g., if any required data is missing or malformed). Errors are caught individually
147
+ for topology entries (Vertices, Lines, Strips, Polygons) and handled by defaulting
148
+ corresponding elements to None in case of failures.
149
+
150
+ """
151
+ # get point coordinates
152
+ num_points = vtkhdf['NumberOfPoints'][:][0]
153
+ points = vtkhdf['Points'][:]
154
+
155
+ # Get Data Arrays
156
+ # point data
157
+ point_data = read_data_arrays_from_group(vtkhdf['PointData'])
158
+
159
+ # cell data
160
+ cell_data = read_data_arrays_from_group(vtkhdf['CellData'])
161
+
162
+ # field data
163
+ field_data = read_data_arrays_from_group(vtkhdf['FieldData'])
164
+
165
+ # get vertices
166
+ try:
167
+ data = read_data_arrays_from_group(vtkhdf['Vertices'])
168
+ verts = PolyDataTopology(connectivity=data['Connectivity'][0], offsets=data['Offsets'][0][1::])
169
+ except:
170
+ verts = None
171
+
172
+ # get lines
173
+ try:
174
+ data = read_data_arrays_from_group(vtkhdf['Lines'])
175
+ lines = PolyDataTopology(connectivity=data['Connectivity'][0], offsets=data['Offsets'][0][1::])
176
+ except:
177
+ lines = None
178
+
179
+ # get strips
180
+ try:
181
+ data = read_data_arrays_from_group(vtkhdf['Strips'])
182
+ strips = PolyDataTopology(connectivity=data['Connectivity'][0], offsets=data['Offsets'][0][1::])
183
+ except:
184
+ strips = None
185
+
186
+ # get polys
187
+ try:
188
+ data = read_data_arrays_from_group(vtkhdf['Polygons'])
189
+ polys = PolyDataTopology(connectivity=data['Connectivity'][0], offsets=data['Offsets'][0][1::])
190
+ except:
191
+ polys = None
192
+
193
+
194
+ return PolyData(points=points, verts=verts, lines=lines, strips=strips, polys=polys,
195
+ point_data=point_data, cell_data=cell_data, field_data=field_data)
196
+
197
+
198
+
199
+
200
+ def read_image_data(vtkhdf):
201
+ """
202
+ Reads image data from a VTK HDF5 file and constructs an ImageData object.
203
+
204
+ This function reads various attributes such as Direction, Origin, Spacing,
205
+ Version, and Whole_Extent from the VTK HDF5 file to define the grid topology.
206
+ It then reads PointData, CellData, and FieldData from the corresponding groups
207
+ in the HDF5 file. Finally, it creates and returns an ImageData object that
208
+ encapsulates the read data along with the grid topology information.
209
+
210
+ Parameters
211
+ ----------
212
+ vtkhdf : h5py.File
213
+ An HDF5 file object containing VTK-related attributes and data arrays.
214
+
215
+ Returns
216
+ -------
217
+ ImageData
218
+ An object encapsulating the read point data, cell data, field data,
219
+ and grid topology definitions.
220
+ """
221
+ Direction = vtkhdf.attrs['Direction']
222
+ Origin = vtkhdf.attrs['Origin']
223
+ Spacing = vtkhdf.attrs['Spacing']
224
+ version = vtkhdf.attrs['Version']
225
+ Whole_Extent = vtkhdf.attrs['WholeExtent']
226
+
227
+ grid_topology = Grid(whole_extents=Whole_Extent, origin=Origin, spacing=Spacing, direction=Direction)
228
+
229
+ # point data
230
+ point_data = read_data_arrays_from_group(vtkhdf['PointData'])
231
+
232
+ # cell data
233
+ cell_data = read_data_arrays_from_group(vtkhdf['CellData'])
234
+
235
+ # field data
236
+ field_data = read_data_arrays_from_group(vtkhdf['FieldData'])
237
+
238
+ return ImageData(point_data=point_data, cell_data=cell_data, field_data=field_data, grid=grid_topology)
239
+
240
+
241
+ def read_structured_grid(vtkhdf):
242
+ """
243
+ Reads and processes the structured grid data from the provided vtkhdf dataset. The function
244
+ extracts point coordinates, extents, and associated data arrays such as point data, cell data,
245
+ and field data. It then organizes these elements into a `StructuredData` object, which can
246
+ be used for further analysis or visualization.
247
+
248
+ Parameters
249
+ ----------
250
+ vtkhdf : h5py.File
251
+ An HDF5 file handle that represents a VTK dataset containing structured grid data. It is
252
+ expected to have specific datasets and attributes such as 'NumberOfPoints', 'Points',
253
+ 'WholeExtent', and data groups for 'PointData', 'CellData', and 'FieldData'.
254
+
255
+ Returns
256
+ -------
257
+ StructuredData
258
+ A structured representation of the VTK dataset, encapsulating the point data, cell data,
259
+ field data, point coordinates, and the extents of the structured grid.
260
+ """
261
+ # get point coordinates
262
+ num_points = vtkhdf['NumberOfPoints'][:][0]
263
+ points = vtkhdf['Points'][:]
264
+ whole_extents = vtkhdf.attrs['WholeExtent']
265
+
266
+ # Get Data Arrays
267
+ # point data
268
+ point_data = read_data_arrays_from_group(vtkhdf['PointData'])
269
+
270
+ # cell data
271
+ cell_data = read_data_arrays_from_group(vtkhdf['CellData'])
272
+
273
+ # field data
274
+ field_data = read_data_arrays_from_group(vtkhdf['FieldData'])
275
+
276
+
277
+ return StructuredData(point_data=point_data, cell_data=cell_data, field_data=field_data, points=points,
278
+ whole_extents=whole_extents)
279
+
280
+
281
+ def read_rectilinear_grid(vtkhdf):
282
+ """
283
+ Reads a rectilinear grid from the provided HDF5 file formatted in the VTK convention.
284
+
285
+ This function parses the given VTK-compliant HDF5 file to extract rectilinear grid
286
+ data. It reads the grid topology, including its coordinates and extents, as well
287
+ as associated data arrays for points, cells, and fields. The extracted information
288
+ is then packed into a `RectilinearData` object, which serves as a structured
289
+ representation of the grid.
290
+
291
+ Parameters
292
+ ----------
293
+ vtkhdf : h5py.Group
294
+ A group object from an HDF5 file structured in compliance with the VTK format.
295
+ This object is expected to have the following datasets and attributes:
296
+ - Datasets: `XCoordinates`, `YCoordinates`, `ZCoordinates`.
297
+ - Attributes: `WholeExtent`.
298
+ - Groups: `PointData`, `CellData`, `FieldData`.
299
+
300
+ Returns
301
+ -------
302
+ RectilinearData
303
+ An object containing the extracted rectilinear grid data, including the grid
304
+ topology (coordinates and extents), point data, cell data, and field data.
305
+
306
+ Notes
307
+ -----
308
+ This function assumes a specific structure for the input HDF5 group, which is
309
+ dictated by the VTK convention for rectilinear grids. Ensure that the input
310
+ conforms to this structure before invoking the function.
311
+ """
312
+ # read coordinates - always present
313
+ grid_topology = GridCoordinates(x=vtkhdf['XCoordinates'][:],
314
+ y=vtkhdf['YCoordinates'][:],
315
+ z=vtkhdf['ZCoordinates'][:],
316
+ whole_extents=vtkhdf.attrs['WholeExtent'])
317
+
318
+ # Get Data Arrays
319
+ # point data
320
+ point_data = read_data_arrays_from_group(vtkhdf['PointData'])
321
+
322
+ # cell data
323
+ cell_data = read_data_arrays_from_group(vtkhdf['CellData'])
324
+
325
+ # field data
326
+ field_data = read_data_arrays_from_group(vtkhdf['FieldData'])
327
+
328
+ return RectilinearData(point_data=point_data, cell_data=cell_data, field_data=field_data, coordinates=grid_topology)
329
+
330
+
331
+
332
+
333
+ def read_vtkhdf_data(filepath):
334
+ """
335
+ Reads VTKHDF data from an HDF5 file and returns a corresponding data structure
336
+ based on the type of VTKHDF dataset.
337
+
338
+ The function identifies the dataset type using the `Type` attribute within the
339
+ VTKHDF group of the HDF5 file. It then calls the appropriate function to parse
340
+ the dataset. Supported dataset types include ImageData, PolyData, UnstructuredGrid,
341
+ StructuredGrid, and RectilinearGrid.
342
+
343
+ Parameters
344
+ ----------
345
+ filepath : str
346
+ Path to the HDF5 file containing the VTKHDF data.
347
+
348
+ Returns
349
+ -------
350
+ object
351
+ A data structure corresponding to the VTKHDF dataset type, parsed from the
352
+ specified HDF5 file.
353
+
354
+ Raises
355
+ ------
356
+ TypeError
357
+ If the file does not contain a VTKHDF group or has an unsupported type.
358
+ """
359
+ with (h5py.File(filepath, 'r') as f):
360
+
361
+ try:
362
+ vtkhdf = f['VTKHDF']
363
+ Type = vtkhdf.attrs['Type'].decode('UTF-8')
364
+
365
+ if Type == 'ImageData':
366
+ return read_image_data(vtkhdf)
367
+ elif Type == 'PolyData':
368
+ return read_polydata(vtkhdf)
369
+ elif Type == 'UnstructuredGrid':
370
+ return read_unstructured_grid(vtkhdf)
371
+ elif Type == 'StructuredGrid':
372
+ return read_structured_grid(vtkhdf)
373
+ elif Type == 'RectilinearGrid':
374
+ return read_rectilinear_grid(vtkhdf)
375
+ else:
376
+ raise TypeError(f'Unexpected type {Type}')
377
+
378
+ except:
379
+ raise TypeError("The specified file is not a VTKHDF file.")