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

magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1249
Last change on this file since dbfd307 was 4cbb2f5, checked in by Paul Kienzle <pkienzle@…>, 5 years ago

pull conflicting changes from master

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