Statistiques
| Révision :

root / ase / test / vtk_data.py @ 13

Historique | Voir | Annoter | Télécharger (18,79 ko)

1
#!/usr/bin/env python
2

    
3
from ase.visualize.vtk import requirevtk, probe_vtk_kilobyte
4

    
5
requirevtk()
6
vtk_kilobyte = probe_vtk_kilobyte(1024)
7

    
8
import numpy as np
9

    
10
import sys, unittest, gc
11
from ase.test import CustomTestCase, CustomTextTestRunner
12
from ase.utils.memory import MemoryStatistics, MemorySingleton, shapeopt
13

    
14
from vtk import vtkDataArray
15
from ase.visualize.vtk.data import vtkFloatArrayFromNumPyArray, \
16
                                   vtkDoubleArrayFromNumPyArray, \
17
                                   vtkFloatArrayFromNumPyMultiArray, \
18
                                   vtkDoubleArrayFromNumPyMultiArray
19

    
20
# -------------------------------------------------------------------
21

    
22
class UTConversionDataArrayNumPy(CustomTestCase):
23
    """
24
    Abstract class with test cases for VTK/NumPy data conversion.
25

26
    Leak tests the six possible permutations of deletion order for the
27
    objects involved in conversion between VTK and NumPy data formats.
28

29
    Objects:
30

31
    conv: instance of vtkDataArrayFromNumPyBuffer of subclass thereof
32
        The object in charge of the conversion
33
    data: NumPy array
34
        NumPy data with a specific memory footprint
35
    vtk_da: instance of vtkDataArray of subclass thereof
36
        VTK data array with a similar memory footprint
37

38
    Permutations:
39

40
    Case A: 012 i.e. deletion order is conv, data, vtk_da
41
    Case B: 021 i.e. deletion order is conv, vtk_da, data
42
    Case C: 102 i.e. deletion order is data, conv, vtk_da
43
    Case D: 120 i.e. deletion order is data, vtk_da, conv
44
    Case E: 201 i.e. deletion order is vtk_da, conv, data
45
    Case F: 210 i.e. deletion order is vtk_da, data, conv
46
    """
47
    footprint = 100*1024**2
48
    dtype = None
49
    verbose = 0
50
    gc_threshold = (300,5,5) #default is (700,10,10)
51
    gc_flags = gc.DEBUG_LEAK # | gc.DEBUG_STATS
52
    ctol = -7 #10MB
53
    etol = -7 #10MB
54

    
55
    def setUp(self):
56
        self.mem_ini = MemorySingleton(self.verbose-1)
57
        self.mem_ref = MemoryStatistics(self.verbose-1)
58
        self.mem_cur = self.mem_ref.copy()
59

    
60
        self.gc_threshold_old = gc.get_threshold()
61
        self.gc_flags_old = gc.get_debug()
62
        gc.set_threshold(*self.gc_threshold)
63
        gc.set_debug(self.gc_flags)
64

    
65
        # Try to obtain a clean slate
66
        gc.collect()
67
        self.gc_count = len(gc.garbage)
68
        del gc.garbage[:]
69

    
70
    def tearDown(self):
71
        gc.collect()
72
        self.assertEqual(len(gc.garbage), self.gc_count)
73
        if len(gc.garbage)>0:
74
            if self.verbose>1: print gc.get_objects()
75
            #TODO be pedantic and fail?
76
        del gc.garbage[:]
77
        gc.set_threshold(*self.gc_threshold_old)
78
        gc.set_debug(self.gc_flags_old)
79

    
80
    def assertAlmostConsumed(self, bytes, digits=0, key='VmSize'):
81
        self.mem_cur.update()
82
        dm = self.mem_cur-self.mem_ref
83
        self.assertAlmostEqual(dm[key], bytes, digits)
84

    
85
    def assertAlmostExceeded(self, bytes, digits=0, key='VmPeak'):
86
        self.mem_cur.update()
87
        dm = self.mem_cur-self.mem_ini
88
        #self.assertAlmostEqual(dm[key], bytes, digits) #TODO what really?
89
        #self.assertAlmostEqual(max(0, dm[key]-bytes), 0, digits) #TODO ???
90
        #dm = 200 MB, bytes = 100MB     ok
91
        #dm = 101 MB, bytes = 100MB     ok
92
        #dm = 0 MB, bytes = 100MB       bad
93

    
94
    def convert_to_vtk_array(self, data):
95
        """Convert an ndarray to a VTK data array.
96

97
         data: NumPy array
98
            NumPy data with a specific memory footprint
99
        """
100

    
101
        raise RuntimeError('Virtual member function.')
102

    
103
    def get_leaktest_scenario(self):
104
        """Construct the necessary conversion objects for leak testing.
105

106
        Returns tuple of the form (conv, data, vtk_da,) where:
107

108
        conv: instance of vtkDataArrayFromNumPyBuffer of subclass thereof
109
            The object in charge of the conversion
110
        data: NumPy array
111
            NumPy data with a specific memory footprint
112
        vtk_da: instance of vtkDataArray of subclass thereof
113
            VTK data array with a similar memory footprint
114
        """
115

    
116
        raise RuntimeError('Virtual member function.')
117

    
118
    # =================================
119

    
120
    def test_deletion_case_a(self):
121
        # Case A: 012 i.e. deletion order is conv, data, vtk_da
122
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
123

    
124
        # Conversion cleanup
125
        del conv
126
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
127
        self.assertAlmostExceeded(2*self.footprint, self.etol)
128
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
129

    
130
        # NumPy cleanup
131
        del data
132
        self.assertAlmostConsumed(self.footprint, self.ctol)
133
        self.assertAlmostExceeded(2*self.footprint, self.etol)
134
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
135

    
136
        # VTK cleanup
137
        del vtk_da
138
        self.assertAlmostConsumed(0, self.ctol)
139
        self.assertAlmostExceeded(2*self.footprint, self.etol)
140
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
141

    
142
    def test_deletion_case_b(self):
143
        # Case B: 021 i.e. deletion order is conv, vtk_da, data
144
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
145

    
146
        # Conversion cleanup
147
        del conv
148
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
149
        self.assertAlmostExceeded(2*self.footprint, self.etol)
150
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
151

    
152
        # VTK cleanup
153
        del vtk_da
154
        self.assertAlmostConsumed(self.footprint, self.ctol)
155
        self.assertAlmostExceeded(2*self.footprint, self.etol)
156
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
157

    
158
        # Numpy cleanup
159
        del data
160
        self.assertAlmostConsumed(0, self.ctol)
161
        self.assertAlmostExceeded(2*self.footprint, self.etol)
162
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
163

    
164
    def test_deletion_case_c(self):
165
        # Case C: 102 i.e. deletion order is data, conv, vtk_da
166
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
167

    
168
        # NumPy cleanup
169
        del data
170
        self.assertAlmostConsumed(self.footprint, self.ctol)
171
        self.assertAlmostExceeded(2*self.footprint, self.etol)
172
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
173

    
174
        # Conversion cleanup
175
        del conv
176
        self.assertAlmostConsumed(self.footprint, self.ctol)
177
        self.assertAlmostExceeded(2*self.footprint, self.etol)
178
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
179

    
180
        # VTK cleanup
181
        del vtk_da
182
        self.assertAlmostConsumed(0, self.ctol)
183
        self.assertAlmostExceeded(2*self.footprint, self.etol)
184
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
185

    
186
    def test_deletion_case_d(self):
187
        # Case D: 120 i.e. deletion order is data, vtk_da, conv
188
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
189

    
190
        # NumPy cleanup
191
        del data
192
        self.assertAlmostConsumed(self.footprint, self.ctol)
193
        self.assertAlmostExceeded(2*self.footprint, self.etol)
194
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
195

    
196
        # VTK cleanup
197
        del vtk_da
198
        self.assertAlmostConsumed(self.footprint, self.ctol)
199
        self.assertAlmostExceeded(2*self.footprint, self.etol)
200
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
201

    
202
        # Conversion cleanup
203
        del conv
204
        self.assertAlmostConsumed(0, self.ctol)
205
        self.assertAlmostExceeded(2*self.footprint, self.etol)
206
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
207

    
208
    def test_deletion_case_e(self):
209
        # Case E: 201 i.e. deletion order is vtk_da, conv, data
210
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
211

    
212
        # VTK cleanup
213
        del vtk_da
214
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
215
        self.assertAlmostExceeded(2*self.footprint, self.etol)
216
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
217

    
218
        # Conversion cleanup
219
        del conv
220
        self.assertAlmostConsumed(self.footprint, self.ctol)
221
        self.assertAlmostExceeded(2*self.footprint, self.etol)
222
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
223

    
224
        # NumPy cleanup
225
        del data
226
        self.assertAlmostConsumed(0, self.ctol)
227
        self.assertAlmostExceeded(2*self.footprint, self.etol)
228
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
229

    
230
    def test_deletion_case_f(self):
231
        # Case F: 210 i.e. deletion order is vtk_da, data, conv
232
        (conv, data, vtk_da,) = self.get_leaktest_scenario()
233

    
234
        # VTK cleanup
235
        del vtk_da
236
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
237
        self.assertAlmostExceeded(2*self.footprint, self.etol)
238
        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
239

    
240
        # NumPy cleanup
241
        del data
242
        self.assertAlmostConsumed(self.footprint, self.ctol)
243
        self.assertAlmostExceeded(2*self.footprint, self.etol)
244
        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
245

    
246
        # Conversion cleanup
247
        del conv
248
        self.assertAlmostConsumed(0, self.ctol)
249
        self.assertAlmostExceeded(2*self.footprint, self.etol)
250
        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
251

    
252
# -------------------------------------------------------------------
253

    
254
# class UTDataArrayFromNumPyBuffer(...): TODO
255

    
256
# -------------------------------------------------------------------
257

    
258
class UTDataArrayFromNumPyArray_Scalar(UTConversionDataArrayNumPy):
259
    """
260
    Test cases for memory leaks during VTK/NumPy data conversion.
261
    Conversion of 1D NumPy array to VTK data array using buffers."""
262

    
263
    def setUp(self):
264
        UTConversionDataArrayNumPy.setUp(self)
265
        self.shape = self.footprint//np.nbytes[self.dtype]
266

    
267
    def get_leaktest_scenario(self):
268
        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
269
                               self.footprint, -2) #100B
270

    
271
        # Update memory reference
272
        self.mem_ref.update()
273

    
274
        # NumPy allocation
275
        data = np.empty(self.shape, self.dtype)
276
        self.assertAlmostConsumed(self.footprint, self.ctol)
277
        self.assertAlmostExceeded(self.footprint, self.etol)
278
        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
279

    
280
        # NumPy to VTK conversion
281
        np2da = self.convert_to_vtk_array(data)
282
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
283
        self.assertAlmostExceeded(2*self.footprint, self.etol)
284
        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
285

    
286
        # VTK retrieval
287
        vtk_da = np2da.get_output()
288
        self.assertTrue(isinstance(vtk_da, vtkDataArray))
289
        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
290
                               self.footprint, -3) #1kB
291
        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
292

    
293
        return (np2da, data, vtk_da,)
294

    
295
class UTFloatArrayFromNumPyArray_Scalar(UTDataArrayFromNumPyArray_Scalar):
296
    __doc__ = UTDataArrayFromNumPyArray_Scalar.__doc__
297
    dtype = np.float32
298
    convert_to_vtk_array = vtkFloatArrayFromNumPyArray
299

    
300
class UTDoubleArrayFromNumPyArray_Scalar(UTDataArrayFromNumPyArray_Scalar):
301
    __doc__ = UTDataArrayFromNumPyArray_Scalar.__doc__
302
    dtype = np.float64
303
    convert_to_vtk_array = vtkDoubleArrayFromNumPyArray
304

    
305
class UTDataArrayFromNumPyArray_Vector(UTConversionDataArrayNumPy):
306
    """
307
    Test cases for memory leaks during VTK/NumPy data conversion.
308
    Conversion of 2D NumPy array to VTK data array using buffers."""
309

    
310
    def setUp(self):
311
        UTConversionDataArrayNumPy.setUp(self)
312
        size = self.footprint//np.nbytes[self.dtype]
313
        self.shape = (size//3, 3)
314

    
315
    def get_leaktest_scenario(self):
316
        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
317
                               self.footprint, -2) #100B
318

    
319
        # Update memory reference
320
        self.mem_ref.update()
321

    
322
        # NumPy allocation
323
        data = np.empty(self.shape, self.dtype)
324
        self.assertAlmostConsumed(self.footprint, self.ctol)
325
        self.assertAlmostExceeded(self.footprint, self.etol)
326
        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
327

    
328
        # NumPy to VTK conversion
329
        np2da = self.convert_to_vtk_array(data)
330
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
331
        self.assertAlmostExceeded(2*self.footprint, self.etol)
332
        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
333

    
334
        # VTK retrieval
335
        vtk_da = np2da.get_output()
336
        self.assertTrue(isinstance(vtk_da, vtkDataArray))
337
        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
338
                               self.footprint, -3) #1kB
339
        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
340

    
341
        return (np2da, data, vtk_da,)
342

    
343
class UTFloatArrayFromNumPyArray_Vector(UTDataArrayFromNumPyArray_Vector):
344
    __doc__ = UTDataArrayFromNumPyArray_Vector.__doc__
345
    dtype = np.float32
346
    convert_to_vtk_array = vtkFloatArrayFromNumPyArray
347

    
348
class UTDoubleArrayFromNumPyArray_Vector(UTDataArrayFromNumPyArray_Vector):
349
    __doc__ = UTDataArrayFromNumPyArray_Vector.__doc__
350
    dtype = np.float64
351
    convert_to_vtk_array = vtkDoubleArrayFromNumPyArray
352

    
353
# -------------------------------------------------------------------
354

    
355
class UTDataArrayFromNumPyMultiArray_Scalar(UTConversionDataArrayNumPy):
356
    """
357
    Test cases for memory leaks during VTK/NumPy data conversion.
358
    Conversion of NumPy grid scalars to VTK data array using buffers."""
359

    
360
    def setUp(self):
361
        UTConversionDataArrayNumPy.setUp(self)
362
        size = self.footprint//np.nbytes[self.dtype]
363
        digits, shape = shapeopt(1000, size, ndims=3, ecc=0.3)
364
        if self.verbose>=1: print 'digits=%8.3f, shape=%s' % (digits,shape)
365
        self.shape = shape + (1,)
366
        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
367
                               self.footprint, -4) #10kB
368

    
369
    def get_leaktest_scenario(self):
370

    
371
        # Update memory reference
372
        self.mem_ref.update()
373

    
374
        # NumPy allocation
375
        data = np.empty(self.shape, self.dtype)
376
        self.assertAlmostConsumed(self.footprint, self.ctol)
377
        self.assertAlmostExceeded(self.footprint, self.etol)
378
        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
379

    
380
        # NumPy to VTK conversion
381
        np2da = self.convert_to_vtk_array(data)
382
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
383
        self.assertAlmostExceeded(2*self.footprint, self.etol)
384
        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
385

    
386
        # VTK retrieval
387
        vtk_da = np2da.get_output()
388
        self.assertTrue(isinstance(vtk_da, vtkDataArray))
389
        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
390
                               self.footprint, -4) #10kB
391
        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
392

    
393
        return (np2da, data, vtk_da,)
394

    
395
class UTFloatArrayFromNumPyMultiArray_Scalar(UTDataArrayFromNumPyMultiArray_Scalar):
396
    __doc__ = UTDataArrayFromNumPyMultiArray_Scalar.__doc__
397
    dtype = np.float32
398
    convert_to_vtk_array = vtkFloatArrayFromNumPyMultiArray
399

    
400
class UTDoubleArrayFromNumPyMultiArray_Scalar(UTDataArrayFromNumPyMultiArray_Scalar):
401
    __doc__ = UTDataArrayFromNumPyMultiArray_Scalar.__doc__
402
    dtype = np.float64
403
    convert_to_vtk_array = vtkDoubleArrayFromNumPyMultiArray
404

    
405
class UTDataArrayFromNumPyMultiArray_Vector(UTConversionDataArrayNumPy):
406
    """
407
    Test cases for memory leaks during VTK/NumPy data conversion.
408
    Conversion of NumPy grid vectors to VTK data array using buffers."""
409

    
410
    def setUp(self):
411
        UTConversionDataArrayNumPy.setUp(self)
412
        size = self.footprint//np.nbytes[self.dtype]
413
        digits, shape = shapeopt(1000, size//3, ndims=3, ecc=0.3)
414
        if self.verbose>=1: print 'digits=%8.3f, shape=%s' % (digits,shape)
415
        self.shape = shape + (3,)
416
        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
417
                               self.footprint, -4) #10kB
418

    
419
    def get_leaktest_scenario(self):
420

    
421
        # Update memory reference
422
        self.mem_ref.update()
423

    
424
        # NumPy allocation
425
        data = np.empty(self.shape, self.dtype)
426
        self.assertAlmostConsumed(self.footprint, self.ctol)
427
        self.assertAlmostExceeded(self.footprint, self.etol)
428
        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
429

    
430
        # NumPy to VTK conversion
431
        np2da = self.convert_to_vtk_array(data)
432
        self.assertAlmostConsumed(2*self.footprint, self.ctol)
433
        self.assertAlmostExceeded(2*self.footprint, self.etol)
434
        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
435

    
436
        # VTK retrieval
437
        vtk_da = np2da.get_output()
438
        self.assertTrue(isinstance(vtk_da, vtkDataArray))
439
        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
440
                               self.footprint, -4) #10kB
441
        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
442

    
443
        return (np2da, data, vtk_da,)
444

    
445
class UTFloatArrayFromNumPyMultiArray_Vector(UTDataArrayFromNumPyMultiArray_Vector):
446
    __doc__ = UTDataArrayFromNumPyMultiArray_Vector.__doc__
447
    dtype = np.float32
448
    convert_to_vtk_array = vtkFloatArrayFromNumPyMultiArray
449

    
450
class UTDoubleArrayFromNumPyMultiArray_Vector(UTDataArrayFromNumPyMultiArray_Vector):
451
    __doc__ = UTDataArrayFromNumPyMultiArray_Vector.__doc__
452
    dtype = np.float64
453
    convert_to_vtk_array = vtkDoubleArrayFromNumPyMultiArray
454

    
455
# -------------------------------------------------------------------
456

    
457
if __name__ in ['__main__', '__builtin__']:
458
    # We may have been imported by test.py, if so we should redirect to logfile
459
    if __name__ == '__builtin__':
460
        testrunner = CustomTextTestRunner('vtk_data.log', verbosity=2)
461
    else:
462
        testrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
463

    
464
    testcases = [UTFloatArrayFromNumPyArray_Scalar, \
465
                 UTDoubleArrayFromNumPyArray_Scalar, \
466
                 UTFloatArrayFromNumPyArray_Vector, \
467
                 UTDoubleArrayFromNumPyArray_Vector, \
468
                 UTFloatArrayFromNumPyMultiArray_Scalar, \
469
                 UTDoubleArrayFromNumPyMultiArray_Scalar, \
470
                 UTFloatArrayFromNumPyMultiArray_Vector, \
471
                 UTDoubleArrayFromNumPyMultiArray_Vector]
472

    
473
    for test in testcases:
474
        info = '\n' + test.__name__ + '\n' + test.__doc__.strip('\n') + '\n'
475
        testsuite = unittest.defaultTestLoader.loadTestsFromTestCase(test)
476
        testrunner.stream.writeln(info)
477
        testresult = testrunner.run(testsuite)
478
        # Provide feedback on failed tests if imported by test.py
479
        if __name__ == '__builtin__' and not testresult.wasSuccessful():
480
            raise SystemExit('Test failed. Check vtk_data.log for details.')
481