/[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 2881 - (show annotations)
Thu Jan 28 02:03:15 2010 UTC (11 years, 1 month ago) by jfenwick
File MIME type: text/x-python
File size: 14608 byte(s)
Don't panic.
Updating copyright stamps

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

  ViewVC Help
Powered by ViewVC 1.1.26