Changeset 132db16 in sasview


Ignore:
Timestamp:
Sep 15, 2016 8:33:57 AM (4 years ago)
Author:
Piotr Rozyczko <rozyczko@…>
Branches:
ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
Children:
ef51b63
Parents:
fac2619
git-author:
Jeff Krzywon <krzywon@…> (08/04/16 13:37:51)
git-committer:
Piotr Rozyczko <rozyczko@…> (09/15/16 08:33:57)
Message:

Refactored Cansas HDF5 reader to allow multiple instances of SASdata of any dimensionality in a single SASentry and multiple instances of SASentry in a single file. Added unit tests to test all types of data (1 entry | 1 data, 1 entry | many data, many entry | 1 data each, many entr |, many data).

Files:
4 added
3 edited
6 moved

Legend:

Unmodified
Added
Removed
  • src/sas/sascalc/dataloader/data_info.py

    rb699768 r132db16  
    10731073        clone.source = deepcopy(self.source) 
    10741074        clone.collimation = deepcopy(self.collimation) 
     1075        clone.trans_spectrum = deepcopy(self.trans_spectrum) 
    10751076        clone.meta_data = deepcopy(self.meta_data) 
    10761077        clone.errors = deepcopy(self.errors) 
     
    12261227 
    12271228        return result 
     1229 
     1230 
     1231def combine_data_info_with_plottable(data, datainfo): 
     1232    """ 
     1233    A function that combines the DataInfo data in self.current_datainto with a plottable_1D or 2D data object. 
     1234 
     1235    :param data: A plottable_1D or plottable_2D data object 
     1236    :return: A fully specified Data1D or Data2D object 
     1237    """ 
     1238 
     1239    final_dataset = None 
     1240    if isinstance(data, plottable_1D): 
     1241        final_dataset = Data1D(data.x, data.y) 
     1242        final_dataset.dx = data.dx 
     1243        final_dataset.dy = data.dy 
     1244        final_dataset.dxl = data.dxl 
     1245        final_dataset.dxw = data.dxw 
     1246        final_dataset.xaxis(data._xaxis, data._xunit) 
     1247        final_dataset.yaxis(data._yaxis, data._yunit) 
     1248    elif isinstance(data, plottable_2D): 
     1249        final_dataset = Data2D(data.data, data.err_data, data.qx_data, data.qy_data, data.q_data, 
     1250                               data.mask, data.dqx_data, data.dqy_data) 
     1251        final_dataset.xaxis(data._xaxis, data._xunit) 
     1252        final_dataset.yaxis(data._yaxis, data._yunit) 
     1253        final_dataset.zaxis(data._zaxis, data._zunit) 
     1254    else: 
     1255        return_string = "Should Never Happen: _combine_data_info_with_plottable input is not a plottable1d or " + \ 
     1256                        "plottable2d data object" 
     1257        return return_string 
     1258 
     1259    final_dataset.xmax = data.xmax 
     1260    final_dataset.ymax = data.ymax 
     1261    final_dataset.xmin = data.xmin 
     1262    final_dataset.ymin = data.ymin 
     1263    final_dataset.title = datainfo.title 
     1264    final_dataset.run = datainfo.run 
     1265    final_dataset.run_name = datainfo.run_name 
     1266    final_dataset.filename = datainfo.filename 
     1267    final_dataset.notes = datainfo.notes 
     1268    final_dataset.process = datainfo.process 
     1269    final_dataset.instrument = datainfo.instrument 
     1270    final_dataset.detector = datainfo.detector 
     1271    final_dataset.sample = datainfo.sample 
     1272    final_dataset.source = datainfo.source 
     1273    final_dataset.collimation = datainfo.collimation 
     1274    final_dataset.trans_spectrum = datainfo.trans_spectrum 
     1275    final_dataset.meta_data = datainfo.meta_data 
     1276    final_dataset.errors = datainfo.errors 
     1277    return final_dataset 
  • src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py

    rd398285 r132db16  
    99import sys 
    1010 
    11 from sas.sascalc.dataloader.data_info import plottable_1D, plottable_2D, Data1D, Data2D, Sample, Source 
    12 from sas.sascalc.dataloader.data_info import Process, Aperture, Collimation, TransmissionSpectrum, Detector 
     11from sas.sascalc.dataloader.data_info import plottable_1D, plottable_2D, Data1D, Data2D, DataInfo, Process, Aperture 
     12from sas.sascalc.dataloader.data_info import Collimation, TransmissionSpectrum, Detector 
     13from sas.sascalc.dataloader.data_info import combine_data_info_with_plottable 
     14 
    1315 
    1416 
     
    1820    with file extension .h5/.H5. Any number of data sets may be present within the file and any dimensionality of data 
    1921    may be used. Currently 1D and 2D SAS data sets are supported, but future implementations will include 1D and 2D 
    20     SESANS data. This class assumes a single data set for each sasentry. 
     22    SESANS data. 
     23 
     24    Any number of SASdata sets may be present in a SASentry and the data within can be either 1D I(Q) or 2D I(Qx, Qy). 
    2125 
    2226    :Dependencies: 
    23         The CanSAS HDF5 reader requires h5py v2.5.0 or later. 
     27        The CanSAS HDF5 reader requires h5py => v2.5.0 or later. 
    2428    """ 
    2529 
     
    3236    ## Raw file contents to be processed 
    3337    raw_data = None 
    34     ## Data set being modified 
     38    ## Data info currently being read in 
     39    current_datainfo = None 
     40    ## SASdata set currently being read in 
    3541    current_dataset = None 
    36     ## For recursion and saving purposes, remember parent objects 
    37     parent_list = None 
     42    ## List of plottable1D objects that should be linked to the current_datainfo 
     43    data1d = None 
     44    ## List of plottable2D objects that should be linked to the current_datainfo 
     45    data2d = None 
    3846    ## Data type name 
    3947    type_name = "CanSAS 2.0" 
     
    4755    output = None 
    4856 
    49     def __init__(self): 
    50         """ 
    51         Create the reader object and define initial states for class variables 
    52         """ 
    53         self.current_dataset = None 
    54         self.datasets = [] 
    55         self.raw_data = None 
    56         self.errors = set() 
    57         self.logging = [] 
    58         self.parent_list = [] 
    59         self.output = [] 
    60         self.detector = Detector() 
    61         self.collimation = Collimation() 
    62         self.aperture = Aperture() 
    63         self.process = Process() 
    64         self.sample = Sample() 
    65         self.source = Source() 
    66         self.trans_spectrum  = TransmissionSpectrum() 
    67  
    6857    def read(self, filename): 
    6958        """ 
     
    7160 
    7261        :param filename: A path for an HDF5 formatted CanSAS 2D data file. 
    73         :return: List of Data1D/2D objects or a list of errors. 
     62        :return: List of Data1D/2D objects and/or a list of errors. 
    7463        """ 
    7564 
    7665        ## Reinitialize the class when loading a new data file to reset all class variables 
    77         self.__init__() 
     66        self.reset_class_variables() 
    7867        ## Check that the file exists 
    7968        if os.path.isfile(filename): 
     
    8574                self.raw_data = h5py.File(filename, 'r') 
    8675                ## Read in all child elements of top level SASroot 
    87                 self.read_children(self.raw_data) 
     76                self.read_children(self.raw_data, []) 
    8877                ## Add the last data set to the list of outputs 
    8978                self.add_data_set() 
     
    9180        return self.output 
    9281 
    93     def read_children(self, data, parent=u'SASroot'): 
     82    def reset_class_variables(self): 
     83        """ 
     84        Create the reader object and define initial states for class variables 
     85        """ 
     86        self.current_datainfo = None 
     87        self.current_dataset = None 
     88        self.data1d = [] 
     89        self.data2d = [] 
     90        self.raw_data = None 
     91        self.errors = set() 
     92        self.logging = [] 
     93        self.output = [] 
     94        self.parent_class = u'' 
     95        self.detector = Detector() 
     96        self.collimation = Collimation() 
     97        self.aperture = Aperture() 
     98        self.process = Process() 
     99        self.trans_spectrum = TransmissionSpectrum() 
     100 
     101    def read_children(self, data, parent_list): 
    94102        """ 
    95103        A recursive method for stepping through the hierarchical data file. 
     
    97105        :param data: h5py Group object of any kind 
    98106        :param parent: h5py Group parent name 
    99         :return: None 
    100         """ 
    101  
    102         ## Create regex for base sasentry and for parent 
    103         parent_prog = re.compile(parent) 
     107        """ 
    104108 
    105109        ## Loop through each element of the parent and process accordingly 
     
    107111            ## Get all information for the current key 
    108112            value = data.get(key) 
    109             attr_keys = value.attrs.keys() 
    110             attr_values = value.attrs.values() 
    111113            if value.attrs.get(u'canSAS_class') is not None: 
    112114                class_name = value.attrs.get(u'canSAS_class') 
     
    119121 
    120122            if isinstance(value, h5py.Group): 
    121                 ##TODO: Rework this for multiple SASdata objects within a single SASentry to allow for both 1D and 2D 
    122                 ##TODO:     data within the same SASentry - One 1D and one 2D data object for all SASdata sets? 
    123                 ## If this is a new sasentry, store the current data set and create a fresh Data1D/2D object 
     123                self.parent_class = class_name 
     124                parent_list.append(key) 
     125                ## If this is a new sasentry, store the current data sets and create a fresh Data1D/2D object 
    124126                if class_prog.match(u'SASentry'): 
    125127                    self.add_data_set(key) 
     128                elif class_prog.match(u'SASdata'): 
     129                    self._initialize_new_data_set(parent_list) 
    126130                ## Recursion step to access data within the group 
    127                 self.read_children(value, class_name) 
    128                 self.add_intermediate(class_name) 
     131                self.read_children(value, parent_list) 
     132                self.add_intermediate() 
     133                parent_list.remove(key) 
    129134 
    130135            elif isinstance(value, h5py.Dataset): 
     
    136141                    unit = self._get_unit(value) 
    137142                    if key == u'definition': 
    138                         self.current_dataset.meta_data['reader'] = data_point 
     143                        self.current_datainfo.meta_data['reader'] = data_point 
    139144                    elif key == u'run': 
    140                         self.current_dataset.run.append(data_point) 
     145                        self.current_datainfo.run.append(data_point) 
    141146                    elif key == u'title': 
    142                         self.current_dataset.title = data_point 
     147                        self.current_datainfo.title = data_point 
    143148                    elif key == u'SASnote': 
    144                         self.current_dataset.notes.append(data_point) 
     149                        self.current_datainfo.notes.append(data_point) 
    145150 
    146151                    ## I and Q Data 
    147152                    elif key == u'I': 
    148                         if type(self.current_dataset) is Data2D: 
     153                        if type(self.current_dataset) is plottable_2D: 
    149154                            self.current_dataset.data = np.append(self.current_dataset.data, data_point) 
    150155                            self.current_dataset.zaxis("Intensity", unit) 
     
    153158                            self.current_dataset.yaxis("Intensity", unit) 
    154159                    elif key == u'Idev': 
    155                         if type(self.current_dataset) is Data2D: 
     160                        if type(self.current_dataset) is plottable_2D: 
    156161                            self.current_dataset.err_data = np.append(self.current_dataset.err_data, data_point) 
    157162                        else: 
     
    159164                    elif key == u'Q': 
    160165                        self.current_dataset.xaxis("Q", unit) 
    161                         if type(self.current_dataset) is Data2D: 
     166                        if type(self.current_dataset) is plottable_2D: 
    162167                            self.current_dataset.q = np.append(self.current_dataset.q, data_point) 
    163168                        else: 
     
    177182 
    178183                    ## Sample Information 
    179                     elif key == u'Title' and parent == u'SASsample': 
    180                         self.sample.name = data_point 
    181                     elif key == u'thickness' and parent == u'SASsample': 
    182                         self.sample.thickness = data_point 
    183                     elif key == u'temperature' and parent == u'SASsample': 
    184                         self.sample.temperature = data_point 
     184                    elif key == u'Title' and self.parent_class == u'SASsample': 
     185                        self.current_datainfo.sample.name = data_point 
     186                    elif key == u'thickness' and self.parent_class == u'SASsample': 
     187                        self.current_datainfo.sample.thickness = data_point 
     188                    elif key == u'temperature' and self.parent_class == u'SASsample': 
     189                        self.current_datainfo.sample.temperature = data_point 
    185190 
    186191                    ## Instrumental Information 
    187                     elif key == u'name' and parent == u'SASinstrument': 
    188                         self.current_dataset.instrument = data_point 
    189                     elif key == u'name' and parent == u'SASdetector': 
     192                    elif key == u'name' and self.parent_class == u'SASinstrument': 
     193                        self.current_datainfo.instrument = data_point 
     194                    elif key == u'name' and self.parent_class == u'SASdetector': 
    190195                        self.detector.name = data_point 
    191                     elif key == u'SDD' and parent == u'SASdetector': 
    192                         self.detector.distance = data_point 
     196                    elif key == u'SDD' and self.parent_class == u'SASdetector': 
     197                        self.detector.distance = float(data_point) 
    193198                        self.detector.distance_unit = unit 
    194                     elif key == u'SSD' and parent == u'SAScollimation': 
     199                    elif key == u'SSD' and self.parent_class == u'SAScollimation': 
    195200                        self.collimation.length = data_point 
    196201                        self.collimation.length_unit = unit 
    197                     elif key == u'name' and parent == u'SAScollimation': 
     202                    elif key == u'name' and self.parent_class == u'SAScollimation': 
    198203                        self.collimation.name = data_point 
    199204 
    200205                    ## Process Information 
    201                     elif key == u'name' and parent == u'SASprocess': 
     206                    elif key == u'name' and self.parent_class == u'SASprocess': 
    202207                        self.process.name = data_point 
    203                     elif key == u'Title' and parent == u'SASprocess': 
     208                    elif key == u'Title' and self.parent_class == u'SASprocess': 
    204209                        self.process.name = data_point 
    205                     elif key == u'description' and parent == u'SASprocess': 
     210                    elif key == u'description' and self.parent_class == u'SASprocess': 
    206211                        self.process.description = data_point 
    207                     elif key == u'date' and parent == u'SASprocess': 
     212                    elif key == u'date' and self.parent_class == u'SASprocess': 
    208213                        self.process.date = data_point 
    209                     elif parent == u'SASprocess': 
     214                    elif self.parent_class == u'SASprocess': 
    210215                        self.process.notes.append(data_point) 
    211216 
    212217                    ## Transmission Spectrum 
    213                     elif key == u'T' and parent == u'SAStransmission_spectrum': 
     218                    elif key == u'T' and self.parent_class == u'SAStransmission_spectrum': 
    214219                        self.trans_spectrum.transmission.append(data_point) 
    215                     elif key == u'Tdev' and parent == u'SAStransmission_spectrum': 
     220                    elif key == u'Tdev' and self.parent_class == u'SAStransmission_spectrum': 
    216221                        self.trans_spectrum.transmission_deviation.append(data_point) 
    217                     elif key == u'lambda' and parent == u'SAStransmission_spectrum': 
     222                    elif key == u'lambda' and self.parent_class == u'SAStransmission_spectrum': 
    218223                        self.trans_spectrum.wavelength.append(data_point) 
    219224 
    220225                    ## Other Information 
    221                     elif key == u'wavelength' and parent == u'SASdata': 
    222                         self.source.wavelength = data_point 
    223                         self.source.wavelength.unit = unit 
    224                     elif key == u'radiation' and parent == u'SASsource': 
    225                         self.source.radiation = data_point 
    226                     elif key == u'transmission' and parent == u'SASdata': 
    227                         self.sample.transmission = data_point 
     226                    elif key == u'wavelength' and self.parent_class == u'SASdata': 
     227                        self.current_datainfo.source.wavelength = data_point 
     228                        self.current_datainfo.source.wavelength.unit = unit 
     229                    elif key == u'radiation' and self.parent_class == u'SASsource': 
     230                        self.current_datainfo.source.radiation = data_point 
     231                    elif key == u'transmission' and self.parent_class == u'SASdata': 
     232                        self.current_datainfo.sample.transmission = data_point 
    228233 
    229234                    ## Everything else goes in meta_data 
    230235                    else: 
    231                         new_key = self._create_unique_key(self.current_dataset.meta_data, key) 
    232                         self.current_dataset.meta_data[new_key] = data_point 
     236                        new_key = self._create_unique_key(self.current_datainfo.meta_data, key) 
     237                        self.current_datainfo.meta_data[new_key] = data_point 
    233238 
    234239            else: 
     
    236241                self.errors.add("ShouldNeverHappenException") 
    237242 
    238     def add_intermediate(self, parent): 
     243    def add_intermediate(self): 
    239244        """ 
    240245        This method stores any intermediate objects within the final data set after fully reading the set. 
    241246 
    242247        :param parent: The NXclass name for the h5py Group object that just finished being processed 
    243         :return: 
    244         """ 
    245  
    246         if parent == u'SASprocess': 
    247             self.current_dataset.process.append(self.process) 
     248        """ 
     249 
     250        if self.parent_class == u'SASprocess': 
     251            self.current_datainfo.process.append(self.process) 
    248252            self.process = Process() 
    249         elif parent == u'SASdetector': 
    250             self.current_dataset.detector.append(self.detector) 
     253        elif self.parent_class == u'SASdetector': 
     254            self.current_datainfo.detector.append(self.detector) 
    251255            self.detector = Detector() 
    252         elif parent == u'SAStransmission_spectrum': 
    253             self.current_dataset.trans_spectrum.append(self.trans_spectrum) 
     256        elif self.parent_class == u'SAStransmission_spectrum': 
     257            self.current_datainfo.trans_spectrum.append(self.trans_spectrum) 
    254258            self.trans_spectrum = TransmissionSpectrum() 
    255         elif parent == u'SASsource': 
    256             self.current_dataset.source = self.source 
    257             self.source = Source() 
    258         elif parent == u'SASsample': 
    259             self.current_dataset.sample = self.sample 
    260             self.sample = Sample() 
    261         elif parent == u'SAScollimation': 
    262             self.current_dataset.collimation.append(self.collimation) 
     259        elif self.parent_class == u'SAScollimation': 
     260            self.current_datainfo.collimation.append(self.collimation) 
    263261            self.collimation = Collimation() 
    264         elif parent == u'SASaperture': 
     262        elif self.parent_class == u'SASaperture': 
    265263            self.collimation.aperture.append(self.aperture) 
    266264            self.aperture = Aperture() 
     265        elif self.parent_class == u'SASdata': 
     266            if type(self.current_dataset) is plottable_2D: 
     267                self.data2d.append(self.current_dataset) 
     268            elif type(self.current_dataset) is plottable_1D: 
     269                self.data1d.append(self.current_dataset) 
    267270 
    268271    def final_data_cleanup(self): 
    269272        """ 
    270         Does some final cleanup and formatting on self.current_dataset 
    271         """ 
    272  
    273         ## Type cast data arrays to float64 and find min/max as appropriate 
    274         if type(self.current_dataset) is Data2D: 
    275             self.current_dataset.data = np.delete(self.current_dataset.data, [0]) 
    276             self.current_dataset.data = self.current_dataset.data.astype(np.float64) 
    277             self.current_dataset.err_data = np.delete(self.current_dataset.err_data, [0]) 
    278             self.current_dataset.err_data = self.current_dataset.err_data.astype(np.float64) 
    279             self.current_dataset.mask = np.delete(self.current_dataset.mask, [0]) 
    280             if self.current_dataset.qx_data is not None: 
    281                 self.current_dataset.qx_data = np.delete(self.current_dataset.qx_data, [0]) 
    282                 self.current_dataset.xmin = np.min(self.current_dataset.qx_data) 
    283                 self.current_dataset.xmax = np.max(self.current_dataset.qx_data) 
    284                 self.current_dataset.qx_data = self.current_dataset.qx_data.astype(np.float64) 
    285             if self.current_dataset.dqx_data is not None: 
    286                 self.current_dataset.dqx_data = np.delete(self.current_dataset.dqx_data, [0]) 
    287                 self.current_dataset.dqx_data = self.current_dataset.dqx_data.astype(np.float64) 
    288             if self.current_dataset.qy_data is not None: 
    289                 self.current_dataset.qy_data = np.delete(self.current_dataset.qy_data, [0]) 
    290                 self.current_dataset.ymin = np.min(self.current_dataset.qy_data) 
    291                 self.current_dataset.ymax = np.max(self.current_dataset.qy_data) 
    292                 self.current_dataset.qy_data = self.current_dataset.qy_data.astype(np.float64) 
    293             if self.current_dataset.dqy_data is not None: 
    294                 self.current_dataset.dqy_data = np.delete(self.current_dataset.dqy_data, [0]) 
    295                 self.current_dataset.dqy_data = self.current_dataset.dqy_data.astype(np.float64) 
    296             if self.current_dataset.q_data is not None: 
    297                 self.current_dataset.q_data = np.delete(self.current_dataset.q_data, [0]) 
    298                 self.current_dataset.q_data = self.current_dataset.q_data.astype(np.float64) 
    299             zeros = np.ones(self.current_dataset.data.size, dtype=bool) 
    300             try: 
    301                 for i in range (0, self.current_dataset.mask.size - 1): 
    302                     zeros[i] = self.current_dataset.mask[i] 
    303             except: 
    304                 self.errors.add(sys.exc_value) 
    305             self.current_dataset.mask = zeros 
    306  
    307             ## Calculate the actual Q matrix 
    308             try: 
    309                 if self.current_dataset.q_data.size <= 1: 
    310                     self.current_dataset.q_data = np.sqrt(self.current_dataset.qx_data * self.current_dataset.qx_data + 
    311                             self.current_dataset.qy_data * self.current_dataset.qy_data) 
    312             except: 
    313                 self.current_dataset.q_data = None 
    314  
    315         elif type(self.current_dataset) is Data1D: 
    316             if self.current_dataset.x is not None: 
    317                 self.current_dataset.x = np.delete(self.current_dataset.x, [0]) 
    318                 self.current_dataset.x = self.current_dataset.x.astype(np.float64) 
    319                 self.current_dataset.xmin = np.min(self.current_dataset.x) 
    320                 self.current_dataset.xmax = np.max(self.current_dataset.x) 
    321             if self.current_dataset.y is not None: 
    322                 self.current_dataset.y = np.delete(self.current_dataset.y, [0]) 
    323                 self.current_dataset.y = self.current_dataset.y.astype(np.float64) 
    324                 self.current_dataset.ymin = np.min(self.current_dataset.y) 
    325                 self.current_dataset.ymax = np.max(self.current_dataset.y) 
    326             if self.current_dataset.dx is not None: 
    327                 self.current_dataset.dx = np.delete(self.current_dataset.dx, [0]) 
    328                 self.current_dataset.dx = self.current_dataset.dx.astype(np.float64) 
    329             if self.current_dataset.dxl is not None: 
    330                 self.current_dataset.dxl = np.delete(self.current_dataset.dxl, [0]) 
    331                 self.current_dataset.dxl = self.current_dataset.dxl.astype(np.float64) 
    332             if self.current_dataset.dxw is not None: 
    333                 self.current_dataset.dxw = np.delete(self.current_dataset.dxw, [0]) 
    334                 self.current_dataset.dxw = self.current_dataset.dxw.astype(np.float64) 
    335             if self.current_dataset.dy is not None: 
    336                 self.current_dataset.dy = np.delete(self.current_dataset.dy, [0]) 
    337                 self.current_dataset.dy =self.current_dataset.dy.astype(np.float64) 
    338  
    339         if len(self.current_dataset.trans_spectrum) is not 0: 
     273        Does some final cleanup and formatting on self.current_datainfo and all data1D and data2D objects and then 
     274        combines the data and info into Data1D and Data2D objects 
     275        """ 
     276 
     277        ## Type cast data arrays to float64 
     278        if len(self.current_datainfo.trans_spectrum) > 0: 
    340279            spectrum_list = [] 
    341             for spectrum in self.current_dataset.trans_spectrum: 
     280            for spectrum in self.current_datainfo.trans_spectrum: 
    342281                spectrum.transmission = np.delete(spectrum.transmission, [0]) 
    343282                spectrum.transmission = spectrum.transmission.astype(np.float64) 
     
    346285                spectrum.wavelength = np.delete(spectrum.wavelength, [0]) 
    347286                spectrum.wavelength = spectrum.wavelength.astype(np.float64) 
    348                 spectrum_list.append(spectrum) 
    349             self.current_dataset.trans_spectrum = spectrum_list 
    350  
    351         else: 
    352             self.errors.add("ShouldNeverHappenException") 
    353  
    354         ## Append intermediate objects to data 
    355         self.current_dataset.sample = self.sample 
    356         self.current_dataset.source = self.source 
    357         self.current_dataset.collimation.append(self.collimation) 
     287                if len(spectrum.transmission) > 0: 
     288                    spectrum_list.append(spectrum) 
     289            self.current_datainfo.trans_spectrum = spectrum_list 
    358290 
    359291        ## Append errors to dataset and reset class errors 
    360         self.current_dataset.errors = self.errors 
     292        self.current_datainfo.errors = self.errors 
    361293        self.errors.clear() 
     294 
     295        ## Combine all plottables with datainfo and append each to output 
     296        ## Type cast data arrays to float64 and find min/max as appropriate 
     297        for dataset in self.data2d: 
     298            dataset.data = np.delete(dataset.data, [0]) 
     299            dataset.data = dataset.data.astype(np.float64) 
     300            dataset.err_data = np.delete(dataset.err_data, [0]) 
     301            dataset.err_data = dataset.err_data.astype(np.float64) 
     302            dataset.mask = np.delete(dataset.mask, [0]) 
     303            if dataset.qx_data is not None: 
     304                dataset.qx_data = np.delete(dataset.qx_data, [0]) 
     305                dataset.xmin = np.min(dataset.qx_data) 
     306                dataset.xmax = np.max(dataset.qx_data) 
     307                dataset.qx_data = dataset.qx_data.astype(np.float64) 
     308            if dataset.dqx_data is not None: 
     309                dataset.dqx_data = np.delete(dataset.dqx_data, [0]) 
     310                dataset.dqx_data = dataset.dqx_data.astype(np.float64) 
     311            if dataset.qy_data is not None: 
     312                dataset.qy_data = np.delete(dataset.qy_data, [0]) 
     313                dataset.ymin = np.min(dataset.qy_data) 
     314                dataset.ymax = np.max(dataset.qy_data) 
     315                dataset.qy_data = dataset.qy_data.astype(np.float64) 
     316            if dataset.dqy_data is not None: 
     317                dataset.dqy_data = np.delete(dataset.dqy_data, [0]) 
     318                dataset.dqy_data = dataset.dqy_data.astype(np.float64) 
     319            if dataset.q_data is not None: 
     320                dataset.q_data = np.delete(dataset.q_data, [0]) 
     321                dataset.q_data = dataset.q_data.astype(np.float64) 
     322            zeros = np.ones(dataset.data.size, dtype=bool) 
     323            try: 
     324                for i in range (0, dataset.mask.size - 1): 
     325                    zeros[i] = dataset.mask[i] 
     326            except: 
     327                self.errors.add(sys.exc_value) 
     328            dataset.mask = zeros 
     329            ## Calculate the actual Q matrix 
     330            try: 
     331                if dataset.q_data.size <= 1: 
     332                    dataset.q_data = np.sqrt(dataset.qx_data * dataset.qx_data + dataset.qy_data * dataset.qy_data) 
     333            except: 
     334                dataset.q_data = None 
     335            final_dataset = combine_data_info_with_plottable(dataset, self.current_datainfo) 
     336            self.output.append(final_dataset) 
     337 
     338        for dataset in self.data1d: 
     339            if dataset.x is not None: 
     340                dataset.x = np.delete(dataset.x, [0]) 
     341                dataset.x = dataset.x.astype(np.float64) 
     342                dataset.xmin = np.min(dataset.x) 
     343                dataset.xmax = np.max(dataset.x) 
     344            if dataset.y is not None: 
     345                dataset.y = np.delete(dataset.y, [0]) 
     346                dataset.y = dataset.y.astype(np.float64) 
     347                dataset.ymin = np.min(dataset.y) 
     348                dataset.ymax = np.max(dataset.y) 
     349            if dataset.dx is not None: 
     350                dataset.dx = np.delete(dataset.dx, [0]) 
     351                dataset.dx = dataset.dx.astype(np.float64) 
     352            if dataset.dxl is not None: 
     353                dataset.dxl = np.delete(dataset.dxl, [0]) 
     354                dataset.dxl = dataset.dxl.astype(np.float64) 
     355            if dataset.dxw is not None: 
     356                dataset.dxw = np.delete(dataset.dxw, [0]) 
     357                dataset.dxw = dataset.dxw.astype(np.float64) 
     358            if dataset.dy is not None: 
     359                dataset.dy = np.delete(dataset.dy, [0]) 
     360                dataset.dy = dataset.dy.astype(np.float64) 
     361            final_dataset = combine_data_info_with_plottable(dataset, self.current_datainfo) 
     362            self.output.append(final_dataset) 
    362363 
    363364    def add_data_set(self, key=""): 
     
    367368 
    368369        :param key: NeXus group name for current tree level 
    369         :return: None 
    370         """ 
    371         if self.current_dataset is not None: 
     370        """ 
     371 
     372        if self.current_datainfo and self.current_dataset: 
    372373            self.final_data_cleanup() 
    373             self.output.append(self.current_dataset) 
    374         self._initialize_new_data_set(key) 
    375  
    376     def _initialize_new_data_set(self, key=""): 
     374        self.data1d = [] 
     375        self.data2d = [] 
     376        self.current_datainfo = DataInfo() 
     377 
     378    def _initialize_new_data_set(self, parent_list = None): 
    377379        """ 
    378380        A private class method to generate a new 1D or 2D data object based on the type of data within the set. 
    379381        Outside methods should call add_data_set() to be sure any existing data is stored properly. 
    380382 
    381         :param key: NeXus group name for current tree level 
    382         :return: None 
    383         """ 
    384         entry = self._find_intermediate(key, "sasentry*") 
    385         data = entry.get("sasdata") 
    386         if data.get("Qx") is not None: 
    387             self.current_dataset = Data2D() 
     383        :param parent_list: List of names of parent elements 
     384        """ 
     385 
     386        if parent_list is None: 
     387            parent_list = [] 
     388        if self._find_intermediate(parent_list, "Qx"): 
     389            self.current_dataset = plottable_2D() 
    388390        else: 
    389391            x = np.array(0) 
    390392            y = np.array(0) 
    391             self.current_dataset = Data1D(x, y) 
    392         self.current_dataset.filename = self.raw_data.filename 
    393  
    394     def _find_intermediate(self, key="", basename=""): 
     393            self.current_dataset = plottable_1D(x, y) 
     394        self.current_datainfo.filename = self.raw_data.filename 
     395 
     396    def _find_intermediate(self, parent_list, basename=""): 
    395397        """ 
    396398        A private class used to find an entry by either using a direct key or knowing the approximate basename. 
    397399 
    398         :param key: Exact keyname of an entry 
    399         :param basename: Approximate name of an entry 
     400        :param parent_list: List of parents to the current level in the HDF5 file 
     401        :param basename: Approximate name of an entry to search for 
    400402        :return: 
    401403        """ 
    402         entry = [] 
    403         if key is not "": 
    404             entry = self.raw_data.get(key) 
    405         else: 
    406             key_prog = re.compile(basename) 
    407             for key in self.raw_data.keys(): 
    408                 if (key_prog.match(key)): 
    409                     entry = self.raw_data.get(key) 
    410                     break 
     404 
     405        entry = False 
     406        key_prog = re.compile(basename) 
     407        top = self.raw_data 
     408        for parent in parent_list: 
     409            top = top.get(parent) 
     410        for key in top.keys(): 
     411            if (key_prog.match(key)): 
     412                entry = True 
     413                break 
    411414        return entry 
    412415 
     
    419422        :param name: The index of the item to be added to dictionary 
    420423        :param numb: The number to be appended to the name, starts at 0 
     424        :return: The new name for the dictionary entry 
    421425        """ 
    422426        if dictionary.get(name) is not None: 
     
    432436 
    433437        :param value: attribute dictionary for a particular value set 
    434         :return: 
     438        :return: unit for the value passed to the method 
    435439        """ 
    436440        unit = value.attrs.get(u'units') 
    437441        if unit == None: 
    438442            unit = value.attrs.get(u'unit') 
    439  
    440443        ## Convert the unit formats 
    441444        if unit == "1/A": 
     
    443446        elif unit == "1/cm": 
    444447            unit = "cm^{-1}" 
    445  
    446448        return unit 
  • test/sasdataloader/test/utest_cansas.py

    rb699768 r132db16  
    22    Unit tests for the new recursive cansas reader 
    33""" 
    4 import logging 
    5 import warnings 
    6 warnings.simplefilter("ignore") 
    7  
    84import sas.sascalc.dataloader.readers.cansas_reader as cansas 
    95from sas.sascalc.dataloader.loader import Loader 
    10 from sas.sascalc.dataloader.data_info import Data1D 
     6from sas.sascalc.dataloader.data_info import Data1D, Data2D 
    117from sas.sascalc.dataloader.readers.xml_reader import XMLreader 
    128from sas.sascalc.dataloader.readers.cansas_reader import Reader 
     
    2016import unittest 
    2117import numpy 
     18import logging 
     19import warnings 
    2220 
    2321from lxml import etree 
    2422from xml.dom import minidom 
    25   
     23 
     24warnings.simplefilter("ignore") 
     25 
    2626CANSAS_FORMAT = CansasConstants.CANSAS_FORMAT 
    2727CANSAS_NS = CansasConstants.CANSAS_NS 
    28      
    29 class cansas_reader(unittest.TestCase): 
    30      
     28 
     29class cansas_reader_xml(unittest.TestCase): 
     30 
    3131    def setUp(self): 
    3232        self.loader = Loader() 
     
    4343        self.schema_1_0 = "cansas1d_v1_0.xsd" 
    4444        self.schema_1_1 = "cansas1d_v1_1.xsd" 
    45          
    46      
     45 
     46 
    4747    def get_number_of_entries(self, dictionary, name, i): 
    4848        if dictionary.get(name) is not None: 
     
    5252            name = self.get_number_of_entries(dictionary, name, i) 
    5353        return name 
    54      
    55      
     54 
     55 
    5656    def test_invalid_xml(self): 
    5757        """ 
     
    6060        invalid = StringIO.StringIO('<a><c></b></a>') 
    6161        reader = XMLreader(invalid) 
    62         
     62 
    6363 
    6464    def test_xml_validate(self): 
     
    7878        self.assertTrue(xmlschema.validate(valid)) 
    7979        self.assertFalse(xmlschema.validate(invalid)) 
    80          
    81          
     80 
     81 
    8282    def test_real_xml(self): 
    8383        reader = XMLreader(self.xml_valid, self.schema_1_0) 
     
    8787        else: 
    8888            self.assertFalse(valid) 
    89              
    90              
     89 
     90 
    9191    def _check_data(self, data): 
    9292        self.assertTrue(data.title == "TK49 c10_SANS") 
     
    101101        self.assertTrue(data.process[0].name == "Mantid generated CanSAS1D XML") 
    102102        self.assertTrue(data.meta_data["xmlpreprocess"] != None) 
    103          
    104      
     103 
     104 
    105105    def _check_data_1_1(self, data): 
    106106        spectrum = data.trans_spectrum[0] 
    107107        self.assertTrue(len(spectrum.wavelength) == 138) 
    108          
    109      
     108 
     109 
    110110    def test_cansas_xml(self): 
    111111        filename = "isis_1_1_write_test.xml" 
     
    132132            written_data = return_data[0] 
    133133            self._check_data(written_data) 
    134      
    135      
     134 
     135 
    136136    def test_double_trans_spectra(self): 
    137137        xmlreader = XMLreader(self.isis_1_1_doubletrans, self.schema_1_1) 
     
    141141        for item in data: 
    142142            self._check_data(item) 
    143      
    144                      
     143 
     144 
    145145    def test_entry_name_recurse(self): 
    146146        test_values = [1,2,3,4,5,6] 
     
    151151            d[new_key] = value 
    152152        self.assertTrue(len(d) == 6) 
    153          
    154      
     153 
     154 
    155155    def test_load_cansas_file(self): 
    156156        valid = [] 
     
    169169        reader7 = XMLreader(self.isis_1_1, self.schema_1_0) 
    170170        self.assertFalse(reader7.validate_xml()) 
    171          
    172         
     171 
     172 
    173173    def test_old_cansas_files(self): 
    174174        reader1 = XMLreader(self.cansas1d, self.schema_1_0) 
     
    182182        reader4 = XMLreader(self.cansas1d_slit, self.schema_1_0) 
    183183        self.assertTrue(reader4.validate_xml()) 
    184          
    185      
     184 
     185 
    186186    def test_save_cansas_v1_0(self): 
    187187        filename = "isis_1_0_write_test.xml" 
     
    204204            self.assertTrue(valid) 
    205205            self._check_data(written_data) 
    206          
    207          
     206 
     207 
    208208    def test_processing_instructions(self): 
    209209        reader = XMLreader(self.isis_1_1, self.schema_1_1) 
     
    214214            self.assertTrue(dic == {'xml-stylesheet': \ 
    215215                                    'type="text/xsl" href="cansas1d.xsl" '}) 
    216              
     216 
    217217            xml = "<test><a><b><c></c></b></a></test>" 
    218218            xmldoc = minidom.parseString(xml) 
    219              
     219 
    220220            ## take the processing instructions and put them back in 
    221221            xmldoc = self.set_processing_instructions(xmldoc, dic) 
    222222            xml_output = xmldoc.toprettyxml() 
    223              
    224      
     223 
     224 
    225225    def set_processing_instructions(self, minidom_object, dic): 
    226226        xmlroot = minidom_object.firstChild 
     
    229229            minidom_object.insertBefore(pi, xmlroot) 
    230230        return minidom_object 
    231      
    232      
     231 
     232 
    233233    def get_processing_instructions(self, xml_reader_object): 
    234234        dict = {} 
     
    247247            pi = pi.getprevious() 
    248248        return dict 
    249          
     249 
     250 
     251class cansas_reader_hdf5(unittest.TestCase): 
     252 
     253    def setUp(self): 
     254        self.loader = Loader() 
     255        self.datafile_basic = "simpleexamplefile.h5" 
     256        self.datafile_multiplesasentry = "cansas_1Dand2D_samedatafile.h5" 
     257        self.datafile_multiplesasdata = "cansas_1Dand2D_samesasentry.h5" 
     258        self.datafile_multiplesasdata_multiplesasentry = "cansas_1Dand2D_multiplesasentry_multiplesasdata.h5" 
     259 
     260    def test_real_data(self): 
     261        self.data = self.loader.load(self.datafile_basic) 
     262        self._check_example_data(self.data[0]) 
     263 
     264    def test_multiple_sasentries(self): 
     265        self.data = self.loader.load(self.datafile_multiplesasentry) 
     266        self.assertTrue(len(self.data) == 2) 
     267        self._check_multiple_data(self.data[0]) 
     268        self._check_multiple_data(self.data[1]) 
     269        self._check_1d_data(self.data[0]) 
     270 
     271    def _check_multiple_data(self, data): 
     272        self.assertTrue(data.title == "MH4_5deg_16T_SLOW") 
     273        self.assertTrue(data.run[0] == '33837') 
     274        self.assertTrue(len(data.run) == 1) 
     275        self.assertTrue(data.instrument == "SANS2D") 
     276        self.assertTrue(data.source.radiation == "Spallation Neutron Source") 
     277        self.assertTrue(len(data.detector) == 1) 
     278        self.assertTrue(data.detector[0].name == "rear-detector") 
     279        self.assertTrue(data.detector[0].distance == 4.385281) 
     280        self.assertTrue(data.detector[0].distance_unit == 'm') 
     281        self.assertTrue(len(data.trans_spectrum) == 1) 
     282 
     283    def _check_1d_data(self, data): 
     284        self.assertTrue(isinstance(data, Data1D)) 
     285        self.assertTrue(len(data.x) == 66) 
     286        self.assertTrue(len(data.x) == len(data.y)) 
     287        self.assertTrue(data.dy[10] == 0.20721350111248701) 
     288        self.assertTrue(data.y[10] == 24.193889608153476) 
     289        self.assertTrue(data.x[10] == 0.008981127988654792) 
     290 
     291    def _check_2d_data(self, data): 
     292        self.assertTrue(isinstance(data, Data2D)) 
     293        self.assertTrue(len(data.x) == 66) 
     294        self.assertTrue(len(data.x) == len(data.y)) 
     295        self.assertTrue(data.dy[10] == 0.20721350111248701) 
     296        self.assertTrue(data.y[10] == 24.193889608153476) 
     297        self.assertTrue(data.x[10] == 0.008981127988654792) 
     298 
     299    def _check_example_data(self, data): 
     300        self.assertTrue(data.title == "") 
     301        self.assertTrue(data.x.size == 100) 
     302        self.assertTrue(data._xunit == "A^{-1}") 
     303        self.assertTrue(data._yunit == "cm^{-1}") 
     304        self.assertTrue(data.y.size == 100) 
     305        self.assertAlmostEqual(data.y[9], 0.952749011516985) 
     306        self.assertAlmostEqual(data.x[9], 0.3834415188257777) 
     307        self.assertAlmostEqual(len(data.meta_data), 0) 
     308 
    250309 
    251310if __name__ == '__main__': 
Note: See TracChangeset for help on using the changeset viewer.