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

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

File loader fixes and code cleanup.

  • Property mode set to 100644
File size: 29.0 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_name = []
107        self.mask_name = u''
108        self.i_name = u''
109        self.i_node = u''
110        self.q_uncertainties = None
111        self.q_resolutions = None
112        self.i_uncertainties = u''
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 class_name is None:
134                class_name = h5attr(value, u'NX_class')
135            if class_name is not None:
136                class_prog = re.compile(class_name)
137            else:
138                class_prog = re.compile(value.name)
139
140            if isinstance(value, h5py.Group):
141                # Set parent class before recursion
142                last_parent_class = self.parent_class
143                self.parent_class = class_name
144                parent_list.append(key)
145                # If a new sasentry, store the current data sets and create
146                # a fresh Data1D/2D object
147                if class_prog.match(u'SASentry'):
148                    self.add_data_set(key)
149                elif class_prog.match(u'SASdata'):
150                    self._initialize_new_data_set(value)
151                    self._find_data_attributes(value)
152                # Recursion step to access data within the group
153                self.read_children(value, parent_list)
154                self.add_intermediate()
155                # Reset parent class when returning from recursive method
156                self.parent_class = last_parent_class
157                parent_list.remove(key)
158
159            elif isinstance(value, h5py.Dataset):
160                # If this is a dataset, store the data appropriately
161                data_set = value.value
162                unit = self._get_unit(value)
163
164                for data_point in data_set:
165                    if isinstance(data_point, np.ndarray):
166                        if data_point.dtype.char == 'S':
167                            data_point = decode(bytes(data_point))
168                    else:
169                        data_point = decode(data_point)
170                    # Top Level Meta Data
171                    if key == u'definition':
172                        self.current_datainfo.meta_data['reader'] = data_point
173                    # Run
174                    elif key == u'run':
175                        self.current_datainfo.run.append(data_point)
176                        try:
177                            run_name = h5attr(value, 'name')
178                            run_dict = {data_point: run_name}
179                            self.current_datainfo.run_name = run_dict
180                        except Exception:
181                            pass
182                    # Title
183                    elif key == u'title':
184                        self.current_datainfo.title = data_point
185                    # Note
186                    elif key == u'SASnote':
187                        self.current_datainfo.notes.append(data_point)
188                    # Sample Information
189                    elif self.parent_class == u'SASsample':
190                        self.process_sample(data_point, key)
191                    # Instrumental Information
192                    elif (key == u'name'
193                          and self.parent_class == u'SASinstrument'):
194                        self.current_datainfo.instrument = data_point
195                    # Detector
196                    elif self.parent_class == u'SASdetector':
197                        self.process_detector(data_point, key, unit)
198                    # Collimation
199                    elif self.parent_class == u'SAScollimation':
200                        self.process_collimation(data_point, key, unit)
201                    # Aperture
202                    elif self.parent_class == u'SASaperture':
203                        self.process_aperture(data_point, key)
204                    # Process Information
205                    elif self.parent_class == u'SASprocess': # CanSAS 2.0
206                        self.process_process(data_point, key)
207                    # Source
208                    elif self.parent_class == u'SASsource':
209                        self.process_source(data_point, key, unit)
210                    # Everything else goes in meta_data
211                    elif self.parent_class == u'SASdata':
212                        if isinstance(self.current_dataset, plottable_2D):
213                            self.process_2d_data_object(data_set, key, unit)
214                        else:
215                            self.process_1d_data_object(data_set, key, unit)
216
217                        break
218                    elif self.parent_class == u'SAStransmission_spectrum':
219                        self.process_trans_spectrum(data_set, key)
220                        break
221                    else:
222                        new_key = self._create_unique_key(
223                            self.current_datainfo.meta_data, key)
224                        self.current_datainfo.meta_data[new_key] = data_point
225
226            else:
227                # I don't know if this reachable code
228                self.errors.add("ShouldNeverHappenException")
229
230    def process_1d_data_object(self, data_set, key, unit):
231        """
232        SASdata processor method for 1d data items
233        :param data_set: data from HDF5 file
234        :param key: canSAS_class attribute
235        :param unit: unit attribute
236        """
237        if key == self.i_name:
238            self.current_dataset.y = data_set.flatten()
239            self.current_dataset.yaxis("Intensity", unit)
240        elif key == self.i_uncertainties:
241            self.current_dataset.dy = data_set.flatten()
242        elif key in self.q_name:
243            self.current_dataset.xaxis("Q", unit)
244            self.current_dataset.x = data_set.flatten()
245        elif key in self.q_uncertainties or key in self.q_resolutions:
246            if (len(self.q_resolutions) > 1
247                    and np.where(self.q_resolutions == key)[0] == 0):
248                self.current_dataset.dxw = data_set.flatten()
249            elif (len(self.q_resolutions) > 1
250                  and np.where(self.q_resolutions == key)[0] == 1):
251                self.current_dataset.dxl = data_set.flatten()
252            else:
253                self.current_dataset.dx = data_set.flatten()
254        elif key == self.mask_name:
255            self.current_dataset.mask = data_set.flatten()
256        elif key == u'wavelength':
257            self.current_datainfo.source.wavelength = data_set[0]
258            self.current_datainfo.source.wavelength_unit = unit
259
260    def process_2d_data_object(self, data_set, key, unit):
261        if key == self.i_name:
262            self.current_dataset.data = data_set
263            self.current_dataset.zaxis("Intensity", unit)
264        elif key == self.i_uncertainties:
265            self.current_dataset.err_data = data_set.flatten()
266        elif key in self.q_name:
267            self.current_dataset.xaxis("Q_x", unit)
268            self.current_dataset.yaxis("Q_y", unit)
269            if self.q_name[0] == self.q_name[1]:
270                # All q data in a single array
271                self.current_dataset.qx_data = data_set[0]
272                self.current_dataset.qy_data = data_set[1]
273            elif self.q_name.index(key) == 0:
274                self.current_dataset.qx_data = data_set
275            elif self.q_name.index(key) == 1:
276                self.current_dataset.qy_data = data_set
277        elif key in self.q_uncertainties or key in self.q_resolutions:
278            if ((self.q_uncertainties[0] == self.q_uncertainties[1]) or
279                    (self.q_resolutions[0] == self.q_resolutions[1])):
280                # All q data in a single array
281                self.current_dataset.dqx_data = data_set[0].flatten()
282                self.current_dataset.dqy_data = data_set[1].flatten()
283            elif (self.q_uncertainties.index(key) == 0 or
284                  self.q_resolutions.index(key) == 0):
285                self.current_dataset.dqx_data = data_set.flatten()
286            elif (self.q_uncertainties.index(key) == 1 or
287                  self.q_resolutions.index(key) == 1):
288                self.current_dataset.dqy_data = data_set.flatten()
289                self.current_dataset.yaxis("Q_y", unit)
290        elif key == self.mask_name:
291            self.current_dataset.mask = data_set.flatten()
292        elif key == u'Qy':
293            self.current_dataset.yaxis("Q_y", unit)
294            self.current_dataset.qy_data = data_set.flatten()
295        elif key == u'Qydev':
296            self.current_dataset.dqy_data = data_set.flatten()
297        elif key == u'Qx':
298            self.current_dataset.xaxis("Q_x", unit)
299            self.current_dataset.qx_data = data_set.flatten()
300        elif key == u'Qxdev':
301            self.current_dataset.dqx_data = data_set.flatten()
302
303    def process_trans_spectrum(self, data_set, key):
304        """
305        SAStransmission_spectrum processor
306        :param data_set: data from HDF5 file
307        :param key: canSAS_class attribute
308        """
309        if key == u'T':
310            self.trans_spectrum.transmission = data_set.flatten()
311        elif key == u'Tdev':
312            self.trans_spectrum.transmission_deviation = data_set.flatten()
313        elif key == u'lambda':
314            self.trans_spectrum.wavelength = data_set.flatten()
315
316    def process_sample(self, data_point, key):
317        """
318        SASsample processor
319        :param data_point: Single point from an HDF5 data file
320        :param key: class name data_point was taken from
321        """
322        if key == u'Title':
323            self.current_datainfo.sample.name = data_point
324        elif key == u'name':
325            self.current_datainfo.sample.name = data_point
326        elif key == u'ID':
327            self.current_datainfo.sample.name = data_point
328        elif key == u'thickness':
329            self.current_datainfo.sample.thickness = data_point
330        elif key == u'temperature':
331            self.current_datainfo.sample.temperature = data_point
332        elif key == u'transmission':
333            self.current_datainfo.sample.transmission = data_point
334        elif key == u'x_position':
335            self.current_datainfo.sample.position.x = data_point
336        elif key == u'y_position':
337            self.current_datainfo.sample.position.y = data_point
338        elif key == u'pitch':
339            self.current_datainfo.sample.orientation.x = data_point
340        elif key == u'yaw':
341            self.current_datainfo.sample.orientation.y = data_point
342        elif key == u'roll':
343            self.current_datainfo.sample.orientation.z = data_point
344        elif key == u'details':
345            self.current_datainfo.sample.details.append(data_point)
346
347    def process_detector(self, data_point, key, unit):
348        """
349        SASdetector processor
350        :param data_point: Single point from an HDF5 data file
351        :param key: class name data_point was taken from
352        :param unit: unit attribute from data set
353        """
354        if key == u'name':
355            self.detector.name = data_point
356        elif key == u'SDD':
357            self.detector.distance = float(data_point)
358            self.detector.distance_unit = unit
359        elif key == u'slit_length':
360            self.detector.slit_length = float(data_point)
361            self.detector.slit_length_unit = unit
362        elif key == u'x_position':
363            self.detector.offset.x = float(data_point)
364            self.detector.offset_unit = unit
365        elif key == u'y_position':
366            self.detector.offset.y = float(data_point)
367            self.detector.offset_unit = unit
368        elif key == u'pitch':
369            self.detector.orientation.x = float(data_point)
370            self.detector.orientation_unit = unit
371        elif key == u'roll':
372            self.detector.orientation.z = float(data_point)
373            self.detector.orientation_unit = unit
374        elif key == u'yaw':
375            self.detector.orientation.y = float(data_point)
376            self.detector.orientation_unit = unit
377        elif key == u'beam_center_x':
378            self.detector.beam_center.x = float(data_point)
379            self.detector.beam_center_unit = unit
380        elif key == u'beam_center_y':
381            self.detector.beam_center.y = float(data_point)
382            self.detector.beam_center_unit = unit
383        elif key == u'x_pixel_size':
384            self.detector.pixel_size.x = float(data_point)
385            self.detector.pixel_size_unit = unit
386        elif key == u'y_pixel_size':
387            self.detector.pixel_size.y = float(data_point)
388            self.detector.pixel_size_unit = unit
389
390    def process_collimation(self, data_point, key, unit):
391        """
392        SAScollimation processor
393        :param data_point: Single point from an HDF5 data file
394        :param key: class name data_point was taken from
395        :param unit: unit attribute from data set
396        """
397        if key == u'distance':
398            self.collimation.length = data_point
399            self.collimation.length_unit = unit
400        elif key == u'name':
401            self.collimation.name = data_point
402
403    def process_aperture(self, data_point, key):
404        """
405        SASaperture processor
406        :param data_point: Single point from an HDF5 data file
407        :param key: class name data_point was taken from
408        """
409        if key == u'shape':
410            self.aperture.shape = data_point
411        elif key == u'x_gap':
412            self.aperture.size.x = data_point
413        elif key == u'y_gap':
414            self.aperture.size.y = data_point
415
416    def process_source(self, data_point, key, unit):
417        """
418        SASsource processor
419        :param data_point: Single point from an HDF5 data file
420        :param key: class name data_point was taken from
421        :param unit: unit attribute from data set
422        """
423        if key == u'incident_wavelength':
424            self.current_datainfo.source.wavelength = data_point
425            self.current_datainfo.source.wavelength_unit = unit
426        elif key == u'wavelength_max':
427            self.current_datainfo.source.wavelength_max = data_point
428            self.current_datainfo.source.wavelength_max_unit = unit
429        elif key == u'wavelength_min':
430            self.current_datainfo.source.wavelength_min = data_point
431            self.current_datainfo.source.wavelength_min_unit = unit
432        elif key == u'incident_wavelength_spread':
433            self.current_datainfo.source.wavelength_spread = data_point
434            self.current_datainfo.source.wavelength_spread_unit = unit
435        elif key == u'beam_size_x':
436            self.current_datainfo.source.beam_size.x = data_point
437            self.current_datainfo.source.beam_size_unit = unit
438        elif key == u'beam_size_y':
439            self.current_datainfo.source.beam_size.y = data_point
440            self.current_datainfo.source.beam_size_unit = unit
441        elif key == u'beam_shape':
442            self.current_datainfo.source.beam_shape = data_point
443        elif key == u'radiation':
444            self.current_datainfo.source.radiation = data_point
445
446    def process_process(self, data_point, key):
447        """
448        SASprocess processor
449        :param data_point: Single point from an HDF5 data file
450        :param key: class name data_point was taken from
451        """
452        term_match = re.compile(u'^term[0-9]+$')
453        if key == u'Title':  # CanSAS 2.0
454            self.process.name = data_point
455        elif key == u'name':  # NXcanSAS
456            self.process.name = data_point
457        elif key == u'description':
458            self.process.description = data_point
459        elif key == u'date':
460            self.process.date = data_point
461        elif term_match.match(key):
462            self.process.term.append(data_point)
463        else:
464            self.process.notes.append(data_point)
465
466    def add_intermediate(self):
467        """
468        This method stores any intermediate objects within the final data set
469        after fully reading the set.
470
471        :param parent: The NXclass name for the h5py Group object that just
472                       finished being processed
473        """
474
475        if self.parent_class == u'SASprocess':
476            self.current_datainfo.process.append(self.process)
477            self.process = Process()
478        elif self.parent_class == u'SASdetector':
479            self.current_datainfo.detector.append(self.detector)
480            self.detector = Detector()
481        elif self.parent_class == u'SAStransmission_spectrum':
482            self.current_datainfo.trans_spectrum.append(self.trans_spectrum)
483            self.trans_spectrum = TransmissionSpectrum()
484        elif self.parent_class == u'SAScollimation':
485            self.current_datainfo.collimation.append(self.collimation)
486            self.collimation = Collimation()
487        elif self.parent_class == u'SASaperture':
488            self.collimation.aperture.append(self.aperture)
489            self.aperture = Aperture()
490        elif self.parent_class == u'SASdata':
491            if isinstance(self.current_dataset, plottable_2D):
492                self.data2d.append(self.current_dataset)
493            elif isinstance(self.current_dataset, plottable_1D):
494                self.data1d.append(self.current_dataset)
495
496    def final_data_cleanup(self):
497        """
498        Does some final cleanup and formatting on self.current_datainfo and
499        all data1D and data2D objects and then combines the data and info into
500        Data1D and Data2D objects
501        """
502        # Type cast data arrays to float64
503        if len(self.current_datainfo.trans_spectrum) > 0:
504            spectrum_list = []
505            for spectrum in self.current_datainfo.trans_spectrum:
506                spectrum.transmission = spectrum.transmission.astype(np.float64)
507                spectrum.transmission_deviation = \
508                    spectrum.transmission_deviation.astype(np.float64)
509                spectrum.wavelength = spectrum.wavelength.astype(np.float64)
510                if len(spectrum.transmission) > 0:
511                    spectrum_list.append(spectrum)
512            self.current_datainfo.trans_spectrum = spectrum_list
513
514        # Append errors to dataset and reset class errors
515        self.current_datainfo.errors = self.errors
516        self.errors.clear()
517
518        # Combine all plottables with datainfo and append each to output
519        # Type cast data arrays to float64 and find min/max as appropriate
520        for dataset in self.data2d:
521            zeros = np.ones(dataset.data.size, dtype=bool)
522            try:
523                for i in range(0, dataset.mask.size - 1):
524                    zeros[i] = dataset.mask[i]
525            except:
526                self.errors.add(sys.exc_value)
527            dataset.mask = zeros
528            # Calculate the actual Q matrix
529            try:
530                if dataset.q_data.size <= 1:
531                    dataset.q_data = np.sqrt(dataset.qx_data
532                                             * dataset.qx_data
533                                             + dataset.qy_data
534                                             * dataset.qy_data)
535            except:
536                dataset.q_data = None
537
538            if dataset.data.ndim == 2:
539                (n_rows, n_cols) = dataset.data.shape
540                print(n_rows)
541                print(n_cols)
542                flat_qy = dataset.qy_data[0::n_cols].flatten()
543                if flat_qy[0] == flat_qy[1]:
544                    flat_qy = np.transpose(dataset.qy_data)[0::n_cols].flatten()
545                dataset.y_bins = np.unique(flat_qy)
546                flat_qx = dataset.qx_data[0::n_rows].flatten()
547                if flat_qx[0] == flat_qx[1]:
548                    flat_qx = np.transpose(dataset.qx_data)[0::n_rows].flatten()
549                dataset.x_bins = np.unique(flat_qx)
550                print(dataset.x_bins)
551                print(len(dataset.x_bins))
552                print(dataset.y_bins)
553                print(len(dataset.y_bins))
554                dataset.data = dataset.data.flatten()
555                dataset.qx_data = dataset.qx_data.flatten()
556                dataset.qy_data = dataset.qy_data.flatten()
557            self.current_dataset = dataset
558            self.send_to_output()
559
560        for dataset in self.data1d:
561            self.current_dataset = dataset
562            self.send_to_output()
563
564    def add_data_set(self, key=""):
565        """
566        Adds the current_dataset to the list of outputs after preforming final
567        processing on the data and then calls a private method to generate a
568        new data set.
569
570        :param key: NeXus group name for current tree level
571        """
572
573        if self.current_datainfo and self.current_dataset:
574            self.final_data_cleanup()
575        self.data1d = []
576        self.data2d = []
577        self.current_datainfo = DataInfo()
578
579    def _initialize_new_data_set(self, value=None):
580        """
581        A private class method to generate a new 1D or 2D data object based on
582        the type of data within the set. Outside methods should call
583        add_data_set() to be sure any existing data is stored properly.
584
585        :param parent_list: List of names of parent elements
586        """
587        if self._is2d(value):
588            self.current_dataset = plottable_2D()
589        else:
590            x = np.array(0)
591            y = np.array(0)
592            self.current_dataset = plottable_1D(x, y)
593        self.current_datainfo.filename = self.raw_data.filename
594        self.mask_name = ""
595        self.i_name = ""
596        self.i_node = ""
597        self.q_name = []
598        self.q_uncertainties = []
599        self.q_resolutions = []
600        self.i_uncertainties = ""
601
602    @staticmethod
603    def check_is_list_or_array(iterable):
604        try:
605            iter(iterable)
606            if (not isinstance(iterable, np.ndarray) and not isinstance(
607                    iterable, list)) or (isinstance(iterable, str) or
608                                         isinstance(iterable, unicode)):
609                raise TypeError
610        except TypeError:
611            iterable = iterable.split(",")
612        return iterable
613
614    def _find_data_attributes(self, value):
615        """
616        A class to find the indices for Q, the name of the Qdev and Idev, and
617        the name of the mask.
618        :param value: SASdata/NXdata HDF5 Group
619        """
620        attrs = value.attrs
621        signal = attrs.get("signal", "I")
622        i_axes = attrs.get("I_axes", ["Q"])
623        q_indices = attrs.get("Q_indices", [0])
624        q_indices = map(int, self.check_is_list_or_array(q_indices))
625        i_axes = self.check_is_list_or_array(i_axes)
626        keys = value.keys()
627        self.mask_name = attrs.get("mask")
628        for val in q_indices:
629            self.q_name.append(i_axes[val])
630        self.i_name = signal
631        self.i_node = value.get(self.i_name)
632        for item in self.q_name:
633            if item in keys:
634                q_vals = value.get(item)
635                if q_vals.attrs.get("uncertainties") is not None:
636                    self.q_uncertainties = q_vals.attrs.get("uncertainties")
637                elif q_vals.attrs.get("uncertainty") is not None:
638                    self.q_uncertainties = q_vals.attrs.get("uncertainty")
639                if isinstance(self.q_uncertainties, str) is not None:
640                    self.q_uncertainties = [self.q_uncertainties]
641                if q_vals.attrs.get("resolutions") is not None:
642                    self.q_resolutions = q_vals.attrs.get("resolutions")
643                if isinstance(self.q_resolutions, str):
644                    self.q_resolutions = self.q_resolutions.split(",")
645        if self.i_name in keys:
646            i_vals = value.get(self.i_name)
647            self.i_uncertainties = i_vals.attrs.get("uncertainties")
648            if self.i_uncertainties is None:
649                self.i_uncertainties = i_vals.attrs.get("uncertainty")
650
651    def _is2d(self, value, basename="I"):
652        """
653        A private class to determine if the data set is 1d or 2d.
654
655        :param parent_list: List of parents nodes in the HDF5 file
656        :param basename: Approximate name of an entry to search for
657        :return: True if 2D, otherwise false
658        """
659
660        vals = value.get(basename)
661        return (vals is not None and vals.shape is not None
662                and len(vals.shape) != 1)
663
664    def _create_unique_key(self, dictionary, name, numb=0):
665        """
666        Create a unique key value for any dictionary to prevent overwriting
667        Recurses until a unique key value is found.
668
669        :param dictionary: A dictionary with any number of entries
670        :param name: The index of the item to be added to dictionary
671        :param numb: The number to be appended to the name, starts at 0
672        :return: The new name for the dictionary entry
673        """
674        if dictionary.get(name) is not None:
675            numb += 1
676            name = name.split("_")[0]
677            name += "_{0}".format(numb)
678            name = self._create_unique_key(dictionary, name, numb)
679        return name
680
681    def _get_unit(self, value):
682        """
683        Find the unit for a particular value within the h5py dictionary
684
685        :param value: attribute dictionary for a particular value set
686        :return: unit for the value passed to the method
687        """
688        unit = h5attr(value, u'units')
689        if unit is None:
690            unit = h5attr(value, u'unit')
691        return unit
Note: See TracBrowser for help on using the repository browser.