/[escript]/trunk/pyvisi/py_src/datacollector.py
ViewVC logotype

Contents of /trunk/pyvisi/py_src/datacollector.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2313 - (show annotations)
Tue Mar 17 03:41:17 2009 UTC (10 years, 5 months ago) by gross
File MIME type: text/x-python
File size: 14613 byte(s)
pyvisi is now rejecting to work under MPI with more than 1 processor. Tests for this configuration are dropped now.



1
2 ########################################################
3 #
4 # Copyright (c) 2003-2008 by University of Queensland
5 # Earth Systems Science Computational Center (ESSCC)
6 # http://www.uq.edu.au/esscc
7 #
8 # Primary Business: Queensland, Australia
9 # Licensed under the Open Software License version 3.0
10 # http://www.opensource.org/licenses/osl-3.0.php
11 #
12 ########################################################
13
14 __copyright__="""Copyright (c) 2003-2008 by University of Queensland
15 Earth Systems Science Computational Center (ESSCC)
16 http://www.uq.edu.au/esscc
17 Primary Business: Queensland, Australia"""
18 __license__="""Licensed under the Open Software License version 3.0
19 http://www.opensource.org/licenses/osl-3.0.php"""
20 __url__="http://www.uq.edu.au/esscc/escript-finley"
21
22 """
23 @var __author__: name of author
24 @var __copyright__: copyrights
25 @var __license__: licence agreement
26 @var __url__: url entry point on documentation
27 @var __version__: version
28 @var __date__: date of the version
29 """
30
31 __author__="John Ngui, john.ngui@uq.edu.au"
32
33
34 import vtk
35 import tempfile, os, sys
36 from constant import Source, ColorMode
37 from esys.escript import getMPISizeWorld
38 import esys.escript
39
40 class DataCollector:
41 """
42 Class that defines a data collector. A data collector is used to read
43 data from a XML file or from an escript object directly. Writing XML
44 files are expensive, but this approach has the advantage given that the
45 results can be analyzed easily after the simulation has completed.
46
47 @attention: A DataCollector instance can only be used to specify one
48 scalar, vector and tensor attribute from a source at any one time. If a
49 second scalar, vector or tensor attribute needs to be specified from the
50 same source, a second DataCollector instance must be created.
51 """
52
53 def __init__(self, source = Source.XML):
54 """
55 Initialise the data collector.
56
57 @type source: L{Source <constant.Source>} constant
58 @param source: Source type
59 """
60 if getMPISizeWorld()>1:
61 raise ValueError,"pyvisi.DataCollector is not running on more than one processor"
62 self.__source = source
63 # Keeps track on whether DataCollector have been modified.
64 self.__modified = True
65 # Keeps track of the number of times the 'setFileName' or 'setData'
66 # method have been executed.
67 self.__count = 0
68 # Keeps track on whether any specific scalar, vector or tensor
69 # field have been specified.
70 self.__set_scalar = False
71 self.__set_vector= False
72 self.__set_tensor= False
73 self.__tmp_fd = None
74 self.__tmp_file = None
75 if(source == Source.XML): # Source is an XML file.
76 self.__vtk_xml_reader = vtk.vtkXMLUnstructuredGridReader()
77 # Source is a escript data object using a temp file in the background.
78 elif (self.__source == Source.ESCRIPT):
79 self.__vtk_xml_reader = vtk.vtkXMLUnstructuredGridReader()
80 # Create a temporary .xml file and retrieve its path.
81 # Should raise IOError on failure, in wich case
82 # __tmp_fd will remain None.
83 fd_and_name = tempfile.mkstemp(suffix=".xml")
84 self.__tmp_fd = fd_and_name[0]
85 self.__tmp_file = fd_and_name[1]
86
87 def __del__(self):
88 """
89 Perform some clean up of ths assumese temporary file.
90 """
91 # remove this test and rely upon the existance of an open
92 # __tmp_file to decide upon closing and unlinking.
93 # if (self.__source == Source.ESCRIPT):
94 if self.__tmp_fd != None :
95 os.close(self.__tmp_fd)
96 os.unlink(self.__tmp_file)
97
98 def setFileName(self, file_name):
99 """
100 Set the XML file name to read.
101
102 @type file_name: String
103 @param file_name: Name of the file to read
104 """
105
106 self.__modified = True
107 self.__count += 1
108
109 if(self.__source == Source.XML):
110 # Check whether the specified file exists, otherwise an error is
111 # raised.
112 if not(os.access(file_name, os.F_OK)):
113 raise IOError("\nERROR: '%s' file does NOT exists.\n" % \
114 file_name)
115
116 self.__vtk_xml_reader.SetFileName(file_name)
117 # Update must be called after SetFileName to make the reader
118 # up-to-date. Otherwise, some output values may be incorrect.
119 self.__vtk_xml_reader.Update()
120 self.__get_attribute_lists()
121 else:
122 raise ValueError("Source type %s does not support \
123 'setFileName'\n" % self.__source)
124
125 def setData(self,**args):
126 """
127 Create data using the <name>=<data> pairing. Assumption is made
128 that the data will be given in the appropriate format.
129 """
130
131 self.__modified = True
132 self.__count += 1
133
134 if self.__source == Source.ESCRIPT:
135 esys.escript.saveVTK(self.__tmp_file,**args)
136 self.__vtk_xml_reader.SetFileName(self.__tmp_file)
137
138 # Modified must be called for setData but NOT for
139 # setFileName. If Modified is not called, only the first file
140 # will always be displayed. The reason Modified is used is
141 # because the same temporary file name is always used
142 # (previous file is overwritten). Modified MUST NOT be used in
143 # setFileName, as it can cause incorrect output such as map.
144 self.__vtk_xml_reader.Modified()
145
146 # Update must be called after Modified. If Update is called before
147 # Modified, then the first/second image(s) may not be updated
148 # correctly.
149 self.__vtk_xml_reader.Update()
150 self.__get_attribute_lists()
151 else:
152 raise ValueError("Source type %s does not support 'setData'\n" \
153 % self.__source)
154
155 # This method is used to delay the execution of setting the active scalar
156 # until 'setFileName' or 'setData' have been executed.
157 def setActiveScalar(self, scalar):
158 """
159 Specify the scalar field to load.
160
161 @type scalar: String
162 @param scalar: Scalar field to load from the file.
163 """
164
165 self.__set_scalar = True
166 self.__active_scalar = scalar
167
168 def _setActiveScalar(self):
169 """
170 Load the specified scalar field.
171 """
172
173 # Check whether the specified scalar is available in either point
174 # or cell data. If not available, an error is raised.
175
176 # NOTE: This check is similar to the check used in _getScalarRange
177 # but this is used only when a scalar attribute has been specified.
178 if self.__active_scalar in self.__point_attribute['scalars']:
179 self._getDataCollectorOutput().GetPointData().SetActiveScalars(
180 self.__active_scalar)
181 elif self.__active_scalar in self.__cell_attribute['scalars']:
182 self._getDataCollectorOutput().GetCellData().SetActiveScalars(
183 self.__active_scalar)
184 else:
185 raise IOError("ERROR: No scalar called '%s' is available." % \
186 self.__active_scalar)
187
188 # This method is used to delay the execution of setting the active vector
189 # until 'setFileName' or 'setData' have been executed.
190 def setActiveVector(self, vector):
191 """
192 Specify the vector field to load.
193
194 @type vector: String
195 @param vector: Vector field to load from the file.
196 """
197
198 self.__set_vector = True
199 self.__active_vector = vector
200
201 def _setActiveVector(self):
202 """
203 Load the specified vector field.
204 """
205
206 # Check whether the specified vector is available in either point
207 # or cell data. If not available, error is raised.
208
209 # NOTE: This check is similar to the check used in _getVectorRange
210 # but this is used only when a vector attribute has been specified.
211 if self.__active_vector in self.__point_attribute['vectors']:
212 self._getDataCollectorOutput().GetPointData().SetActiveVectors(
213 self.__active_vector)
214 elif self.__active_vector in self.__cell_attribute['vectors']:
215 self._getDataCollectorOutput().GetCellData().SetActiveVectors(
216 self.__active_vector)
217 else:
218 raise IOError("ERROR: No vector called '%s' is available." % \
219 self.__active_vector)
220
221 # This method is used to delay the execution of setting the active tensor
222 # until 'setFileName' or 'setData' have been executed.
223 def setActiveTensor(self, tensor):
224 """
225 Specify the tensor field to load.
226
227 @type tensor: String
228 @param tensor: Tensor field to load from the file.
229 """
230
231 self.__set_tensor = True
232 self.__active_tensor = tensor
233
234 def _setActiveTensor(self):
235 """
236 Load the the specified tensor field.
237 """
238
239 # Check whether the specified tensor is available in either point
240 # or cell data. If not available, error is raised.
241
242 # NOTE: This check is similar to the check used in _getTensorRange
243 # but this is used only when a tensor attribute has been specified.
244 if self.__active_tensor in self.__point_attribute['tensors']:
245 self._getDataCollectorOutput().GetPointData().SetActiveTensors(
246 self.__active_tensor)
247 elif self.__active_tensor in self.__cell_attribute['tensors']:
248 self._getDataCollectorOutput().GetCellData().SetActiveTensors(
249 self.__active_tensor)
250 else:
251 raise IOError("ERROR: No tensor called '%s' is available." % \
252 self.__active_tensor)
253
254 def __get_array_type(self, arr):
255 """
256 Return whether an array type is scalar, vector or tensor by looking
257 at the number of components in the array.
258
259 @type arr: vtkDataArray
260 @param arr: An array from the source.
261 @rtype: String
262 @return: Array type ('scalar', vector' or 'tensor')
263 """
264
265 # Number of components in an array.
266 num_components = arr.GetNumberOfComponents()
267
268 if num_components == 1:
269 return 'scalars'
270 elif num_components == 3:
271 return 'vectors'
272 elif num_components == 9:
273 return 'tensors'
274
275 def __get_attribute_list(self, data):
276 """
277 Return the available scalar, vector and tensor attributes
278 (either point or cell data).
279
280 @type data: vtkPointData or vtkCellData
281 @param data: Available point data or cell data from the source
282 @rtype: Dictionary
283 @return: Dictionary containing the available scalar, vector and \
284 tensor attributes
285 """
286
287 attribute = {'scalars':[], 'vectors':[], 'tensors':[]}
288 if data:
289 num_arrays = data.GetNumberOfArrays() # Number of arrays.
290 for i in range(num_arrays):
291 name = data.GetArrayName(i) # Get an array name.
292 type = self.__get_array_type(data.GetArray(i)) # Get array type.
293 attribute[type].extend([name]) # Add array name to dictionary.
294
295 return attribute
296
297 def __get_attribute_lists(self):
298 """
299 Get all the available point and cell data attributes from the source.
300 """
301
302 # Get all the available point data attributes into a list.
303 self.__point_attribute = \
304 self.__get_attribute_list(
305 self._getDataCollectorOutput().GetPointData())
306
307 # Get all the available cell data attributes into another list.
308 self.__cell_attribute = \
309 self.__get_attribute_list(
310 self._getDataCollectorOutput().GetCellData())
311
312 def _getScalarRange(self):
313 """
314 Return the scalar range.
315
316 @rtype: Two column tuple containing numbers
317 @return: Scalar range
318 """
319
320 # Check whether any scalar is available in either point or cell data.
321 # If not available, an error is raised.
322
323 # NOTE: This check is similar to the check used in _setActiveScalar
324 # but this is used only when no scalar attribute has been specified.
325 if(len(self.__point_attribute['scalars']) != 0):
326 return self._getDataCollectorOutput().GetPointData().\
327 GetScalars().GetRange(-1)
328 elif(len(self.__cell_attribute['scalars']) != 0):
329 return self._getDataCollectorOutput().GetCellData().\
330 GetScalars().GetRange(-1)
331 else:
332 raise IOError("\nERROR: No scalar is available.\n")
333
334 def _getVectorRange(self):
335 """
336 Return the vector range.
337
338 @rtype: Two column tuple containing numbers
339 @return: Vector range
340 """
341
342 # Check whether any vector is available in either point or cell data.
343 # If not available, an error is raised.
344
345 # NOTE: This check is similar to the check used in _setActiveVector
346 # but this is used only when no vector attribute has been specified.
347
348 # NOTE: Generally GetRange(-1) returns the correct vector range.
349 # However, there are certain data sets where GetRange(-1) seems
350 # to return incorrect mimimum vector although the maximum vector is
351 # correct. As a result, the mimimum vector has been hard coded to 0.0
352 # to accommodate for the incorrect cases.
353 if(len(self.__point_attribute['vectors']) != 0):
354 vector_range = \
355 self._getDataCollectorOutput().GetPointData().\
356 GetVectors().GetRange(-1)
357 return (0.0, vector_range[1])
358 elif(len(self.__cell_attribute['vectors']) != 0):
359 vector_range = \
360 self._getDataCollectorOutput().GetCellData().\
361 GetVectors().GetRange(-1)
362 return (0.0, vector_range[1])
363 else:
364 raise IOError("\nERROR: No vector is available.\n")
365
366 def _getTensorRange(self):
367 """
368 Return the tensor range.
369
370 @rtype: Two column tuple containing numbers
371 @return: Tensor range
372 """
373
374 # Check whether any tensor is available in either point or cell data.
375 # If not available, an error is raised.
376
377 # NOTE: This check is similar to the check used in _setActiveTensor
378 # but this is used only when no tensor attribute has been specified.
379 if(len(self.__point_attribute['tensors']) != 0):
380 return self._getDataCollectorOutput().GetPointData().\
381 GetTensors().GetRange(-1)
382 elif(len(self.__cell_attribute['tensors']) != 0):
383 return self._getDataCollectorOutput().GetCellData().\
384 GetTensors().GetRange(-1)
385 else:
386 raise IOError("\nERROR: No tensor is available.\n")
387
388 def _getDataCollectorOutput(self):
389 """
390 Return the output of the data collector.
391
392 @rtype: vtkUnstructuredGrid
393 @return: Unstructured grid
394 """
395 return self.__vtk_xml_reader.GetOutput()
396
397 def _isModified(self):
398 """
399 Return whether the DataCollector has been modified.
400
401 @rtype: Boolean
402 @return: True or False
403 """
404
405 if(self.__modified == True):
406 # 'self.__modified' is set to False only if the 'setFileName' or
407 # 'setData' method have been called once. This is to prevent
408 # the scalar range and active field (i.e. scalar, vector or tensor)
409 # from being updated as no changes has taken place (for performance
410 # reasons). However if the 'setFileName' or 'setData' method is
411 # called more than once, then 'self.__modified' remains True.
412 if(self.__count == 1):
413 self.__modified = False
414 return True
415 else:
416 return False
417
418 def _isScalarSet(self):
419 """
420 Return whether a specific scalar field has been specified.
421
422 @rtype: Boolean
423 @return: True or False
424 """
425
426 return self.__set_scalar
427
428 def _isVectorSet(self):
429 """
430 Return whether a specific vector field has been specified.
431
432 @rtype: Boolean
433 @return: True or False
434 """
435
436 return self.__set_vector
437
438 def _isTensorSet(self):
439 """
440 Return whether a specific tensor field has been specified.
441
442 @rtype: Boolean
443 @return: True or False
444 """
445
446 return self.__set_tensor
447
448 def _getCenter(self):
449 """
450 Return the center of the rendered object.
451
452 @rtype: Three column tuple containing numbers
453 @return: Center of the rendered object
454 """
455
456 return self._getDataCollectorOutput().GetCenter()
457

  ViewVC Help
Powered by ViewVC 1.1.26