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 +27 -0
- vtkio/_git.py +45 -0
- vtkio/helpers.py +110 -0
- vtkio/reader/__init__.py +15 -0
- vtkio/reader/hdf5.py +379 -0
- vtkio/reader/xml.py +712 -0
- vtkio/simplified.py +621 -0
- vtkio/utilities.py +222 -0
- vtkio/version.py +78 -0
- vtkio/vtk_cell_types.py +98 -0
- vtkio/vtk_structures.py +306 -0
- vtkio/writer/__init__.py +16 -0
- vtkio/writer/pvd_writer.py +132 -0
- vtkio/writer/vtkhdf.py +1184 -0
- vtkio/writer/writers.py +393 -0
- vtkio/writer/xml_writer.py +1597 -0
- vtkio-0.1.0.dev2.dist-info/METADATA +86 -0
- vtkio-0.1.0.dev2.dist-info/RECORD +20 -0
- vtkio-0.1.0.dev2.dist-info/WHEEL +4 -0
- vtkio-0.1.0.dev2.dist-info/licenses/LICENSE +28 -0
vtkio/writer/vtkhdf.py
ADDED
|
@@ -0,0 +1,1184 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
VTKHDF Writer Module.
|
|
4
|
+
|
|
5
|
+
Includes all functions for writing VTKHDF files, including image data, poly data,
|
|
6
|
+
unstructured grid, structured grid, and rectilinear grid. Also includes functions
|
|
7
|
+
for writing additional metadata and creating multiblock datasets.
|
|
8
|
+
|
|
9
|
+
Note: This module writes structured grids and rectilinear grids in a VTKHDF format
|
|
10
|
+
although the definition is not yet formally included in the VTKHDF specification.
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__author__ = 'J.P. Morrissey'
|
|
15
|
+
__copyright__ = 'Copyright 2022-2025'
|
|
16
|
+
__maintainer__ = 'J.P. Morrissey'
|
|
17
|
+
__email__ = 'morrissey.jp@gmail.com'
|
|
18
|
+
__status__ = 'Development'
|
|
19
|
+
|
|
20
|
+
# Standard library
|
|
21
|
+
from abc import ABCMeta, abstractmethod
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
# Imports
|
|
25
|
+
import h5py
|
|
26
|
+
import numpy as np
|
|
27
|
+
|
|
28
|
+
# Local imports
|
|
29
|
+
from ..utilities import flatten
|
|
30
|
+
|
|
31
|
+
# Global metadata
|
|
32
|
+
fType = 'f'
|
|
33
|
+
idType = 'i8'
|
|
34
|
+
charType = 'uint8'
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
'VTKHDFMultiBlockWriter',
|
|
38
|
+
'VTKHDFImageDataWriter',
|
|
39
|
+
'VTKHDFUnstructuredGridWriter',
|
|
40
|
+
'VTKHDFPolyDataWriter',
|
|
41
|
+
'VTKHDFStructuredGridWriter',
|
|
42
|
+
'VTKHDFRectilinearGridWriter'
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
class VTKHDFWriterBase(metaclass=ABCMeta):
|
|
46
|
+
"""
|
|
47
|
+
Base class for writing VTKHDF files.
|
|
48
|
+
|
|
49
|
+
This class provides the basic structure and methods for writing VTKHDF files,
|
|
50
|
+
including methods for writing metadata, topology, and data arrays. It is intended
|
|
51
|
+
to be subclassed for specific VTKHDF dataset types such as ImageData, PolyData,
|
|
52
|
+
UnstructuredGrid, and StructuredGrid.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
filename : str
|
|
57
|
+
The name of the file to write the VTKHDF dataset to. Should end with '.vtkhdf'.
|
|
58
|
+
version : tuple, optional
|
|
59
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
60
|
+
additional_metadata : dict, optional
|
|
61
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
62
|
+
that should be stored in the file, such as attributes or custom data.
|
|
63
|
+
|
|
64
|
+
Attributes
|
|
65
|
+
----------
|
|
66
|
+
field_data : dict
|
|
67
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
68
|
+
and values are the corresponding numpy arrays.
|
|
69
|
+
point_data : dict
|
|
70
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
71
|
+
and values are the corresponding numpy arrays.
|
|
72
|
+
cell_data : dict
|
|
73
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
74
|
+
and values are the corresponding numpy arrays.
|
|
75
|
+
root : h5py.Group
|
|
76
|
+
The root group of the HDF5 file where the VTKHDF dataset will be written.
|
|
77
|
+
path : str
|
|
78
|
+
The name of the file to write the VTKHDF dataset to, including the '.vtkhdf' extension.
|
|
79
|
+
version : tuple
|
|
80
|
+
The version of the VTKHDF format being used.
|
|
81
|
+
npoints : int
|
|
82
|
+
The number of points in the dataset, calculated from the extents for structured grids.
|
|
83
|
+
ncells : int
|
|
84
|
+
The number of cells in the dataset, calculated from the extents for structured grids.
|
|
85
|
+
nverts : int
|
|
86
|
+
The number of vertices in the dataset, to be calculated from the data.
|
|
87
|
+
nlines : int
|
|
88
|
+
The number of lines in the dataset, to be calculated from the data.
|
|
89
|
+
nstrips : int
|
|
90
|
+
The number of strips in the dataset, to be calculated from the data.
|
|
91
|
+
npolys : int
|
|
92
|
+
The number of polygons in the dataset, to be calculated from the data.
|
|
93
|
+
additional_metadata : dict
|
|
94
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
95
|
+
that should be stored in the file, such as attributes or custom data.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
supported_versions = [(2, 2), (2, 3), (2, 4)]
|
|
99
|
+
extension = '.vtkhdf'
|
|
100
|
+
|
|
101
|
+
def __init__(self, filename, version=(2,2), additional_metadata=None):
|
|
102
|
+
self.field_data = None
|
|
103
|
+
self.point_data = None
|
|
104
|
+
self.cell_data = None
|
|
105
|
+
self.root = None
|
|
106
|
+
self.path = Path(filename).with_suffix(self.extension)
|
|
107
|
+
|
|
108
|
+
if version not in VTKHDFWriterBase.supported_versions:
|
|
109
|
+
raise ValueError("Unsupported VTKHDF version. Supported versions are (2, 2), (2, 3), and (2, 4).")
|
|
110
|
+
self.version = version
|
|
111
|
+
|
|
112
|
+
# set data attributes
|
|
113
|
+
# num points and cells can be calculated from the extents for ImageData, StructuredGrid and RectilinearGrid
|
|
114
|
+
self.npoints = 0
|
|
115
|
+
self.ncells = 0
|
|
116
|
+
|
|
117
|
+
# needs to be calculated from the data first and passed at instantiation
|
|
118
|
+
self.nverts = 0
|
|
119
|
+
self.nlines = 0
|
|
120
|
+
self.nstrips = 0
|
|
121
|
+
self.npolys = 0
|
|
122
|
+
|
|
123
|
+
self.additional_metadata = additional_metadata
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def write_additional_metadata(self, dictionary, group_path: str = '/VTKHDF'):
|
|
127
|
+
"""
|
|
128
|
+
Recursively write a nested dictionary to an HDF5 file group.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
dictionary : dict
|
|
133
|
+
The dictionary to write to the HDF5 file. It can contain nested dictionaries, lists, numpy arrays,
|
|
134
|
+
or simple scalar values (int, float, str, bool).
|
|
135
|
+
group_path : str, optional
|
|
136
|
+
The path within the HDF5 file where the dictionary should be written.
|
|
137
|
+
Default is '/VTKHDF'.
|
|
138
|
+
"""
|
|
139
|
+
if dictionary is None:
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Create group if it doesn't exist
|
|
143
|
+
# Handle case where group_path is an h5py.Group
|
|
144
|
+
if isinstance(group_path, h5py.Group):
|
|
145
|
+
current_group = self.root.create_group('additional_data') if self.root.name == '/VTKHDF' else group_path
|
|
146
|
+
|
|
147
|
+
group_path = current_group.name
|
|
148
|
+
else:
|
|
149
|
+
if group_path == '/VTKHDF':
|
|
150
|
+
group_path = 'additional_data'
|
|
151
|
+
current_group = self.root.create_group(group_path)
|
|
152
|
+
else:
|
|
153
|
+
# Create group if it doesn't exist
|
|
154
|
+
try:
|
|
155
|
+
current_group = self.root[group_path]
|
|
156
|
+
except KeyError:
|
|
157
|
+
current_group = self.root.create_group(group_path)
|
|
158
|
+
|
|
159
|
+
# Iterate through dictionary items
|
|
160
|
+
for key, value in dictionary.items():
|
|
161
|
+
# Ensure key is a string
|
|
162
|
+
key = str(key)
|
|
163
|
+
|
|
164
|
+
# Handle different types of values
|
|
165
|
+
if isinstance(value, dict):
|
|
166
|
+
if key == 'attrs':
|
|
167
|
+
# If the key is 'attrs', treat it as attributes
|
|
168
|
+
for attr_key, attr_value in value.items():
|
|
169
|
+
current_group.attrs[attr_key] = attr_value
|
|
170
|
+
else:
|
|
171
|
+
# For nested dictionaries, create a new group and recurse
|
|
172
|
+
new_group_path = f"{group_path}/{key}"
|
|
173
|
+
self.write_additional_metadata(value, new_group_path)
|
|
174
|
+
|
|
175
|
+
elif isinstance(value, (np.ndarray, list, tuple)):
|
|
176
|
+
# Convert lists/tuples to numpy arrays
|
|
177
|
+
if not isinstance(value, np.ndarray):
|
|
178
|
+
value = np.array(value)
|
|
179
|
+
|
|
180
|
+
# Create dataset
|
|
181
|
+
current_group.create_dataset(key, data=value)
|
|
182
|
+
|
|
183
|
+
elif isinstance(value, (int, float, str, bool, np.number)):
|
|
184
|
+
# Store simple scalar types as attributes
|
|
185
|
+
current_group.attrs[key] = value
|
|
186
|
+
|
|
187
|
+
else:
|
|
188
|
+
# Try to convert to string or numpy array for other types
|
|
189
|
+
try:
|
|
190
|
+
current_group.attrs[key] = str(value)
|
|
191
|
+
except:
|
|
192
|
+
try:
|
|
193
|
+
current_group.create_dataset(key, data=np.array(value))
|
|
194
|
+
except:
|
|
195
|
+
print(f"Warning: Could not store value for key {key} of type {type(value)}")
|
|
196
|
+
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def write_topology(self):
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
def write_vtkhdf_file(self):
|
|
202
|
+
"""
|
|
203
|
+
Write the VTKHDF dataset to the specified file.
|
|
204
|
+
|
|
205
|
+
This method creates the root group in the HDF5 file, initializes the CellData, PointData, and FieldData groups,
|
|
206
|
+
and calls the write_topology method to write the dataset topology. It then writes the data arrays for structured
|
|
207
|
+
grids (ImageData, StructuredGrid, RectilinearGrid) or unstructured grids (UnstructuredGrid, PolyData) based on
|
|
208
|
+
the type of dataset being written. Finally, it writes any additional metadata to the file.
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
with h5py.File(self.path, 'w') as f:
|
|
212
|
+
self.root = f.create_group('VTKHDF')
|
|
213
|
+
self.CellData = self.root.create_group("CellData")
|
|
214
|
+
self.PointData = self.root.create_group("PointData")
|
|
215
|
+
self.FieldData = self.root.create_group("FieldData")
|
|
216
|
+
self.write_topology()
|
|
217
|
+
if self.root.attrs['Type'].decode('utf8') == 'ImageData':
|
|
218
|
+
self.write_grid_data_arrays()
|
|
219
|
+
else:
|
|
220
|
+
self.write_nongrid_data_arrays()
|
|
221
|
+
self.write_additional_metadata(self.additional_metadata)
|
|
222
|
+
|
|
223
|
+
def add_structured_data_to_group(self, group, data_shape, dname, darray):
|
|
224
|
+
"""
|
|
225
|
+
Add a data array to an HDF5 group as a dataset.
|
|
226
|
+
|
|
227
|
+
Adds a data array to an HDF5 group as a dataset, reshaping it to match the specified data shape and
|
|
228
|
+
accounting for VTK's Fortran-style axis order.
|
|
229
|
+
root : h5py.Group
|
|
230
|
+
The HDF5 group to which the dataset will be added.
|
|
231
|
+
data_shape : tuple or list of int
|
|
232
|
+
The shape of the data (excluding the number of components).
|
|
233
|
+
dname : str
|
|
234
|
+
The name of the dataset to be created within the group.
|
|
235
|
+
darray : numpy.ndarray
|
|
236
|
+
The data array to be stored. Should have shape (N, n_comp) or (N,) if single component.
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
Notes
|
|
240
|
+
-----
|
|
241
|
+
The function attempts to determine the number of components in the data array. If the array is 1D,
|
|
242
|
+
it assumes a single component. The data is reshaped to match the reversed data shape (to account for
|
|
243
|
+
VTK's Fortran axis order) with the number of components as the last dimension.
|
|
244
|
+
"""
|
|
245
|
+
try:
|
|
246
|
+
n_comp = darray.shape[1]
|
|
247
|
+
except:
|
|
248
|
+
n_comp = 1
|
|
249
|
+
|
|
250
|
+
# root.create_dataset(dname, data=darray.reshape([*data_shape, n_comp], order='F'))
|
|
251
|
+
# reverse order of dimensions to account for VTK fortran axis order
|
|
252
|
+
group.create_dataset(dname, data=darray.reshape([*data_shape[::-1], n_comp]))
|
|
253
|
+
|
|
254
|
+
def add_unstructured_group_data(self, group, name, data):
|
|
255
|
+
"""
|
|
256
|
+
Add a data array to an HDF5 group as a dataset for unstructured grids.
|
|
257
|
+
|
|
258
|
+
Adds a data array to an HDF5 group as a dataset. If the data is a dictionary, it assumes that
|
|
259
|
+
the dictionary contains data grouped by data array type and creates datasets for each type.
|
|
260
|
+
If the data is not a dictionary, it assumes that it is a single data array and creates a dataset
|
|
261
|
+
with the specified name.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
group : h5py.Group
|
|
266
|
+
The HDF5 group to which the dataset will be added.
|
|
267
|
+
name : str
|
|
268
|
+
The name of the dataset to be created within the group.
|
|
269
|
+
data : numpy.ndarray or dict
|
|
270
|
+
The data array to be stored. If a dictionary, it should contain data grouped by data array type.
|
|
271
|
+
|
|
272
|
+
Notes
|
|
273
|
+
-----
|
|
274
|
+
This method is used for unstructured grids, where the data arrays do not require reshaping like
|
|
275
|
+
structured grids. The data is stored directly in the group as datasets without reshaping.
|
|
276
|
+
|
|
277
|
+
"""
|
|
278
|
+
if isinstance(data, dict):
|
|
279
|
+
# if data is a dict, we assume it contains data grouped by data array type
|
|
280
|
+
for darray_name, darray in data.items():
|
|
281
|
+
group.create_dataset(darray_name, data=darray)
|
|
282
|
+
else:
|
|
283
|
+
# if data is not a dict, we assume it is a single data array
|
|
284
|
+
group.create_dataset(name, data=data)
|
|
285
|
+
|
|
286
|
+
def add_structured_group_data(self, group, name, data, datasize):
|
|
287
|
+
"""
|
|
288
|
+
Add a data array to an HDF5 group as a dataset for structured grids.
|
|
289
|
+
|
|
290
|
+
Adds a data array to an HDF5 group as a dataset. If the data is a dictionary, it assumes that
|
|
291
|
+
the dictionary contains data grouped by data array type and creates datasets for each type.
|
|
292
|
+
If the data is not a dictionary, it assumes that it is a single data array and creates a dataset
|
|
293
|
+
with the specified name.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
group : h5py.Group
|
|
298
|
+
The HDF5 group to which the dataset will be added.
|
|
299
|
+
name : str
|
|
300
|
+
The name of the dataset to be created within the group.
|
|
301
|
+
data : numpy.ndarray or dict
|
|
302
|
+
The data array to be stored. If a dictionary, it should contain data grouped by data array type.
|
|
303
|
+
datasize : tuple(ints)
|
|
304
|
+
The size of the data array, which is used to reshape the data correctly for structured grids.
|
|
305
|
+
|
|
306
|
+
Notes
|
|
307
|
+
-----
|
|
308
|
+
This method is used for structured grids (ImageData, StructuredGrid, RectilinearGrid), where the data
|
|
309
|
+
arrays need to be reshaped to match the grid structure. The reshaping accounts for VTK's Fortran-style
|
|
310
|
+
axis order. The data is stored in the group as datasets, reshaped to the specified data size.
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
"""
|
|
314
|
+
if isinstance(data, dict):
|
|
315
|
+
# if data is a dict, we assume it contains data grouped by data array type
|
|
316
|
+
for darray_name, darray in data.items():
|
|
317
|
+
self.add_structured_data_to_group(group, datasize, darray_name, darray)
|
|
318
|
+
else:
|
|
319
|
+
# if data is not a dict, we assume it is a single data array
|
|
320
|
+
self.add_structured_data_to_group(group, datasize, name, data)
|
|
321
|
+
|
|
322
|
+
def write_grid_data_arrays(self):
|
|
323
|
+
"""
|
|
324
|
+
Write data arrays for structured grids (ImageData, StructuredGrid, RectilinearGrid).
|
|
325
|
+
|
|
326
|
+
This method writes point data, cell data, and field data to the HDF5 file in the appropriate groups.
|
|
327
|
+
It handles the structured nature of the data, ensuring that the data arrays are reshaped correctly
|
|
328
|
+
to match the grid structure. The reshaping accounts for VTK's Fortran-style axis order.
|
|
329
|
+
|
|
330
|
+
The method assumes that the data arrays are provided in a dictionary format, where keys are the names
|
|
331
|
+
of the data arrays and values are the corresponding numpy arrays. If the data arrays are not provided,
|
|
332
|
+
the method will skip writing that type of data.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
# write cell data if present
|
|
336
|
+
if self.cell_data is not None:
|
|
337
|
+
for name, data in self.cell_data.items():
|
|
338
|
+
self.add_structured_group_data(self.CellData, name, data, self.num_cells)
|
|
339
|
+
|
|
340
|
+
# write point data if present
|
|
341
|
+
if self.point_data is not None:
|
|
342
|
+
for name, data in self.point_data.items():
|
|
343
|
+
self.add_structured_group_data(self.PointData, name, data, self.num_points)
|
|
344
|
+
|
|
345
|
+
# write field data if present
|
|
346
|
+
if self.field_data is not None:
|
|
347
|
+
# field data can be any shape, so we store it directly
|
|
348
|
+
for name, darray in self.field_data.items():
|
|
349
|
+
if isinstance(darray, np.ndarray):
|
|
350
|
+
self.FieldData.create_dataset(name, data=darray)
|
|
351
|
+
else:
|
|
352
|
+
self.FieldData.create_dataset(name, data=np.array([darray]))
|
|
353
|
+
|
|
354
|
+
def write_nongrid_data_arrays(self):
|
|
355
|
+
"""
|
|
356
|
+
Write data arrays for unstructured grids (UnstructuredGrid, PolyData).
|
|
357
|
+
|
|
358
|
+
This method writes point data, cell data, and field data to the HDF5 file in the appropriate groups.
|
|
359
|
+
It handles the unstructured nature of the data, ensuring that the data arrays are stored correctly - unstructured data does not require reshaping like structured data.
|
|
360
|
+
|
|
361
|
+
The method assumes that the data arrays are provided in a dictionary format, where keys are the names
|
|
362
|
+
of the data arrays and values are the corresponding numpy arrays. If the data arrays are not provided,
|
|
363
|
+
the method will skip writing that type of data.
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
# write cell data if present
|
|
367
|
+
if self.cell_data is not None:
|
|
368
|
+
for name, data in self.cell_data.items():
|
|
369
|
+
self.add_unstructured_group_data(self.CellData, name, data)
|
|
370
|
+
|
|
371
|
+
# write point data if present
|
|
372
|
+
if self.point_data is not None:
|
|
373
|
+
for name, data in self.point_data.items():
|
|
374
|
+
self.add_unstructured_group_data(self.PointData, name, data)
|
|
375
|
+
|
|
376
|
+
# write field data if present
|
|
377
|
+
if self.field_data is not None:
|
|
378
|
+
# field data can be any shape, so we store it directly
|
|
379
|
+
for name, darray in self.field_data.items():
|
|
380
|
+
if isinstance(darray, np.ndarray):
|
|
381
|
+
self.FieldData.create_dataset(name, data=darray)
|
|
382
|
+
else:
|
|
383
|
+
self.FieldData.create_dataset(name, data=np.array([darray]))
|
|
384
|
+
|
|
385
|
+
@staticmethod
|
|
386
|
+
def _check_array_sizes(array_data):
|
|
387
|
+
"""
|
|
388
|
+
Check the size of all data arrays to be written to ensure they are all the same length.
|
|
389
|
+
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
array_data : dictionary of data arrays to be written.
|
|
393
|
+
|
|
394
|
+
Returns
|
|
395
|
+
-------
|
|
396
|
+
Array Size : Int
|
|
397
|
+
|
|
398
|
+
Raises
|
|
399
|
+
------
|
|
400
|
+
ValueError : If the sizes of the arrays are not all equal.
|
|
401
|
+
|
|
402
|
+
"""
|
|
403
|
+
# flatten dictionary to check sizes more easily
|
|
404
|
+
flattened_arrays = flatten(array_data, parent_key='', separator='_')
|
|
405
|
+
|
|
406
|
+
sizes = []
|
|
407
|
+
for _key, val in flattened_arrays.items():
|
|
408
|
+
if val.ndim == 1:
|
|
409
|
+
sizes.append(val.size)
|
|
410
|
+
else:
|
|
411
|
+
sizes.append(val.shape[0])
|
|
412
|
+
|
|
413
|
+
all_equal = all(sizes)
|
|
414
|
+
|
|
415
|
+
if all_equal:
|
|
416
|
+
return sizes[0]
|
|
417
|
+
else:
|
|
418
|
+
raise ValueError("Warning: Arrays provided are not all the same length. Data not written to file.")
|
|
419
|
+
|
|
420
|
+
# Keep specialised methods for regular XML writing:
|
|
421
|
+
def check_array_sizes_for_cell_data(self, cell_data):
|
|
422
|
+
"""
|
|
423
|
+
Compare size of cell data with the number of cells in the file.
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
cell_data : dict
|
|
428
|
+
Dictionary containing cell data arrays to be checked.
|
|
429
|
+
|
|
430
|
+
Raises
|
|
431
|
+
-------
|
|
432
|
+
ValueError : If the size of cell data does not match the number of cells.
|
|
433
|
+
|
|
434
|
+
"""
|
|
435
|
+
if cell_data is not None:
|
|
436
|
+
cell_data_size = self._check_array_sizes(cell_data)
|
|
437
|
+
if cell_data_size != self.ncells:
|
|
438
|
+
raise ValueError('Cells and cell data sizes do not match')
|
|
439
|
+
|
|
440
|
+
def check_array_sizes_for_point_data(self, point_data):
|
|
441
|
+
"""
|
|
442
|
+
Compare size of point data with the number of points in the file.
|
|
443
|
+
|
|
444
|
+
Parameters
|
|
445
|
+
----------
|
|
446
|
+
point_data : dict
|
|
447
|
+
Dictionary containing point data arrays to be checked.
|
|
448
|
+
|
|
449
|
+
Raises
|
|
450
|
+
-------
|
|
451
|
+
ValueError : If the size of point data does not match the number of points.
|
|
452
|
+
"""
|
|
453
|
+
if point_data is not None:
|
|
454
|
+
point_data_size = self._check_array_sizes(point_data)
|
|
455
|
+
if point_data_size != self.npoints:
|
|
456
|
+
raise ValueError('Points and point data sizes do not match')
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class VTKHDFMultiBlockWriter(VTKHDFWriterBase):
|
|
460
|
+
"""
|
|
461
|
+
A class for writing multiblock datasets in VTKHDF format.
|
|
462
|
+
|
|
463
|
+
This class allows for the creation of a multiblock dataset where each block can be
|
|
464
|
+
a different type of VTK dataset (e.g., UnstructuredGrid, ImageData, PolyData).
|
|
465
|
+
Each block is written as a separate group within the main VTKHDF group, and an assembly
|
|
466
|
+
group is created to link these blocks together.
|
|
467
|
+
|
|
468
|
+
The class inherits from VTKHDFWriterBase and implements the write_vtkhdf_file method to handle
|
|
469
|
+
the specifics of writing multiple blocks to a single VTKHDF file.
|
|
470
|
+
"""
|
|
471
|
+
def __init__(self, filename, blocks, additional_metadata=None):
|
|
472
|
+
"""
|
|
473
|
+
Initialize the VTKHDFMultiBlockWriter with a path and a dictionary of blocks.
|
|
474
|
+
|
|
475
|
+
Parameters
|
|
476
|
+
----------
|
|
477
|
+
filename : str
|
|
478
|
+
The name of the file to write the multiblock dataset to. Should end with '.vtkhdf'.
|
|
479
|
+
blocks : dict
|
|
480
|
+
A dictionary where keys are block names and values are instances of VTKHDFWriterBase
|
|
481
|
+
(e.g., VTKHDFUnstructuredGridWriter, VTKHDFImageDataWriter, etc.) that will be written as blocks.
|
|
482
|
+
additional_metadata : dict, optional
|
|
483
|
+
Additional metadata to be written to the VTKHDF file. Default is None.
|
|
484
|
+
|
|
485
|
+
"""
|
|
486
|
+
super().__init__(filename, additional_metadata)
|
|
487
|
+
self.blocks = blocks # dict: name -> writer instance
|
|
488
|
+
|
|
489
|
+
def write_vtkhdf_file(self):
|
|
490
|
+
"""
|
|
491
|
+
Write the multiblock dataset to the VTKHDF file.
|
|
492
|
+
|
|
493
|
+
This method creates the main VTKHDF group, writes the metadata, and iterates over the blocks
|
|
494
|
+
to write each one as a separate group. It also creates an assembly group that links the blocks
|
|
495
|
+
together using soft links.
|
|
496
|
+
"""
|
|
497
|
+
with h5py.File(self.path, 'w', track_order=True) as f:
|
|
498
|
+
root = f.create_group('VTKHDF', track_order=True)
|
|
499
|
+
root.attrs['Version'] = (2, 2)
|
|
500
|
+
ascii_type = 'PartitionedDataSetCollection'.encode('ascii')
|
|
501
|
+
root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
502
|
+
root.create_group('Assembly', track_order=True)
|
|
503
|
+
self.write_metadata(root)
|
|
504
|
+
|
|
505
|
+
for idx, (blk_name, writer) in enumerate(self.blocks.items()):
|
|
506
|
+
blk = root.create_group(blk_name, track_order=True)
|
|
507
|
+
writer.multiblock_index = idx
|
|
508
|
+
writer.write_vtkhdf_file(root=blk)
|
|
509
|
+
assembly_blk = root['Assembly'].create_group(blk_name, track_order=True)
|
|
510
|
+
assembly_blk[blk_name] = h5py.SoftLink(f'/VTKHDF/{blk_name}')
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class VTKHDFImageDataWriter(VTKHDFWriterBase):
|
|
514
|
+
"""
|
|
515
|
+
Write ImageData to a file in the VTKHDF format.
|
|
516
|
+
|
|
517
|
+
This class allows for the creation of ImageData datasets, which are structured grids
|
|
518
|
+
defined by their whole extent, origin, spacing, and direction. It inherits from
|
|
519
|
+
VTKHDFWriterBase and implements the write_vtkhdf_file method to handle the specifics of writing
|
|
520
|
+
ImageData to a VTKHDF file.
|
|
521
|
+
|
|
522
|
+
Parameters
|
|
523
|
+
----------
|
|
524
|
+
filename : str
|
|
525
|
+
The name of the file to write the ImageData to. Should end with '.vtkhdf'.
|
|
526
|
+
whole_extent : list or array-like
|
|
527
|
+
The whole extent of the ImageData, defined as [xmin, xmax, ymin, ymax, zmin, zmax].
|
|
528
|
+
origin : list or array-like
|
|
529
|
+
The origin of the ImageData, defined as [x_origin, y_origin, z_origin].
|
|
530
|
+
spacing : list or array-like
|
|
531
|
+
The spacing of the ImageData, defined as [x_spacing, y_spacing, z_spacing].
|
|
532
|
+
direction : list or array-like, optional
|
|
533
|
+
The direction cosines of the ImageData, defined as a flattened 3x3 matrix.
|
|
534
|
+
If not provided, defaults to the identity matrix (no rotation).
|
|
535
|
+
version : tuple, optional
|
|
536
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
537
|
+
point_data : dict, optional
|
|
538
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
539
|
+
and values are the corresponding numpy arrays.
|
|
540
|
+
cell_data : dict, optional
|
|
541
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
542
|
+
and values are the corresponding numpy arrays.
|
|
543
|
+
field_data : dict, optional
|
|
544
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
545
|
+
and values are the corresponding numpy arrays.
|
|
546
|
+
additional_metadata : dict, optional
|
|
547
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
548
|
+
that should be stored in the file, such as attributes or custom data.
|
|
549
|
+
multiblock_index : int, optional
|
|
550
|
+
An index for the multiblock dataset, if this ImageData is part of a larger multiblock dataset.
|
|
551
|
+
If not provided, defaults to None.
|
|
552
|
+
"""
|
|
553
|
+
|
|
554
|
+
def __init__(self, filename, whole_extent, origin, spacing, direction=None, version=(2, 2),
|
|
555
|
+
point_data=None, cell_data=None, field_data=None, additional_metadata=None, multiblock_index=None):
|
|
556
|
+
"""
|
|
557
|
+
Initialise the VTKHDFImageDataWriter with the necessary parameters for ImageData.
|
|
558
|
+
|
|
559
|
+
Parameters
|
|
560
|
+
----------
|
|
561
|
+
filename : str
|
|
562
|
+
The name of the file to write the ImageData to. Should end with '.vtkhdf'.
|
|
563
|
+
whole_extent : list or array-like
|
|
564
|
+
The whole extent of the ImageData, defined as [xmin, xmax, ymin, ymax, zmin, zmax].
|
|
565
|
+
origin : list or array-like
|
|
566
|
+
The origin of the ImageData, defined as [x_origin, y_origin, z_origin].
|
|
567
|
+
spacing : list or array-like
|
|
568
|
+
The spacing of the ImageData, defined as [x_spacing, y_spacing, z_spacing].
|
|
569
|
+
direction : list or array-like, optional
|
|
570
|
+
The direction cosines of the ImageData, defined as a flattened 3x3 matrix.
|
|
571
|
+
If not provided, defaults to the identity matrix (no rotation).
|
|
572
|
+
version : tuple, optional
|
|
573
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
574
|
+
point_data : dict, optional
|
|
575
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
576
|
+
and values are the corresponding numpy arrays.
|
|
577
|
+
cell_data : dict, optional
|
|
578
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
579
|
+
and values are the corresponding numpy arrays.
|
|
580
|
+
field_data : dict, optional
|
|
581
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
582
|
+
and values are the corresponding numpy arrays.
|
|
583
|
+
additional_metadata : dict, optional
|
|
584
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
585
|
+
that should be stored in the file, such as attributes or custom data.
|
|
586
|
+
multiblock_index : int, optional
|
|
587
|
+
An index for the multiblock dataset, if this ImageData is part of a larger multiblock dataset.
|
|
588
|
+
If not provided, defaults to None.
|
|
589
|
+
"""
|
|
590
|
+
super().__init__(filename, additional_metadata=additional_metadata, version=version)
|
|
591
|
+
self.whole_extent = np.asarray(whole_extent)
|
|
592
|
+
if len(self.whole_extent) != 6:
|
|
593
|
+
raise ValueError("whole_extent must be a list or array of length 6.")
|
|
594
|
+
|
|
595
|
+
self.origin = np.asarray(origin)
|
|
596
|
+
self.spacing = np.asarray(spacing)
|
|
597
|
+
self.direction = np.asarray(direction) if direction is not None else np.eye(3).flatten()
|
|
598
|
+
self.multiblock_index = multiblock_index
|
|
599
|
+
|
|
600
|
+
self.num_cells = (self.whole_extent[1::2] - self.whole_extent[0::2])
|
|
601
|
+
self.num_points = self.num_cells + 1
|
|
602
|
+
self.ncells = np.prod(self.num_cells)
|
|
603
|
+
self.npoints = np.prod(self.num_cells + 1)
|
|
604
|
+
|
|
605
|
+
# do check on provided data sizes before attempting to write
|
|
606
|
+
self.check_array_sizes_for_point_data(point_data)
|
|
607
|
+
self.check_array_sizes_for_cell_data(cell_data)
|
|
608
|
+
|
|
609
|
+
self.point_data = point_data
|
|
610
|
+
self.cell_data = cell_data
|
|
611
|
+
self.field_data = field_data
|
|
612
|
+
|
|
613
|
+
def write_topology(self):
|
|
614
|
+
"""
|
|
615
|
+
Write the topology of the ImageData to the HDF5 file.
|
|
616
|
+
|
|
617
|
+
This method creates the necessary attributes and datasets in the HDF5 file to represent
|
|
618
|
+
the ImageData topology, including whole extent, origin, spacing, and direction.
|
|
619
|
+
If a multiblock index is provided, it is stored as an attribute. The version and type of the dataset
|
|
620
|
+
are also set as attributes. The whole extent, origin, spacing, and direction are stored as attributes
|
|
621
|
+
within the root group of the HDF5 file.
|
|
622
|
+
|
|
623
|
+
"""
|
|
624
|
+
if self.multiblock_index is not None:
|
|
625
|
+
self.root.attrs.create('Index', self.multiblock_index, dtype=idType)
|
|
626
|
+
self.root.attrs['Version'] = self.version
|
|
627
|
+
ascii_type = 'ImageData'.encode('ascii')
|
|
628
|
+
self.root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
629
|
+
self.root.attrs.create('WholeExtent', self.whole_extent, dtype=idType)
|
|
630
|
+
self.root.attrs.create('Origin', self.origin, dtype=fType)
|
|
631
|
+
self.root.attrs.create('Spacing', self.spacing, dtype=fType)
|
|
632
|
+
self.root.attrs.create('Direction', self.direction, dtype=fType)
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class VTKHDFUnstructuredGridWriter(VTKHDFWriterBase):
|
|
636
|
+
"""
|
|
637
|
+
Write UnstructuredGrid datasets to a file in VTKHDF format.
|
|
638
|
+
|
|
639
|
+
This class allows for the creation of UnstructuredGrid datasets, which are defined by their nodes,
|
|
640
|
+
cell types, connectivity, and offsets. It inherits from VTKHDFWriterBase and implements the
|
|
641
|
+
write_vtkhdf_file method to handle the specifics of writing UnstructuredGrid data to a VTKHDF file.
|
|
642
|
+
|
|
643
|
+
Parameters
|
|
644
|
+
----------
|
|
645
|
+
filename : str
|
|
646
|
+
The name of the file to write the UnstructuredGrid to. Should end with '.vtkhdf'.
|
|
647
|
+
nodes : numpy.ndarray
|
|
648
|
+
An array of nodes defining the vertices of the UnstructuredGrid. Should be of shape (N, 3),
|
|
649
|
+
where N is the number of nodes.
|
|
650
|
+
cell_types : numpy.ndarray
|
|
651
|
+
An array of cell types for the UnstructuredGrid. Should be a 1D array of integers representing
|
|
652
|
+
the types of cells (e.g., VTK_TRIANGLE, VTK_QUAD, etc.).
|
|
653
|
+
connectivity : numpy.ndarray
|
|
654
|
+
An array of connectivity indices for the UnstructuredGrid. Should be a 1D array of integers
|
|
655
|
+
representing the indices of nodes that form each cell.
|
|
656
|
+
offsets : list or numpy.ndarray
|
|
657
|
+
An array of offsets for the connectivity array. Should be a 1D array of integers indicating
|
|
658
|
+
the start of each cell in the connectivity array. The length of this array should be equal to
|
|
659
|
+
the number of cells plus one (to account for the end of the last cell).
|
|
660
|
+
version : tuple, optional
|
|
661
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
662
|
+
point_data : dict, optional
|
|
663
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
664
|
+
and values are the corresponding numpy arrays.
|
|
665
|
+
cell_data : dict, optional
|
|
666
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
667
|
+
and values are the corresponding numpy arrays.
|
|
668
|
+
field_data : dict, optional
|
|
669
|
+
A dictionary containing field data arrays to be written. Keys and values
|
|
670
|
+
should match the desired metadata structure.
|
|
671
|
+
additional_metadata : dict, optional
|
|
672
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
673
|
+
that should be stored in the file, such as attributes or custom data.
|
|
674
|
+
multiblock_index : int, optional
|
|
675
|
+
An index for the multiblock dataset, if this UnstructuredGrid is part of a larger multiblock dataset.
|
|
676
|
+
If not provided, defaults to None.
|
|
677
|
+
"""
|
|
678
|
+
def __init__(self, filename, nodes, cell_types, connectivity, offsets, version=(2,2),
|
|
679
|
+
point_data=None, cell_data=None, field_data=None, additional_metadata=None, multiblock_index=None):
|
|
680
|
+
super().__init__(filename, additional_metadata=additional_metadata, version=version)
|
|
681
|
+
self.nodes = np.asarray(nodes)
|
|
682
|
+
self.cell_types = np.asarray(cell_types)
|
|
683
|
+
self.connectivity = np.asarray(connectivity)
|
|
684
|
+
self.offsets = self._correct_offsets(offsets, self.cell_types)
|
|
685
|
+
self.point_data = point_data or {}
|
|
686
|
+
self.cell_data = cell_data or {}
|
|
687
|
+
self.field_data = field_data or {}
|
|
688
|
+
self.multiblock_index = multiblock_index
|
|
689
|
+
|
|
690
|
+
@staticmethod
|
|
691
|
+
def _correct_offsets(offsets, cell_types):
|
|
692
|
+
"""
|
|
693
|
+
Correct the offsets array to ensure it is in the correct format for VTKHDF.
|
|
694
|
+
|
|
695
|
+
Parameters
|
|
696
|
+
----------
|
|
697
|
+
offsets : list or array-like
|
|
698
|
+
The offsets for the connectivity array. Can be a list or array of integers.
|
|
699
|
+
cell_types : list or array-like
|
|
700
|
+
The types of cells in the unstructured grid. Used to determine the number of cells.
|
|
701
|
+
|
|
702
|
+
Returns
|
|
703
|
+
-------
|
|
704
|
+
offsets : numpy.ndarray
|
|
705
|
+
A numpy array of offsets, corrected to ensure it starts with 0 and has the correct length.
|
|
706
|
+
|
|
707
|
+
Raises
|
|
708
|
+
-------
|
|
709
|
+
ValueError : If the offsets array is not of the correct length.
|
|
710
|
+
"""
|
|
711
|
+
|
|
712
|
+
offsets = np.asarray(offsets)
|
|
713
|
+
ncells = len(cell_types)
|
|
714
|
+
if len(offsets) == ncells:
|
|
715
|
+
offsets = np.hstack([0, offsets])
|
|
716
|
+
elif len(offsets) != ncells + 1:
|
|
717
|
+
raise ValueError("Offsets must be length ncells+1 or ncells (will prepend 0 if needed).")
|
|
718
|
+
if offsets[0] != 0:
|
|
719
|
+
offsets = offsets - offsets[0]
|
|
720
|
+
return offsets
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def write_topology(self):
|
|
724
|
+
"""
|
|
725
|
+
Write the topology of the unstructured grid to the HDF5 file.
|
|
726
|
+
|
|
727
|
+
This method creates the necessary attributes and datasets in the HDF5 file to represent
|
|
728
|
+
the unstructured grid topology, including nodes, connectivity, offsets, cell types, and counts.
|
|
729
|
+
|
|
730
|
+
If a multiblock index is provided, it is stored as an attribute. The version and type of the dataset
|
|
731
|
+
are also set as attributes. The nodes, connectivity, offsets, and cell types are stored as datasets
|
|
732
|
+
within the root group of the HDF5 file.
|
|
733
|
+
"""
|
|
734
|
+
if self.multiblock_index is not None:
|
|
735
|
+
self.root.attrs.create('Index', self.multiblock_index, dtype=idType)
|
|
736
|
+
self.root.attrs['Version'] = (2, 2)
|
|
737
|
+
ascii_type = 'UnstructuredGrid'.encode('ascii')
|
|
738
|
+
self.root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
739
|
+
self.root.create_dataset('Points', data=self.nodes, maxshape=(None, 3), dtype=fType)
|
|
740
|
+
self.root.create_dataset('Connectivity', data=self.connectivity, maxshape=(None,), dtype=idType)
|
|
741
|
+
self.root.create_dataset('Offsets', data=self.offsets, maxshape=(None,), dtype=idType)
|
|
742
|
+
self.root.create_dataset('Types', data=self.cell_types, maxshape=(None,), dtype=charType)
|
|
743
|
+
self.root.create_dataset('NumberOfPoints', data=np.array([len(self.nodes)]), dtype=idType)
|
|
744
|
+
self.root.create_dataset('NumberOfConnectivityIds', data=np.array([len(self.connectivity)]), dtype=idType)
|
|
745
|
+
self.root.create_dataset('NumberOfCells', data=np.array([len(self.cell_types)]), dtype=idType)
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
class VTKHDFPolyDataWriter(VTKHDFWriterBase):
|
|
750
|
+
"""
|
|
751
|
+
Write PolyData datasetes to a file in VTKHDF format.
|
|
752
|
+
|
|
753
|
+
This class allows for the creation of PolyData datasets, which are unstructured grids
|
|
754
|
+
defined by their points, vertices, lines, polygons, and strips. It inherits from
|
|
755
|
+
VTKHDFWriterBase and implements the write_vtkhdf_file method to handle the specifics of writing
|
|
756
|
+
PolyData to a VTKHDF file.
|
|
757
|
+
|
|
758
|
+
Parameters
|
|
759
|
+
----------
|
|
760
|
+
filename : str
|
|
761
|
+
The name of the file to write the PolyData to. Should end with '.vtkhdf'.
|
|
762
|
+
points : numpy.ndarray
|
|
763
|
+
An array of points defining the vertices of the PolyData. Should be of shape (N, 3),
|
|
764
|
+
where N is the number of points.
|
|
765
|
+
verts : tuple, optional
|
|
766
|
+
A tuple containing the connectivity and offsets for vertices in the PolyData.
|
|
767
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
768
|
+
of vertex indices and offsets is a 1D array indicating the start of each vertex.
|
|
769
|
+
lines : tuple, optional
|
|
770
|
+
A tuple containing the connectivity and offsets for lines in the PolyData.
|
|
771
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
772
|
+
of line indices and offsets is a 1D array indicating the start of each line.
|
|
773
|
+
polys : tuple, optional
|
|
774
|
+
A tuple containing the connectivity and offsets for polygons in the PolyData.
|
|
775
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
776
|
+
of polygon indices and offsets is a 1D array indicating the start of each polygon.
|
|
777
|
+
strips : tuple, optional
|
|
778
|
+
A tuple containing the connectivity and offsets for strips in the PolyData.
|
|
779
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
780
|
+
of strip indices and offsets is a 1D array indicating the start of each strip.
|
|
781
|
+
version : tuple, optional
|
|
782
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
783
|
+
point_data : dict, optional
|
|
784
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
785
|
+
and values are the corresponding numpy arrays.
|
|
786
|
+
cell_data : dict, optional
|
|
787
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
788
|
+
and values are the corresponding numpy arrays.
|
|
789
|
+
field_data : dict, optional
|
|
790
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
791
|
+
and values are the corresponding numpy arrays.
|
|
792
|
+
additional_metadata : dict, optional
|
|
793
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
794
|
+
that should be stored in the file, such as attributes or custom data.
|
|
795
|
+
multiblock_index : int, optional
|
|
796
|
+
An index for the multiblock dataset, if this PolyData is part of a larger multiblock dataset.
|
|
797
|
+
If not provided, defaults to None.
|
|
798
|
+
|
|
799
|
+
"""
|
|
800
|
+
def __init__(self, filename, points, verts=None, lines=None, polys=None, strips=None, version=(2, 2),
|
|
801
|
+
point_data=None, cell_data=None, field_data=None, additional_metadata=None, multiblock_index=None):
|
|
802
|
+
"""
|
|
803
|
+
Initialise the VTKHDFPolyDataWriter with the necessary parameters for PolyData.
|
|
804
|
+
|
|
805
|
+
Parameters
|
|
806
|
+
----------
|
|
807
|
+
filename : str
|
|
808
|
+
The name of the file to write the PolyData to. Should end with '.vtkhdf'.
|
|
809
|
+
points : numpy.ndarray
|
|
810
|
+
An array of points defining the vertices of the PolyData. Should be of shape (N, 3),
|
|
811
|
+
where N is the number of points.
|
|
812
|
+
verts : tuple, optional
|
|
813
|
+
A tuple containing the connectivity and offsets for vertices in the PolyData.
|
|
814
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
815
|
+
of vertex indices and offsets is a 1D array indicating the start of each vertex.
|
|
816
|
+
lines : tuple, optional
|
|
817
|
+
A tuple containing the connectivity and offsets for lines in the PolyData.
|
|
818
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
819
|
+
of line indices and offsets is a 1D array indicating the start of each line.
|
|
820
|
+
polys : tuple, optional
|
|
821
|
+
A tuple containing the connectivity and offsets for polygons in the PolyData.
|
|
822
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
823
|
+
of polygon indices and offsets is a 1D array indicating the start of each polygon.
|
|
824
|
+
strips : tuple, optional
|
|
825
|
+
A tuple containing the connectivity and offsets for strips in the PolyData.
|
|
826
|
+
Should be of the form (connectivity, offsets), where connectivity is a 1D array
|
|
827
|
+
of strip indices and offsets is a 1D array indicating the start of each strip.
|
|
828
|
+
version : tuple, optional
|
|
829
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
830
|
+
point_data : dict, optional
|
|
831
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
832
|
+
and values are the corresponding numpy arrays.
|
|
833
|
+
cell_data : dict, optional
|
|
834
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
835
|
+
and values are the corresponding numpy arrays.
|
|
836
|
+
field_data : dict, optional
|
|
837
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
838
|
+
and values are the corresponding numpy arrays.
|
|
839
|
+
additional_metadata : dict, optional
|
|
840
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
841
|
+
that should be stored in the file, such as attributes or custom data.
|
|
842
|
+
multiblock_index : int, optional
|
|
843
|
+
An index for the multiblock dataset, if this PolyData is part of a larger multiblock dataset.
|
|
844
|
+
If not provided, defaults to None.
|
|
845
|
+
|
|
846
|
+
"""
|
|
847
|
+
|
|
848
|
+
super().__init__(filename, version=version, additional_metadata=additional_metadata)
|
|
849
|
+
|
|
850
|
+
self.points = points
|
|
851
|
+
self.verts = verts
|
|
852
|
+
self.lines = lines
|
|
853
|
+
self.strips = strips
|
|
854
|
+
self.polys = polys
|
|
855
|
+
|
|
856
|
+
self.point_data = point_data
|
|
857
|
+
self.cell_data = cell_data
|
|
858
|
+
self.field_data = field_data
|
|
859
|
+
|
|
860
|
+
# set topology attributes
|
|
861
|
+
if self.points is not None:
|
|
862
|
+
self.npoints = len(points)
|
|
863
|
+
if self.verts is not None:
|
|
864
|
+
self.nverts = len(verts[1])
|
|
865
|
+
if self.lines is not None:
|
|
866
|
+
self.nlines = len(lines[1])
|
|
867
|
+
if self.strips is not None:
|
|
868
|
+
self.nstrips = len(strips[1])
|
|
869
|
+
if self.polys is not None:
|
|
870
|
+
self.npolys = len(polys[1])
|
|
871
|
+
|
|
872
|
+
if self.cell_data:
|
|
873
|
+
self.ncells = self.nverts + self.nlines + self.nstrips + self.npolys
|
|
874
|
+
|
|
875
|
+
self.multiblock_index = multiblock_index
|
|
876
|
+
|
|
877
|
+
# data size checks
|
|
878
|
+
self.check_array_sizes_for_point_data(point_data)
|
|
879
|
+
self.check_array_sizes_for_cell_data(cell_data)
|
|
880
|
+
|
|
881
|
+
def write_topology(self):
|
|
882
|
+
"""
|
|
883
|
+
Write the topology of the PolyData to the HDF5 file.
|
|
884
|
+
|
|
885
|
+
This method creates the necessary attributes and datasets in the HDF5 file to represent
|
|
886
|
+
the PolyData topology, including points, vertices, lines, polygons, and strips.
|
|
887
|
+
If a multiblock index is provided, it is stored as an attribute. The version and type of the dataset
|
|
888
|
+
are also set as attributes. The points, vertices, lines, polygons, and strips are stored as datasets
|
|
889
|
+
within the root group of the HDF5 file.
|
|
890
|
+
|
|
891
|
+
"""
|
|
892
|
+
if self.multiblock_index is not None:
|
|
893
|
+
self.root.attrs.create('Index', self.multiblock_index, dtype=idType)
|
|
894
|
+
self.root.attrs['Version'] = (2, 2)
|
|
895
|
+
ascii_type = 'PolyData'.encode('ascii')
|
|
896
|
+
self.root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
897
|
+
self.root.create_dataset('Points', data=self.points, maxshape=(None, 3), dtype=fType)
|
|
898
|
+
self.root.create_dataset('NumberOfPoints', data=np.array([len(self.points)]), dtype=idType)
|
|
899
|
+
for name, topo in zip(['Vertices', 'Lines', 'Polygons', 'Strips'],
|
|
900
|
+
[self.verts, self.lines, self.polys, self.strips]):
|
|
901
|
+
group = self.root.create_group(name)
|
|
902
|
+
if topo is not None:
|
|
903
|
+
connectivity, offsets = topo
|
|
904
|
+
if len(offsets) > 0 and offsets[0] != 0:
|
|
905
|
+
offsets = np.hstack([0, offsets])
|
|
906
|
+
group.create_dataset('Connectivity', data=connectivity, maxshape=(None,), dtype=idType)
|
|
907
|
+
group.create_dataset('Offsets', data=offsets, maxshape=(None,), dtype=idType)
|
|
908
|
+
group.create_dataset('NumberOfConnectivityIds', data=np.array([len(connectivity)]), dtype=idType)
|
|
909
|
+
group.create_dataset('NumberOfCells', data=np.array([len(offsets) - 1]), dtype=idType)
|
|
910
|
+
else:
|
|
911
|
+
group.create_dataset('NumberOfConnectivityIds', data=np.array([0]), maxshape=(None,), dtype=idType)
|
|
912
|
+
group.create_dataset('NumberOfCells', data=np.array([0]), maxshape=(None,), dtype=idType)
|
|
913
|
+
group.create_dataset('Offsets', data=np.array([0]), maxshape=(None,), dtype=idType)
|
|
914
|
+
group.create_dataset('Connectivity', (0,), maxshape=(None,), dtype=idType)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
class VTKHDFRectilinearGridWriter(VTKHDFWriterBase):
|
|
920
|
+
"""
|
|
921
|
+
Write RectilinearGrid dtasets to a file in VTKHDF format.
|
|
922
|
+
|
|
923
|
+
This class allows for the creation of RectilinearGrid datasets, which are structured grids
|
|
924
|
+
defined by their points along each axis. It inherits from VTKHDFWriterBase and implements the
|
|
925
|
+
write_vtkhdf_file method to handle the specifics of writing RectilinearGrid to a VTKHDF file.
|
|
926
|
+
|
|
927
|
+
Parameters
|
|
928
|
+
----------
|
|
929
|
+
filename : str
|
|
930
|
+
The name of the file to write the RectilinearGrid to. Should end with '.vtkhdf'.
|
|
931
|
+
x_coords : numpy.ndarray
|
|
932
|
+
An array of x-coordinates defining the points along the x-axis.
|
|
933
|
+
y_coords : numpy.ndarray
|
|
934
|
+
An array of y-coordinates defining the points along the y-axis.
|
|
935
|
+
z_coords : numpy.ndarray
|
|
936
|
+
An array of z-coordinates defining the points along the z-axis.
|
|
937
|
+
version : tuple, optional
|
|
938
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
939
|
+
point_data : dict, optional
|
|
940
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
941
|
+
and values are the corresponding numpy arrays.
|
|
942
|
+
cell_data : dict, optional
|
|
943
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
944
|
+
and values are the corresponding numpy arrays.
|
|
945
|
+
field_data : dict, optional
|
|
946
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
947
|
+
and values are the corresponding numpy arrays.
|
|
948
|
+
additional_metadata : dict, optional
|
|
949
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
950
|
+
that should be stored in the file, such as attributes or custom data.
|
|
951
|
+
multiblock_index : int, optional
|
|
952
|
+
An index for the multiblock dataset, if this RectilinearGrid is part of a larger multiblock dataset.
|
|
953
|
+
If not provided, defaults to None.
|
|
954
|
+
|
|
955
|
+
"""
|
|
956
|
+
def __init__(self, filename, x_coords, y_coords, z_coords, version=(2, 2),
|
|
957
|
+
point_data=None, cell_data=None, field_data=None,
|
|
958
|
+
additional_metadata=None, multiblock_index=None):
|
|
959
|
+
"""
|
|
960
|
+
Initialize the VTKHDFRectilinearGridWriter with the necessary parameters for RectilinearGrid.
|
|
961
|
+
|
|
962
|
+
Parameters
|
|
963
|
+
----------
|
|
964
|
+
filename : str
|
|
965
|
+
The name of the file to write the RectilinearGrid to. Should end with '.vtkhdf'.
|
|
966
|
+
x_coords : numpy.ndarray
|
|
967
|
+
An array of x-coordinates defining the points along the x-axis.
|
|
968
|
+
y_coords : numpy.ndarray
|
|
969
|
+
An array of y-coordinates defining the points along the y-axis.
|
|
970
|
+
z_coords : numpy.ndarray
|
|
971
|
+
An array of z-coordinates defining the points along the z-axis.
|
|
972
|
+
version : tuple, optional
|
|
973
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
974
|
+
point_data : dict, optional
|
|
975
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
976
|
+
and values are the corresponding numpy arrays.
|
|
977
|
+
cell_data : dict, optional
|
|
978
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
979
|
+
and values are the corresponding numpy arrays.
|
|
980
|
+
field_data : dict, optional
|
|
981
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
982
|
+
and values are the corresponding numpy arrays.
|
|
983
|
+
additional_metadata : dict, optional
|
|
984
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
985
|
+
that should be stored in the file, such as attributes or custom data.
|
|
986
|
+
multiblock_index : int, optional
|
|
987
|
+
An index for the multiblock dataset, if this RectilinearGrid is part of a larger multiblock dataset.
|
|
988
|
+
If not provided, defaults to None.
|
|
989
|
+
|
|
990
|
+
"""
|
|
991
|
+
# simple check on coordinates - must first be a list or array
|
|
992
|
+
if not (isinstance(x_coords, (np.ndarray, list, tuple))):
|
|
993
|
+
raise TypeError("x must be a numpy array, tuple or list")
|
|
994
|
+
if not (isinstance(y_coords, (np.ndarray, list, tuple))):
|
|
995
|
+
raise TypeError("y must be a numpy array, tuple or list")
|
|
996
|
+
if not (isinstance(z_coords, (np.ndarray, list, tuple))):
|
|
997
|
+
raise TypeError("z must be a numpy array, tuple or list")
|
|
998
|
+
|
|
999
|
+
# check if list or array is numeric
|
|
1000
|
+
x = np.asarray(x_coords)
|
|
1001
|
+
y = np.asarray(y_coords)
|
|
1002
|
+
z = np.asarray(z_coords)
|
|
1003
|
+
if not (np.issubdtype(x.dtype, np.number)):
|
|
1004
|
+
raise TypeError("x must be a numeric numpy array or list")
|
|
1005
|
+
if not (np.issubdtype(y.dtype, np.number)):
|
|
1006
|
+
raise TypeError("y must be a numeric numpy array or list")
|
|
1007
|
+
if not (np.issubdtype(z.dtype, np.number)):
|
|
1008
|
+
raise TypeError("z must be a numeric numpy array or list")
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
super().__init__(filename, additional_metadata=additional_metadata, version=version)
|
|
1012
|
+
|
|
1013
|
+
self.x = x
|
|
1014
|
+
self.y = y
|
|
1015
|
+
self.z = z
|
|
1016
|
+
|
|
1017
|
+
self.whole_extent = np.array([np.zeros(3), np.array([len(x), len(y), len(z)]) - 1]).T.flatten()
|
|
1018
|
+
|
|
1019
|
+
self.num_cells = (len(x_coords) - 1, len(y_coords) - 1, len(z_coords) - 1)
|
|
1020
|
+
self.num_points = (len(x_coords), len(y_coords), len(z_coords))
|
|
1021
|
+
self.ncells = np.prod(self.num_cells)
|
|
1022
|
+
self.npoints = np.prod(self.num_points)
|
|
1023
|
+
|
|
1024
|
+
self.multiblock_index = multiblock_index
|
|
1025
|
+
|
|
1026
|
+
# do check on provided data sizes before attempting to write
|
|
1027
|
+
self.check_array_sizes_for_cell_data(cell_data)
|
|
1028
|
+
self.check_array_sizes_for_point_data(point_data)
|
|
1029
|
+
|
|
1030
|
+
self.point_data = point_data
|
|
1031
|
+
self.cell_data = cell_data
|
|
1032
|
+
self.field_data = field_data
|
|
1033
|
+
|
|
1034
|
+
def write_topology(self):
|
|
1035
|
+
"""
|
|
1036
|
+
Write the topology of the RectilinearGrid to the HDF5 file.
|
|
1037
|
+
|
|
1038
|
+
This method creates the necessary attributes and datasets in the HDF5 file to represent
|
|
1039
|
+
the RectilinearGrid topology, including x-coordinates, y-coordinates, z-coordinates,
|
|
1040
|
+
number of cells, and number of points. If a multiblock index is provided, it is stored as an attribute.
|
|
1041
|
+
The version and type of the dataset are also set as attributes. The x-coordinates, y-coordinates,
|
|
1042
|
+
and z-coordinates are stored as datasets within the root group of the HDF5 file.
|
|
1043
|
+
|
|
1044
|
+
Notes
|
|
1045
|
+
-----
|
|
1046
|
+
This is experimental and may not be fully functional yet. It is currently not supported by VTKHDF.
|
|
1047
|
+
|
|
1048
|
+
"""
|
|
1049
|
+
if self.multiblock_index is not None:
|
|
1050
|
+
self.root.attrs.create('Index', self.multiblock_index, dtype=idType)
|
|
1051
|
+
self.root.attrs['Version'] = self.version
|
|
1052
|
+
ascii_type = 'RectilinearGrid'.encode('ascii')
|
|
1053
|
+
self.root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
1054
|
+
|
|
1055
|
+
self.root.attrs.create('WholeExtent', self.whole_extent, dtype=fType)
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
class VTKHDFStructuredGridWriter(VTKHDFWriterBase):
|
|
1059
|
+
"""
|
|
1060
|
+
Write StructuredGrid datasets to a file in VTKHDF format.
|
|
1061
|
+
|
|
1062
|
+
This class allows for the creation of StructuredGrid datasets, which are structured grids
|
|
1063
|
+
defined by their points along each axis. It inherits from VTKHDFWriterBase and implements the
|
|
1064
|
+
write_vtkhdf_file method to handle the specifics of writing StructuredGrid to a VTKHDF file.
|
|
1065
|
+
|
|
1066
|
+
Parameters
|
|
1067
|
+
----------
|
|
1068
|
+
filename : str
|
|
1069
|
+
The name of the file to write the StructuredGrid to. Should end with '.vtkhdf'.
|
|
1070
|
+
points : numpy.ndarray
|
|
1071
|
+
An array of points defining the vertices of the StructuredGrid. Should be of shape (N, 3),
|
|
1072
|
+
where N is the number of points.
|
|
1073
|
+
num_cells : list or tuple
|
|
1074
|
+
A list or tuple defining the number of cells along each axis of the StructuredGrid.
|
|
1075
|
+
Should be of length 3, representing the number of cells in the x, y, and z directions.
|
|
1076
|
+
version : tuple, optional
|
|
1077
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
1078
|
+
point_data : dict, optional
|
|
1079
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
1080
|
+
and values are the corresponding numpy arrays.
|
|
1081
|
+
cell_data : dict, optional
|
|
1082
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
1083
|
+
and values are the corresponding numpy arrays.
|
|
1084
|
+
field_data : dict, optional
|
|
1085
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
1086
|
+
and values are the corresponding numpy arrays.
|
|
1087
|
+
additional_metadata : dict, optional
|
|
1088
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
1089
|
+
that should be stored in the file, such as attributes or custom data.
|
|
1090
|
+
multiblock_index : int, optional
|
|
1091
|
+
An index for the multiblock dataset, if this StructuredGrid is part of a larger multiblock dataset.
|
|
1092
|
+
If not provided, defaults to None.
|
|
1093
|
+
|
|
1094
|
+
"""
|
|
1095
|
+
|
|
1096
|
+
def __init__(self, filename, points, num_cells, version=(2, 2),
|
|
1097
|
+
point_data=None, cell_data=None, field_data=None, additional_metadata=None, multiblock_index=None):
|
|
1098
|
+
"""
|
|
1099
|
+
Initialise the VTKHDFStructuredGridWriter with the necessary parameters for StructuredGrid.
|
|
1100
|
+
|
|
1101
|
+
Parameters
|
|
1102
|
+
----------
|
|
1103
|
+
filename : str
|
|
1104
|
+
The name of the file to write the StructuredGrid to. Should end with '.vtkhdf'.
|
|
1105
|
+
points : numpy.ndarray
|
|
1106
|
+
An array of points defining the vertices of the StructuredGrid. Should be of shape (N, 3),
|
|
1107
|
+
where N is the number of points.
|
|
1108
|
+
version : tuple, optional
|
|
1109
|
+
The version of the VTKHDF format to use. Default is (2, 2).
|
|
1110
|
+
point_data : dict, optional
|
|
1111
|
+
A dictionary containing point data arrays to be written. Keys are the names of the data arrays,
|
|
1112
|
+
and values are the corresponding numpy arrays.
|
|
1113
|
+
cell_data : dict, optional
|
|
1114
|
+
A dictionary containing cell data arrays to be written. Keys are the names of the data arrays,
|
|
1115
|
+
and values are the corresponding numpy arrays.
|
|
1116
|
+
field_data : dict, optional
|
|
1117
|
+
A dictionary containing field data arrays to be written. Keys are the names of the data arrays,
|
|
1118
|
+
and values are the corresponding numpy arrays.
|
|
1119
|
+
additional_metadata : dict, optional
|
|
1120
|
+
Additional metadata to be written to the VTKHDF file. This can include any additional information
|
|
1121
|
+
that should be stored in the file, such as attributes or custom data.
|
|
1122
|
+
multiblock_index : int, optional
|
|
1123
|
+
An index for the multiblock dataset, if this StructuredGrid is part of a larger multiblock dataset.
|
|
1124
|
+
If not provided, defaults to None.
|
|
1125
|
+
|
|
1126
|
+
"""
|
|
1127
|
+
|
|
1128
|
+
super().__init__(filename, additional_metadata=additional_metadata, version=version)
|
|
1129
|
+
|
|
1130
|
+
self.points = np.asarray(points)
|
|
1131
|
+
|
|
1132
|
+
if not (np.issubdtype(points.dtype, np.number)):
|
|
1133
|
+
raise TypeError("points must be a numeric numpy array")
|
|
1134
|
+
|
|
1135
|
+
if len(self.points.shape) != 2 or self.points.shape[1] != 3:
|
|
1136
|
+
raise ValueError("Points must be a 2D array with shape (N, 3) where N is the number of points.")
|
|
1137
|
+
|
|
1138
|
+
self.npoints = len(self.points)
|
|
1139
|
+
|
|
1140
|
+
self.num_cells = np.asarray(num_cells)
|
|
1141
|
+
self.num_points = self.num_cells + 1
|
|
1142
|
+
self.ncells = np.prod(self.num_cells)
|
|
1143
|
+
|
|
1144
|
+
self.multiblock_index = multiblock_index
|
|
1145
|
+
|
|
1146
|
+
self.whole_extent = np.array([np.zeros(3), num_cells]).T.flatten()
|
|
1147
|
+
self.piece_extent = self.whole_extent
|
|
1148
|
+
|
|
1149
|
+
# do check on provided data sizes before attempting to write
|
|
1150
|
+
self.check_array_sizes_for_point_data(point_data)
|
|
1151
|
+
self.check_array_sizes_for_cell_data(cell_data)
|
|
1152
|
+
|
|
1153
|
+
self.point_data = point_data
|
|
1154
|
+
self.cell_data = cell_data
|
|
1155
|
+
self.field_data = field_data
|
|
1156
|
+
|
|
1157
|
+
def write_topology(self):
|
|
1158
|
+
"""
|
|
1159
|
+
Write the topology of the StructuredGrid to the HDF5 file.
|
|
1160
|
+
|
|
1161
|
+
This method creates the necessary attributes and datasets in the HDF5 file to represent
|
|
1162
|
+
the StructuredGrid topology, including points, number of points, and optionally
|
|
1163
|
+
multiblock index, version, and type. The points are stored as a dataset within the root group
|
|
1164
|
+
of the HDF5 file.
|
|
1165
|
+
|
|
1166
|
+
If a multiblock index is provided, it is stored as an attribute.
|
|
1167
|
+
|
|
1168
|
+
Notes
|
|
1169
|
+
-----
|
|
1170
|
+
This is experimental and may not be fully functional yet. It is currently not supported by VTKHDF.
|
|
1171
|
+
|
|
1172
|
+
"""
|
|
1173
|
+
|
|
1174
|
+
if self.multiblock_index is not None:
|
|
1175
|
+
self.root.attrs.create('Index', self.multiblock_index, dtype=idType)
|
|
1176
|
+
|
|
1177
|
+
self.root.attrs['Version'] = self.version
|
|
1178
|
+
ascii_type = 'StructuredGrid'.encode('ascii')
|
|
1179
|
+
self.root.attrs.create('Type', ascii_type, dtype=h5py.string_dtype('ascii', len(ascii_type)))
|
|
1180
|
+
self.root.create_dataset('Points', data=self.points, maxshape=(None, 3), dtype=fType)
|
|
1181
|
+
self.root.create_dataset('NumberOfPoints', data=np.array([self.npoints]), dtype=idType)
|
|
1182
|
+
self.root.attrs.create('WholeExtent', self.whole_extent, dtype=fType)
|
|
1183
|
+
self.root.attrs.create('PieceExtent', self.piece_extent, dtype=fType)
|
|
1184
|
+
|