source: sasview/src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py @ b204004

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249unittest-saveload
Last change on this file since b204004 was b204004, checked in by krzywon, 6 years ago

Remove unneeded shape check, give better error when saving fails, and data info cleanup.

  • Property mode set to 100644
File size: 29.8 KB
Line 
1"""
2    CanSAS 2D data reader for reading HDF5 formatted CanSAS files.
3"""
4
5import h5py
6import numpy as np
7import re
8import os
9import sys
10
11from ..data_info import plottable_1D, plottable_2D,\
12    Data1D, Data2D, DataInfo, Process, Aperture, Collimation, \
13    TransmissionSpectrum, Detector
14from ..loader_exceptions import FileContentsException, DefaultReaderException
15from ..file_reader_base_class import FileReader, decode
16
17
18def h5attr(node, key, default=None):
19    return decode(node.attrs.get(key, default))
20
21
22class Reader(FileReader):
23    """
24    A class for reading in CanSAS v2.0 data files. The existing iteration opens
25    Mantid generated HDF5 formatted files with file extension .h5/.H5. Any
26    number of data sets may be present within the file and any dimensionality
27    of data may be used. Currently 1D and 2D SAS data sets are supported, but
28    future implementations will include 1D and 2D SESANS data.
29
30    Any number of SASdata sets may be present in a SASentry and the data within
31    can be either 1D I(Q) or 2D I(Qx, Qy).
32
33    Also supports reading NXcanSAS formatted HDF5 files
34
35    :Dependencies:
36        The CanSAS HDF5 reader requires h5py => v2.5.0 or later.
37    """
38
39    # CanSAS version
40    cansas_version = 2.0
41    # Data type name
42    type_name = "NXcanSAS"
43    # Wildcards
44    type = ["NXcanSAS HDF5 Files (*.h5)|*.h5|"]
45    # List of allowed extensions
46    ext = ['.h5', '.H5']
47    # Flag to bypass extension check
48    allow_all = True
49
50    def get_file_contents(self):
51        """
52        This is the general read method that all SasView data_loaders must have.
53
54        :param filename: A path for an HDF5 formatted CanSAS 2D data file.
55        :return: List of Data1D/2D objects and/or a list of errors.
56        """
57        # Reinitialize when loading a new data file to reset all class variables
58        self.reset_state()
59
60        filename = self.f_open.name
61        self.f_open.close() # IO handled by h5py
62
63        # Check that the file exists
64        if os.path.isfile(filename):
65            basename = os.path.basename(filename)
66            _, extension = os.path.splitext(basename)
67            # If the file type is not allowed, return empty list
68            if extension in self.ext or self.allow_all:
69                # Load the data file
70                try:
71                    self.raw_data = h5py.File(filename, 'r')
72                except Exception as e:
73                    if extension not in self.ext:
74                        msg = "NXcanSAS Reader could not load file {}".format(
75                            basename + extension)
76                        raise DefaultReaderException(msg)
77                    raise FileContentsException(e.message)
78                try:
79                    # Read in all child elements of top level SASroot
80                    self.read_children(self.raw_data, [])
81                    # Add the last data set to the list of outputs
82                    self.add_data_set()
83                except Exception as exc:
84                    raise FileContentsException(exc.message)
85                finally:
86                    # Close the data file
87                    self.raw_data.close()
88
89                for data_set in self.output:
90                    if isinstance(data_set, Data1D):
91                        if data_set.x.size < 5:
92                            exception = FileContentsException(
93                                "Fewer than 5 data points found.")
94                            data_set.errors.append(exception)
95
96    def reset_state(self):
97        """
98        Create the reader object and define initial states for class variables
99        """
100        super(Reader, self).reset_state()
101        self.data1d = []
102        self.data2d = []
103        self.raw_data = None
104        self.errors = set()
105        self.logging = []
106        self.q_names = []
107        self.mask_name = u''
108        self.i_name = u''
109        self.i_node = u''
110        self.i_uncertainties_name = u''
111        self.q_uncertainty_names = []
112        self.q_resolution_names = []
113        self.parent_class = u''
114        self.detector = Detector()
115        self.collimation = Collimation()
116        self.aperture = Aperture()
117        self.process = Process()
118        self.trans_spectrum = TransmissionSpectrum()
119
120    def read_children(self, data, parent_list):
121        """
122        A recursive method for stepping through the hierarchical data file.
123
124        :param data: h5py Group object of any kind
125        :param parent: h5py Group parent name
126        """
127
128        # Loop through each element of the parent and process accordingly
129        for key in data.keys():
130            # Get all information for the current key
131            value = data.get(key)
132            class_name = h5attr(value, u'canSAS_class')
133            if isinstance(class_name, (list, tuple, np.ndarray)):
134                class_name = class_name[0]
135            if class_name is None:
136                class_name = h5attr(value, u'NX_class')
137            if class_name is not None:
138                class_prog = re.compile(class_name)
139            else:
140                class_prog = re.compile(value.name)
141
142            if isinstance(value, h5py.Group):
143                # Set parent class before recursion
144                last_parent_class = self.parent_class
145                self.parent_class = class_name
146                parent_list.append(key)
147                # If a new sasentry, store the current data sets and create
148                # a fresh Data1D/2D object
149                if class_prog.match(u'SASentry'):
150                    self.add_data_set(key)
151                elif class_prog.match(u'SASdata'):
152                    self._initialize_new_data_set(value)
153                    self._find_data_attributes(value)
154                # Recursion step to access data within the group
155                self.read_children(value, parent_list)
156                self.add_intermediate()
157                # Reset parent class when returning from recursive method
158                self.parent_class = last_parent_class
159                parent_list.remove(key)
160
161            elif isinstance(value, h5py.Dataset):
162                # If this is a dataset, store the data appropriately
163                data_set = value.value
164                unit = self._get_unit(value)
165
166                for data_point in data_set:
167                    if isinstance(data_point, np.ndarray):
168                        if data_point.dtype.char == 'S':
169                            data_point = decode(bytes(data_point))
170                    else:
171                        data_point = decode(data_point)
172                    # Top Level Meta Data
173                    if key == u'definition':
174                        self.current_datainfo.meta_data['reader'] = data_point
175                    # Run
176                    elif key == u'run':
177                        self.current_datainfo.run.append(data_point)
178                        try:
179                            run_name = h5attr(value, 'name')
180                            run_dict = {data_point: run_name}
181                            self.current_datainfo.run_name = run_dict
182                        except Exception:
183                            pass
184                    # Title
185                    elif key == u'title':
186                        self.current_datainfo.title = data_point
187                    # Note
188                    elif key == u'SASnote':
189                        self.current_datainfo.notes.append(data_point)
190                    # Sample Information
191                    elif self.parent_class == u'SASsample':
192                        self.process_sample(data_point, key)
193                    # Instrumental Information
194                    elif (key == u'name'
195                          and self.parent_class == u'SASinstrument'):
196                        self.current_datainfo.instrument = data_point
197                    # Detector
198                    elif self.parent_class == u'SASdetector':
199                        self.process_detector(data_point, key, unit)
200                    # Collimation
201                    elif self.parent_class == u'SAScollimation':
202                        self.process_collimation(data_point, key, unit)
203                    # Aperture
204                    elif self.parent_class == u'SASaperture':
205                        self.process_aperture(data_point, key)
206                    # Process Information
207                    elif self.parent_class == u'SASprocess': # CanSAS 2.0
208                        self.process_process(data_point, key)
209                    # Source
210                    elif self.parent_class == u'SASsource':
211                        self.process_source(data_point, key, unit)
212                    # Everything else goes in meta_data
213                    elif self.parent_class == u'SASdata':
214                        if isinstance(self.current_dataset, plottable_2D):
215                            self.process_2d_data_object(data_set, key, unit)
216                        else:
217                            self.process_1d_data_object(data_set, key, unit)
218
219                        break
220                    elif self.parent_class == u'SAStransmission_spectrum':
221                        self.process_trans_spectrum(data_set, key)
222                        break
223                    else:
224                        new_key = self._create_unique_key(
225                            self.current_datainfo.meta_data, key)
226                        self.current_datainfo.meta_data[new_key] = data_point
227
228            else:
229                # I don't know if this reachable code
230                self.errors.add("ShouldNeverHappenException")
231
232    def process_1d_data_object(self, data_set, key, unit):
233        """
234        SASdata processor method for 1d data items
235        :param data_set: data from HDF5 file
236        :param key: canSAS_class attribute
237        :param unit: unit attribute
238        """
239        if key == self.i_name:
240            self.current_dataset.y = data_set.flatten()
241            self.current_dataset.yaxis("Intensity", unit)
242        elif key == self.i_uncertainties_name:
243            self.current_dataset.dy = data_set.flatten()
244        elif key in self.q_names:
245            self.current_dataset.xaxis("Q", unit)
246            self.current_dataset.x = data_set.flatten()
247        elif key in self.q_resolution_names:
248            if (len(self.q_resolution_names) > 1
249                    and np.where(self.q_resolution_names == key)[0] == 0):
250                self.current_dataset.dxw = data_set.flatten()
251            elif (len(self.q_resolution_names) > 1
252                  and np.where(self.q_resolution_names == key)[0] == 1):
253                self.current_dataset.dxl = data_set.flatten()
254            else:
255                self.current_dataset.dx = data_set.flatten()
256        elif key in self.q_uncertainty_names:
257            if (len(self.q_uncertainty_names) > 1
258                    and np.where(self.q_uncertainty_names == key)[0] == 0):
259                self.current_dataset.dxw = data_set.flatten()
260            elif (len(self.q_uncertainty_names) > 1
261                  and np.where(self.q_uncertainty_names == key)[0] == 1):
262                self.current_dataset.dxl = data_set.flatten()
263            else:
264                self.current_dataset.dx = data_set.flatten()
265        elif key == self.mask_name:
266            self.current_dataset.mask = data_set.flatten()
267        elif key == u'wavelength':
268            self.current_datainfo.source.wavelength = data_set[0]
269            self.current_datainfo.source.wavelength_unit = unit
270
271    def process_2d_data_object(self, data_set, key, unit):
272        if key == self.i_name:
273            self.current_dataset.data = data_set
274            self.current_dataset.zaxis("Intensity", unit)
275        elif key == self.i_uncertainties_name:
276            self.current_dataset.err_data = data_set.flatten()
277        elif key in self.q_names:
278            self.current_dataset.xaxis("Q_x", unit)
279            self.current_dataset.yaxis("Q_y", unit)
280            if self.q_names[0] == self.q_names[1]:
281                # All q data in a single array
282                self.current_dataset.qx_data = data_set[0]
283                self.current_dataset.qy_data = data_set[1]
284            elif self.q_names.index(key) == 0:
285                self.current_dataset.qx_data = data_set
286            elif self.q_names.index(key) == 1:
287                self.current_dataset.qy_data = data_set
288        elif key in self.q_uncertainty_names or key in self.q_resolution_names:
289            if ((self.q_uncertainty_names[0] == self.q_uncertainty_names[1]) or
290                    (self.q_resolution_names[0] == self.q_resolution_names[1])):
291                # All q data in a single array
292                self.current_dataset.dqx_data = data_set[0].flatten()
293                self.current_dataset.dqy_data = data_set[1].flatten()
294            elif (self.q_uncertainty_names.index(key) == 0 or
295                  self.q_resolution_names.index(key) == 0):
296                self.current_dataset.dqx_data = data_set.flatten()
297            elif (self.q_uncertainty_names.index(key) == 1 or
298                  self.q_resolution_names.index(key) == 1):
299                self.current_dataset.dqy_data = data_set.flatten()
300                self.current_dataset.yaxis("Q_y", unit)
301        elif key == self.mask_name:
302            self.current_dataset.mask = data_set.flatten()
303        elif key == u'Qy':
304            self.current_dataset.yaxis("Q_y", unit)
305            self.current_dataset.qy_data = data_set.flatten()
306        elif key == u'Qydev':
307            self.current_dataset.dqy_data = data_set.flatten()
308        elif key == u'Qx':
309            self.current_dataset.xaxis("Q_x", unit)
310            self.current_dataset.qx_data = data_set.flatten()
311        elif key == u'Qxdev':
312            self.current_dataset.dqx_data = data_set.flatten()
313
314    def process_trans_spectrum(self, data_set, key):
315        """
316        SAStransmission_spectrum processor
317        :param data_set: data from HDF5 file
318        :param key: canSAS_class attribute
319        """
320        if key == u'T':
321            self.trans_spectrum.transmission = data_set.flatten()
322        elif key == u'Tdev':
323            self.trans_spectrum.transmission_deviation = data_set.flatten()
324        elif key == u'lambda':
325            self.trans_spectrum.wavelength = data_set.flatten()
326
327    def process_sample(self, data_point, key):
328        """
329        SASsample processor
330        :param data_point: Single point from an HDF5 data file
331        :param key: class name data_point was taken from
332        """
333        if key == u'Title':
334            self.current_datainfo.sample.name = data_point
335        elif key == u'name':
336            self.current_datainfo.sample.name = data_point
337        elif key == u'ID':
338            self.current_datainfo.sample.name = data_point
339        elif key == u'thickness':
340            self.current_datainfo.sample.thickness = data_point
341        elif key == u'temperature':
342            self.current_datainfo.sample.temperature = data_point
343        elif key == u'transmission':
344            self.current_datainfo.sample.transmission = data_point
345        elif key == u'x_position':
346            self.current_datainfo.sample.position.x = data_point
347        elif key == u'y_position':
348            self.current_datainfo.sample.position.y = data_point
349        elif key == u'pitch':
350            self.current_datainfo.sample.orientation.x = data_point
351        elif key == u'yaw':
352            self.current_datainfo.sample.orientation.y = data_point
353        elif key == u'roll':
354            self.current_datainfo.sample.orientation.z = data_point
355        elif key == u'details':
356            self.current_datainfo.sample.details.append(data_point)
357
358    def process_detector(self, data_point, key, unit):
359        """
360        SASdetector processor
361        :param data_point: Single point from an HDF5 data file
362        :param key: class name data_point was taken from
363        :param unit: unit attribute from data set
364        """
365        if key == u'name':
366            self.detector.name = data_point
367        elif key == u'SDD':
368            self.detector.distance = float(data_point)
369            self.detector.distance_unit = unit
370        elif key == u'slit_length':
371            self.detector.slit_length = float(data_point)
372            self.detector.slit_length_unit = unit
373        elif key == u'x_position':
374            self.detector.offset.x = float(data_point)
375            self.detector.offset_unit = unit
376        elif key == u'y_position':
377            self.detector.offset.y = float(data_point)
378            self.detector.offset_unit = unit
379        elif key == u'pitch':
380            self.detector.orientation.x = float(data_point)
381            self.detector.orientation_unit = unit
382        elif key == u'roll':
383            self.detector.orientation.z = float(data_point)
384            self.detector.orientation_unit = unit
385        elif key == u'yaw':
386            self.detector.orientation.y = float(data_point)
387            self.detector.orientation_unit = unit
388        elif key == u'beam_center_x':
389            self.detector.beam_center.x = float(data_point)
390            self.detector.beam_center_unit = unit
391        elif key == u'beam_center_y':
392            self.detector.beam_center.y = float(data_point)
393            self.detector.beam_center_unit = unit
394        elif key == u'x_pixel_size':
395            self.detector.pixel_size.x = float(data_point)
396            self.detector.pixel_size_unit = unit
397        elif key == u'y_pixel_size':
398            self.detector.pixel_size.y = float(data_point)
399            self.detector.pixel_size_unit = unit
400
401    def process_collimation(self, data_point, key, unit):
402        """
403        SAScollimation processor
404        :param data_point: Single point from an HDF5 data file
405        :param key: class name data_point was taken from
406        :param unit: unit attribute from data set
407        """
408        if key == u'distance':
409            self.collimation.length = data_point
410            self.collimation.length_unit = unit
411        elif key == u'name':
412            self.collimation.name = data_point
413
414    def process_aperture(self, data_point, key):
415        """
416        SASaperture processor
417        :param data_point: Single point from an HDF5 data file
418        :param key: class name data_point was taken from
419        """
420        if key == u'shape':
421            self.aperture.shape = data_point
422        elif key == u'x_gap':
423            self.aperture.size.x = data_point
424        elif key == u'y_gap':
425            self.aperture.size.y = data_point
426
427    def process_source(self, data_point, key, unit):
428        """
429        SASsource processor
430        :param data_point: Single point from an HDF5 data file
431        :param key: class name data_point was taken from
432        :param unit: unit attribute from data set
433        """
434        if key == u'incident_wavelength':
435            self.current_datainfo.source.wavelength = data_point
436            self.current_datainfo.source.wavelength_unit = unit
437        elif key == u'wavelength_max':
438            self.current_datainfo.source.wavelength_max = data_point
439            self.current_datainfo.source.wavelength_max_unit = unit
440        elif key == u'wavelength_min':
441            self.current_datainfo.source.wavelength_min = data_point
442            self.current_datainfo.source.wavelength_min_unit = unit
443        elif key == u'incident_wavelength_spread':
444            self.current_datainfo.source.wavelength_spread = data_point
445            self.current_datainfo.source.wavelength_spread_unit = unit
446        elif key == u'beam_size_x':
447            self.current_datainfo.source.beam_size.x = data_point
448            self.current_datainfo.source.beam_size_unit = unit
449        elif key == u'beam_size_y':
450            self.current_datainfo.source.beam_size.y = data_point
451            self.current_datainfo.source.beam_size_unit = unit
452        elif key == u'beam_shape':
453            self.current_datainfo.source.beam_shape = data_point
454        elif key == u'radiation':
455            self.current_datainfo.source.radiation = data_point
456
457    def process_process(self, data_point, key):
458        """
459        SASprocess processor
460        :param data_point: Single point from an HDF5 data file
461        :param key: class name data_point was taken from
462        """
463        term_match = re.compile(u'^term[0-9]+$')
464        if key == u'Title':  # CanSAS 2.0
465            self.process.name = data_point
466        elif key == u'name':  # NXcanSAS
467            self.process.name = data_point
468        elif key == u'description':
469            self.process.description = data_point
470        elif key == u'date':
471            self.process.date = data_point
472        elif term_match.match(key):
473            self.process.term.append(data_point)
474        else:
475            self.process.notes.append(data_point)
476
477    def add_intermediate(self):
478        """
479        This method stores any intermediate objects within the final data set
480        after fully reading the set.
481
482        :param parent: The NXclass name for the h5py Group object that just
483                       finished being processed
484        """
485
486        if self.parent_class == u'SASprocess':
487            self.current_datainfo.process.append(self.process)
488            self.process = Process()
489        elif self.parent_class == u'SASdetector':
490            self.current_datainfo.detector.append(self.detector)
491            self.detector = Detector()
492        elif self.parent_class == u'SAStransmission_spectrum':
493            self.current_datainfo.trans_spectrum.append(self.trans_spectrum)
494            self.trans_spectrum = TransmissionSpectrum()
495        elif self.parent_class == u'SAScollimation':
496            self.current_datainfo.collimation.append(self.collimation)
497            self.collimation = Collimation()
498        elif self.parent_class == u'SASaperture':
499            self.collimation.aperture.append(self.aperture)
500            self.aperture = Aperture()
501        elif self.parent_class == u'SASdata':
502            if isinstance(self.current_dataset, plottable_2D):
503                self.data2d.append(self.current_dataset)
504            elif isinstance(self.current_dataset, plottable_1D):
505                self.data1d.append(self.current_dataset)
506
507    def final_data_cleanup(self):
508        """
509        Does some final cleanup and formatting on self.current_datainfo and
510        all data1D and data2D objects and then combines the data and info into
511        Data1D and Data2D objects
512        """
513        # Type cast data arrays to float64
514        if len(self.current_datainfo.trans_spectrum) > 0:
515            spectrum_list = []
516            for spectrum in self.current_datainfo.trans_spectrum:
517                spectrum.transmission = spectrum.transmission.astype(np.float64)
518                spectrum.transmission_deviation = \
519                    spectrum.transmission_deviation.astype(np.float64)
520                spectrum.wavelength = spectrum.wavelength.astype(np.float64)
521                if len(spectrum.transmission) > 0:
522                    spectrum_list.append(spectrum)
523            self.current_datainfo.trans_spectrum = spectrum_list
524
525        # Append errors to dataset and reset class errors
526        self.current_datainfo.errors = self.errors
527        self.errors.clear()
528
529        # Combine all plottables with datainfo and append each to output
530        # Type cast data arrays to float64 and find min/max as appropriate
531        for dataset in self.data2d:
532            zeros = np.ones(dataset.data.size, dtype=bool)
533            try:
534                for i in range(0, dataset.mask.size - 1):
535                    zeros[i] = dataset.mask[i]
536            except:
537                self.errors.add(sys.exc_value)
538            dataset.mask = zeros
539            # Calculate the actual Q matrix
540            try:
541                if dataset.q_data.size <= 1:
542                    dataset.q_data = np.sqrt(dataset.qx_data
543                                             * dataset.qx_data
544                                             + dataset.qy_data
545                                             * dataset.qy_data)
546            except:
547                dataset.q_data = None
548
549            if dataset.data.ndim == 2:
550                (n_rows, n_cols) = dataset.data.shape
551                flat_qy = dataset.qy_data[0::n_cols].flatten()
552                # For 2D arrays of Qx and Qy, the Q value should be constant
553                # along each row -OR- each column. The direction is not
554                # specified in the NXcanSAS standard.
555                if flat_qy[0] == flat_qy[1]:
556                    flat_qy = np.transpose(dataset.qy_data)[0::n_cols].flatten()
557                dataset.y_bins = np.unique(flat_qy)
558                flat_qx = dataset.qx_data[0::n_rows].flatten()
559                # For 2D arrays of Qx and Qy, the Q value should be constant
560                # along each row -OR- each column. The direction is not
561                # specified in the NXcanSAS standard.
562                if flat_qx[0] == flat_qx[1]:
563                    flat_qx = np.transpose(dataset.qx_data)[0::n_rows].flatten()
564                dataset.x_bins = np.unique(flat_qx)
565                dataset.data = dataset.data.flatten()
566                dataset.qx_data = dataset.qx_data.flatten()
567                dataset.qy_data = dataset.qy_data.flatten()
568            self.current_dataset = dataset
569            self.send_to_output()
570
571        for dataset in self.data1d:
572            self.current_dataset = dataset
573            self.send_to_output()
574
575    def add_data_set(self, key=""):
576        """
577        Adds the current_dataset to the list of outputs after preforming final
578        processing on the data and then calls a private method to generate a
579        new data set.
580
581        :param key: NeXus group name for current tree level
582        """
583
584        if self.current_datainfo and self.current_dataset:
585            self.final_data_cleanup()
586        self.data1d = []
587        self.data2d = []
588        self.current_datainfo = DataInfo()
589
590    def _initialize_new_data_set(self, value=None):
591        """
592        A private class method to generate a new 1D or 2D data object based on
593        the type of data within the set. Outside methods should call
594        add_data_set() to be sure any existing data is stored properly.
595
596        :param parent_list: List of names of parent elements
597        """
598        if self._is2d(value):
599            self.current_dataset = plottable_2D()
600        else:
601            x = np.array(0)
602            y = np.array(0)
603            self.current_dataset = plottable_1D(x, y)
604        self.current_datainfo.filename = self.raw_data.filename
605        self.mask_name = u''
606        self.i_name = u''
607        self.i_node = u''
608        self.i_uncertainties_name = u''
609        self.q_names = []
610        self.q_uncertainty_names = []
611        self.q_resolution_names = []
612
613    @staticmethod
614    def check_is_list_or_array(iterable):
615        try:
616            iter(iterable)
617            if (not isinstance(iterable, np.ndarray) and not isinstance(
618                    iterable, list)) or (isinstance(iterable, basestring)):
619                raise TypeError
620        except TypeError:
621            iterable = iterable.split(",")
622        return iterable
623
624    def _find_data_attributes(self, value):
625        """
626        A class to find the indices for Q, the name of the Qdev and Idev, and
627        the name of the mask.
628        :param value: SASdata/NXdata HDF5 Group
629        """
630        attrs = value.attrs
631        signal = attrs.get("signal", "I")
632        i_axes = attrs.get("I_axes", ["Q"])
633        q_indices = attrs.get("Q_indices", [0])
634        q_indices = map(int, self.check_is_list_or_array(q_indices))
635        i_axes = self.check_is_list_or_array(i_axes)
636        keys = value.keys()
637        self.mask_name = attrs.get("mask")
638        for val in q_indices:
639            self.q_names.append(i_axes[val])
640        self.i_name = signal
641        self.i_node = value.get(self.i_name)
642        for item in self.q_names:
643            if item in keys:
644                q_vals = value.get(item)
645                if q_vals.attrs.get("uncertainties") is not None:
646                    self.q_uncertainty_names = q_vals.attrs.get("uncertainties")
647                elif q_vals.attrs.get("uncertainty") is not None:
648                    self.q_uncertainty_names = q_vals.attrs.get("uncertainty")
649                if isinstance(self.q_uncertainty_names, basestring):
650                    self.q_uncertainty_names = self.q_uncertainty_names.split(",")
651                if q_vals.attrs.get("resolutions") is not None:
652                    self.q_resolution_names = q_vals.attrs.get("resolutions")
653                if isinstance(self.q_resolution_names, basestring):
654                    self.q_resolution_names = self.q_resolution_names.split(",")
655        if self.i_name in keys:
656            i_vals = value.get(self.i_name)
657            self.i_uncertainties_name = i_vals.attrs.get("uncertainties")
658            if self.i_uncertainties_name is None:
659                self.i_uncertainties_name = i_vals.attrs.get("uncertainty")
660
661    def _is2d(self, value, basename="I"):
662        """
663        A private class to determine if the data set is 1d or 2d.
664
665        :param parent_list: List of parents nodes in the HDF5 file
666        :param basename: Approximate name of an entry to search for
667        :return: True if 2D, otherwise false
668        """
669
670        vals = value.get(basename)
671        return (vals is not None and vals.shape is not None
672                and len(vals.shape) != 1)
673
674    def _create_unique_key(self, dictionary, name, numb=0):
675        """
676        Create a unique key value for any dictionary to prevent overwriting
677        Recurses until a unique key value is found.
678
679        :param dictionary: A dictionary with any number of entries
680        :param name: The index of the item to be added to dictionary
681        :param numb: The number to be appended to the name, starts at 0
682        :return: The new name for the dictionary entry
683        """
684        if dictionary.get(name) is not None:
685            numb += 1
686            name = name.split("_")[0]
687            name += "_{0}".format(numb)
688            name = self._create_unique_key(dictionary, name, numb)
689        return name
690
691    def _get_unit(self, value):
692        """
693        Find the unit for a particular value within the h5py dictionary
694
695        :param value: attribute dictionary for a particular value set
696        :return: unit for the value passed to the method
697        """
698        unit = h5attr(value, u'units')
699        if unit is None:
700            unit = h5attr(value, u'unit')
701        return unit
Note: See TracBrowser for help on using the repository browser.