savu.data.data_structures.data.Data.get_name()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
# Copyright 2014 Diamond Light Source Ltd.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
15
"""
16
.. module:: data
17
   :platform: Unix
18
   :synopsis: The Data class dynamically inherits from transport specific data\
19
   class and holds the data array, along with associated information.
20
21
.. moduleauthor:: Nicola Wadeson <[email protected]>
22
23
"""
24
25
import savu.core.utils as cu
26
from savu.data.meta_data import MetaData
27
import savu.data.data_structures.utils as dsu
28
from savu.data.data_structures.preview import Preview
29
from savu.data.data_structures.data_create import DataCreate
30
31
32
class Data(DataCreate):
33
    """The Data class dynamically inherits from transport specific data class
34
    and holds the data array, along with associated information.
35
    """
36
37
    def __init__(self, name, exp):
38
        super(Data, self).__init__(name)
39
        self.meta_data = MetaData()
40
        self.related = {}
41
        self.pattern_list = self.__get_available_pattern_list()
42
        self.data_info = MetaData()
43
        self.__initialise_data_info(name)
44
        self._preview = Preview(self)
45
        self.exp = exp
46
        self.group_name = None
47
        self.group = None
48
        self._plugin_data_obj = None
49
        self.raw = None
50
        self.backing_file = None
51
        self.data = None
52
        self.next_shape = None
53
        self.orig_shape = None
54
        self.previous_pattern = None
55
        self.transport_data = None
56
57
#    def get_data(self, related):
58
#        return self.related[related].data
59
60
    def __initialise_data_info(self, name):
61
        """ Initialise entries in the data_info meta data.
62
        """
63
        self.data_info.set('name', name)
64
        self.data_info.set('data_patterns', {})
65
        self.data_info.set('shape', None)
66
        self.data_info.set('nDims', None)
67
68
    def _set_plugin_data(self, plugin_data_obj):
69
        """ Encapsulate a PluginData object.
70
        """
71
        self._plugin_data_obj = plugin_data_obj
72
73
    def _clear_plugin_data(self):
74
        """ Set encapsulated PluginData object to None.
75
        """
76
        self._plugin_data_obj = None
77
78
    def _get_plugin_data(self):
79
        """ Get encapsulated PluginData object.
80
        """
81
        if self._plugin_data_obj is not None:
82
            return self._plugin_data_obj
83
        else:
84
            raise Exception("There is no PluginData object associated with "
85
                            "the Data object.")
86
87
    def get_preview(self):
88
        """ Get the Preview instance associated with the data object
89
        """
90
        return self._preview
91
92
    def _set_transport_data(self, transport):
93
        """ Import the data transport mechanism
94
95
        :returns: instance of data transport
96
        :rtype: transport_data
97
        """
98
        transport_data = "savu.data.transport_data." + transport + \
99
                         "_transport_data"
100
        transport_data = cu.import_class(transport_data)
101
        self.transport_data = transport_data(self)
102
        self.data_info.set('transport', transport)
103
104
    def _get_transport_data(self):
105
        return self.transport_data
106
107
    def __deepcopy__(self, memo):
108
        """ Copy the data object.
109
        """
110
        name = self.data_info.get('name')
111
        return dsu._deepcopy_data_object(self, Data(name, self.exp))
112
113
    def get_data_patterns(self):
114
        """ Get data patterns associated with this data object.
115
116
        :returns: A dictionary of associated patterns.
117
        :rtype: dict
118
        """
119
        return self.data_info.get('data_patterns')
120
121
    def _set_previous_pattern(self, pattern):
122
        self.previous_pattern = pattern
123
124
    def get_previous_pattern(self):
125
        return self.previous_pattern
126
127
    def set_shape(self, shape):
128
        """ Set the dataset shape.
129
        """
130
        self.data_info.set('shape', shape)
131
        self.__check_dims()
132
133
    def set_original_shape(self, shape):
134
        """ Set the original data shape before previewing
135
        """
136
        self.orig_shape = shape
137
        self.set_shape(shape)
138
139
    def get_original_shape(self):
140
        """
141
        Returns the original shape of the data before previewing
142
        
143
        Returns
144
        -------
145
        tuple
146
            Original data shape.
147
        """
148
        return self.orig_shape
149
150
    def get_shape(self):
151
        """ Get the dataset shape
152
153
        :returns: data shape
154
        :rtype: tuple
155
        """
156
        shape = self.data_info.get('shape')
157
        return shape
158
159
    def __check_dims(self):
160
        """ Check the ``shape`` and ``nDims`` entries in the data_info
161
        meta_data dictionary are equal.
162
        """
163
        nDims = self.data_info.get("nDims")
164
        shape = self.data_info.get('shape')
165
        if nDims:
166
            if len(shape) != nDims:
167
                error_msg = ("The number of axis labels, %d, does not "
168
                             "coincide with the number of data "
169
                             "dimensions %d." % (nDims, len(shape)))
170
                raise Exception(error_msg)
171
172
    def _set_name(self, name):
173
        self.data_info.set('name', name)
174
175
    def get_name(self, orig=False):
176
        """ Get data name.
177
178
        :keyword bool orig: Set this flag to true to return the original cloned
179
            dataset name if this dataset is a clone
180
        :returns: the name associated with the dataset
181
        :rtype: str
182
        """
183
        if orig:
184
            dinfo = self.data_info.get_dictionary()
185
            return dinfo['clone'] if 'clone' in dinfo.keys() else dinfo['name']
186
        return self.data_info.get('name')
187
188
    def __get_available_pattern_list(self):
189
        """ Get a list of ALL pattern names that are currently allowed in the
190
        framework.
191
        """
192
        pattern_list = dsu.get_available_pattern_types()
193
        return pattern_list
194
195
    def add_pattern(self, dtype, **kwargs):
196
        """ Add a pattern.
197
198
        :params str dtype: The *type* of pattern to add, which can be anything
199
            from the :const:`savu.data.data_structures.utils.pattern_list`
200
            :const:`pattern_list`
201
            :data:`savu.data.data_structures.utils.pattern_list`
202
            :data:`pattern_list`:
203
        :keyword tuple core_dims: Dimension indices of core dimensions
204
        :keyword tuple slice_dims: Dimension indices of slice dimensions
205
        """
206
        if dtype in self.pattern_list:
207
            nDims = 0
208
            for args in kwargs:
209
                dlen = len(kwargs[args])
210
                if not dlen:
211
                    raise Exception("Pattern Error: Pattern %s must have at"
212
                                    " least one %s" % (dtype, args))
213
                nDims += len(kwargs[args])
214
                self.data_info.set(['data_patterns', dtype, args],
215
                                   kwargs[args])
216
217
            self.__convert_pattern_dimensions(dtype)
218
            if self.get_shape():
219
                diff = len(self.get_shape()) - nDims
220
                if diff:
221
                    pattern = {dtype: self.get_data_patterns()[dtype]}
222
                    self._set_data_patterns(pattern)
223
                    nDims += diff
224
            try:
225
                if nDims != self.data_info.get("nDims"):
226
                    actualDims = self.data_info.get('nDims')
227
                    err_msg = ("The pattern %s has an incorrect number of "
228
                               "dimensions: %d required but %d specified."
229
                               % (dtype, actualDims, nDims))
230
                    raise Exception(err_msg)
231
            except KeyError:
232
                self.data_info.set('nDims', nDims)
233
        else:
234
            raise Exception("The data pattern '%s'does not exist. Please "
235
                            "choose from the following list: \n'%s'",
236
                            dtype, str(self.pattern_list))
237
238
    def add_volume_patterns(self, x, y, z):
239
        """ Adds volume patterns
240
241
        :params int x: dimension to be associated with x-axis
242
        :params int y: dimension to be associated with y-axis
243
        :params int z: dimension to be associated with z-axis
244
        """
245
        self.add_pattern("VOLUME_XZ", **self.__get_dirs_for_volume(x, z, y))
246
247
        if y:
248
            self.add_pattern(
249
                "VOLUME_YZ", **self.__get_dirs_for_volume(y, z, x))
250
            self.add_pattern(
251
                "VOLUME_XY", **self.__get_dirs_for_volume(x, y, z))
252
253
        if self.data_info.get("nDims") > 3 and y:
254
            self.add_pattern("VOLUME_3D", **self.__get_dirs_for_volume_3D())
255
256
    def __get_dirs_for_volume(self, dim1, dim2, sdir, dim3=None):
257
        """ Calculate core_dir and slice_dir for a volume pattern.
258
        """
259
        all_dims = range(self.data_info.get("nDims"))
260
        vol_dict = {}
261
        vol_dict['core_dims'] = (dim1, dim2)
262
        slice_dir = [sdir] if type(sdir) is int else []
263
        for ddir in all_dims:
264
            if ddir not in [dim1, dim2, sdir]:
265
                slice_dir.append(ddir)                
266
        vol_dict['slice_dims'] = tuple(slice_dir)
267
        return vol_dict
268
269
    def __get_dirs_for_volume_3D(self):
270
        # create volume 3D pattern here
271
        patterns = self.get_data_patterns()
272
        cdim = []
273
        for v in ['VOLUME_YZ', 'VOLUME_XY', 'VOLUME_XZ']:
274
            cdim += (patterns[v]['core_dims'])
275
            
276
        cdim = set(cdim)
277
        sdim = tuple(set(range(self.data_info.get("nDims"))).difference(cdim))
278
        return {"core_dims": tuple(cdim), "slice_dims": sdim}
279
280
    def set_axis_labels(self, *args):
281
        """ Set the axis labels associated with each data dimension.
282
283
        :arg str: Each arg should be of the form ``name.unit``. If ``name`` is\
284
        a data_obj.meta_data entry, it will be output to the final .nxs file.
285
        """
286
        self.data_info.set('nDims', len(args))
287
        axis_labels = []
288
        for arg in args:
289
            if isinstance(arg, dict):
290
                axis_labels.append(arg)
291
            else:
292
                try:
293
                    axis = arg.split('.')
294
                    axis_labels.append({axis[0]: axis[1]})
295
                except:
296
                    # data arrives here, but that may be an error
297
                    pass
298
        self.data_info.set('axis_labels', axis_labels)
299
300
    def get_axis_labels(self):
301
        """ Get axis labels.
302
303
        :returns: Axis labels
304
        :rtype: list(dict)
305
        """
306
        return self.data_info.get('axis_labels')
307
308
    def get_data_dimension_by_axis_label(self, name, contains=False, exists=False):
309
        """ Get the dimension of the data associated with a particular
310
        axis_label.
311
312
        :param str name: The name of the axis_label
313
        :keyword bool contains: Set this flag to true if the name is only part
314
            of the axis_label name
315
        :keyword bool exists: Set to True to return False rather than Exception
316
        :returns: The associated axis number
317
        :rtype: int
318
        """
319
        axis_labels = self.data_info.get('axis_labels')
320
        for i in range(len(axis_labels)):
321
            if contains is True:
322
                for names in list(axis_labels[i].keys()):
323
                    if name in names:
324
                        return i
325
            else:
326
                if name in list(axis_labels[i].keys()):
327
                    return i
328
        if exists:
329
            return False
330
        raise Exception("Cannot find the specifed axis label.")
331
332
    def _finalise_patterns(self):
333
        """ Adds a main axis (fastest changing) to SINOGRAM and PROJECTON
334
        patterns.
335
        """
336
        check = 0        
337
        check += self.__check_pattern('SINOGRAM')
338
        check += self.__check_pattern('PROJECTION')
339
340
        if check == 2 and len(self.get_shape()) > 2:
341
            self.__set_main_axis('SINOGRAM')
342
            self.__set_main_axis('PROJECTION')
343
344
    def __check_pattern(self, pattern_name):
345
        """ Check if a pattern exists.
346
        """
347
        patterns = self.get_data_patterns()
348
        try:
349
            patterns[pattern_name]
350
        except KeyError:
351
            return 0
352
        return 1
353
354
    def __convert_pattern_dimensions(self, dtype):
355
        """ Replace negative indices in pattern kwargs.
356
        """
357
        pattern = self.get_data_patterns()[dtype]
358
        if 'main_dir' in list(pattern.keys()):
359
            del pattern['main_dir']
360
361
        nDims = sum([len(i) for i in list(pattern.values())])
362
        for p in pattern:
363
            ddirs = pattern[p]
364
            pattern[p] = self._non_negative_directions(ddirs, nDims)
365
366
    def _non_negative_directions(self, ddirs, nDims):
367
        """ Replace negative indexing values with positive counterparts.
368
369
        :params tuple(int) ddirs: data dimension indices
370
        :params int nDims: The number of data dimensions
371
        :returns: non-negative data dimension indices
372
        :rtype: tuple(int)
373
        """
374
        index = [i for i in range(len(ddirs)) if ddirs[i] < 0]
375
        list_ddirs = list(ddirs)
376
        for i in index:
377
            list_ddirs[i] = nDims + ddirs[i]
378
        return tuple(list_ddirs)
379
380
    def __set_main_axis(self, pname):
381
        """ Set the ``main_dir`` pattern kwarg to the fastest changing
382
        dimension
383
        """
384
        patterns = self.get_data_patterns()
385
        n1 = 'PROJECTION' if pname == 'SINOGRAM' else 'SINOGRAM'
386
        d1 = patterns[n1]['core_dims']
387
        d2 = patterns[pname]['slice_dims']
388
        tdir = set(d1).intersection(set(d2))
389
390
        # this is required when a single sinogram exists in the mm case, and a
391
        # dimension is added via parameter tuning.
392
        if not tdir:
393
            tdir = [d2[0]]
394
395
        self.data_info.set(['data_patterns', pname, 'main_dir'], list(tdir)[0])
396
397
    def get_axis_label_keys(self):
398
        """ Get axis_label names
399
400
        :returns: A list containing associated axis names for each dimension
401
        :rtype: list(str)
402
        """
403
        axis_labels = self.data_info.get('axis_labels')
404
        axis_label_keys = []
405
        for labels in axis_labels:
406
            for key in list(labels.keys()):
407
                axis_label_keys.append(key)
408
        return axis_label_keys
409
410
    def amend_axis_label_values(self, slice_list):
411
        """ Amend all axis label values based on the slice_list parameter.\
412
        This is required if the data is reduced.
413
        """
414
        axis_labels = self.get_axis_labels()
415
        for i in range(len(slice_list)):
416
            label = list(axis_labels[i].keys())[0]
417
            if label in list(self.meta_data.get_dictionary().keys()):
418
                values = self.meta_data.get(label)
419
                preview_sl = [slice(None)]*len(values.shape)
420
                preview_sl[0] = slice_list[i]
421
                self.meta_data.set(label, values[tuple(preview_sl)])
422
423
    def get_core_dimensions(self):
424
        """ Get the core data dimensions associated with the current pattern.
425
426
        :returns: value associated with pattern key ``core_dims``
427
        :rtype: tuple
428
        """
429
        return list(self._get_plugin_data().get_pattern().values())[0]['core_dims']
430
431
    def get_slice_dimensions(self):
432
        """ Get the slice data dimensions associated with the current pattern.
433
434
        :returns: value associated with pattern key ``slice_dims``
435
        :rtype: tuple
436
        """
437
        return list(self._get_plugin_data().get_pattern().values())[0]['slice_dims']
438
439
    def get_itemsize(self):
440
        """ Returns bytes per entry """
441
        dtype = self.get_dtype()
442
        if not dtype:
443
            self.set_dtype(None)
444
            dtype = self.get_dtype()
445
        return self.get_dtype().itemsize
446