forked from aslakbergersen/viz-tools
/
vtkread.py
234 lines (180 loc) · 7.96 KB
/
vtkread.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
from xml.etree.ElementTree import ElementTree
import numpy as np
import os
import vtk
import array_handler
from dolfin import Mesh, MeshEditor, VectorFunctionSpace, FunctionSpace, Function, \
MPI, mpi_comm_world
def vtk_ug_to_dolfin_mesh(ug):
"""
Create a DOLFIN Mesh from a vtkUnstructuredGrid object
"""
if not isinstance(ug, vtk.vtkUnstructuredGrid):
raise TypeError("Expected a 'vtkUnstructuredGrid'")
# Get mesh data
num_cells = int(ug.GetNumberOfCells())
num_vertices = int(ug.GetNumberOfPoints())
# Get topological and geometrical dimensions
cell = ug.GetCell(0)
gdim = int(cell.GetCellDimension())
cell_type = cell.GetCellType()
if cell_type not in [vtk.VTK_TETRA, vtk.VTK_TRIANGLE]:
raise TypeError("DOLFIN only support meshes of triangles " + \
"and tetrahedrons.")
tdim = 3 if cell_type == vtk.VTK_TETRA else 2
# Create empty DOLFIN Mesh
mesh = Mesh()
editor = MeshEditor()
editor.open(mesh, tdim, gdim)
editor.init_cells(num_cells)
editor.init_vertices(num_vertices)
editor.close()
# Assign the cell and vertex informations directly from the vtk data
cells_array = array_handler.vtk2array(ug.GetCells().GetData())
# Get the assumed fixed size of indices and create an index array
cell_size = cell.GetPointIds().GetNumberOfIds()
cellinds = np.arange(len(cells_array))
# Each cell_ids_size:th data point need to be deleted from the
# index array
ind_delete = slice(0, len(cells_array), cell_size+1)
# Check that the removed value all have the same value which should
# be the size of the data
if not np.all(cells_array[ind_delete]==cell_size):
raise ValueError("Expected all cells to be of the same size")
cellinds = np.delete(cellinds, ind_delete)
# Get cell data from mesh and make it writeable (cell data is non
# writeable by default) and update the values
mesh_cells = mesh.cells()
mesh_cells.flags.writeable = True
mesh_cells[:] = np.reshape(cells_array[cellinds], \
(num_cells , cell_size))
# Set coordinates from vtk data
vertex_array = array_handler.vtk2array(ug.GetPoints().GetData())
if vertex_array.shape[1] != gdim:
vertex_array = vertex_array[:,:gdim]
mesh.coordinates()[:] = vertex_array
return mesh
class VTKToDOLFIN(object):
"""
A wrapper around vtk to simplify handling of VTK files
generated from DOLFIN.
The class handles reading of data into DOLFIN objects for further processing
"""
def __init__(self, filename, mesh=None, deepcopy=False):
"""
Initialize a the reader with a pvd or a vtu filename
"""
if not os.path.isfile(filename):
raise IOError("File '%s' does not excist"%filename)
filetype = filename.split(".")[-1]
self._name = ".".join(filename.split(".")[0:-1])
if filetype not in ["pvd", "vtu"]:
raise TypeError("Expected a 'pvd' or a 'vtu' file")
# Get dirname
dirname = os.path.dirname(filename)
# Check mesh argument
if mesh is not None and not isinstance(mesh, Mesh):
raise TypeError, "Expected a 'Mesh' for the mesh arguments"
# Store deepcopy argument
self._deepcopy = deepcopy
# Store mesh
self._mesh = mesh
# Initialize the filename cache
self._filenames = []
if filetype == "vtu":
self._filenames.append(filename)
self._times = np.array([])
else:
# Parse pvd file
tree = ElementTree(file=filename)
times = []
for item in tree.iter():
if item.tag == "DataSet":
self._filenames.append(os.path.join(\
dirname,item.attrib["file"]))
times.append(float(item.attrib["timestep"]))
times = np.array(times, dtype='d')
# If there are no time data stored in the file use an empty array
if np.all(np.diff(times)==1):
times = np.array([])
# Store time data
self._times = times
# Construct file reader
self.reader = vtk.vtkXMLUnstructuredGridReader()
# Read in data from file
self._update_vtk_data()
# Init dolfin structures (Function, FunctionSpace)
self._init_dolfin_data()
def _update_vtk_data(self, index=0):
"Set a new data file"
# Update file name
print "Reading '%s'"%self._filenames[index]
self.reader.SetFileName(self._filenames[index])
# Read data
self.reader.Update()
# Set data type (scalar or vector)
# FIXME: Include Tensors when that is supported by DOLFIN
self.scalar = self.reader.GetOutput().GetPointData().GetScalars() is not None
print "Scalar data set" if self.scalar else "Vector data set"
def _init_dolfin_data(self):
"Update DOLFIN function from vtk data"
if self.reader.GetNumberOfPointArrays() != 1:
raise ValueError("Expected the vtk file to include one data "\
"set per vertex.")
# Initilize FunctionSpace and Function if not initialized
if self.scalar:
self._V = FunctionSpace(self.mesh(), "CG", 1)
else:
self._V = VectorFunctionSpace(self.mesh(), "CG", 1)
self._u = Function(self._V)
def _update_dolfin_data(self):
"Update dolfin data from present VTK file"
# Get VTK point data
point_data = self.reader.GetOutput().GetPointData()
# Get data and update Function
if self.scalar:
self._u.vector()[:] = array_handler.vtk2array(point_data.GetScalars())
else:
values = array_handler.vtk2array(point_data.GetVectors()).transpose()
self._u.vector()[:] = np.reshape(values, (np.prod(values.shape),))
def functions_space(self):
"Return the FunctionSpace"
return self._V
def mesh(self):
"Return the dolfin mesh"
# If no mesh is stored read in from UnstructuredGridData
if self._mesh is None:
self._mesh = vtk_ug_to_dolfin_mesh(self.reader.GetOutput())
# Small sanity check, only works in parallel
if MPI.size(mpi_comm_world()) == 1:
assert(self._mesh.num_vertices() == \
self.reader.GetOutput().GetNumberOfPoints() and \
self._mesh.num_cells() == \
self.reader.GetOutput().GetNumberOfCells())
return self._mesh
def name(self):
"Return the name"
return self._name
def __getitem__(self, index):
"x.__getitem__(y) <==> x[y]"
# Update data structures to next index if not out of files
if not isinstance(index, int):
raise TypeError("Expected an int for the index argument")
if index < 0 or index >= len(self):
raise IndexError("index need to be smaller than ")
# Update the stored data
self._update_vtk_data(index)
self._update_dolfin_data()
# Should we return a copy of the stored data?
u = self._u.copy() if self._deepcopy else self._u
# If time is registered return with this information
if len(self._times):
return self._times[index], u
return u
def __len__(self):
"x.__len__() <==> len(x)"
return len(self._filenames)
def __iter__(self):
"x.__iter__() <==> iter(x)"
for i in xrange(len(self)):
yield self[i]